hal_nordic/drivers/nrf_802154/driver/src/mac_features/nrf_802154_filter.c

502 lines
17 KiB
C

/*
* Copyright (c) 2018 - 2021, Nordic Semiconductor ASA
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
/**
* @file
* This file implements incoming frame filtering according to 3 and 4 levels of filtering.
*
* Filtering details are specified in 802.15.4-2015: 6.7.2.
* 1st and 2nd filtering level is performed by FSM module depending on promiscuous mode, when FCS is
* received.
*/
#include "nrf_802154_filter.h"
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "nrf_802154_const.h"
#include "nrf_802154_frame_parser.h"
#include "nrf_802154_pib.h"
#define FCF_CHECK_OFFSET (PHR_SIZE + FCF_SIZE)
#define PANID_CHECK_OFFSET (DEST_ADDR_OFFSET)
#define SHORT_ADDR_CHECK_OFFSET (DEST_ADDR_OFFSET + SHORT_ADDRESS_SIZE)
#define EXTENDED_ADDR_CHECK_OFFSET (DEST_ADDR_OFFSET + EXTENDED_ADDRESS_SIZE)
/**
* @brief Check if given frame version is allowed for given frame type.
*
* @param[in] frame_type Type of incoming frame.
* @param[in] frame_version Version of incoming frame.
*
* @retval true Given frame version is allowed for given frame type.
* @retval false Given frame version is not allowed for given frame type.
*/
static bool frame_type_and_version_filter(uint8_t frame_type, uint8_t frame_version)
{
bool result;
switch (frame_type)
{
case FRAME_TYPE_BEACON:
case FRAME_TYPE_DATA:
case FRAME_TYPE_ACK:
case FRAME_TYPE_COMMAND:
result = (frame_version != FRAME_VERSION_3);
break;
case FRAME_TYPE_MULTIPURPOSE:
result = (frame_version == FRAME_VERSION_0);
break;
case FRAME_TYPE_FRAGMENT:
case FRAME_TYPE_EXTENDED:
result = true;
break;
default:
result = false;
}
return result;
}
/**
* @brief Check if given frame type may include destination address fields.
*
* @note Actual presence of destination address fields in the frame is indicated by FCF.
*
* @param[in] frame_type Type of incoming frame.
*
* @retval true Given frame type may include addressing fields.
* @retval false Given frame type may not include addressing fields.
*/
static bool dst_addressing_may_be_present(uint8_t frame_type)
{
bool result;
switch (frame_type)
{
case FRAME_TYPE_BEACON:
case FRAME_TYPE_DATA:
case FRAME_TYPE_ACK:
case FRAME_TYPE_COMMAND:
case FRAME_TYPE_MULTIPURPOSE:
result = true;
break;
case FRAME_TYPE_FRAGMENT:
case FRAME_TYPE_EXTENDED:
result = false;
break;
default:
result = false;
}
return result;
}
/**
* @brief Get offset of end of addressing fields for given frame assuming its version is 2006.
*
* If given frame contains errors that prevent getting offset, this function returns
* NRF_802154_RX_ERROR_INVALID_FRAME. If there are no destination address fields in given frame,
* this function verifies if the driver is configured as a PAN coordinator or if the frame is a
* beacon.
* If both of those conditions are not met, the function returns
* NRF_802154_RX_ERROR_INVALID_DEST_ADDR.
* Otherwise, this function returns NRF_802154_RX_ERROR_NONE and inserts offset of addressing
* fields end to @p p_num_bytes.
*
* @param[in] p_frame_data Pointer to the frame parser data.
* @param[out] p_num_bytes Offset of addressing fields end.
* @param[in] frame_type Type of incoming frame.
*
* @retval NRF_802154_RX_ERROR_NONE No errors in given frame were detected - it may be
* further processed.
* @retval NRF_802154_RX_ERROR_INVALID_DEST_ADDR The frame is valid but addressed to another node.
* @retval NRF_802154_RX_ERROR_INVALID_FRAME Detected an error in given frame - it should be
* discarded.
*/
static nrf_802154_rx_error_t dst_addressing_end_offset_get_2006(
const nrf_802154_frame_parser_data_t * p_frame_data,
uint8_t * p_num_bytes,
uint8_t frame_type)
{
nrf_802154_rx_error_t result;
switch (nrf_802154_frame_parser_dst_addr_type_get(p_frame_data))
{
case DEST_ADDR_TYPE_SHORT:
*p_num_bytes = SHORT_ADDR_CHECK_OFFSET;
result = NRF_802154_RX_ERROR_NONE;
break;
case DEST_ADDR_TYPE_EXTENDED:
*p_num_bytes = EXTENDED_ADDR_CHECK_OFFSET;
result = NRF_802154_RX_ERROR_NONE;
break;
case DEST_ADDR_TYPE_NONE:
if (nrf_802154_pib_pan_coord_get() || (frame_type == FRAME_TYPE_BEACON))
{
switch (nrf_802154_frame_parser_src_addr_type_get(p_frame_data))
{
case SRC_ADDR_TYPE_SHORT:
case SRC_ADDR_TYPE_EXTENDED:
*p_num_bytes = PANID_CHECK_OFFSET;
result = NRF_802154_RX_ERROR_NONE;
break;
default:
result = NRF_802154_RX_ERROR_INVALID_FRAME;
}
}
else
{
result = NRF_802154_RX_ERROR_INVALID_DEST_ADDR;
}
break;
default:
result = NRF_802154_RX_ERROR_INVALID_FRAME;
}
return result;
}
/**
* @brief Get offset of end of addressing fields for given frame assuming its version is 2015.
*
* If given frame contains errors that prevent getting offset, this function returns
* NRF_802154_RX_ERROR_INVALID_FRAME.
* Otherwise, this function returns NRF_802154_RX_ERROR_NONE and inserts offset of addressing
* fields end to @p p_num_bytes.
*
* @param[in] p_frame_data Pointer to the frame parser data.
* @param[out] p_num_bytes Offset of addressing fields end.
* @param[in] frame_type Type of incoming frame.
*
* @retval NRF_802154_RX_ERROR_NONE No errors in given frame were detected - it may be
* further processed.
* @retval NRF_802154_RX_ERROR_INVALID_FRAME Detected an error in given frame - it should be
* discarded.
*/
static nrf_802154_rx_error_t dst_addressing_end_offset_get_2015(
const nrf_802154_frame_parser_data_t * p_frame_data,
uint8_t * p_num_bytes,
uint8_t frame_type)
{
nrf_802154_rx_error_t result;
switch (frame_type)
{
case FRAME_TYPE_BEACON:
case FRAME_TYPE_DATA:
case FRAME_TYPE_ACK:
case FRAME_TYPE_COMMAND:
{
uint8_t end_offset =
nrf_802154_frame_parser_dst_addressing_end_offset_get(p_frame_data);
if (end_offset == NRF_802154_FRAME_PARSER_INVALID_OFFSET)
{
result = NRF_802154_RX_ERROR_INVALID_FRAME;
}
else
{
*p_num_bytes = end_offset;
result = NRF_802154_RX_ERROR_NONE;
}
}
break;
case FRAME_TYPE_MULTIPURPOSE:
// TODO: Implement dst addressing filtering according to 2015 spec
result = NRF_802154_RX_ERROR_INVALID_FRAME;
break;
case FRAME_TYPE_FRAGMENT:
case FRAME_TYPE_EXTENDED:
// No addressing data
result = NRF_802154_RX_ERROR_NONE;
break;
default:
result = NRF_802154_RX_ERROR_INVALID_FRAME;
}
return result;
}
/**
* @brief Get offset of end of addressing fields for given frame.
*
* This function relays its arguments to either @ref dst_addressing_end_offset_get_2006
* or @ref dst_addressing_end_offset_get_2015 depending on the frame version.
*
* @param[in] p_frame_data Pointer to the frame parser data.
* @param[out] p_num_bytes Offset of addressing fields end.
* @param[in] frame_type Type of incoming frame.
* @param[in] frame_version Version of the incoming frame.
*
* @retval NRF_802154_RX_ERROR_NONE No errors in given frame were detected - it may be
* further processed.
* @retval NRF_802154_RX_ERROR_INVALID_DEST_ADDR The frame is valid but addressed to another node.
* @retval NRF_802154_RX_ERROR_INVALID_FRAME Detected an error in given frame - it should be
* discarded.
*/
static nrf_802154_rx_error_t dst_addressing_end_offset_get(
const nrf_802154_frame_parser_data_t * p_frame_data,
uint8_t * p_num_bytes,
uint8_t frame_type,
uint8_t frame_version)
{
nrf_802154_rx_error_t result;
switch (frame_version)
{
case FRAME_VERSION_0:
case FRAME_VERSION_1:
result = dst_addressing_end_offset_get_2006(p_frame_data, p_num_bytes, frame_type);
break;
case FRAME_VERSION_2:
result = dst_addressing_end_offset_get_2015(p_frame_data, p_num_bytes, frame_type);
break;
default:
result = NRF_802154_RX_ERROR_INVALID_FRAME;
}
return result;
}
/**
* Verify if destination PAN Id of incoming frame allows processing by this node.
*
* @param[in] p_panid Pointer of PAN ID of incoming frame.
* @param[in] frame_type Type of the frame being filtered.
*
* @retval true PAN Id of incoming frame allows further processing of the frame.
* @retval false PAN Id of incoming frame does not allow further processing.
*/
static bool dst_pan_id_check(const uint8_t * p_panid, uint8_t frame_type)
{
bool result;
if ((0 == memcmp(p_panid, nrf_802154_pib_pan_id_get(), PAN_ID_SIZE)) ||
(0 == memcmp(p_panid, BROADCAST_ADDRESS, PAN_ID_SIZE)))
{
result = true;
}
else if ((FRAME_TYPE_BEACON == frame_type) &&
(0 == memcmp(nrf_802154_pib_pan_id_get(), BROADCAST_ADDRESS, PAN_ID_SIZE)))
{
result = true;
}
else
{
result = false;
}
return result;
}
/**
* Verify if destination short address of incoming frame allows processing by this node.
*
* @param[in] p_dst_addr Pointer of destination address of incoming frame.
*
* @retval true Destination address of incoming frame allows further processing of the frame.
* @retval false Destination address of incoming frame does not allow further processing.
*/
static bool dst_short_addr_check(const uint8_t * p_dst_addr)
{
bool result;
if ((0 == memcmp(p_dst_addr, nrf_802154_pib_short_address_get(), SHORT_ADDRESS_SIZE)) ||
(0 == memcmp(p_dst_addr, BROADCAST_ADDRESS, SHORT_ADDRESS_SIZE)))
{
result = true;
}
else
{
result = false;
}
return result;
}
/**
* Verify if destination extended address of incoming frame allows processing by this node.
*
* @param[in] p_dst_addr Pointer of destination address of incoming frame.
*
* @retval true Destination address of incoming frame allows further processing of the frame.
* @retval false Destination address of incoming frame does not allow further processing.
*/
static bool dst_extended_addr_check(const uint8_t * p_dst_addr)
{
bool result;
if (0 == memcmp(p_dst_addr, nrf_802154_pib_extended_address_get(), EXTENDED_ADDRESS_SIZE))
{
result = true;
}
else
{
result = false;
}
return result;
}
/**
* Verify if destination addressing of incoming frame allows processing by this node.
* This function checks addressing according to IEEE 802.15.4-2015.
*
* @param[in] p_frame_data Pointer to the frame parser data.
*
* @retval NRF_802154_RX_ERROR_NONE Destination address of incoming frame allows further processing of the frame.
* @retval NRF_802154_RX_ERROR_INVALID_FRAME Received frame is invalid.
* @retval NRF_802154_RX_ERROR_INVALID_DEST_ADDR Destination address of incoming frame does not allow further processing.
*/
static nrf_802154_rx_error_t dst_addr_check(const nrf_802154_frame_parser_data_t * p_frame_data)
{
const uint8_t * p_dst_panid = nrf_802154_frame_parser_dst_panid_get(p_frame_data);
const uint8_t * p_dst_addr = nrf_802154_frame_parser_dst_addr_get(p_frame_data);
uint8_t frame_type = nrf_802154_frame_parser_frame_type_get(p_frame_data);
if (p_dst_panid != NULL)
{
if (!dst_pan_id_check(p_dst_panid, frame_type))
{
return NRF_802154_RX_ERROR_INVALID_DEST_ADDR;
}
}
uint8_t dst_addr_size =
p_dst_addr ? nrf_802154_frame_parser_dst_addr_size_get(p_frame_data) : 0U;
switch (dst_addr_size)
{
case SHORT_ADDRESS_SIZE:
return dst_short_addr_check(p_dst_addr) ? NRF_802154_RX_ERROR_NONE :
NRF_802154_RX_ERROR_INVALID_DEST_ADDR;
case EXTENDED_ADDRESS_SIZE:
return dst_extended_addr_check(p_dst_addr) ? NRF_802154_RX_ERROR_NONE :
NRF_802154_RX_ERROR_INVALID_DEST_ADDR;
case 0:
// Allow frames destined to the Pan Coordinator without destination address or
// beacon frames without destination address
return (nrf_802154_pib_pan_coord_get() ||
(frame_type ==
FRAME_TYPE_BEACON)) ? NRF_802154_RX_ERROR_NONE :
NRF_802154_RX_ERROR_INVALID_DEST_ADDR;
default:
assert(false);
}
return NRF_802154_RX_ERROR_INVALID_FRAME;
}
nrf_802154_rx_error_t nrf_802154_filter_frame_part(
nrf_802154_frame_parser_data_t * p_frame_data,
uint8_t * p_num_bytes)
{
nrf_802154_rx_error_t result = NRF_802154_RX_ERROR_INVALID_FRAME;
uint8_t frame_type = nrf_802154_frame_parser_frame_type_get(p_frame_data);
uint8_t frame_version = nrf_802154_frame_parser_frame_version_get(p_frame_data);
uint8_t psdu_length = nrf_802154_frame_parser_frame_length_get(p_frame_data);
switch (*p_num_bytes)
{
case FCF_CHECK_OFFSET:
if ((psdu_length < IMM_ACK_LENGTH) || (psdu_length > MAX_PACKET_SIZE))
{
result = NRF_802154_RX_ERROR_INVALID_LENGTH;
break;
}
if (!nrf_802154_frame_parser_valid_data_extend(p_frame_data,
*p_num_bytes,
PARSE_LEVEL_FCF_OFFSETS))
{
result = NRF_802154_RX_ERROR_INVALID_FRAME;
break;
}
if (!frame_type_and_version_filter(frame_type, frame_version))
{
result = NRF_802154_RX_ERROR_INVALID_FRAME;
break;
}
if (!dst_addressing_may_be_present(frame_type))
{
result = NRF_802154_RX_ERROR_NONE;
break;
}
result = dst_addressing_end_offset_get(p_frame_data,
p_num_bytes,
frame_type,
frame_version);
break;
default:
if (!nrf_802154_frame_parser_valid_data_extend(p_frame_data,
*p_num_bytes,
PARSE_LEVEL_DST_ADDRESSING_END))
{
result = NRF_802154_RX_ERROR_INVALID_FRAME;
break;
}
result = dst_addr_check(p_frame_data);
break;
}
return result;
}