2016-06-16 16:00:11 +02:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2016 Intel Corporation.
|
|
|
|
*
|
2017-01-18 17:01:01 -08:00
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
2016-06-16 16:00:11 +02:00
|
|
|
*/
|
|
|
|
|
2016-12-15 13:55:01 +01:00
|
|
|
#if defined(CONFIG_NET_DEBUG_L2_IEEE802154)
|
2016-06-16 16:00:11 +02:00
|
|
|
#define SYS_LOG_DOMAIN "net/ieee802154"
|
2016-12-15 13:55:01 +01:00
|
|
|
#define NET_LOG_ENABLED 1
|
2016-06-16 16:00:11 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <net/net_core.h>
|
|
|
|
#include <net/net_l2.h>
|
|
|
|
#include <net/net_if.h>
|
|
|
|
|
2016-11-08 12:33:53 +01:00
|
|
|
#include "ipv6.h"
|
|
|
|
|
2016-06-16 16:00:11 +02:00
|
|
|
#include <errno.h>
|
|
|
|
|
2016-07-07 14:11:06 +02:00
|
|
|
#ifdef CONFIG_NET_6LO
|
2016-11-16 09:39:31 +01:00
|
|
|
#ifdef CONFIG_NET_L2_IEEE802154_FRAGMENT
|
|
|
|
#include "ieee802154_fragment.h"
|
|
|
|
#endif
|
2016-07-07 14:11:06 +02:00
|
|
|
#include <6lo.h>
|
|
|
|
#endif /* CONFIG_NET_6LO */
|
|
|
|
|
2016-06-16 16:00:11 +02:00
|
|
|
#include <net/ieee802154_radio.h>
|
|
|
|
|
|
|
|
#include "ieee802154_frame.h"
|
2016-10-07 10:53:22 +02:00
|
|
|
#include "ieee802154_mgmt.h"
|
2017-03-10 15:22:21 +01:00
|
|
|
#include "ieee802154_security.h"
|
2017-09-05 13:52:29 +02:00
|
|
|
#include "ieee802154_utils.h"
|
2016-06-16 16:00:11 +02:00
|
|
|
|
2017-10-18 17:41:04 +03:00
|
|
|
#define PKT_TITLE "IEEE 802.15.4 packet content:"
|
|
|
|
#define TX_PKT_TITLE "> " PKT_TITLE
|
|
|
|
#define RX_PKT_TITLE "< " PKT_TITLE
|
|
|
|
|
2017-09-29 09:40:15 +02:00
|
|
|
#ifdef CONFIG_NET_DEBUG_L2_IEEE802154_DISPLAY_PACKET
|
2016-07-12 16:09:29 +02:00
|
|
|
|
2017-08-17 15:54:00 +02:00
|
|
|
#include "net_private.h"
|
2016-07-12 16:09:29 +02:00
|
|
|
|
2017-10-18 17:41:04 +03:00
|
|
|
static inline void pkt_hexdump(const char *title, struct net_pkt *pkt,
|
|
|
|
bool full)
|
2016-07-12 16:09:29 +02:00
|
|
|
{
|
2017-10-18 17:41:04 +03:00
|
|
|
net_hexdump_frags(title, pkt, full);
|
2016-07-12 16:09:29 +02:00
|
|
|
}
|
2016-10-26 15:37:05 +02:00
|
|
|
|
2016-07-12 16:09:29 +02:00
|
|
|
#else
|
|
|
|
#define pkt_hexdump(...)
|
2017-09-29 09:40:15 +02:00
|
|
|
#endif /* CONFIG_NET_DEBUG_L2_IEEE802154_DISPLAY_PACKET */
|
2016-07-12 16:09:29 +02:00
|
|
|
|
2016-06-28 15:05:07 +02:00
|
|
|
#ifdef CONFIG_NET_L2_IEEE802154_ACK_REPLY
|
|
|
|
static inline void ieee802154_acknowledge(struct net_if *iface,
|
|
|
|
struct ieee802154_mpdu *mpdu)
|
|
|
|
{
|
2017-04-05 08:37:44 +02:00
|
|
|
struct net_pkt *pkt;
|
|
|
|
struct net_buf *frag;
|
2016-06-28 15:05:07 +02:00
|
|
|
|
|
|
|
if (!mpdu->mhr.fs->fc.ar) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
pkt = net_pkt_get_reserve_tx(IEEE802154_ACK_PKT_LENGTH, K_FOREVER);
|
|
|
|
if (!pkt) {
|
2016-06-28 15:05:07 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
frag = net_pkt_get_frag(pkt, K_FOREVER);
|
2016-06-28 15:05:07 +02:00
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
net_pkt_frag_insert(pkt, frag);
|
2016-06-28 15:05:07 +02:00
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
if (ieee802154_create_ack_frame(iface, pkt, mpdu->mhr.fs->sequence)) {
|
2016-12-20 10:29:52 +00:00
|
|
|
const struct ieee802154_radio_api *radio =
|
|
|
|
iface->dev->driver_api;
|
2016-06-28 15:05:07 +02:00
|
|
|
|
|
|
|
net_buf_add(frag, IEEE802154_ACK_PKT_LENGTH);
|
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
radio->tx(iface->dev, pkt, frag);
|
2016-06-28 15:05:07 +02:00
|
|
|
}
|
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
net_pkt_unref(pkt);
|
2016-06-28 15:05:07 +02:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
#define ieee802154_acknowledge(...)
|
|
|
|
#endif /* CONFIG_NET_L2_IEEE802154_ACK_REPLY */
|
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
static inline void set_pkt_ll_addr(struct net_linkaddr *addr, bool comp,
|
2016-08-10 15:39:01 +02:00
|
|
|
enum ieee802154_addressing_mode mode,
|
|
|
|
struct ieee802154_address_field *ll)
|
|
|
|
{
|
|
|
|
if (mode == IEEE802154_ADDR_MODE_NONE) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mode == IEEE802154_ADDR_MODE_EXTENDED) {
|
|
|
|
addr->len = IEEE802154_EXT_ADDR_LENGTH;
|
|
|
|
|
|
|
|
if (comp) {
|
|
|
|
addr->addr = ll->comp.addr.ext_addr;
|
|
|
|
} else {
|
|
|
|
addr->addr = ll->plain.addr.ext_addr;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* ToDo: Handle short address (lookup known nbr, ...) */
|
|
|
|
addr->len = 0;
|
|
|
|
addr->addr = NULL;
|
|
|
|
}
|
2017-02-15 13:20:31 +02:00
|
|
|
|
|
|
|
addr->type = NET_LINK_IEEE802154;
|
2016-08-10 15:39:01 +02:00
|
|
|
}
|
|
|
|
|
2016-07-07 14:11:06 +02:00
|
|
|
#ifdef CONFIG_NET_6LO
|
2016-10-25 09:59:33 +02:00
|
|
|
static inline
|
2017-04-05 08:37:44 +02:00
|
|
|
enum net_verdict ieee802154_manage_recv_packet(struct net_if *iface,
|
|
|
|
struct net_pkt *pkt)
|
2016-10-25 09:59:33 +02:00
|
|
|
{
|
|
|
|
enum net_verdict verdict = NET_CONTINUE;
|
2017-04-21 09:27:50 -05:00
|
|
|
u32_t src;
|
|
|
|
u32_t dst;
|
2016-10-25 09:59:33 +02:00
|
|
|
|
2016-10-31 15:02:47 +02:00
|
|
|
/* Upper IP stack expects the link layer address to be in
|
|
|
|
* big endian format so we must swap it here.
|
|
|
|
*/
|
2017-04-05 08:37:44 +02:00
|
|
|
if (net_pkt_ll_src(pkt)->addr &&
|
|
|
|
net_pkt_ll_src(pkt)->len == IEEE802154_EXT_ADDR_LENGTH) {
|
|
|
|
sys_mem_swap(net_pkt_ll_src(pkt)->addr,
|
|
|
|
net_pkt_ll_src(pkt)->len);
|
2016-10-31 15:02:47 +02:00
|
|
|
}
|
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
if (net_pkt_ll_dst(pkt)->addr &&
|
|
|
|
net_pkt_ll_dst(pkt)->len == IEEE802154_EXT_ADDR_LENGTH) {
|
|
|
|
sys_mem_swap(net_pkt_ll_dst(pkt)->addr,
|
|
|
|
net_pkt_ll_dst(pkt)->len);
|
2016-10-31 15:02:47 +02:00
|
|
|
}
|
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
/** Uncompress will drop the current fragment. Pkt ll src/dst address
|
2016-10-25 09:59:33 +02:00
|
|
|
* will then be wrong and must be updated according to the new fragment.
|
|
|
|
*/
|
2017-04-05 08:37:44 +02:00
|
|
|
src = net_pkt_ll_src(pkt)->addr ?
|
|
|
|
net_pkt_ll_src(pkt)->addr - net_pkt_ll(pkt) : 0;
|
|
|
|
dst = net_pkt_ll_dst(pkt)->addr ?
|
|
|
|
net_pkt_ll_dst(pkt)->addr - net_pkt_ll(pkt) : 0;
|
2016-10-25 09:59:33 +02:00
|
|
|
|
2016-11-16 09:39:31 +01:00
|
|
|
#ifdef CONFIG_NET_L2_IEEE802154_FRAGMENT
|
2017-04-05 08:37:44 +02:00
|
|
|
verdict = ieee802154_reassemble(pkt);
|
2016-10-25 09:59:33 +02:00
|
|
|
if (verdict == NET_DROP) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
#else
|
2017-04-05 08:37:44 +02:00
|
|
|
if (!net_6lo_uncompress(pkt)) {
|
2016-10-25 09:59:33 +02:00
|
|
|
NET_DBG("Packet decompression failed");
|
|
|
|
verdict = NET_DROP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
#endif
|
2017-04-05 08:37:44 +02:00
|
|
|
net_pkt_ll_src(pkt)->addr = src ? net_pkt_ll(pkt) + src : NULL;
|
|
|
|
net_pkt_ll_dst(pkt)->addr = dst ? net_pkt_ll(pkt) + dst : NULL;
|
2016-10-25 09:59:33 +02:00
|
|
|
|
2017-10-18 17:41:04 +03:00
|
|
|
pkt_hexdump(RX_PKT_TITLE, pkt, false);
|
2016-10-25 09:59:33 +02:00
|
|
|
out:
|
|
|
|
return verdict;
|
|
|
|
}
|
2016-10-26 14:56:47 +02:00
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
static inline bool ieee802154_manage_send_packet(struct net_if *iface,
|
|
|
|
struct net_pkt *pkt)
|
2016-10-26 14:56:47 +02:00
|
|
|
{
|
|
|
|
bool ret;
|
|
|
|
|
2017-10-18 17:41:04 +03:00
|
|
|
pkt_hexdump(TX_PKT_TITLE " (before 6lo)", pkt, false);
|
2016-11-15 08:49:10 +01:00
|
|
|
|
2016-11-16 09:39:31 +01:00
|
|
|
#ifdef CONFIG_NET_L2_IEEE802154_FRAGMENT
|
2017-04-05 08:37:44 +02:00
|
|
|
ret = net_6lo_compress(pkt, true, ieee802154_fragment);
|
2016-10-26 14:56:47 +02:00
|
|
|
#else
|
2017-04-05 08:37:44 +02:00
|
|
|
ret = net_6lo_compress(pkt, true, NULL);
|
2016-10-26 14:56:47 +02:00
|
|
|
#endif
|
2016-10-31 15:02:47 +02:00
|
|
|
|
2017-10-18 17:41:04 +03:00
|
|
|
pkt_hexdump(TX_PKT_TITLE " (after 6lo)", pkt, false);
|
2016-10-26 14:56:47 +02:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-10-25 09:59:33 +02:00
|
|
|
#else /* CONFIG_NET_6LO */
|
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
#define ieee802154_manage_recv_packet(...) NET_CONTINUE
|
|
|
|
#define ieee802154_manage_send_packet(...) true
|
2016-10-25 09:59:33 +02:00
|
|
|
|
2016-07-07 14:11:06 +02:00
|
|
|
#endif /* CONFIG_NET_6LO */
|
2016-06-16 16:00:11 +02:00
|
|
|
|
2016-10-25 09:59:33 +02:00
|
|
|
static enum net_verdict ieee802154_recv(struct net_if *iface,
|
2017-04-05 08:37:44 +02:00
|
|
|
struct net_pkt *pkt)
|
2016-10-25 09:59:33 +02:00
|
|
|
{
|
|
|
|
struct ieee802154_mpdu mpdu;
|
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
if (!ieee802154_validate_frame(net_pkt_ll(pkt),
|
|
|
|
net_pkt_get_len(pkt), &mpdu)) {
|
2016-06-16 16:00:11 +02:00
|
|
|
return NET_DROP;
|
|
|
|
}
|
|
|
|
|
2016-10-07 10:53:22 +02:00
|
|
|
if (mpdu.mhr.fs->fc.frame_type == IEEE802154_FRAME_TYPE_BEACON) {
|
2017-10-03 10:03:37 +02:00
|
|
|
return ieee802154_handle_beacon(iface, &mpdu,
|
|
|
|
net_pkt_ieee802154_lqi(pkt));
|
2016-10-07 10:53:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ieee802154_is_scanning(iface)) {
|
|
|
|
return NET_DROP;
|
|
|
|
}
|
|
|
|
|
2016-10-19 17:08:09 +02:00
|
|
|
if (mpdu.mhr.fs->fc.frame_type == IEEE802154_FRAME_TYPE_MAC_COMMAND) {
|
|
|
|
return ieee802154_handle_mac_command(iface, &mpdu);
|
2016-06-16 16:00:11 +02:00
|
|
|
}
|
|
|
|
|
2016-10-19 17:08:09 +02:00
|
|
|
/* At this point the frame has to be a DATA one */
|
|
|
|
|
2016-08-10 15:39:01 +02:00
|
|
|
ieee802154_acknowledge(iface, &mpdu);
|
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
net_pkt_set_ll_reserve(pkt, mpdu.payload - (void *)net_pkt_ll(pkt));
|
|
|
|
net_buf_pull(pkt->frags, net_pkt_ll_reserve(pkt));
|
2016-06-16 16:00:11 +02:00
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
set_pkt_ll_addr(net_pkt_ll_src(pkt), mpdu.mhr.fs->fc.pan_id_comp,
|
2016-08-10 15:39:01 +02:00
|
|
|
mpdu.mhr.fs->fc.src_addr_mode, mpdu.mhr.src_addr);
|
2016-06-16 16:00:11 +02:00
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
set_pkt_ll_addr(net_pkt_ll_dst(pkt), false,
|
2016-08-10 15:39:01 +02:00
|
|
|
mpdu.mhr.fs->fc.dst_addr_mode, mpdu.mhr.dst_addr);
|
2016-06-16 16:00:11 +02:00
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
if (!ieee802154_decipher_data_frame(iface, pkt, &mpdu)) {
|
2017-03-10 15:22:21 +01:00
|
|
|
return NET_DROP;
|
|
|
|
}
|
|
|
|
|
2017-10-18 17:41:04 +03:00
|
|
|
pkt_hexdump(RX_PKT_TITLE " (with ll)", pkt, true);
|
2016-07-12 16:09:29 +02:00
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
return ieee802154_manage_recv_packet(iface, pkt);
|
2016-06-16 16:00:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static enum net_verdict ieee802154_send(struct net_if *iface,
|
2017-04-05 08:37:44 +02:00
|
|
|
struct net_pkt *pkt)
|
2016-06-16 16:00:11 +02:00
|
|
|
{
|
2017-03-28 08:33:43 +02:00
|
|
|
struct ieee802154_context *ctx = net_if_l2_data(iface);
|
2017-04-21 09:27:50 -05:00
|
|
|
u8_t reserved_space = net_pkt_ll_reserve(pkt);
|
2016-10-26 10:58:45 +02:00
|
|
|
struct net_buf *frag;
|
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
if (net_pkt_family(pkt) != AF_INET6) {
|
2016-06-16 16:00:11 +02:00
|
|
|
return NET_DROP;
|
|
|
|
}
|
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
if (!ieee802154_manage_send_packet(iface, pkt)) {
|
2016-11-08 19:27:04 +01:00
|
|
|
return NET_DROP;
|
|
|
|
}
|
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
frag = pkt->frags;
|
2016-10-26 10:58:45 +02:00
|
|
|
while (frag) {
|
2016-11-08 17:22:03 +01:00
|
|
|
if (frag->len > IEEE802154_MTU) {
|
|
|
|
NET_ERR("Frag %p as too big length %u",
|
|
|
|
frag, frag->len);
|
|
|
|
return NET_DROP;
|
|
|
|
}
|
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
if (!ieee802154_create_data_frame(ctx, net_pkt_ll_dst(pkt),
|
2017-01-18 14:18:05 +01:00
|
|
|
frag, reserved_space)) {
|
2016-10-26 10:58:45 +02:00
|
|
|
return NET_DROP;
|
|
|
|
}
|
|
|
|
|
|
|
|
frag = frag->frags;
|
|
|
|
}
|
|
|
|
|
2017-10-18 17:41:04 +03:00
|
|
|
pkt_hexdump(TX_PKT_TITLE " (with ll)", pkt, true);
|
2016-07-12 16:09:29 +02:00
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
net_if_queue_tx(iface, pkt);
|
2016-06-16 16:00:11 +02:00
|
|
|
|
|
|
|
return NET_OK;
|
|
|
|
}
|
|
|
|
|
2017-04-21 09:27:50 -05:00
|
|
|
static u16_t ieee802154_reserve(struct net_if *iface, void *data)
|
2016-06-16 16:00:11 +02:00
|
|
|
{
|
|
|
|
return ieee802154_compute_header_size(iface, (struct in6_addr *)data);
|
|
|
|
}
|
|
|
|
|
|
|
|
NET_L2_INIT(IEEE802154_L2,
|
2017-02-18 16:59:55 +00:00
|
|
|
ieee802154_recv, ieee802154_send, ieee802154_reserve, NULL);
|
2016-06-16 16:00:11 +02:00
|
|
|
|
|
|
|
void ieee802154_init(struct net_if *iface)
|
|
|
|
{
|
2017-02-01 14:34:44 +01:00
|
|
|
struct ieee802154_context *ctx = net_if_l2_data(iface);
|
2017-09-05 14:15:37 +02:00
|
|
|
const struct ieee802154_radio_api *radio = iface->dev->driver_api;
|
2017-04-21 09:27:50 -05:00
|
|
|
const u8_t *mac = iface->link_addr.addr;
|
|
|
|
u8_t long_addr[8];
|
2016-06-16 16:00:11 +02:00
|
|
|
|
|
|
|
NET_DBG("Initializing IEEE 802.15.4 stack on iface %p", iface);
|
|
|
|
|
2016-10-07 10:53:22 +02:00
|
|
|
ieee802154_mgmt_init(iface);
|
|
|
|
|
2017-03-10 15:22:21 +01:00
|
|
|
#ifdef CONFIG_NET_L2_IEEE802154_SECURITY
|
|
|
|
if (ieee802154_security_init(&ctx->sec_ctx)) {
|
|
|
|
NET_ERR("Initializing link-layer security failed");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-02-01 14:34:44 +01:00
|
|
|
sys_memcpy_swap(long_addr, mac, 8);
|
|
|
|
memcpy(ctx->ext_addr, long_addr, 8);
|
2017-09-05 13:52:29 +02:00
|
|
|
ieee802154_filter_ieee_addr(iface, ctx->ext_addr);
|
2017-02-01 14:34:44 +01:00
|
|
|
|
2017-04-12 13:34:44 +02:00
|
|
|
if (!radio->set_txpower(iface->dev,
|
|
|
|
CONFIG_NET_L2_IEEE802154_RADIO_DFLT_TX_POWER)) {
|
|
|
|
ctx->tx_power = CONFIG_NET_L2_IEEE802154_RADIO_DFLT_TX_POWER;
|
|
|
|
}
|
|
|
|
|
2016-06-16 16:00:11 +02:00
|
|
|
radio->start(iface->dev);
|
|
|
|
}
|