The L2 function `ieee802154_decipher_data_frame()` relied on upper layer LL address fields which breaks encapsulation. Also fixes a bug introduced in another fix that went overboard (#53734). Fixes: #78490 Signed-off-by: Florian Grandel <fgrandel@code-for-humans.de>
679 lines
19 KiB
C
679 lines
19 KiB
C
/*
|
|
* Copyright (c) 2016 Intel Corporation.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
* @brief IEEE 802.15.4 MAC layer implementation
|
|
*
|
|
* All references to the spec refer to IEEE 802.15.4-2020.
|
|
*/
|
|
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(net_ieee802154, CONFIG_NET_L2_IEEE802154_LOG_LEVEL);
|
|
|
|
#include <errno.h>
|
|
|
|
#include <zephyr/net/capture.h>
|
|
#include <zephyr/net/ethernet.h>
|
|
#include <zephyr/net/net_core.h>
|
|
#include <zephyr/net/net_if.h>
|
|
#include <zephyr/net/net_l2.h>
|
|
#include <zephyr/net/net_linkaddr.h>
|
|
#include <zephyr/random/random.h>
|
|
|
|
#ifdef CONFIG_NET_6LO
|
|
#include "ieee802154_6lo.h"
|
|
|
|
#include <6lo.h>
|
|
#include <ipv6.h>
|
|
|
|
#ifdef CONFIG_NET_L2_IEEE802154_FRAGMENT
|
|
#include "ieee802154_6lo_fragment.h"
|
|
#endif /* CONFIG_NET_L2_IEEE802154_FRAGMENT */
|
|
#endif /* CONFIG_NET_6LO */
|
|
|
|
#include "ieee802154_frame.h"
|
|
#include "ieee802154_mgmt_priv.h"
|
|
#include "ieee802154_priv.h"
|
|
#include "ieee802154_security.h"
|
|
#include "ieee802154_utils.h"
|
|
|
|
#define BUF_TIMEOUT K_MSEC(50)
|
|
|
|
NET_BUF_POOL_DEFINE(tx_frame_buf_pool, 1, IEEE802154_MTU, 8, NULL);
|
|
|
|
#define PKT_TITLE "IEEE 802.15.4 packet content:"
|
|
#define TX_PKT_TITLE "> " PKT_TITLE
|
|
#define RX_PKT_TITLE "< " PKT_TITLE
|
|
|
|
#ifdef CONFIG_NET_DEBUG_L2_IEEE802154_DISPLAY_PACKET
|
|
|
|
#include "net_private.h"
|
|
|
|
static inline void pkt_hexdump(const char *title, struct net_pkt *pkt, bool in)
|
|
{
|
|
if (IS_ENABLED(CONFIG_NET_DEBUG_L2_IEEE802154_DISPLAY_PACKET_RX) && in) {
|
|
net_pkt_hexdump(pkt, title);
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_NET_DEBUG_L2_IEEE802154_DISPLAY_PACKET_TX) && !in) {
|
|
net_pkt_hexdump(pkt, title);
|
|
}
|
|
}
|
|
|
|
#else
|
|
#define pkt_hexdump(...)
|
|
#endif /* CONFIG_NET_DEBUG_L2_IEEE802154_DISPLAY_PACKET */
|
|
|
|
static inline void ieee802154_acknowledge(struct net_if *iface, struct ieee802154_mpdu *mpdu)
|
|
{
|
|
struct net_pkt *pkt;
|
|
|
|
if (ieee802154_radio_get_hw_capabilities(iface) & IEEE802154_HW_RX_TX_ACK) {
|
|
return;
|
|
}
|
|
|
|
if (!mpdu->mhr.fs->fc.ar) {
|
|
return;
|
|
}
|
|
|
|
pkt = net_pkt_alloc_with_buffer(iface, IEEE802154_ACK_PKT_LENGTH, AF_UNSPEC, 0,
|
|
BUF_TIMEOUT);
|
|
if (!pkt) {
|
|
return;
|
|
}
|
|
|
|
if (ieee802154_create_ack_frame(iface, pkt, mpdu->mhr.fs->sequence)) {
|
|
/* ACK frames must not use the CSMA/CA procedure, see section 6.2.5.1. */
|
|
ieee802154_radio_tx(iface, IEEE802154_TX_MODE_DIRECT, pkt, pkt->buffer);
|
|
}
|
|
|
|
net_pkt_unref(pkt);
|
|
|
|
return;
|
|
}
|
|
|
|
inline bool ieee802154_prepare_for_ack(struct net_if *iface, struct net_pkt *pkt,
|
|
struct net_buf *frag)
|
|
{
|
|
bool ack_required = ieee802154_is_ar_flag_set(frag);
|
|
|
|
if (ieee802154_radio_get_hw_capabilities(iface) & IEEE802154_HW_TX_RX_ACK) {
|
|
return ack_required;
|
|
}
|
|
|
|
if (ack_required) {
|
|
struct ieee802154_fcf_seq *fs = (struct ieee802154_fcf_seq *)frag->data;
|
|
struct ieee802154_context *ctx = net_if_l2_data(iface);
|
|
|
|
ctx->ack_seq = fs->sequence;
|
|
if (k_sem_count_get(&ctx->ack_lock) == 1U) {
|
|
k_sem_take(&ctx->ack_lock, K_NO_WAIT);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
enum net_verdict ieee802154_handle_ack(struct net_if *iface, struct net_pkt *pkt)
|
|
{
|
|
struct ieee802154_context *ctx = net_if_l2_data(iface);
|
|
|
|
if (ieee802154_radio_get_hw_capabilities(iface) & IEEE802154_HW_TX_RX_ACK) {
|
|
__ASSERT_NO_MSG(ctx->ack_seq == 0U);
|
|
/* TODO: Release packet in L2 as we're taking ownership. */
|
|
return NET_OK;
|
|
}
|
|
|
|
if (pkt->buffer->len == IEEE802154_ACK_PKT_LENGTH) {
|
|
uint8_t len = IEEE802154_ACK_PKT_LENGTH;
|
|
struct ieee802154_fcf_seq *fs;
|
|
|
|
fs = ieee802154_validate_fc_seq(net_pkt_data(pkt), NULL, &len);
|
|
if (!fs || fs->fc.frame_type != IEEE802154_FRAME_TYPE_ACK ||
|
|
fs->sequence != ctx->ack_seq) {
|
|
return NET_CONTINUE;
|
|
}
|
|
|
|
k_sem_give(&ctx->ack_lock);
|
|
|
|
/* TODO: Release packet in L2 as we're taking ownership. */
|
|
return NET_OK;
|
|
}
|
|
|
|
return NET_CONTINUE;
|
|
}
|
|
|
|
inline int ieee802154_wait_for_ack(struct net_if *iface, bool ack_required)
|
|
{
|
|
struct ieee802154_context *ctx = net_if_l2_data(iface);
|
|
int ret;
|
|
|
|
if (!ack_required ||
|
|
(ieee802154_radio_get_hw_capabilities(iface) & IEEE802154_HW_TX_RX_ACK)) {
|
|
__ASSERT_NO_MSG(ctx->ack_seq == 0U);
|
|
return 0;
|
|
}
|
|
|
|
ret = k_sem_take(&ctx->ack_lock, K_MSEC(10));
|
|
if (ret == 0) {
|
|
/* no-op */
|
|
} else if (ret == -EAGAIN) {
|
|
ret = -ETIME;
|
|
} else {
|
|
NET_ERR("Error while waiting for ACK.");
|
|
ret = -EFAULT;
|
|
}
|
|
|
|
ctx->ack_seq = 0U;
|
|
return ret;
|
|
}
|
|
|
|
int ieee802154_radio_send(struct net_if *iface, struct net_pkt *pkt, struct net_buf *frag)
|
|
{
|
|
uint8_t remaining_attempts = CONFIG_NET_L2_IEEE802154_RADIO_TX_RETRIES + 1;
|
|
bool hw_csma, ack_required;
|
|
int ret;
|
|
|
|
NET_DBG("frag %p", frag);
|
|
|
|
if (ieee802154_radio_get_hw_capabilities(iface) & IEEE802154_HW_RETRANSMISSION) {
|
|
/* A driver that claims retransmission capability must also be able
|
|
* to wait for ACK frames otherwise it could not decide whether or
|
|
* not retransmission is required in a standard conforming way.
|
|
*/
|
|
__ASSERT_NO_MSG(ieee802154_radio_get_hw_capabilities(iface) &
|
|
IEEE802154_HW_TX_RX_ACK);
|
|
remaining_attempts = 1;
|
|
}
|
|
|
|
hw_csma = IS_ENABLED(CONFIG_NET_L2_IEEE802154_RADIO_CSMA_CA) &&
|
|
ieee802154_radio_get_hw_capabilities(iface) & IEEE802154_HW_CSMA;
|
|
|
|
/* Media access (CSMA, ALOHA, ...) and retransmission, see section 6.7.4.4. */
|
|
while (remaining_attempts) {
|
|
if (!hw_csma) {
|
|
ret = ieee802154_wait_for_clear_channel(iface);
|
|
if (ret != 0) {
|
|
NET_WARN("Clear channel assessment failed: dropping fragment %p on "
|
|
"interface %p.",
|
|
frag, iface);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/* No-op in case the driver has IEEE802154_HW_TX_RX_ACK capability. */
|
|
ack_required = ieee802154_prepare_for_ack(iface, pkt, frag);
|
|
|
|
/* TX including:
|
|
* - CSMA/CA in case the driver has IEEE802154_HW_CSMA capability,
|
|
* - waiting for ACK in case the driver has IEEE802154_HW_TX_RX_ACK capability,
|
|
* - retransmission on ACK timeout in case the driver has
|
|
* IEEE802154_HW_RETRANSMISSION capability.
|
|
*/
|
|
ret = ieee802154_radio_tx(
|
|
iface, hw_csma ? IEEE802154_TX_MODE_CSMA_CA : IEEE802154_TX_MODE_DIRECT,
|
|
pkt, frag);
|
|
if (ret) {
|
|
/* Transmission failure. */
|
|
return ret;
|
|
}
|
|
|
|
if (!ack_required) {
|
|
/* See section 6.7.4.4: "A device that sends a frame with the AR field set
|
|
* to indicate no acknowledgment requested may assume that the transmission
|
|
* was successfully received and shall not perform the retransmission
|
|
* procedure."
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* No-op in case the driver has IEEE802154_HW_TX_RX_ACK capability. */
|
|
ret = ieee802154_wait_for_ack(iface, ack_required);
|
|
if (ret == 0) {
|
|
/* ACK received - transmission is successful. */
|
|
return 0;
|
|
}
|
|
|
|
remaining_attempts--;
|
|
}
|
|
|
|
return -EIO;
|
|
}
|
|
|
|
static inline void swap_and_set_pkt_ll_addr(struct net_linkaddr *addr, bool has_pan_id,
|
|
enum ieee802154_addressing_mode mode,
|
|
struct ieee802154_address_field *ll)
|
|
{
|
|
addr->type = NET_LINK_IEEE802154;
|
|
|
|
switch (mode) {
|
|
case IEEE802154_ADDR_MODE_EXTENDED:
|
|
addr->len = IEEE802154_EXT_ADDR_LENGTH;
|
|
addr->addr = has_pan_id ? ll->plain.addr.ext_addr : ll->comp.addr.ext_addr;
|
|
break;
|
|
|
|
case IEEE802154_ADDR_MODE_SHORT:
|
|
addr->len = IEEE802154_SHORT_ADDR_LENGTH;
|
|
addr->addr = (uint8_t *)(has_pan_id ? &ll->plain.addr.short_addr
|
|
: &ll->comp.addr.short_addr);
|
|
break;
|
|
|
|
case IEEE802154_ADDR_MODE_NONE:
|
|
default:
|
|
addr->len = 0U;
|
|
addr->addr = NULL;
|
|
}
|
|
|
|
/* The net stack expects big endian link layer addresses for POSIX compliance
|
|
* so we must swap it. This is ok as the L2 address field points into the L2
|
|
* header of the frame buffer which will no longer be accessible once the
|
|
* packet reaches upper layers.
|
|
*/
|
|
if (addr->len > 0) {
|
|
sys_mem_swap(addr->addr, addr->len);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Filters the destination address of the frame.
|
|
*
|
|
* This is done before deciphering and authenticating encrypted frames.
|
|
*/
|
|
static bool ieee802154_check_dst_addr(struct net_if *iface, struct ieee802154_mhr *mhr)
|
|
{
|
|
struct ieee802154_address_field_plain *dst_plain = &mhr->dst_addr->plain;
|
|
struct ieee802154_context *ctx = net_if_l2_data(iface);
|
|
bool ret = false;
|
|
|
|
/* Apply filtering requirements from section 6.7.2 c)-e). For a)-b),
|
|
* see ieee802154_parse_fcf_seq()
|
|
*/
|
|
|
|
if (mhr->fs->fc.dst_addr_mode == IEEE802154_ADDR_MODE_NONE) {
|
|
if (mhr->fs->fc.frame_version < IEEE802154_VERSION_802154 &&
|
|
mhr->fs->fc.frame_type == IEEE802154_FRAME_TYPE_BEACON) {
|
|
/* See IEEE 802.15.4-2015, section 7.3.1.1. */
|
|
return true;
|
|
}
|
|
|
|
/* TODO: apply d.4 and d.5 when PAN coordinator is implemented */
|
|
/* also, macImplicitBroadcast is not implemented */
|
|
return false;
|
|
}
|
|
|
|
k_sem_take(&ctx->ctx_lock, K_FOREVER);
|
|
|
|
/* c) If a destination PAN ID is included in the frame, it shall match
|
|
* macPanId or shall be the broadcast PAN ID.
|
|
*/
|
|
if (!(dst_plain->pan_id == IEEE802154_BROADCAST_PAN_ID ||
|
|
dst_plain->pan_id == sys_cpu_to_le16(ctx->pan_id))) {
|
|
LOG_DBG("Frame PAN ID does not match!");
|
|
goto out;
|
|
}
|
|
|
|
if (mhr->fs->fc.dst_addr_mode == IEEE802154_ADDR_MODE_SHORT) {
|
|
/* d.1) A short destination address is included in the frame,
|
|
* and it matches either macShortAddress or the broadcast
|
|
* address.
|
|
*/
|
|
if (!(dst_plain->addr.short_addr == IEEE802154_BROADCAST_ADDRESS ||
|
|
dst_plain->addr.short_addr == sys_cpu_to_le16(ctx->short_addr))) {
|
|
LOG_DBG("Frame dst address (short) does not match!");
|
|
goto out;
|
|
}
|
|
|
|
} else if (mhr->fs->fc.dst_addr_mode == IEEE802154_ADDR_MODE_EXTENDED) {
|
|
/* d.2) An extended destination address is included in the frame and
|
|
* matches [...] macExtendedAddress [...].
|
|
*/
|
|
if (memcmp(dst_plain->addr.ext_addr, ctx->ext_addr,
|
|
IEEE802154_EXT_ADDR_LENGTH) != 0) {
|
|
LOG_DBG("Frame dst address (ext) does not match!");
|
|
goto out;
|
|
}
|
|
|
|
/* TODO: d.3) The Destination Address field and the Destination PAN ID
|
|
* field are not included in the frame and macImplicitBroadcast is TRUE.
|
|
*/
|
|
|
|
/* TODO: d.4) The device is the PAN coordinator, only source addressing fields
|
|
* are included in a Data frame or MAC command and the source PAN ID
|
|
* matches macPanId.
|
|
*/
|
|
}
|
|
ret = true;
|
|
|
|
out:
|
|
k_sem_give(&ctx->ctx_lock);
|
|
return ret;
|
|
}
|
|
|
|
static enum net_verdict ieee802154_recv(struct net_if *iface, struct net_pkt *pkt)
|
|
{
|
|
const struct ieee802154_radio_api *radio = net_if_get_device(iface)->api;
|
|
enum net_verdict verdict = NET_CONTINUE;
|
|
struct ieee802154_fcf_seq *fs;
|
|
struct ieee802154_mpdu mpdu;
|
|
bool is_broadcast;
|
|
size_t ll_hdr_len;
|
|
|
|
/* The IEEE 802.15.4 stack assumes that drivers provide a single-fragment package. */
|
|
__ASSERT_NO_MSG(pkt->buffer && pkt->buffer->frags == NULL);
|
|
|
|
if (!ieee802154_validate_frame(net_pkt_data(pkt), net_pkt_get_len(pkt), &mpdu)) {
|
|
return NET_DROP;
|
|
}
|
|
|
|
/* validate LL destination address (when IEEE802154_HW_FILTER not available) */
|
|
if (!(radio->get_capabilities(net_if_get_device(iface)) & IEEE802154_HW_FILTER) &&
|
|
!ieee802154_check_dst_addr(iface, &mpdu.mhr)) {
|
|
return NET_DROP;
|
|
}
|
|
|
|
fs = mpdu.mhr.fs;
|
|
|
|
if (fs->fc.frame_type == IEEE802154_FRAME_TYPE_ACK) {
|
|
return NET_DROP;
|
|
}
|
|
|
|
if (fs->fc.frame_type == IEEE802154_FRAME_TYPE_BEACON) {
|
|
verdict = ieee802154_handle_beacon(iface, &mpdu, net_pkt_ieee802154_lqi(pkt));
|
|
if (verdict == NET_CONTINUE) {
|
|
net_pkt_unref(pkt);
|
|
return NET_OK;
|
|
}
|
|
/* Beacons must not be acknowledged, see section 6.7.4.1. */
|
|
return verdict;
|
|
}
|
|
|
|
if (ieee802154_is_scanning(iface)) {
|
|
return NET_DROP;
|
|
}
|
|
|
|
if (fs->fc.frame_type == IEEE802154_FRAME_TYPE_MAC_COMMAND) {
|
|
verdict = ieee802154_handle_mac_command(iface, &mpdu);
|
|
if (verdict == NET_DROP) {
|
|
return verdict;
|
|
}
|
|
}
|
|
|
|
/* At this point the frame is either a MAC command or a data frame
|
|
* which may have to be acknowledged, see section 6.7.4.1.
|
|
*/
|
|
|
|
is_broadcast = false;
|
|
|
|
if (fs->fc.dst_addr_mode == IEEE802154_ADDR_MODE_SHORT) {
|
|
struct ieee802154_address_field *dst_addr = mpdu.mhr.dst_addr;
|
|
uint16_t short_dst_addr;
|
|
|
|
short_dst_addr = fs->fc.pan_id_comp ? dst_addr->comp.addr.short_addr
|
|
: dst_addr->plain.addr.short_addr;
|
|
is_broadcast = short_dst_addr == IEEE802154_BROADCAST_ADDRESS;
|
|
}
|
|
|
|
/* Frames that are broadcast must not be acknowledged, see section 6.7.2. */
|
|
if (!is_broadcast) {
|
|
ieee802154_acknowledge(iface, &mpdu);
|
|
}
|
|
|
|
if (fs->fc.frame_type == IEEE802154_FRAME_TYPE_MAC_COMMAND) {
|
|
net_pkt_unref(pkt);
|
|
return NET_OK;
|
|
}
|
|
|
|
if (!ieee802154_decipher_data_frame(iface, pkt, &mpdu)) {
|
|
return NET_DROP;
|
|
}
|
|
|
|
/* Setting LL addresses for upper layers must be done after L2 packet
|
|
* handling as it will mangle the L2 frame header to comply with upper
|
|
* layers' (POSIX) requirement to represent network addresses in big endian.
|
|
*/
|
|
swap_and_set_pkt_ll_addr(net_pkt_lladdr_src(pkt), !fs->fc.pan_id_comp,
|
|
fs->fc.src_addr_mode, mpdu.mhr.src_addr);
|
|
|
|
swap_and_set_pkt_ll_addr(net_pkt_lladdr_dst(pkt), true, fs->fc.dst_addr_mode,
|
|
mpdu.mhr.dst_addr);
|
|
|
|
net_pkt_set_ll_proto_type(pkt, ETH_P_IEEE802154);
|
|
|
|
pkt_hexdump(RX_PKT_TITLE " (with ll)", pkt, true);
|
|
|
|
ll_hdr_len = (uint8_t *)mpdu.payload - net_pkt_data(pkt);
|
|
net_buf_pull(pkt->buffer, ll_hdr_len);
|
|
|
|
#ifdef CONFIG_NET_6LO
|
|
verdict = ieee802154_6lo_decode_pkt(iface, pkt);
|
|
#endif /* CONFIG_NET_6LO */
|
|
|
|
if (verdict == NET_CONTINUE) {
|
|
pkt_hexdump(RX_PKT_TITLE, pkt, true);
|
|
}
|
|
|
|
return verdict;
|
|
|
|
/* At this point the call amounts to (part of) an
|
|
* MCPS-DATA.indication primitive, see section 8.3.3.
|
|
*/
|
|
}
|
|
|
|
/**
|
|
* Implements (part of) the MCPS-DATA.request/confirm primitives, see sections 8.3.2/3.
|
|
*/
|
|
static int ieee802154_send(struct net_if *iface, struct net_pkt *pkt)
|
|
{
|
|
struct ieee802154_context *ctx = net_if_l2_data(iface);
|
|
uint8_t ll_hdr_len = 0, authtag_len = 0;
|
|
static struct net_buf *frame_buf;
|
|
static struct net_buf *pkt_buf;
|
|
bool send_raw = false;
|
|
int len;
|
|
#ifdef CONFIG_NET_L2_IEEE802154_FRAGMENT
|
|
struct ieee802154_6lo_fragment_ctx frag_ctx;
|
|
int requires_fragmentation = 0;
|
|
#endif
|
|
|
|
if (frame_buf == NULL) {
|
|
frame_buf = net_buf_alloc(&tx_frame_buf_pool, K_FOREVER);
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_NET_SOCKETS_PACKET) && net_pkt_family(pkt) == AF_PACKET) {
|
|
enum net_sock_type socket_type;
|
|
struct net_context *context;
|
|
|
|
context = net_pkt_context(pkt);
|
|
if (!context) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
socket_type = net_context_get_type(context);
|
|
if (socket_type == SOCK_RAW) {
|
|
send_raw = true;
|
|
} else if (IS_ENABLED(CONFIG_NET_SOCKETS_PACKET_DGRAM) &&
|
|
socket_type == SOCK_DGRAM) {
|
|
struct sockaddr_ll *dst_addr = (struct sockaddr_ll *)&context->remote;
|
|
struct sockaddr_ll_ptr *src_addr =
|
|
(struct sockaddr_ll_ptr *)&context->local;
|
|
|
|
net_pkt_lladdr_dst(pkt)->addr = dst_addr->sll_addr;
|
|
net_pkt_lladdr_dst(pkt)->len = dst_addr->sll_halen;
|
|
net_pkt_lladdr_src(pkt)->addr = src_addr->sll_addr;
|
|
net_pkt_lladdr_src(pkt)->len = src_addr->sll_halen;
|
|
} else {
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
if (!send_raw) {
|
|
ieee802154_compute_header_and_authtag_len(iface, net_pkt_lladdr_dst(pkt),
|
|
net_pkt_lladdr_src(pkt), &ll_hdr_len,
|
|
&authtag_len);
|
|
|
|
#ifdef CONFIG_NET_6LO
|
|
#ifdef CONFIG_NET_L2_IEEE802154_FRAGMENT
|
|
requires_fragmentation =
|
|
ieee802154_6lo_encode_pkt(iface, pkt, &frag_ctx, ll_hdr_len, authtag_len);
|
|
if (requires_fragmentation < 0) {
|
|
return requires_fragmentation;
|
|
}
|
|
#else
|
|
ieee802154_6lo_encode_pkt(iface, pkt, NULL, ll_hdr_len, authtag_len);
|
|
#endif /* CONFIG_NET_L2_IEEE802154_FRAGMENT */
|
|
#endif /* CONFIG_NET_6LO */
|
|
}
|
|
|
|
net_capture_pkt(iface, pkt);
|
|
|
|
len = 0;
|
|
pkt_buf = pkt->buffer;
|
|
while (pkt_buf) {
|
|
int ret;
|
|
|
|
/* Reinitializing frame_buf */
|
|
net_buf_reset(frame_buf);
|
|
net_buf_add(frame_buf, ll_hdr_len);
|
|
|
|
#ifdef CONFIG_NET_L2_IEEE802154_FRAGMENT
|
|
if (requires_fragmentation) {
|
|
pkt_buf = ieee802154_6lo_fragment(&frag_ctx, frame_buf, true);
|
|
} else {
|
|
net_buf_add_mem(frame_buf, pkt_buf->data, pkt_buf->len);
|
|
pkt_buf = pkt_buf->frags;
|
|
}
|
|
#else
|
|
if (ll_hdr_len + pkt_buf->len + authtag_len > IEEE802154_MTU) {
|
|
NET_ERR("Frame too long: %d", pkt_buf->len);
|
|
return -EINVAL;
|
|
}
|
|
net_buf_add_mem(frame_buf, pkt_buf->data, pkt_buf->len);
|
|
pkt_buf = pkt_buf->frags;
|
|
#endif /* CONFIG_NET_L2_IEEE802154_FRAGMENT */
|
|
|
|
__ASSERT_NO_MSG(authtag_len <= net_buf_tailroom(frame_buf));
|
|
net_buf_add(frame_buf, authtag_len);
|
|
|
|
if (!(send_raw || ieee802154_create_data_frame(ctx, net_pkt_lladdr_dst(pkt),
|
|
net_pkt_lladdr_src(pkt),
|
|
frame_buf, ll_hdr_len))) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = ieee802154_radio_send(iface, pkt, frame_buf);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
len += frame_buf->len;
|
|
}
|
|
|
|
net_pkt_unref(pkt);
|
|
|
|
return len;
|
|
}
|
|
|
|
static int ieee802154_enable(struct net_if *iface, bool state)
|
|
{
|
|
struct ieee802154_context *ctx = net_if_l2_data(iface);
|
|
|
|
NET_DBG("iface %p %s", iface, state ? "up" : "down");
|
|
|
|
k_sem_take(&ctx->ctx_lock, K_FOREVER);
|
|
|
|
if (ctx->channel == IEEE802154_NO_CHANNEL) {
|
|
k_sem_give(&ctx->ctx_lock);
|
|
return -ENETDOWN;
|
|
}
|
|
|
|
k_sem_give(&ctx->ctx_lock);
|
|
|
|
if (state) {
|
|
return ieee802154_radio_start(iface);
|
|
}
|
|
|
|
return ieee802154_radio_stop(iface);
|
|
}
|
|
|
|
static enum net_l2_flags ieee802154_flags(struct net_if *iface)
|
|
{
|
|
struct ieee802154_context *ctx = net_if_l2_data(iface);
|
|
|
|
/* No need for locking as these flags are set once
|
|
* during L2 initialization and then never changed.
|
|
*/
|
|
return ctx->flags;
|
|
}
|
|
|
|
NET_L2_INIT(IEEE802154_L2, ieee802154_recv, ieee802154_send, ieee802154_enable, ieee802154_flags);
|
|
|
|
void ieee802154_init(struct net_if *iface)
|
|
{
|
|
struct ieee802154_context *ctx = net_if_l2_data(iface);
|
|
const uint8_t *eui64_be = net_if_get_link_addr(iface)->addr;
|
|
int16_t tx_power = CONFIG_NET_L2_IEEE802154_RADIO_DFLT_TX_POWER;
|
|
|
|
NET_DBG("Initializing IEEE 802.15.4 stack on iface %p", iface);
|
|
|
|
k_sem_init(&ctx->ctx_lock, 1, 1);
|
|
k_sem_init(&ctx->ack_lock, 0, 1);
|
|
|
|
/* no need to lock the context here as it has
|
|
* not been published yet.
|
|
*/
|
|
|
|
/* See section 6.7.1 - Transmission: "Each device shall initialize its data sequence number
|
|
* (DSN) to a random value and store its current DSN value in the MAC PIB attribute macDsn
|
|
* [...]."
|
|
*/
|
|
ctx->sequence = sys_rand32_get() & 0xFF;
|
|
|
|
ctx->channel = IEEE802154_NO_CHANNEL;
|
|
ctx->flags = NET_L2_MULTICAST;
|
|
if (ieee802154_radio_get_hw_capabilities(iface) & IEEE802154_HW_PROMISC) {
|
|
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
|
|
* own so that we can write user defined short
|
|
* or extended addresses w/o mutating internal
|
|
* driver storage.
|
|
*/
|
|
ctx->linkaddr.type = NET_LINK_IEEE802154;
|
|
ctx->linkaddr.len = IEEE802154_EXT_ADDR_LENGTH;
|
|
memcpy(ctx->linkaddr.addr, eui64_be, IEEE802154_EXT_ADDR_LENGTH);
|
|
net_if_set_link_addr(iface, ctx->linkaddr.addr, ctx->linkaddr.len, ctx->linkaddr.type);
|
|
|
|
if (IS_ENABLED(CONFIG_IEEE802154_NET_IF_NO_AUTO_START) ||
|
|
IS_ENABLED(CONFIG_NET_CONFIG_SETTINGS)) {
|
|
LOG_DBG("Interface auto start disabled.");
|
|
net_if_flag_set(iface, NET_IF_NO_AUTO_START);
|
|
}
|
|
|
|
ieee802154_mgmt_init(iface);
|
|
|
|
#ifdef CONFIG_NET_L2_IEEE802154_SECURITY
|
|
if (ieee802154_security_init(&ctx->sec_ctx)) {
|
|
NET_ERR("Initializing link-layer security failed");
|
|
}
|
|
#endif
|
|
|
|
sys_memcpy_swap(ctx->ext_addr, eui64_be, IEEE802154_EXT_ADDR_LENGTH);
|
|
ieee802154_radio_filter_ieee_addr(iface, ctx->ext_addr);
|
|
|
|
if (!ieee802154_radio_set_tx_power(iface, tx_power)) {
|
|
ctx->tx_power = tx_power;
|
|
}
|
|
}
|