net: l2: ieee802154: mgmt: improve association procedure spec compliance

This change introduces test coverage for association request and
response. Based on this coverage, several closely related issues were
found in the association process which cannot be split into separate
changes without breaking the build.

Most notably did the associate and disassociate net_mgmt commands send
already encoded IEEE 802.15.4 MPDUs to L3 rather than L2. L3 treated
them as payload and made L2 wrap them with another LL header/footer
which produced invalid packets.

The tests also enforce better aligment of the association process with
the IEEE 802.15.4-2020 standard:

* Association requests now ask for ACK as required by the standard. The
  fake driver was enhanced to produce ACK packages when requested.
* macPanId and macCoordinator* MAC PIB attributes are set in the right
  order for improved filtering of association responses.
* The coordinator may decide not to assign a short address to the end
  device even when associated. This is now supported.
* The coordinator may or may not use a short address. Coordinators
  choosing not to support short addresses are now supported.
* Updating the association will now remove any previously added short
  address from the hardware filter.
* The short address may no longer be changed by the user while
  associated to a PAN. Only the coordinator is allowed to allocate short
  addresses.
* Validation of outgoing and incoming association request/response
  packets is improved.

All changes are documented by pointers into the spec.

Signed-off-by: Florian Grandel <fgrandel@code-for-humans.de>
This commit is contained in:
Florian Grandel 2023-05-19 14:27:09 +02:00 committed by Carles Cufí
parent 3d54e975a7
commit 27bfe68204
9 changed files with 544 additions and 116 deletions

View file

@ -50,6 +50,7 @@ extern "C" {
/* See IEEE 802.15.4-2020, section 7.3.5 */
#define IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED IEEE802154_BROADCAST_ADDRESS
#define IEEE802154_PAN_ID_NOT_ASSOCIATED IEEE802154_BROADCAST_PAN_ID
/* MAC PIB attribute aUnitBackoffPeriod, see section 8.4.2, table 8-93, in symbol periods, valid for
* all PHYs except SUN PHY in the 920 MHz band.

View file

@ -636,7 +636,9 @@ void ieee802154_init(struct net_if *iface)
ctx->flags |= NET_L2_PROMISC_MODE;
}
ctx->pan_id = IEEE802154_PAN_ID_NOT_ASSOCIATED;
ctx->short_addr = IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED;
ctx->coord_short_addr = IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED;
sys_memcpy_swap(ctx->ext_addr, eui64_be, IEEE802154_EXT_ADDR_LENGTH);
/* We switch to a link address store that we

View file

@ -269,10 +269,10 @@ static inline bool validate_mac_command(struct ieee802154_mpdu *mpdu, uint8_t *b
struct ieee802154_command *command = (struct ieee802154_command *)buf;
uint8_t len = IEEE802154_CMD_CFI_LENGTH;
bool src_pan_brdcst_chk = false;
uint8_t src_bf = 0, dst_bf = 0;
bool dst_brdcst_chk = false;
bool ack_requested = false;
bool has_pan_id = true;
uint8_t src_bf, dst_bf;
if (length < len) {
return false;
@ -295,13 +295,14 @@ static inline bool validate_mac_command(struct ieee802154_mpdu *mpdu, uint8_t *b
case IEEE802154_CFI_DISASSOCIATION_NOTIFICATION:
if (command->cfi == IEEE802154_CFI_DISASSOCIATION_NOTIFICATION) {
len += IEEE802154_CMD_DISASSOC_NOTE_LENGTH;
dst_bf = BIT(IEEE802154_ADDR_MODE_SHORT);
}
__fallthrough;
case IEEE802154_CFI_PAN_ID_CONFLICT_NOTIFICATION:
ack_requested = true;
has_pan_id = false;
src_bf = BIT(IEEE802154_ADDR_MODE_EXTENDED);
dst_bf = BIT(IEEE802154_ADDR_MODE_EXTENDED);
dst_bf |= BIT(IEEE802154_ADDR_MODE_EXTENDED);
break;
case IEEE802154_CFI_DATA_REQUEST:
@ -574,7 +575,7 @@ static inline bool data_addr_to_fs_settings(struct net_linkaddr *dst, struct iee
params->dst.len = IEEE802154_SHORT_ADDR_LENGTH;
} else {
__ASSERT_NO_MSG(dst->len == IEEE802154_EXT_ADDR_LENGTH);
params->dst.ext_addr = dst->addr;
memcpy(params->dst.ext_addr, dst->addr, sizeof(params->dst.ext_addr));
params->dst.len = IEEE802154_EXT_ADDR_LENGTH;
}
}
@ -765,11 +766,11 @@ static inline bool cfi_to_fs_settings(enum ieee802154_cfi cfi, struct ieee802154
{
switch (cfi) {
case IEEE802154_CFI_DISASSOCIATION_NOTIFICATION:
fs->fc.ar = 1U;
fs->fc.pan_id_comp = 1U;
__fallthrough;
case IEEE802154_CFI_ASSOCIATION_REQUEST:
fs->fc.ar = 1U;
fs->fc.src_addr_mode = IEEE802154_ADDR_MODE_EXTENDED;
if (params->dst.len == IEEE802154_SHORT_ADDR_LENGTH) {

View file

@ -453,7 +453,7 @@ struct ieee802154_mpdu {
struct ieee802154_frame_params {
struct {
union {
uint8_t *ext_addr; /* in big endian */
uint8_t ext_addr[IEEE802154_EXT_ADDR_LENGTH]; /* in big endian */
uint16_t short_addr; /* in CPU byte order */
};

View file

