You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
984 lines
29 KiB
984 lines
29 KiB
/* |
|
* Copyright (c) 2016 Intel Corporation. |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
/** |
|
* @file |
|
* @brief IEEE 802.15.4 Net Management Implementation |
|
* |
|
* All references to the spec refer to IEEE 802.15.4-2020. |
|
*/ |
|
|
|
#include <zephyr/logging/log.h> |
|
LOG_MODULE_REGISTER(net_ieee802154_mgmt, CONFIG_NET_L2_IEEE802154_LOG_LEVEL); |
|
|
|
#include <zephyr/net/net_core.h> |
|
|
|
#include <errno.h> |
|
|
|
#include <zephyr/net/net_if.h> |
|
#include <zephyr/net/ieee802154_radio.h> |
|
#include <zephyr/net/ieee802154_mgmt.h> |
|
#include <zephyr/net/ieee802154.h> |
|
|
|
#include "ieee802154_frame.h" |
|
#include "ieee802154_mgmt_priv.h" |
|
#include "ieee802154_priv.h" |
|
#include "ieee802154_security.h" |
|
#include "ieee802154_utils.h" |
|
|
|
/** |
|
* Implements (part of) the MLME-BEACON.notify primitive, see section 8.2.5.2. |
|
*/ |
|
enum net_verdict ieee802154_handle_beacon(struct net_if *iface, |
|
struct ieee802154_mpdu *mpdu, |
|
uint8_t lqi) |
|
{ |
|
struct ieee802154_context *ctx = net_if_l2_data(iface); |
|
int beacon_hdr_len; |
|
|
|
NET_DBG("Beacon received"); |
|
|
|
if (!ctx->scan_ctx) { |
|
return NET_DROP; |
|
} |
|
|
|
ctx->scan_ctx->association_permitted = mpdu->beacon->sf.association; |
|
|
|
k_sem_take(&ctx->scan_ctx_lock, K_FOREVER); |
|
|
|
ctx->scan_ctx->pan_id = mpdu->mhr.src_addr->plain.pan_id; |
|
ctx->scan_ctx->lqi = lqi; |
|
|
|
if (mpdu->mhr.fs->fc.src_addr_mode == IEEE802154_ADDR_MODE_SHORT) { |
|
ctx->scan_ctx->len = IEEE802154_SHORT_ADDR_LENGTH; |
|
ctx->scan_ctx->short_addr = |
|
sys_le16_to_cpu(mpdu->mhr.src_addr->plain.addr.short_addr); |
|
} else { |
|
ctx->scan_ctx->len = IEEE802154_EXT_ADDR_LENGTH; |
|
sys_memcpy_swap(ctx->scan_ctx->addr, |
|
mpdu->mhr.src_addr->plain.addr.ext_addr, |
|
IEEE802154_EXT_ADDR_LENGTH); |
|
} |
|
|
|
beacon_hdr_len = ieee802514_beacon_header_length(mpdu->payload, mpdu->payload_length); |
|
ctx->scan_ctx->beacon_payload_len = mpdu->payload_length - beacon_hdr_len; |
|
ctx->scan_ctx->beacon_payload = (uint8_t *)mpdu->payload + beacon_hdr_len; |
|
|
|
net_mgmt_event_notify(NET_EVENT_IEEE802154_SCAN_RESULT, iface); |
|
|
|
k_sem_give(&ctx->scan_ctx_lock); |
|
|
|
return NET_CONTINUE; |
|
} |
|
|
|
static int ieee802154_cancel_scan(uint64_t mgmt_request, struct net_if *iface, |
|
void *data, size_t len) |
|
{ |
|
struct ieee802154_context *ctx = net_if_l2_data(iface); |
|
|
|
ARG_UNUSED(data); |
|
ARG_UNUSED(len); |
|
|
|
NET_DBG("Cancelling scan request"); |
|
|
|
k_sem_take(&ctx->scan_ctx_lock, K_FOREVER); |
|
ctx->scan_ctx = NULL; |
|
k_sem_give(&ctx->scan_ctx_lock); |
|
|
|
return 0; |
|
} |
|
|
|
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_CANCEL_SCAN, |
|
ieee802154_cancel_scan); |
|
|
|
static int ieee802154_scan(uint64_t mgmt_request, struct net_if *iface, |
|
void *data, size_t len) |
|
{ |
|
const struct ieee802154_phy_supported_channels *supported_channels; |
|
struct ieee802154_context *ctx = net_if_l2_data(iface); |
|
struct ieee802154_attr_value attr_value; |
|
struct ieee802154_req_params *scan; |
|
struct net_pkt *pkt = NULL; |
|
int ret; |
|
|
|
if (len != sizeof(struct ieee802154_req_params) || !data) { |
|
return -EINVAL; |
|
} |
|
|
|
scan = (struct ieee802154_req_params *)data; |
|
|
|
NET_DBG("%s scan requested", |
|
mgmt_request == NET_REQUEST_IEEE802154_ACTIVE_SCAN ? |
|
"Active" : "Passive"); |
|
|
|
k_sem_take(&ctx->scan_ctx_lock, K_FOREVER); |
|
|
|
if (ctx->scan_ctx) { |
|
ret = -EALREADY; |
|
goto out; |
|
} |
|
|
|
if (mgmt_request == NET_REQUEST_IEEE802154_ACTIVE_SCAN) { |
|
struct ieee802154_frame_params params = {0}; |
|
|
|
params.dst.len = IEEE802154_SHORT_ADDR_LENGTH; |
|
params.dst.short_addr = IEEE802154_BROADCAST_ADDRESS; |
|
params.dst.pan_id = IEEE802154_BROADCAST_PAN_ID; |
|
|
|
pkt = ieee802154_create_mac_cmd_frame( |
|
iface, IEEE802154_CFI_BEACON_REQUEST, ¶ms); |
|
if (!pkt) { |
|
k_sem_give(&ctx->scan_ctx_lock); |
|
NET_ERR("Could not create Beacon Request"); |
|
ret = -ENOBUFS; |
|
goto out; |
|
} |
|
|
|
ieee802154_mac_cmd_finalize(pkt, IEEE802154_CFI_BEACON_REQUEST); |
|
} |
|
|
|
ctx->scan_ctx = scan; |
|
k_sem_give(&ctx->scan_ctx_lock); |
|
|
|
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_ERR("Scan request failed: could not start device"); |
|
ret = -EIO; |
|
goto out; |
|
} |
|
|
|
if (ieee802154_radio_attr_get(iface, IEEE802154_ATTR_PHY_SUPPORTED_CHANNEL_RANGES, |
|
&attr_value)) { |
|
NET_ERR("Scan request failed: could not determine supported channels"); |
|
ret = -ENOENT; |
|
goto out; |
|
} |
|
supported_channels = attr_value.phy_supported_channels; |
|
|
|
for (int channel_range = 0; channel_range < supported_channels->num_ranges; |
|
channel_range++) { |
|
for (uint16_t channel = supported_channels->ranges[channel_range].from_channel; |
|
channel <= supported_channels->ranges[channel_range].to_channel; channel++) { |
|
if (IEEE802154_IS_CHAN_UNSCANNED(scan->channel_set, channel)) { |
|
continue; |
|
} |
|
|
|
scan->channel = channel; |
|
NET_DBG("Scanning channel %u", channel); |
|
ieee802154_radio_set_channel(iface, channel); |
|
|
|
/* Active scan sends a beacon request */ |
|
if (mgmt_request == NET_REQUEST_IEEE802154_ACTIVE_SCAN) { |
|
net_pkt_ref(pkt); |
|
net_pkt_frag_ref(pkt->buffer); |
|
|
|
ret = ieee802154_radio_send(iface, pkt, pkt->buffer); |
|
if (ret) { |
|
NET_ERR("Scan request failed: could not send Beacon " |
|
"Request (%d)", |
|
ret); |
|
net_pkt_unref(pkt); |
|
goto out; |
|
} |
|
} |
|
|
|
/* Context aware sleep */ |
|
k_sleep(K_MSEC(scan->duration)); |
|
|
|
k_sem_take(&ctx->scan_ctx_lock, K_FOREVER); |
|
|
|
if (!ctx->scan_ctx) { |
|
NET_DBG("Scan request cancelled"); |
|
ret = -ECANCELED; |
|
goto out; |
|
} |
|
|
|
k_sem_give(&ctx->scan_ctx_lock); |
|
} |
|
} |
|
|
|
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); |
|
|
|
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) { |
|
net_pkt_unref(pkt); |
|
} |
|
|
|
return ret; |
|
} |
|
|
|
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_PASSIVE_SCAN, |
|
ieee802154_scan); |
|
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_ACTIVE_SCAN, |
|
ieee802154_scan); |
|
|
|
/* Requires the context lock to be held. */ |
|
static inline void update_net_if_link_addr(struct net_if *iface, struct ieee802154_context *ctx) |
|
{ |
|
bool was_if_up; |
|
|
|
was_if_up = net_if_flag_test_and_clear(iface, NET_IF_RUNNING); |
|
net_if_set_link_addr(iface, ctx->linkaddr.addr, ctx->linkaddr.len, ctx->linkaddr.type); |
|
|
|
if (was_if_up) { |
|
net_if_flag_set(iface, NET_IF_RUNNING); |
|
} |
|
} |
|
|
|
/* Requires the context lock to be held. */ |
|
static inline void set_linkaddr_to_ext_addr(struct net_if *iface, struct ieee802154_context *ctx) |
|
{ |
|
ctx->linkaddr.len = IEEE802154_EXT_ADDR_LENGTH; |
|
sys_memcpy_swap(ctx->linkaddr.addr, ctx->ext_addr, IEEE802154_EXT_ADDR_LENGTH); |
|
|
|
update_net_if_link_addr(iface, ctx); |
|
} |
|
|
|
/* 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) |
|
{ |
|
uint16_t short_addr_be; |
|
|
|
__ASSERT_NO_MSG(ctx->pan_id != IEEE802154_PAN_ID_NOT_ASSOCIATED); |
|
__ASSERT_NO_MSG(short_addr != IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED); |
|
|
|
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, sizeof(ctx->coord_ext_addr)); |
|
ctx->coord_short_addr = IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED; |
|
|
|
set_linkaddr_to_ext_addr(iface, ctx); |
|
|
|
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) |
|
{ |
|
/* 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, |
|
struct ieee802154_mpdu *mpdu) |
|
{ |
|
struct ieee802154_context *ctx = net_if_l2_data(iface); |
|
|
|
if (mpdu->command->cfi == IEEE802154_CFI_ASSOCIATION_RESPONSE) { |
|
if (mpdu->command->assoc_res.status != |
|
IEEE802154_ASF_SUCCESSFUL) { |
|
return NET_DROP; |
|
} |
|
|
|
/* 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.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)); |
|
|
|
/* 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); |
|
|
|
k_sem_give(&ctx->scan_ctx_lock); |
|
|
|
return NET_CONTINUE; |
|
} |
|
|
|
if (mpdu->command->cfi == IEEE802154_CFI_DISASSOCIATION_NOTIFICATION) { |
|
enum net_verdict ret = NET_DROP; |
|
|
|
if (mpdu->command->disassoc_note.reason != |
|
IEEE802154_DRF_COORDINATOR_WISH) { |
|
return NET_DROP; |
|
} |
|
|
|
k_sem_take(&ctx->ctx_lock, K_FOREVER); |
|
|
|
if (!is_associated(ctx)) { |
|
goto out; |
|
} |
|
|
|
/* 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.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)) { |
|
goto out; |
|
} |
|
|
|
remove_association(iface, ctx); |
|
ret = NET_OK; |
|
|
|
out: |
|
k_sem_give(&ctx->ctx_lock); |
|
return ret; |
|
} |
|
|
|
NET_WARN("Drop MAC command, unsupported CFI: 0x%x", mpdu->command->cfi); |
|
|
|
return NET_DROP; |
|
} |
|
|
|
static int ieee802154_associate(uint64_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 = {0}; |
|
struct ieee802154_req_params *req; |
|
struct ieee802154_command *cmd; |
|
struct net_pkt *pkt; |
|
int ret = 0; |
|
|
|
if (len != sizeof(struct ieee802154_req_params) || !data) { |
|
NET_ERR("Could not associate: invalid request"); |
|
return -EINVAL; |
|
} |
|
|
|
req = (struct ieee802154_req_params *)data; |
|
|
|
/* Validate the coordinator's PAN ID. */ |
|
if (req->pan_id == IEEE802154_PAN_ID_NOT_ASSOCIATED) { |
|
NET_ERR("Could not associate: PAN ID is special value 'not associated'"); |
|
return -EINVAL; |
|
} |
|
|
|
params.dst.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) { |
|
NET_ERR("Could not associate: invalid short address ('not associated' or " |
|
"'not 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 { |
|
NET_ERR("Could not associate: invalid address type"); |
|
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); |
|
NET_WARN("Could not associate: already associated"); |
|
return -EALREADY; |
|
} |
|
|
|
k_sem_give(&ctx->ctx_lock); |
|
|
|
pkt = ieee802154_create_mac_cmd_frame( |
|
iface, IEEE802154_CFI_ASSOCIATION_REQUEST, ¶ms); |
|
if (!pkt) { |
|
ret = -ENOBUFS; |
|
NET_ERR("Could not associate: cannot allocate association request frame"); |
|
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: derive from PM settings */ |
|
cmd->assoc_req.ci.association_type = 0U; /* normal association */ |
|
cmd->assoc_req.ci.reserved_2 = 0U; /* Reserved */ |
|
#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; |
|
NET_ERR("Could not associate: cannot set channel %d", req->channel); |
|
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); |
|
|
|
/* 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; |
|
k_sem_give(&ctx->scan_ctx_lock); |
|
NET_ERR("Could not associate: cannot send association request"); |
|
goto out; |
|
} |
|
|
|
/* 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))); |
|
|
|
/* Release the scan lock in case an association response was not received |
|
* within macResponseWaitTime and we got a timeout instead. |
|
*/ |
|
k_sem_give(&ctx->scan_ctx_lock); |
|
|
|
k_sem_take(&ctx->ctx_lock, K_FOREVER); |
|
|
|
if (is_associated(ctx)) { |
|
bool validated = false; |
|
|
|
if (req->len == IEEE802154_SHORT_ADDR_LENGTH && |
|
ctx->coord_short_addr == req->short_addr) { |
|
validated = true; |
|
} else { |
|
uint8_t ext_addr_le[IEEE802154_EXT_ADDR_LENGTH]; |
|
|
|
__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) { |
|
ret = -EFAULT; |
|
NET_ERR("Could not associate: invalid address assigned by coordinator"); |
|
goto release; |
|
} |
|
|
|
ctx->channel = req->channel; |
|
} else { |
|
ret = -EACCES; |
|
} |
|
|
|
release: |
|
k_sem_give(&ctx->ctx_lock); |
|
out: |
|
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); |
|
} |
|
|
|
return ret; |
|
} |
|
|
|
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_ASSOCIATE, |
|
ieee802154_associate); |
|
|
|
static int ieee802154_disassociate(uint64_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 = {0}; |
|
struct ieee802154_command *cmd; |
|
struct net_pkt *pkt; |
|
|
|
ARG_UNUSED(data); |
|
ARG_UNUSED(len); |
|
|
|
k_sem_take(&ctx->ctx_lock, K_FOREVER); |
|
|
|
if (!is_associated(ctx)) { |
|
k_sem_give(&ctx->ctx_lock); |
|
NET_WARN("Could not disassociate: not associated"); |
|
return -EALREADY; |
|
} |
|
|
|
/* 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 != 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; |
|
sys_memcpy_swap(params.dst.ext_addr, ctx->coord_ext_addr, |
|
sizeof(params.dst.ext_addr)); |
|
} |
|
|
|
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, ¶ms); |
|
if (!pkt) { |
|
NET_ERR("Could not disassociate: cannot allocate disassociation notification " |
|
"frame"); |
|
return -ENOBUFS; |
|
} |
|
|
|
cmd = ieee802154_get_mac_command(pkt); |
|
cmd->disassoc_note.reason = IEEE802154_DRF_DEVICE_WISH; |
|
|
|
ieee802154_mac_cmd_finalize( |
|
pkt, IEEE802154_CFI_DISASSOCIATION_NOTIFICATION); |
|
|
|
if (ieee802154_radio_send(iface, pkt, pkt->buffer)) { |
|
net_pkt_unref(pkt); |
|
NET_ERR("Could not disassociate: cannot send disassociation notification"); |
|
return -EIO; |
|
} |
|
|
|
k_sem_take(&ctx->ctx_lock, K_FOREVER); |
|
remove_association(iface, ctx); |
|
k_sem_give(&ctx->ctx_lock); |
|
|
|
return 0; |
|
} |
|
|
|
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_DISASSOCIATE, |
|
ieee802154_disassociate); |
|
|
|
static int ieee802154_set_ack(uint64_t mgmt_request, struct net_if *iface, |
|
void *data, size_t len) |
|
{ |
|
struct ieee802154_context *ctx = net_if_l2_data(iface); |
|
|
|
ARG_UNUSED(data); |
|
ARG_UNUSED(len); |
|
|
|
k_sem_take(&ctx->ctx_lock, K_FOREVER); |
|
|
|
if (mgmt_request == NET_REQUEST_IEEE802154_SET_ACK) { |
|
ctx->ack_requested = true; |
|
} else if (mgmt_request == NET_REQUEST_IEEE802154_UNSET_ACK) { |
|
ctx->ack_requested = false; |
|
} |
|
|
|
k_sem_give(&ctx->ctx_lock); |
|
|
|
return 0; |
|
} |
|
|
|
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_SET_ACK, |
|
ieee802154_set_ack); |
|
|
|
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_UNSET_ACK, |
|
ieee802154_set_ack); |
|
|
|
static int ieee802154_set_parameters(uint64_t mgmt_request, |
|
struct net_if *iface, |
|
void *data, size_t len) |
|
{ |
|
struct ieee802154_context *ctx = net_if_l2_data(iface); |
|
uint16_t value; |
|
int ret = 0; |
|
|
|
if (!data) { |
|
return -EINVAL; |
|
} |
|
|
|
if (mgmt_request == NET_REQUEST_IEEE802154_SET_EXT_ADDR) { |
|
if (len != IEEE802154_EXT_ADDR_LENGTH) { |
|
return -EINVAL; |
|
} |
|
} else { |
|
if (len != sizeof(uint16_t)) { |
|
return -EINVAL; |
|
} |
|
} |
|
|
|
value = *((uint16_t *) data); |
|
|
|
k_sem_take(&ctx->ctx_lock, K_FOREVER); |
|
|
|
if (is_associated(ctx) && !(mgmt_request == NET_REQUEST_IEEE802154_SET_SHORT_ADDR && |
|
value == IEEE802154_SHORT_ADDRESS_NOT_ASSOCIATED)) { |
|
ret = -EBUSY; |
|
NET_ERR("Could not set parameter: already associated to a PAN"); |
|
goto out; |
|
} |
|
|
|
if (mgmt_request == NET_REQUEST_IEEE802154_SET_CHANNEL) { |
|
if (ctx->channel != value) { |
|
if (!ieee802154_radio_verify_channel(iface, value)) { |
|
ret = -EINVAL; |
|
NET_ERR("Could not set channel: channel %d not supported by " |
|
"driver", |
|
value); |
|
goto out; |
|
} |
|
|
|
ret = ieee802154_radio_set_channel(iface, value); |
|
if (ret) { |
|
NET_ERR("Could not set channel: driver error (%d)", ret); |
|
} else { |
|
ctx->channel = value; |
|
} |
|
} |
|
} 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); |
|
} |
|
} else if (mgmt_request == NET_REQUEST_IEEE802154_SET_EXT_ADDR) { |
|
uint8_t ext_addr_le[IEEE802154_EXT_ADDR_LENGTH]; |
|
|
|
sys_memcpy_swap(ext_addr_le, data, IEEE802154_EXT_ADDR_LENGTH); |
|
|
|
if (memcmp(ctx->ext_addr, ext_addr_le, IEEE802154_EXT_ADDR_LENGTH)) { |
|
memcpy(ctx->ext_addr, ext_addr_le, IEEE802154_EXT_ADDR_LENGTH); |
|
|
|
if (net_if_get_link_addr(iface)->len == IEEE802154_EXT_ADDR_LENGTH) { |
|
set_linkaddr_to_ext_addr(iface, ctx); |
|
} |
|
|
|
ieee802154_radio_filter_ieee_addr(iface, ctx->ext_addr); |
|
} |
|
} else if (mgmt_request == NET_REQUEST_IEEE802154_SET_SHORT_ADDR) { |
|
if (ctx->short_addr != value) { |
|
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; |
|
NET_ERR("Could not set short address: not yet associated " |
|
"to a PAN"); |
|
goto out; |
|
} |
|
set_association(iface, ctx, value); |
|
} |
|
} |
|
} else if (mgmt_request == NET_REQUEST_IEEE802154_SET_TX_POWER) { |
|
if (ctx->tx_power != (int16_t)value) { |
|
ret = ieee802154_radio_set_tx_power(iface, (int16_t)value); |
|
if (ret) { |
|
NET_ERR("Could not set TX power (%d dB)", value); |
|
} else { |
|
ctx->tx_power = (int16_t)value; |
|
} |
|
} |
|
} |
|
|
|
out: |
|
k_sem_give(&ctx->ctx_lock); |
|
return ret; |
|
} |
|
|
|
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_SET_CHANNEL, |
|
ieee802154_set_parameters); |
|
|
|
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_SET_PAN_ID, |
|
ieee802154_set_parameters); |
|
|
|
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_SET_EXT_ADDR, |
|
ieee802154_set_parameters); |
|
|
|
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_SET_SHORT_ADDR, |
|
ieee802154_set_parameters); |
|
|
|
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_SET_TX_POWER, |
|
ieee802154_set_parameters); |
|
|
|
static int ieee802154_get_parameters(uint64_t mgmt_request, |
|
struct net_if *iface, |
|
void *data, size_t len) |
|
{ |
|
struct ieee802154_context *ctx = net_if_l2_data(iface); |
|
uint16_t *value; |
|
int ret = 0; |
|
|
|
if (!data) { |
|
return -EINVAL; |
|
} |
|
|
|
if (mgmt_request == NET_REQUEST_IEEE802154_GET_EXT_ADDR) { |
|
if (len != IEEE802154_EXT_ADDR_LENGTH) { |
|
NET_ERR("Could not get parameter: invalid extended address length"); |
|
return -EINVAL; |
|
} |
|
} else { |
|
if (len != sizeof(uint16_t)) { |
|
NET_ERR("Could not get parameter: invalid short address length"); |
|
return -EINVAL; |
|
} |
|
} |
|
|
|
value = (uint16_t *)data; |
|
|
|
k_sem_take(&ctx->ctx_lock, K_FOREVER); |
|
|
|
if (mgmt_request == NET_REQUEST_IEEE802154_GET_CHANNEL) { |
|
*value = ctx->channel; |
|
} else if (mgmt_request == NET_REQUEST_IEEE802154_GET_PAN_ID) { |
|
*value = ctx->pan_id; |
|
} else if (mgmt_request == NET_REQUEST_IEEE802154_GET_EXT_ADDR) { |
|
sys_memcpy_swap(data, ctx->ext_addr, IEEE802154_EXT_ADDR_LENGTH); |
|
} else if (mgmt_request == NET_REQUEST_IEEE802154_GET_SHORT_ADDR) { |
|
*value = ctx->short_addr; |
|
} else if (mgmt_request == NET_REQUEST_IEEE802154_GET_TX_POWER) { |
|
int16_t *s_value = (int16_t *)data; |
|
|
|
*s_value = ctx->tx_power; |
|
} |
|
|
|
k_sem_give(&ctx->ctx_lock); |
|
return ret; |
|
} |
|
|
|
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_GET_CHANNEL, |
|
ieee802154_get_parameters); |
|
|
|
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_GET_PAN_ID, |
|
ieee802154_get_parameters); |
|
|
|
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_GET_EXT_ADDR, |
|
ieee802154_get_parameters); |
|
|
|
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_GET_SHORT_ADDR, |
|
ieee802154_get_parameters); |
|
|
|
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_GET_TX_POWER, |
|
ieee802154_get_parameters); |
|
|
|
#ifdef CONFIG_NET_L2_IEEE802154_SECURITY |
|
|
|
static int ieee802154_set_security_settings(uint64_t mgmt_request, |
|
struct net_if *iface, |
|
void *data, size_t len) |
|
{ |
|
struct ieee802154_context *ctx = net_if_l2_data(iface); |
|
struct ieee802154_security_params *params; |
|
int ret = 0; |
|
|
|
if (len != sizeof(struct ieee802154_security_params) || !data) { |
|
return -EINVAL; |
|
} |
|
|
|
params = (struct ieee802154_security_params *)data; |
|
|
|
k_sem_take(&ctx->ctx_lock, K_FOREVER); |
|
|
|
if (is_associated(ctx)) { |
|
ret = -EBUSY; |
|
NET_ERR("Could not set security parameters: already associated to a PAN"); |
|
goto out; |
|
} |
|
|
|
ieee802154_security_teardown_session(&ctx->sec_ctx); |
|
|
|
if (ieee802154_security_setup_session(&ctx->sec_ctx, params->level, |
|
params->key_mode, params->key, |
|
params->key_len)) { |
|
NET_ERR("Could not set security parameters: invalid parameters"); |
|
ret = -EINVAL; |
|
} |
|
|
|
out: |
|
k_sem_give(&ctx->ctx_lock); |
|
return ret; |
|
} |
|
|
|
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_SET_SECURITY_SETTINGS, |
|
ieee802154_set_security_settings); |
|
|
|
static int ieee802154_get_security_settings(uint64_t mgmt_request, |
|
struct net_if *iface, |
|
void *data, size_t len) |
|
{ |
|
struct ieee802154_context *ctx = net_if_l2_data(iface); |
|
struct ieee802154_security_params *params; |
|
|
|
if (len != sizeof(struct ieee802154_security_params) || !data) { |
|
return -EINVAL; |
|
} |
|
|
|
params = (struct ieee802154_security_params *)data; |
|
|
|
k_sem_take(&ctx->ctx_lock, K_FOREVER); |
|
|
|
memcpy(params->key, ctx->sec_ctx.key, ctx->sec_ctx.key_len); |
|
params->key_len = ctx->sec_ctx.key_len; |
|
params->key_mode = ctx->sec_ctx.key_mode; |
|
params->level = ctx->sec_ctx.level; |
|
|
|
k_sem_give(&ctx->ctx_lock); |
|
|
|
return 0; |
|
} |
|
|
|
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_IEEE802154_GET_SECURITY_SETTINGS, |
|
ieee802154_get_security_settings); |
|
|
|
#endif /* CONFIG_NET_L2_IEEE802154_SECURITY */
|
|
|