blob: eba480fb64a71ca5769118df4032031d13275e46 [file] [log] [blame]
/*
* Copyright (c) 2016 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#if defined(CONFIG_NET_DEBUG_L2_IEEE802154)
#define SYS_LOG_DOMAIN "net/ieee802154"
#define NET_LOG_ENABLED 1
#endif
#include <net/net_core.h>
#include <net/net_l2.h>
#include <net/net_if.h>
#include "ipv6.h"
#include <errno.h>
#ifdef CONFIG_NET_6LO
#ifdef CONFIG_NET_L2_IEEE802154_FRAGMENT
#include "ieee802154_fragment.h"
#endif
#include <6lo.h>
#endif /* CONFIG_NET_6LO */
#include <net/ieee802154_radio.h>
#include "ieee802154_frame.h"
#include "ieee802154_mgmt.h"
#include "ieee802154_security.h"
#if 0
#include <misc/printk.h>
static inline void hexdump(u8_t *pkt, u16_t length, u8_t reserve)
{
int i;
for (i = 0; i < length;) {
int j;
printk("\t");
for (j = 0; j < 10 && i < length; j++, i++) {
#if defined(CONFIG_SYS_LOG_SHOW_COLOR)
if (i < reserve && reserve) {
printk(SYS_LOG_COLOR_YELLOW);
} else {
printk(SYS_LOG_COLOR_OFF);
}
#endif
printk("%02x ", *pkt++);
}
#if defined(CONFIG_SYS_LOG_SHOW_COLOR)
if (i < reserve) {
printk(SYS_LOG_COLOR_OFF);
}
#endif
printk("\n");
}
}
static void pkt_hexdump(struct net_pkt *pkt, bool each_frag_reserve)
{
u16_t reserve = each_frag_reserve ? net_pkt_ll_reserve(pkt) : 0;
struct net_buf *frag;
printk("IEEE 802.15.4 packet content:\n");
frag = pkt->frags;
while (frag) {
hexdump(each_frag_reserve ?
frag->data - reserve : frag->data,
frag->len + reserve, reserve);
frag = frag->frags;
}
}
#else
#define pkt_hexdump(...)
#endif
#ifdef CONFIG_NET_L2_IEEE802154_ACK_REPLY
static inline void ieee802154_acknowledge(struct net_if *iface,
struct ieee802154_mpdu *mpdu)
{
struct net_pkt *pkt;
struct net_buf *frag;
if (!mpdu->mhr.fs->fc.ar) {
return;
}
pkt = net_pkt_get_reserve_tx(IEEE802154_ACK_PKT_LENGTH, K_FOREVER);
if (!pkt) {
return;
}
frag = net_pkt_get_frag(pkt, K_FOREVER);
net_pkt_frag_insert(pkt, frag);
if (ieee802154_create_ack_frame(iface, pkt, mpdu->mhr.fs->sequence)) {
const struct ieee802154_radio_api *radio =
iface->dev->driver_api;
net_buf_add(frag, IEEE802154_ACK_PKT_LENGTH);
radio->tx(iface->dev, pkt, frag);
}
net_pkt_unref(pkt);
return;
}
#else
#define ieee802154_acknowledge(...)
#endif /* CONFIG_NET_L2_IEEE802154_ACK_REPLY */
static inline void set_pkt_ll_addr(struct net_linkaddr *addr, bool comp,
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;
}
addr->type = NET_LINK_IEEE802154;
}
#ifdef CONFIG_NET_6LO
static inline
enum net_verdict ieee802154_manage_recv_packet(struct net_if *iface,
struct net_pkt *pkt)
{
enum net_verdict verdict = NET_CONTINUE;
u32_t src;
u32_t dst;
/* Upper IP stack expects the link layer address to be in
* big endian format so we must swap it here.
*/
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);
}
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);
}
/** Uncompress will drop the current fragment. Pkt ll src/dst address
* will then be wrong and must be updated according to the new fragment.
*/
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;
#ifdef CONFIG_NET_L2_IEEE802154_FRAGMENT
verdict = ieee802154_reassemble(pkt);
if (verdict == NET_DROP) {
goto out;
}
#else
if (!net_6lo_uncompress(pkt)) {
NET_DBG("Packet decompression failed");
verdict = NET_DROP;
goto out;
}
#endif
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;
pkt_hexdump(pkt, false);
out:
return verdict;
}
static inline bool ieee802154_manage_send_packet(struct net_if *iface,
struct net_pkt *pkt)
{
bool ret;
pkt_hexdump(pkt, false);
#ifdef CONFIG_NET_L2_IEEE802154_FRAGMENT
ret = net_6lo_compress(pkt, true, ieee802154_fragment);
#else
ret = net_6lo_compress(pkt, true, NULL);
#endif
pkt_hexdump(pkt, false);
return ret;
}
#else /* CONFIG_NET_6LO */
#define ieee802154_manage_recv_packet(...) NET_CONTINUE
#define ieee802154_manage_send_packet(...) true
#endif /* CONFIG_NET_6LO */
static enum net_verdict ieee802154_recv(struct net_if *iface,
struct net_pkt *pkt)
{
struct ieee802154_mpdu mpdu;
if (!ieee802154_validate_frame(net_pkt_ll(pkt),
net_pkt_get_len(pkt), &mpdu)) {
return NET_DROP;
}
if (mpdu.mhr.fs->fc.frame_type == IEEE802154_FRAME_TYPE_BEACON) {
return ieee802154_handle_beacon(iface, &mpdu);
}
if (ieee802154_is_scanning(iface)) {
return NET_DROP;
}
if (mpdu.mhr.fs->fc.frame_type == IEEE802154_FRAME_TYPE_MAC_COMMAND) {
return ieee802154_handle_mac_command(iface, &mpdu);
}
/* At this point the frame has to be a DATA one */
ieee802154_acknowledge(iface, &mpdu);
net_pkt_set_ll_reserve(pkt, mpdu.payload - (void *)net_pkt_ll(pkt));
net_buf_pull(pkt->frags, net_pkt_ll_reserve(pkt));
set_pkt_ll_addr(net_pkt_ll_src(pkt), mpdu.mhr.fs->fc.pan_id_comp,
mpdu.mhr.fs->fc.src_addr_mode, mpdu.mhr.src_addr);
set_pkt_ll_addr(net_pkt_ll_dst(pkt), false,
mpdu.mhr.fs->fc.dst_addr_mode, mpdu.mhr.dst_addr);
if (!ieee802154_decipher_data_frame(iface, pkt, &mpdu)) {
return NET_DROP;
}
pkt_hexdump(pkt, true);
return ieee802154_manage_recv_packet(iface, pkt);
}
static enum net_verdict ieee802154_send(struct net_if *iface,
struct net_pkt *pkt)
{
struct ieee802154_context *ctx = net_if_l2_data(iface);
u8_t reserved_space = net_pkt_ll_reserve(pkt);
struct net_buf *frag;
if (net_pkt_family(pkt) != AF_INET6) {
return NET_DROP;
}
if (!ieee802154_manage_send_packet(iface, pkt)) {
return NET_DROP;
}
frag = pkt->frags;
while (frag) {
if (frag->len > IEEE802154_MTU) {
NET_ERR("Frag %p as too big length %u",
frag, frag->len);
return NET_DROP;
}
if (!ieee802154_create_data_frame(ctx, net_pkt_ll_dst(pkt),
frag, reserved_space)) {
return NET_DROP;
}
frag = frag->frags;
}
pkt_hexdump(pkt, true);
net_if_queue_tx(iface, pkt);
return NET_OK;
}
static u16_t ieee802154_reserve(struct net_if *iface, void *data)
{
return ieee802154_compute_header_size(iface, (struct in6_addr *)data);
}
NET_L2_INIT(IEEE802154_L2,
ieee802154_recv, ieee802154_send, ieee802154_reserve, NULL);
void ieee802154_init(struct net_if *iface)
{
struct ieee802154_context *ctx = net_if_l2_data(iface);
const struct ieee802154_radio_api *radio =
iface->dev->driver_api;
const u8_t *mac = iface->link_addr.addr;
u8_t long_addr[8];
NET_DBG("Initializing IEEE 802.15.4 stack on iface %p", iface);
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(long_addr, mac, 8);
radio->set_ieee_addr(iface->dev, long_addr);
memcpy(ctx->ext_addr, long_addr, 8);
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;
}
radio->start(iface->dev);
}