@ -140,12 +140,14 @@ static int ieee802154_scan(uint32_t mgmt_request, struct net_if *iface,
ret = 0;
k_sem_take(&ctx->ctx_lock, K_FOREVER);
ieee802154_radio_remove_pan_id(iface, ctx->pan_id);
k_sem_give(&ctx->ctx_lock);
ieee802154_radio_filter_pan_id(iface, IEEE802154_BROADCAST_PAN_ID);
if (ieee802154_radio_start(iface)) {
NET_DBG("Could not start device");
ret = -EIO;
goto out;
}
@ -171,7 +173,6 @@ static int ieee802154_scan(uint32_t mgmt_request, struct net_if *iface,
NET_DBG("Could not send Beacon Request (%d)",
ret);
net_pkt_unref(pkt);
k_sem_take(&ctx->scan_ctx_lock, K_FOREVER);
goto out;
}
}
@ -192,10 +193,16 @@ static int ieee802154_scan(uint32_t mgmt_request, struct net_if *iface,
out:
/* Let's come back to context's settings. */
ieee802154_radio_remove_pan_id(iface, IEEE802154_BROADCAST_PAN_ID);
k_sem_take(&ctx->ctx_lock, K_FOREVER);
ieee802154_radio_filter_pan_id(iface, ctx->pan_id);
ieee802154_radio_set_channel(iface, ctx->channel);
k_sem_give(&ctx->ctx_lock);
ctx->scan_ctx = NULL;
k_sem_take(&ctx->scan_ctx_lock, K_FOREVER);
if (ctx->scan_ctx) {
ctx->scan_ctx = NULL;
}
k_sem_give(&ctx->scan_ctx_lock);
if (pkt) {
@ -232,37 +239,60 @@ static inline void set_linkaddr_to_ext_addr(struct net_if *iface, struct ieee802
update_net_if_link_addr(iface, ctx);
}
/* Requires the context lock to be held. */
/* Requires the context lock to be held and the PAN ID to be set. */
static inline void set_association(struct net_if *iface, struct ieee802154_context *ctx,
uint16_t short_addr)
{
__ASSERT_NO_MSG(short_addr != IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED);
uint16_t short_addr_be;
ctx->linkaddr.len = IEEE802154_SHORT_ADDR_LENGTH;
ctx->short_addr = short_addr;
short_addr_be = htons(short_addr);
memcpy(ctx->linkaddr.addr, &short_addr_be, IEEE802154_SHORT_ADDR_LENGTH);
__ASSERT_NO_MSG(ctx->pan_id != IEEE802154_PAN_ID_NOT_ASSOCIATED);
__ASSERT_NO_MSG(short_addr != IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED);
update_net_if_link_addr(iface, ctx);
ieee802154_radio_filter_short_addr(iface, ctx->short_addr);
ieee802154_radio_remove_src_short_addr(iface, ctx->short_addr);
ctx->short_addr = short_addr;
if (short_addr == IEEE802154_NO_SHORT_ADDRESS_ASSIGNED) {
set_linkaddr_to_ext_addr(iface, ctx);
} else {
ctx->linkaddr.len = IEEE802154_SHORT_ADDR_LENGTH;
short_addr_be = htons(short_addr);
memcpy(ctx->linkaddr.addr, &short_addr_be, IEEE802154_SHORT_ADDR_LENGTH);
update_net_if_link_addr(iface, ctx);
ieee802154_radio_filter_short_addr(iface, ctx->short_addr);
}
}
/* Requires the context lock to be held. */
static inline void remove_association(struct net_if *iface, struct ieee802154_context *ctx)
{
/* An associated device shall disassociate itself by removing all
* references to the PAN; the MLME shall set macPanId, macShortAddress,
* macAssociatedPanCoord [TODO: implement], macCoordShortAddress, and
* macCoordExtendedAddress to the default values, see section 6.4.2.
*/
ieee802154_radio_remove_pan_id(iface, ctx->pan_id);
ieee802154_radio_remove_src_short_addr(iface, ctx->short_addr);
ctx->pan_id = IEEE802154_PAN_ID_NOT_ASSOCIATED;
ctx->short_addr = IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED;
memset(ctx->coord_ext_addr, 0, IEEE802154_EXT_ADDR_LENGTH);
ctx->coord_short_addr = 0U;
memset(ctx->coord_ext_addr, 0, sizeof(ctx->coord_ext_addr));
ctx->coord_short_addr = IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED;
set_linkaddr_to_ext_addr(iface, ctx);
ieee802154_radio_filter_short_addr(iface, IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED);
ieee802154_radio_filter_pan_id(iface, IEEE802154_BROADCAST_PAN_ID);
ieee802154_radio_filter_short_addr(iface, IEEE802154_BROADCAST_ADDRESS);
}
/* Requires the context lock to be held. */
static inline bool is_associated(struct ieee802154_context *ctx)
{
return ctx->short_addr != IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED;
/* see section 8.4.3.1, table 8-94, macPanId and macShortAddress */
return ctx->pan_id != IEEE802154_PAN_ID_NOT_ASSOCIATED &&
ctx->short_addr != IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED;
}
enum net_verdict ieee802154_handle_mac_command(struct net_if *iface,
@ -276,23 +306,51 @@ enum net_verdict ieee802154_handle_mac_command(struct net_if *iface,
return NET_DROP;
}
/* validation of the association response, see section 7.3.3.1 */
/* Validation of the association response, see section 7.5.3:
* * The Destination Addressing Mode and Source Addressing Mode
* fields shall each be set to indicate extended addressing.
* * The Frame Pending field shall be set to zero and ignored
* upon reception, and the AR field shall be set to one.
* * The Destination PAN ID field shall contain the value of
* macPanId, while the Source PAN ID field shall be omitted.
* * The Destination Address field shall contain the extended
* address of the device requesting association (has been
* tested during generic filtering already).
*
* Note: Unless the packet is authenticated, it cannot be verified
* that the response comes from the requested coordinator.
*/
if (mpdu->mhr.fs->fc.src_addr_mode !=
IEEE802154_ADDR_MODE_EXTENDED ||
mpdu->mhr.fs->fc.dst_addr_mode !=
IEEE802154_ADDR_MODE_EXTENDED ||
mpdu->mhr.fs->fc.ar != 1 ||
mpdu->mhr.fs->fc.pan_id_comp != 1) {
mpdu->mhr.fs->fc.pan_id_comp != 1 ||
mpdu->mhr.dst_addr->plain.pan_id != sys_cpu_to_le16(ctx->pan_id) ||
mpdu->command->assoc_res.short_addr == IEEE802154_PAN_ID_NOT_ASSOCIATED) {
return NET_DROP;
}
k_sem_take(&ctx->ctx_lock, K_FOREVER);
if (is_associated(ctx)) {
k_sem_give(&ctx->ctx_lock);
return NET_DROP;
}
/* If the Association Status field of the Association Response
* command indicates that the association was successful, the
* device shall store the address contained in the Short Address
* field of the command in macShortAddress, see section 6.4.1.
*/
set_association(iface, ctx, sys_le16_to_cpu(mpdu->command->assoc_res.short_addr));
memcpy(ctx->coord_ext_addr,
mpdu->mhr.src_addr->comp.addr.ext_addr,
/* If the [association request] contained the short address of
* the coordinator, the extended address of the coordinator,
* contained in the MHR of the Association Response command,
* shall be stored in macCoordExtendedAddress, see section 6.4.1.
*/
memcpy(ctx->coord_ext_addr, mpdu->mhr.src_addr->comp.addr.ext_addr,
IEEE802154_EXT_ADDR_LENGTH);
k_sem_give(&ctx->ctx_lock);
@ -316,14 +374,35 @@ enum net_verdict ieee802154_handle_mac_command(struct net_if *iface,
goto out;
}
/* validation of the disassociation notification, see section 7.5.4 */
/* Validation of the disassociation notification, see section 7.5.4:
* * The Source Addressing Mode field shall be set to indicate
* extended addressing.
* * The Frame Pending field shall be set to zero and ignored
* upon reception, and the AR field shall be set to one.
* * The Destination PAN ID field shall contain the value of macPanId.
* * The Source PAN ID field shall be omitted.
* * If the coordinator is disassociating a device from the
* PAN, then the Destination Address field shall contain the
* address of the device being removed from the PAN (asserted
* during generic package filtering).
*
* Note: Unless the packet is authenticated, it cannot be verified
* that the command comes from the requested coordinator.
*/
if (mpdu->mhr.fs->fc.src_addr_mode !=
IEEE802154_ADDR_MODE_EXTENDED ||
mpdu->mhr.fs->fc.pan_id_comp != 1) {
mpdu->mhr.fs->fc.ar != 1 ||
mpdu->mhr.fs->fc.pan_id_comp != 1 ||
mpdu->mhr.dst_addr->plain.pan_id != sys_cpu_to_le16(ctx->pan_id)) {
goto out;
}
/* If the source address contained in the Disassociation
* Notification command is equal to macCoordExtendedAddress,
* the device should consider itself disassociated,
* see section 6.4.2.
*/
if (memcmp(ctx->coord_ext_addr,
mpdu->mhr.src_addr->comp.addr.ext_addr,
IEEE802154_EXT_ADDR_LENGTH)) {
@ -348,7 +427,7 @@ static int ieee802154_associate(uint32_t mgmt_request, struct net_if *iface,
void *data, size_t len)
{
struct ieee802154_context *ctx = net_if_l2_data(iface);
struct ieee802154_frame_params params;
struct ieee802154_frame_params params = {0};
struct ieee802154_req_params *req;
struct ieee802154_command *cmd;
struct net_pkt *pkt;
@ -360,15 +439,43 @@ static int ieee802154_associate(uint32_t mgmt_request, struct net_if *iface,
req = (struct ieee802154_req_params *)data;
params.dst.len = req->len;
if (params.dst.len == IEEE802154_SHORT_ADDR_LENGTH) {
params.dst.short_addr = req->short_addr;
} else {
params.dst.ext_addr = req->addr;
/* Validate the coordinator's PAN ID. */
if (req->pan_id == IEEE802154_PAN_ID_NOT_ASSOCIATED) {
return -EINVAL;
}
params.dst.pan_id = req->pan_id;
params.pan_id = req->pan_id;
/* If the Version field is set to 0b10, the Source PAN ID field is
* omitted. Otherwise, the Source PAN ID field shall contain the
* broadcast PAN ID.
*/
params.pan_id = IEEE802154_BROADCAST_PAN_ID;
/* Validate the coordinator's short address - if any. */
if (req->len == IEEE802154_SHORT_ADDR_LENGTH) {
if (req->short_addr == IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED ||
req->short_addr == IEEE802154_NO_SHORT_ADDRESS_ASSIGNED) {
return -EINVAL;
}
params.dst.short_addr = req->short_addr;
} else if (req->len == IEEE802154_EXT_ADDR_LENGTH) {
memcpy(params.dst.ext_addr, req->addr, sizeof(params.dst.ext_addr));
} else {
return -EINVAL;
}
params.dst.len = req->len;
k_sem_take(&ctx->ctx_lock, K_FOREVER);
if (is_associated(ctx)) {
k_sem_give(&ctx->ctx_lock);
return -EALREADY;
}
k_sem_give(&ctx->ctx_lock);
pkt = ieee802154_create_mac_cmd_frame(
iface, IEEE802154_CFI_ASSOCIATION_REQUEST, &params);
@ -377,35 +484,75 @@ static int ieee802154_associate(uint32_t mgmt_request, struct net_if *iface,
goto out;
}
k_sem_take(&ctx->ctx_lock, K_FOREVER);
cmd = ieee802154_get_mac_command(pkt);
cmd->assoc_req.ci.reserved_1 = 0U; /* Reserved */
cmd->assoc_req.ci.dev_type = 0U; /* RFD */
cmd->assoc_req.ci.power_src = 0U; /* TODO: set right power source */
cmd->assoc_req.ci.rx_on = 1U; /* TODO: that will depends on PM */
cmd->assoc_req.ci.rx_on = 1U; /* TODO: derive from PM settings */
cmd->assoc_req.ci.association_type = 0U; /* normal association */
cmd->assoc_req.ci.reserved_2 = 0U; /* Reserved */
cmd->assoc_req.ci.sec_capability = 0U; /* TODO: security support */
cmd->assoc_req.ci.alloc_addr = 0U; /* TODO: handle short addr */
k_sem_take(&ctx->ctx_lock, K_FOREVER);
remove_association(iface, ctx);
k_sem_give(&ctx->ctx_lock);
#ifdef CONFIG_NET_L2_IEEE802154_SECURITY
cmd->assoc_req.ci.sec_capability = ctx->sec_ctx.level > IEEE802154_SECURITY_LEVEL_NONE;
#else
cmd->assoc_req.ci.sec_capability = 0U;
#endif
/* request short address
* TODO: support operation with ext addr.
*/
cmd->assoc_req.ci.alloc_addr = 1U;
ieee802154_mac_cmd_finalize(pkt, IEEE802154_CFI_ASSOCIATION_REQUEST);
/* section 6.4.1, Association: Set phyCurrentPage [TODO: implement] and
* phyCurrentChannel to the requested channel and channel page
* parameters.
*/
if (ieee802154_radio_set_channel(iface, req->channel)) {
ret = -EIO;
goto release;
}
/* section 6.4.1, Association: Set macPanId to the coordinator's PAN ID. */
ieee802154_radio_remove_pan_id(iface, ctx->pan_id);
ctx->pan_id = req->pan_id;
ieee802154_radio_filter_pan_id(iface, req->pan_id);
if (net_if_send_data(iface, pkt)) {
/* section 6.4.1, Association: Set macCoordExtendedAddress or
* macCoordShortAddress, depending on which is known from the Beacon
* frame from the coordinator through which the device wishes to
* associate.
*/
if (req->len == IEEE802154_SHORT_ADDR_LENGTH) {
ctx->coord_short_addr = req->short_addr;
} else {
ctx->coord_short_addr = IEEE802154_NO_SHORT_ADDRESS_ASSIGNED;
sys_memcpy_swap(ctx->coord_ext_addr, req->addr, IEEE802154_EXT_ADDR_LENGTH);
}
k_sem_give(&ctx->ctx_lock);
/* Acquire the scan lock so that the next k_sem_take() blocks. */
k_sem_take(&ctx->scan_ctx_lock, K_FOREVER);
/* section 6.4.1, Association: The MAC sublayer of an unassociated device
* shall initiate the association procedure by sending an Association
* Request command, as described in 7.5.2, to the coordinator of an
* existing PAN.
*/
if (ieee802154_radio_send(iface, pkt, pkt->buffer)) {
net_pkt_unref(pkt);
ret = -EIO;
goto out;
}
/* Acquire the lock so that the next k_sem_take() blocks. */
k_sem_take(&ctx->scan_ctx_lock, K_FOREVER);
/* Wait macResponseWaitTime PHY symbols for the association response, see
* ieee802154_handle_mac_command() and section 6.4.1.
*
* TODO: The Association Response command shall be sent to the device
* requesting association using indirect transmission.
*/
k_sem_take(&ctx->scan_ctx_lock, K_USEC(ieee802154_get_response_wait_time_us(iface)));
@ -423,38 +570,36 @@ static int ieee802154_associate(uint32_t mgmt_request, struct net_if *iface,
ctx->coord_short_addr == req->short_addr) {
validated = true;
} else {
if (req->len == IEEE802154_EXT_ADDR_LENGTH) {
uint8_t ext_addr_le[IEEE802154_EXT_ADDR_LENGTH];
uint8_t ext_addr_le[IEEE802154_EXT_ADDR_LENGTH];
sys_memcpy_swap(ext_addr_le, req->addr,
IEEE802154_EXT_ADDR_LENGTH);
if (!memcmp(ctx->coord_ext_addr, ext_addr_le,
IEEE802154_EXT_ADDR_LENGTH)) {
validated = true;
}
__ASSERT_NO_MSG(req->len == IEEE802154_EXT_ADDR_LENGTH);
sys_memcpy_swap(ext_addr_le, req->addr, sizeof(ext_addr_le));
if (!memcmp(ctx->coord_ext_addr, ext_addr_le, IEEE802154_EXT_ADDR_LENGTH)) {
validated = true;
}
}
if (!validated) {
remove_association(iface, ctx);
ret = -EFAULT;
goto out;
goto release;
}
ctx->channel = req->channel;
ctx->pan_id = req->pan_id;
goto out;
} else {
ret = -EACCES;
}
release:
k_sem_give(&ctx->ctx_lock);
out:
if (ret < 0) {
ieee802154_radio_filter_pan_id(iface, 0);
if (ret) {
k_sem_take(&ctx->ctx_lock, K_FOREVER);
remove_association(iface, ctx);
ieee802154_radio_set_channel(iface, ctx->channel);
k_sem_give(&ctx->ctx_lock);
}
k_sem_give(&ctx->ctx_lock);
return ret;
}
@ -465,11 +610,9 @@ static int ieee802154_disassociate(uint32_t mgmt_request, struct net_if *iface,
void *data, size_t len)
{
struct ieee802154_context *ctx = net_if_l2_data(iface);
uint8_t ext_addr[IEEE802154_MAX_ADDR_LENGTH];
struct ieee802154_frame_params params;
struct ieee802154_frame_params params = {0};
struct ieee802154_command *cmd;
struct net_pkt *pkt;
int ret = 0;
ARG_UNUSED(data);
ARG_UNUSED(len);
@ -477,32 +620,40 @@ static int ieee802154_disassociate(uint32_t mgmt_request, struct net_if *iface,
k_sem_take(&ctx->ctx_lock, K_FOREVER);
if (!is_associated(ctx)) {
ret = -EALREADY;
goto out;
k_sem_give(&ctx->ctx_lock);
return -EALREADY;
}
/* See section 7.5.4 */
/* See section 7.5.4:
* * The Destination PAN ID field shall contain the value of macPanId.
* * If an associated device is disassociating from the PAN, then the
* Destination Address field shall contain the value of either
* macCoordShortAddress, if the Destination Addressing Mode field is
* set to indicated short addressing, or macCoordExtendedAddress, if
* the Destination Addressing Mode field is set to indicated extended
* addressing.
*/
params.dst.pan_id = ctx->pan_id;
if (ctx->coord_short_addr != 0 &&
if (ctx->coord_short_addr != IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED &&
ctx->coord_short_addr != IEEE802154_NO_SHORT_ADDRESS_ASSIGNED) {
params.dst.len = IEEE802154_SHORT_ADDR_LENGTH;
params.dst.short_addr = ctx->coord_short_addr;
} else {
params.dst.len = IEEE802154_EXT_ADDR_LENGTH;
params.dst.ext_addr = ext_addr;
sys_memcpy_swap(params.dst.ext_addr, ctx->coord_ext_addr,
IEEE802154_EXT_ADDR_LENGTH);
sizeof(params.dst.ext_addr));
}
params.pan_id = ctx->pan_id;
k_sem_give(&ctx->ctx_lock);
/* If an associated device wants to leave the PAN, the MLME of the device
* shall send a Disassociation Notification command to its coordinator.
*/
pkt = ieee802154_create_mac_cmd_frame(
iface, IEEE802154_CFI_DISASSOCIATION_NOTIFICATION, &params);
if (!pkt) {
ret = -ENOBUFS;
goto out;
return -ENOBUFS;
}
cmd = ieee802154_get_mac_command(pkt);
@ -511,17 +662,16 @@ static int ieee802154_disassociate(uint32_t mgmt_request, struct net_if *iface,
ieee802154_mac_cmd_finalize(
pkt, IEEE802154_CFI_DISASSOCIATION_NOTIFICATION);
if (net_if_send_data(iface, pkt)) {
if (ieee802154_radio_send(iface, pkt, pkt->buffer)) {
net_pkt_unref(pkt);
ret = -EIO;
goto out;
return -EIO;
}
k_sem_take(&ctx->ctx_lock, K_FOREVER);
remove_association(iface, ctx);
out:
k_sem_give(&ctx->ctx_lock);
return ret;
return 0;
}
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_DISASSOCIATE,
@ -600,6 +750,7 @@ static int ieee802154_set_parameters(uint32_t mgmt_request,
}
} else if (mgmt_request == NET_REQUEST_IEEE802154_SET_PAN_ID) {
if (ctx->pan_id != value) {
ieee802154_radio_remove_pan_id(iface, ctx->pan_id);
ctx->pan_id = value;
ieee802154_radio_filter_pan_id(iface, ctx->pan_id);
}
@ -622,6 +773,13 @@ static int ieee802154_set_parameters(uint32_t mgmt_request,
if (value == IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED) {
remove_association(iface, ctx);
} else {
/* A PAN is required when associating,
* see section 8.4.3.1, table 8-94.
*/
if (ctx->pan_id == IEEE802154_PAN_ID_NOT_ASSOCIATED) {
ret = -EPERM;
goto out;
}
set_association(iface, ctx, value);
}
}

View file

@ -245,6 +245,25 @@ static inline void ieee802154_radio_remove_src_short_addr(struct net_if *iface,
}
}
static inline void ieee802154_radio_remove_pan_id(struct net_if *iface, uint16_t pan_id)
{
const struct ieee802154_radio_api *radio =
net_if_get_device(iface)->api;
if (radio && (radio->get_capabilities(net_if_get_device(iface)) &
IEEE802154_HW_FILTER)) {
struct ieee802154_filter filter;
filter.pan_id = pan_id;
if (radio->filter(net_if_get_device(iface), false,
IEEE802154_FILTER_TYPE_PAN_ID,
&filter) != 0) {
NET_WARN("Could not remove PAN ID filter");
}
}
}
/**
* @brief Calculates the PHY's symbol period in microseconds.
*

View file

@ -10,13 +10,15 @@ LOG_MODULE_REGISTER(net_ieee802154_fake_driver, LOG_LEVEL_DBG);
#include <zephyr/kernel.h>
#include <zephyr/net/net_core.h>
#include "net_private.h"
#include <zephyr/net/net_if.h>
#include <zephyr/net/net_pkt.h>
/** FAKE ieee802.15.4 driver **/
#include <zephyr/net/ieee802154_radio.h>
#include "net_private.h"
#include <ieee802154_frame.h>
struct net_pkt *current_pkt;
K_SEM_DEFINE(driver_lock, 0, UINT_MAX);
@ -73,6 +75,29 @@ static int fake_tx(const struct device *dev,
insert_frag(pkt, frag);
if (ieee802154_is_ar_flag_set(frag)) {
struct net_if *iface = net_if_lookup_by_dev(dev);
struct ieee802154_context *ctx = net_if_l2_data(iface);
struct net_pkt *ack_pkt;
ack_pkt = net_pkt_rx_alloc_with_buffer(iface, IEEE802154_ACK_PKT_LENGTH, AF_UNSPEC,
0, K_FOREVER);
if (!ack_pkt) {
NET_ERR("*** Could not allocate ack pkt.\n");
return -ENOMEM;
}
if (!ieee802154_create_ack_frame(iface, ack_pkt, ctx->ack_seq)) {
NET_ERR("*** Could not create ack frame.\n");
net_pkt_unref(ack_pkt);
return -EFAULT;
}
ieee802154_handle_ack(iface, ack_pkt);
net_pkt_unref(ack_pkt);
}
k_sem_give(&driver_lock);
return 0;
@ -102,7 +127,8 @@ static void fake_iface_init(struct net_if *iface)
ieee802154_init(iface);
ctx->pan_id = 0xabcd;
ctx->pan_id = IEEE802154_PAN_ID_NOT_ASSOCIATED;
ctx->short_addr = IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED;
ctx->channel = 26U;
ctx->sequence = 62U;

View file

@ -12,6 +12,7 @@ LOG_MODULE_REGISTER(net_ieee802154_mgmt_test, LOG_LEVEL_DBG);
#include <zephyr/kernel.h>
#include <zephyr/ztest.h>
#include <zephyr/net/ieee802154.h>
#include <zephyr/net/ieee802154_mgmt.h>
#include <zephyr/net/net_mgmt.h>
#include <zephyr/net/net_if.h>
@ -29,10 +30,16 @@ static struct net_mgmt_event_callback scan_cb;
K_SEM_DEFINE(scan_lock, 0, 1);
#define EXPECTED_COORDINATOR_LQI 15U
#define EXPECTED_COORDINATOR_PAN_LE 0xcd, 0xab
#define EXPECTED_COORDINATOR_PAN_CPU_ORDER 0xabcd
#define EXPECTED_COORDINATOR_ADDR_LE 0xc2, 0xa3, 0x9e, 0x00, 0x00, 0x4b, 0x12, 0x00
#define EXPECTED_COORDINATOR_ADDR_BE 0x00, 0x12, 0x4b, 0x00, 0x00, 0x9e, 0xa3, 0xc2
#define EXPECTED_COORDINATOR_PAN_STR "43981"
#define EXPECTED_COORDINATOR_ADDR_LE 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
#define EXPECTED_COORDINATOR_ADDR_BE 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08
#define EXPECTED_COORDINATOR_ADDR_STR "0f:0e:0d:0c:0b:0a:09:08"
#define EXPECTED_ENDDEVICE_SHORT_ADDR 0xaaaa
static void scan_result_cb(struct net_mgmt_event_callback *cb, uint32_t mgmt_event,
struct net_if *iface)
@ -55,7 +62,7 @@ static void scan_result_cb(struct net_mgmt_event_callback *cb, uint32_t mgmt_eve
k_sem_give(&scan_lock);
}
void test_beacon_request(struct ieee802154_mpdu *mpdu)
static void test_beacon_request(struct ieee802154_mpdu *mpdu)
{
struct ieee802154_command *cmd = mpdu->command;
@ -69,15 +76,37 @@ void test_beacon_request(struct ieee802154_mpdu *mpdu)
"Beacon request: destination PAN should be broadcast PAN.");
}
void test_scan_shell_cmd(void)
static void test_association_request(struct ieee802154_mpdu *mpdu)
{
struct ieee802154_command *cmd = mpdu->command;
zassert_equal(
mpdu->mhr.fs->fc.frame_version, IEEE802154_VERSION_802154_2006,
"Association Request: currently only IEEE 802.15.4 2006 frame version supported.");
zassert_equal(mpdu->mhr.fs->fc.frame_type, IEEE802154_FRAME_TYPE_MAC_COMMAND,
"Association Request: should be a MAC command.");
zassert_equal(mpdu->mhr.fs->fc.ar, true, "Association Request: must request ACK.");
zassert_equal(mpdu->payload_length, 1U + IEEE802154_CMD_ASSOC_REQ_LENGTH);
zassert_equal(cmd->cfi, IEEE802154_CFI_ASSOCIATION_REQUEST,
"Association Request: unexpected CFI.");
zassert_equal(cmd->assoc_req.ci.alloc_addr, true,
"Association Request: should allocate short address.");
zassert_equal(cmd->assoc_req.ci.association_type, false,
"Association Request: fast association is not supported.");
}
static void test_scan_shell_cmd(void)
{
struct ieee802154_mpdu mpdu = {0};
int ret;
/* The beacon placed into the RX queue will be received and handled as
* soon as this command yields waiting for beacons.
*/
ret = shell_execute_cmd(NULL, "ieee802154 scan active 11 500");
zassert_equal(0, ret, "Active scan failed: %d", ret);
/* Expect the beacon to have been received and handled already by the scan command. */
zassert_equal(0, k_sem_take(&scan_lock, K_NO_WAIT), "Active scan: did not receive beacon.");
zassert_not_null(current_pkt);
@ -96,6 +125,49 @@ release_frag:
current_pkt->frags = NULL;
}
static void test_associate_shell_cmd(struct ieee802154_context *ctx)
{
uint8_t expected_coord_addr_le[] = {EXPECTED_COORDINATOR_ADDR_LE};
struct ieee802154_mpdu mpdu = {0};
struct net_buf *assoc_req;
int ret;
/* The association response placed into the RX queue will be received and
* handled as soon as this command yields waiting for a response.
*/
ret = shell_execute_cmd(NULL, "ieee802154 associate " EXPECTED_COORDINATOR_PAN_STR
" " EXPECTED_COORDINATOR_ADDR_STR);
zassert_equal(0, ret, "Association failed: %d", ret);
/* Test that we were associated. */
zassert_equal(ctx->pan_id, EXPECTED_COORDINATOR_PAN_CPU_ORDER,
"Association: did not get associated to the expected PAN.");
zassert_equal(ctx->short_addr, EXPECTED_ENDDEVICE_SHORT_ADDR,
"Association: did not get the expected short address asigned.");
zassert_equal(ctx->coord_short_addr, IEEE802154_NO_SHORT_ADDRESS_ASSIGNED,
"Association: co-ordinator should not use short address.");
zassert_mem_equal(
ctx->coord_ext_addr, expected_coord_addr_le, sizeof(ctx->coord_ext_addr),
"Association: did not get associated co-ordinator by the expected coordinator.");
/* Test the association request that should have been sent out. */
zassert_not_null(current_pkt);
assoc_req = current_pkt->frags;
zassert_not_null(assoc_req);
if (!ieee802154_validate_frame(assoc_req->data, assoc_req->len, &mpdu)) {
NET_ERR("*** Could not parse association request.\n");
ztest_test_fail();
goto release_frag;
}
test_association_request(&mpdu);
release_frag:
net_pkt_frag_unref(current_pkt->frags);
current_pkt->frags = NULL;
}
ZTEST(ieee802154_l2_shell, test_active_scan)
{
uint8_t beacon_pkt[] = {
@ -113,18 +185,17 @@ ZTEST(ieee802154_l2_shell, test_active_scan)
pkt = net_pkt_rx_alloc_with_buffer(iface, sizeof(beacon_pkt), AF_UNSPEC, 0, K_FOREVER);
if (!pkt) {
NET_ERR("*** No buffer to allocate\n");
ztest_test_fail();
return;
goto fail;
}
net_pkt_set_ieee802154_lqi(pkt, EXPECTED_COORDINATOR_LQI);
net_buf_add_mem(pkt->buffer, beacon_pkt, sizeof(beacon_pkt));
/* The packet will be placed in the RX queue but not yet handled. */
if (net_recv_data(iface, pkt) < 0) {
NET_ERR("Recv data failed");
net_pkt_unref(pkt);
ztest_test_fail();
return;
goto fail;
}
net_mgmt_init_event_callback(&scan_cb, scan_result_cb, NET_EVENT_IEEE802154_SCAN_RESULT);
@ -133,6 +204,72 @@ ZTEST(ieee802154_l2_shell, test_active_scan)
test_scan_shell_cmd();
net_mgmt_del_event_callback(&scan_cb);
return;
fail:
ztest_test_fail();
}
ZTEST(ieee802154_l2_shell, test_associate)
{
uint8_t coord_addr_le[] = {EXPECTED_COORDINATOR_ADDR_LE};
struct ieee802154_context *ctx = net_if_l2_data(iface);
struct ieee802154_frame_params params = {
.dst = {
.len = IEEE802154_EXT_ADDR_LENGTH,
.pan_id = EXPECTED_COORDINATOR_PAN_CPU_ORDER,
}};
struct ieee802154_command *cmd;
struct net_pkt *pkt;
sys_memcpy_swap(params.dst.ext_addr, ctx->ext_addr, sizeof(params.dst.ext_addr));
/* Simulate a packet from the coordinator. */
memcpy(ctx->ext_addr, coord_addr_le, sizeof(ctx->ext_addr));
pkt = ieee802154_create_mac_cmd_frame(iface, IEEE802154_CFI_ASSOCIATION_RESPONSE, &params);
if (!pkt) {
NET_ERR("*** Could not create association response\n");
goto fail;
}
cmd = ieee802154_get_mac_command(pkt);
cmd->assoc_res.short_addr = sys_cpu_to_le16(EXPECTED_ENDDEVICE_SHORT_ADDR);
cmd->assoc_res.status = IEEE802154_ASF_SUCCESSFUL;
ieee802154_mac_cmd_finalize(pkt, IEEE802154_CFI_ASSOCIATION_RESPONSE);
/* The packet will be placed in the RX queue but not yet handled. */
if (net_recv_data(iface, pkt) < 0) {
NET_ERR("Recv assoc resp pkt failed");
net_pkt_unref(pkt);
goto fail;
}
/* Restore the end device's extended address. */
sys_memcpy_swap(ctx->ext_addr, params.dst.ext_addr, sizeof(ctx->ext_addr));
test_associate_shell_cmd(ctx);
return;
fail:
sys_memcpy_swap(ctx->ext_addr, params.dst.ext_addr, sizeof(ctx->ext_addr));
ztest_test_fail();
}
static void reset_fake_driver(void *test_fixture)
{
struct ieee802154_context *ctx;
ARG_UNUSED(test_fixture);
__ASSERT_NO_MSG(iface);
/* Set initial conditions. */
ctx = net_if_l2_data(iface);
ctx->pan_id = IEEE802154_PAN_ID_NOT_ASSOCIATED;
ctx->short_addr = IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED;
ctx->coord_short_addr = IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED;
memset(ctx->coord_ext_addr, 0, sizeof(ctx->coord_ext_addr));
}
static void *test_setup(void)
@ -171,4 +308,5 @@ static void test_teardown(void *test_fixture)
current_pkt = NULL;
}
ZTEST_SUITE(ieee802154_l2_shell, NULL, test_setup, NULL, NULL, test_teardown);
ZTEST_SUITE(ieee802154_l2_shell, NULL, test_setup, reset_fake_driver, reset_fake_driver,
test_teardown);

View file

@ -162,6 +162,8 @@ uint8_t raw_payload[] = {
#define RAW_MAC_PAYLOAD_START_INDEX 17
#define RAW_MAC_PAYLOAD_LENGTH 3
#define MOCK_PAN_ID 0xabcd
extern struct net_pkt *current_pkt;
extern struct k_sem driver_lock;
@ -201,36 +203,90 @@ static void ieee_addr_hexdump(uint8_t *addr, uint8_t length)
printk("%02x\n", *addr);
}
static int set_up_short_addr(struct net_if *iface, struct ieee802154_context *ctx)
static int disassociate(struct net_if *iface, struct ieee802154_context *ctx)
{
uint16_t short_addr = 0x5678;
uint16_t short_addr_not_associated = IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED;
int ret;
int ret = net_mgmt(NET_REQUEST_IEEE802154_SET_SHORT_ADDR, iface, &short_addr,
sizeof(short_addr));
if (ret) {
NET_ERR("*** Failed to set short address\n");
if (ctx->short_addr == IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED) {
return 0;
}
return ret;
ret = net_mgmt(NET_REQUEST_IEEE802154_SET_SHORT_ADDR, iface, &short_addr_not_associated,
sizeof(short_addr_not_associated));
if (ret) {
NET_ERR("*** Failed to %s.\n", __func__);
return ret;
}
return 0;
}
static int associate(struct net_if *iface, struct ieee802154_context *ctx, uint16_t short_addr)
{
uint16_t mock_pan_id = MOCK_PAN_ID;
int ret;
if (ctx->short_addr == short_addr) {
return -EALREADY;
}
ret = net_mgmt(NET_REQUEST_IEEE802154_SET_PAN_ID, iface, &mock_pan_id,
sizeof(mock_pan_id));
if (ret) {
NET_ERR("*** Failed to set PAN ID in %s.\n", __func__);
return ret;
}
ret = net_mgmt(NET_REQUEST_IEEE802154_SET_SHORT_ADDR, iface, &short_addr,
sizeof(short_addr));
if (ret) {
NET_ERR("*** Failed to set short addr in %s.\n", __func__);
return ret;
}
return 0;
}
static int set_up_short_addr(struct net_if *iface, struct ieee802154_context *ctx)
{
const uint16_t mock_short_addr = 0x5678;
int ret;
ret = disassociate(iface, ctx);
if (ret) {
return ret;
}
ret = associate(iface, ctx, mock_short_addr);
if (ret) {
return ret;
}
return 0;
}
static int tear_down_short_addr(struct net_if *iface, struct ieee802154_context *ctx)
{
uint16_t short_addr;
uint16_t no_short_addr_assigned = IEEE802154_NO_SHORT_ADDRESS_ASSIGNED;
int ret;
if (ctx->linkaddr.len != IEEE802154_SHORT_ADDR_LENGTH) {
/* nothing to do */
return 0;
}
short_addr = IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED;
int ret = net_mgmt(NET_REQUEST_IEEE802154_SET_SHORT_ADDR, iface, &short_addr,
sizeof(short_addr));
ret = disassociate(iface, ctx);
if (ret) {
NET_ERR("*** Failed to unset short address\n");
return ret;
}
return ret;
ret = associate(iface, ctx, no_short_addr_assigned);
if (ret) {
return ret;
}
return 0;
}
static struct net_pkt *get_data_pkt_with_ar(void)
@ -275,12 +331,18 @@ static struct net_pkt *get_data_pkt_with_ar(void)
#ifdef CONFIG_NET_SOCKETS
static bool set_up_security(uint8_t security_level)
{
struct ieee802154_context *ctx = net_if_l2_data(iface);
uint16_t saved_short_addr = ctx->short_addr;
struct ieee802154_security_params params;
if (security_level == IEEE802154_SECURITY_LEVEL_NONE) {
return true;
}
if (disassociate(iface, ctx) != 0) {
return false;
}
params = (struct ieee802154_security_params){
.key = {0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb,
0xcc, 0xcd, 0xce, 0xcf},
@ -295,19 +357,38 @@ static bool set_up_security(uint8_t security_level)
return false;
}
if (saved_short_addr != IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED &&
associate(iface, ctx, saved_short_addr) != 0) {
return false;
}
return true;
}
static void tear_down_security(void)
static bool tear_down_security(void)
{
struct ieee802154_context *ctx = net_if_l2_data(iface);
uint16_t saved_short_addr = ctx->short_addr;
struct ieee802154_security_params params = {
.level = IEEE802154_SECURITY_LEVEL_NONE,
};
if (disassociate(iface, ctx) != 0) {
return false;
}
if (net_mgmt(NET_REQUEST_IEEE802154_SET_SECURITY_SETTINGS, iface, &params,
sizeof(struct ieee802154_security_params))) {
NET_ERR("*** Failed to tear down security settings\n");
return false;
}
if (saved_short_addr != IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED &&
associate(iface, ctx, saved_short_addr) != 0) {
return false;
}
return true;
}
static int set_up_recv_socket(enum net_sock_type socket_type)
@ -394,10 +475,8 @@ static bool test_ns_sending(struct ieee802154_pkt_test *t, bool with_short_addr)
/* ensure reproducible results */
ctx->sequence = t->sequence;
if (with_short_addr) {
if (set_up_short_addr(iface, ctx)) {
goto out;
}
if (with_short_addr && set_up_short_addr(iface, ctx)) {
goto out;
}
if (net_ipv6_send_ns(iface, NULL, &t->src, &t->dst, &t->dst, false)) {
@ -633,10 +712,8 @@ static bool test_dgram_packet_sending(void *dst_sll, uint8_t dst_sll_halen, uint
bool bind_short_address = pkt_dst_sll.sll_halen == IEEE802154_SHORT_ADDR_LENGTH &&
security_level == IEEE802154_SECURITY_LEVEL_NONE;
if (bind_short_address) {
if (set_up_short_addr(iface, ctx)) {
goto release_fd;
}
if (bind_short_address && set_up_short_addr(iface, ctx)) {
goto release_fd;
}
if (bind(fd, (const struct sockaddr *)&socket_sll, sizeof(struct sockaddr_ll))) {
@ -1133,6 +1210,7 @@ out:
static bool initialize_test_environment(void)
{
uint16_t mock_pan_id = MOCK_PAN_ID;
const struct device *dev;
k_sem_reset(&driver_lock);
@ -1155,6 +1233,11 @@ static bool initialize_test_environment(void)
goto release_pkt;
}
if (net_mgmt(NET_REQUEST_IEEE802154_SET_PAN_ID, iface, &mock_pan_id, sizeof(mock_pan_id))) {
NET_ERR("*** Failed to set PAN ID in %s.\n", __func__);
goto release_pkt;
}
NET_INFO("Fake IEEE 802.15.4 network interface ready\n");
ieee_addr_hexdump(net_if_get_link_addr(iface)->addr, 8);