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

477 lines
16 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 false. If there
* are no destination address fields in given frame, this function returns true and does not modify
* @p p_num_bytes. If there is destination address in given frame, this function returns true and
* inserts offset of addressing fields end to @p p_num_bytes.
*
* @param[in] p_data Pointer to a buffer containing PHR and PSDU of the incoming frame.
* @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 uint8_t * p_data,
uint8_t * p_num_bytes,
uint8_t frame_type)
{
nrf_802154_rx_error_t result;
switch (p_data[DEST_ADDR_TYPE_OFFSET] & DEST_ADDR_TYPE_MASK)
{
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 (p_data[SRC_ADDR_TYPE_OFFSET] & SRC_ADDR_TYPE_MASK)
{
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 false. If there
* are no destination address fields in given frame, this function returns true and does not modify
* @p p_num_bytes. If there is destination address in given frame, this function returns true and
* inserts offset of addressing fields end to @p p_num_bytes.
*
* @param[in] p_data Pointer to a buffer containing PHR and PSDU of the incoming frame.
* @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_2015(const uint8_t * p_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_addr_end_offset_get(p_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.
*
* If given frame contains errors that prevent getting offset, this function returns false. If there
* are no destination address fields in given frame, this function returns true and does not modify
* @p p_num_bytes. If there is destination address in given frame, this function returns true and
* inserts offset of addressing fields end to @p p_num_bytes.
*
* @param[in] p_data Pointer to a buffer containing PHR and PSDU of the incoming frame.
* @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(const uint8_t * p_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_data, p_num_bytes, frame_type);
break;
case FRAME_VERSION_2:
result = dst_addressing_end_offset_get_2015(p_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_data Pointer to a buffer containing PHR and PSDU of the incoming frame.
*
* @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 uint8_t * p_data, uint8_t frame_type)
{
bool result;
nrf_802154_frame_parser_mhr_data_t mhr_data;
result = nrf_802154_frame_parser_mhr_parse(p_data, &mhr_data);
if (!result)
{
return NRF_802154_RX_ERROR_INVALID_FRAME;
}
if (mhr_data.p_dst_panid != NULL)
{
if (!dst_pan_id_check(mhr_data.p_dst_panid, frame_type))
{
return NRF_802154_RX_ERROR_INVALID_DEST_ADDR;
}
}
switch (mhr_data.dst_addr_size)
{
case SHORT_ADDRESS_SIZE:
return dst_short_addr_check(mhr_data.p_dst_addr) ? NRF_802154_RX_ERROR_NONE :
NRF_802154_RX_ERROR_INVALID_DEST_ADDR;
case EXTENDED_ADDRESS_SIZE:
return dst_extended_addr_check(mhr_data.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(const uint8_t * p_data, uint8_t * p_num_bytes)
{
nrf_802154_rx_error_t result = NRF_802154_RX_ERROR_INVALID_FRAME;
uint8_t frame_type = p_data[FRAME_TYPE_OFFSET] & FRAME_TYPE_MASK;
uint8_t frame_version = p_data[FRAME_VERSION_OFFSET] & FRAME_VERSION_MASK;
switch (*p_num_bytes)
{
case FCF_CHECK_OFFSET:
if (p_data[0] < IMM_ACK_LENGTH || p_data[0] > MAX_PACKET_SIZE)
{
result = NRF_802154_RX_ERROR_INVALID_LENGTH;
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_data, p_num_bytes, frame_type, frame_version);
break;
default:
result = dst_addr_check(p_data, frame_type);
break;
}
return result;
}