Porting PicoTCP WIP
This commit is contained in:
454
kernel/picotcp/modules/pico_6lowpan_ll.c
Normal file
454
kernel/picotcp/modules/pico_6lowpan_ll.c
Normal file
@ -0,0 +1,454 @@
|
||||
/*********************************************************************
|
||||
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
||||
See LICENSE and COPYING for usage.
|
||||
|
||||
Authors: Jelle De Vleeschouwer
|
||||
*********************************************************************/
|
||||
|
||||
#include "pico_ipv6.h"
|
||||
#include "pico_stack.h"
|
||||
#include "pico_frame.h"
|
||||
#include "pico_802154.h"
|
||||
#include "pico_6lowpan.h"
|
||||
#include "pico_protocol.h"
|
||||
#include "pico_addressing.h"
|
||||
#include "pico_6lowpan_ll.h"
|
||||
|
||||
#ifdef PICO_SUPPORT_6LOWPAN
|
||||
|
||||
/*******************************************************************************
|
||||
* Macros
|
||||
******************************************************************************/
|
||||
|
||||
#ifdef DEBUG_6LOWPAN
|
||||
#define ll_dbg dbg
|
||||
#else
|
||||
#define ll_dbg(...) do {} while(0)
|
||||
#endif
|
||||
|
||||
/*******************************************************************************
|
||||
* Constants
|
||||
******************************************************************************/
|
||||
|
||||
/* Lifetime check interval */
|
||||
#define ONE_MINUTE ((pico_time)(1000 * 60))
|
||||
|
||||
/* Number of extensions */
|
||||
#define NUM_LL_EXTENSIONS (2)
|
||||
|
||||
/*******************************************************************************
|
||||
* Type definitions
|
||||
******************************************************************************/
|
||||
|
||||
struct extension {
|
||||
int32_t (*estimate)(struct pico_frame *f);
|
||||
int32_t (*out)(struct pico_frame *f);
|
||||
int32_t (*in)(struct pico_frame *f);
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
* Global Variables
|
||||
******************************************************************************/
|
||||
|
||||
static const struct pico_6lowpan_ll_protocol pico_6lowpan_ll_none = {
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
|
||||
};
|
||||
|
||||
/* Declare a global lookup-table for distribution of link layer specific tasks */
|
||||
struct pico_6lowpan_ll_protocol pico_6lowpan_lls[PICO_6LOWPAN_LLS + 1];
|
||||
|
||||
static struct pico_queue pico_6lowpan_ll_in = {
|
||||
0
|
||||
};
|
||||
static struct pico_queue pico_6lowpan_ll_out = {
|
||||
0
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
* CTX
|
||||
******************************************************************************/
|
||||
|
||||
#ifdef PICO_6LOWPAN_IPHC_ENABLED
|
||||
|
||||
/* Compares if the IPv6 prefix of two IPv6 addresses match */
|
||||
static int32_t compare_prefix(uint8_t *a, uint8_t *b, int32_t len)
|
||||
{
|
||||
uint8_t bitmask = (uint8_t)(0xff << (8 - (len % 8)));
|
||||
size_t bytes = (size_t)len / 8;
|
||||
int32_t ret = 0;
|
||||
if ((ret = memcmp(a, b, bytes)))
|
||||
return ret;
|
||||
return (int32_t)((a[bytes] & bitmask) - (b[bytes] & bitmask));
|
||||
}
|
||||
|
||||
/* Compares 2 IPHC context entries */
|
||||
static int32_t compare_ctx(void *a, void *b)
|
||||
{
|
||||
struct iphc_ctx *ca = (struct iphc_ctx *)a;
|
||||
struct iphc_ctx *cb = (struct iphc_ctx *)b;
|
||||
return compare_prefix(ca->prefix.addr, cb->prefix.addr, ca->size);
|
||||
}
|
||||
|
||||
PICO_TREE_DECLARE(CTXtree, compare_ctx);
|
||||
|
||||
/* Searches in the context tree if there's a context entry available with the
|
||||
* prefix of the IPv6 address */
|
||||
struct iphc_ctx * ctx_lookup(struct pico_ip6 addr)
|
||||
{
|
||||
struct iphc_ctx test = { NULL, addr, 0, 0, 0, 0 };
|
||||
return pico_tree_findKey(&CTXtree, &test);
|
||||
}
|
||||
|
||||
/* Looks up the context by ID, for decompression */
|
||||
struct iphc_ctx * ctx_lookup_id(uint8_t id)
|
||||
{
|
||||
struct iphc_ctx *key = NULL;
|
||||
struct pico_tree_node *i = NULL;
|
||||
|
||||
pico_tree_foreach(i, &CTXtree) {
|
||||
key = i->keyValue;
|
||||
if (key && id ==key->id)
|
||||
return key;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Tries to insert a new IPHC-context into the Context-tree */
|
||||
static int32_t ctx_insert(struct pico_ip6 addr, uint8_t id, uint8_t size, pico_time lifetime, uint8_t flags, struct pico_device *dev)
|
||||
{
|
||||
struct iphc_ctx *new = PICO_ZALLOC(sizeof(struct iphc_ctx));
|
||||
if (new) {
|
||||
new->lifetime = lifetime;
|
||||
new->prefix = addr;
|
||||
new->flags = flags;
|
||||
new->size = size;
|
||||
new->dev = dev;
|
||||
new->id = id;
|
||||
if (pico_tree_insert(&CTXtree, new)) {
|
||||
PICO_FREE(new);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Function to update context table from 6LoWPAN Neighbor Discovery */
|
||||
void ctx_update(struct pico_ip6 addr, uint8_t id, uint8_t size, pico_time lifetime, uint8_t flags, struct pico_device *dev)
|
||||
{
|
||||
struct iphc_ctx *entry = ctx_lookup_id(id);
|
||||
if (entry && dev == entry->dev) {
|
||||
if (!lifetime) {
|
||||
pico_tree_delete(&CTXtree, entry);
|
||||
PICO_FREE(entry);
|
||||
return;
|
||||
}
|
||||
entry->prefix = addr;
|
||||
entry->size = size;
|
||||
entry->lifetime = lifetime;
|
||||
entry->flags = flags;
|
||||
} else {
|
||||
/* We don't care if it failed */
|
||||
(void)ctx_insert(addr, id, size, lifetime, flags, dev);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check whether or not particular contexts are expired and remove them if so. Contexts
|
||||
* are reconfirmed before their lifetime expires */
|
||||
static void ctx_lifetime_check(pico_time now, void *arg)
|
||||
{
|
||||
struct pico_tree_node *i = NULL, *next = NULL;
|
||||
struct pico_ipv6_route *gw = NULL;
|
||||
struct iphc_ctx *key = NULL;
|
||||
IGNORE_PARAMETER(now);
|
||||
IGNORE_PARAMETER(arg);
|
||||
|
||||
pico_tree_foreach_safe(i, &CTXtree, next) {
|
||||
if (i && i->keyValue) {
|
||||
key = i->keyValue;
|
||||
key->lifetime--;
|
||||
if (!key->lifetime) {
|
||||
pico_tree_delete(&CTXtree, key);
|
||||
PICO_FREE(key);
|
||||
} else if (key->lifetime == 5) {
|
||||
/* RFC6775: The host SHOULD unicast one or more RSs to the router well before the
|
||||
* shortest of the, Router Lifetime, PIO lifetimes and the lifetime of the 6COs. */
|
||||
gw = pico_ipv6_gateway_by_dev(key->dev);
|
||||
while (gw) {
|
||||
pico_6lp_nd_start_soliciting(pico_ipv6_linklocal_get(key->dev), gw);
|
||||
gw = pico_ipv6_gateway_by_dev_next(key->dev, gw);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(void)pico_timer_add(ONE_MINUTE, ctx_lifetime_check, NULL);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*******************************************************************************
|
||||
* MESH-UNDER ROUTING LAYER
|
||||
******************************************************************************/
|
||||
|
||||
/* XXX: Extensible processing function for outgoing frames. Here, the mesh header
|
||||
* for a Mesh-Under topology can be prepended and the link layer source and
|
||||
* destination addresses can be updated */
|
||||
static int32_t
|
||||
ll_mesh_header_process_in(struct pico_frame *f)
|
||||
{
|
||||
IGNORE_PARAMETER(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* XXX: Extensible processing function for outgoing frames. Here, the mesh header
|
||||
* for a Mesh-Under topology can be prepended and the link layer source and
|
||||
* destination addresses can be updated */
|
||||
static int32_t
|
||||
ll_mesh_header_process_out(struct pico_frame *f)
|
||||
{
|
||||
IGNORE_PARAMETER(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* XXX: Extensible function that estimates the size of the mesh header to be
|
||||
* prepended based on the frame, the source and destination link layer address */
|
||||
static int32_t
|
||||
ll_mesh_header_estimator(struct pico_frame *f)
|
||||
{
|
||||
IGNORE_PARAMETER(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* GENERIC 6LOWPAN LINK LAYER
|
||||
******************************************************************************/
|
||||
|
||||
static int32_t
|
||||
ll_mac_header_process_in(struct pico_frame *f)
|
||||
{
|
||||
if (f && f->dev && pico_6lowpan_lls[f->dev->mode].process_in) {
|
||||
return (int32_t)pico_6lowpan_lls[f->dev->mode].process_in(f);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t
|
||||
ll_mac_header_process_out(struct pico_frame *f)
|
||||
{
|
||||
if (f && f->dev && pico_6lowpan_lls[f->dev->mode].process_out) {
|
||||
return (int32_t)pico_6lowpan_lls[f->dev->mode].process_out(f);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t
|
||||
ll_mac_header_estimator(struct pico_frame *f)
|
||||
{
|
||||
if (f && f->dev && pico_6lowpan_lls[f->dev->mode].estimate) {
|
||||
return (int32_t)pico_6lowpan_lls[f->dev->mode].estimate(f);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Alloc's a frame with device's overhead and maximum IEEE802.15.4 header size */
|
||||
static struct pico_frame *
|
||||
pico_6lowpan_frame_alloc(struct pico_protocol *self, struct pico_device *dev, uint16_t size)
|
||||
{
|
||||
IGNORE_PARAMETER(self);
|
||||
if (dev && pico_6lowpan_lls[dev->mode].alloc) {
|
||||
return pico_6lowpan_lls[dev->mode].alloc(dev, size);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* 6LOWPAN LINK LAYER PROTOCOL
|
||||
******************************************************************************/
|
||||
|
||||
const struct extension exts[] = {
|
||||
{ll_mesh_header_estimator, ll_mesh_header_process_out, ll_mesh_header_process_in},
|
||||
{ll_mac_header_estimator, ll_mac_header_process_out, ll_mac_header_process_in},
|
||||
};
|
||||
|
||||
static int32_t
|
||||
pico_6lowpan_ll_process_out(struct pico_protocol *self, struct pico_frame *f)
|
||||
{
|
||||
uint32_t datalink_len = 0;
|
||||
int32_t ret = 0, i = 0;
|
||||
IGNORE_PARAMETER(self);
|
||||
|
||||
/* Every link layer extension updates the datalink pointer of the frame a little bit. */
|
||||
f->datalink_hdr = f->net_hdr;
|
||||
|
||||
/* Call each of the outgoing processing functions */
|
||||
for (i = 0; i < NUM_LL_EXTENSIONS; i++) {
|
||||
ret = exts[i].out(f);
|
||||
if (ret < 0) /* Processing failed, no way to recover, discard frame */
|
||||
goto fin;
|
||||
datalink_len = (uint32_t)(datalink_len + (uint32_t)ret);
|
||||
if ((f->net_hdr - datalink_len) < f->buffer) /* Before buffer bound check */
|
||||
goto fin;
|
||||
}
|
||||
|
||||
/* Frame is ready for sending to the device driver */
|
||||
f->start = f->datalink_hdr;
|
||||
f->len = (uint32_t)(f->len + datalink_len);
|
||||
return (int32_t)(pico_sendto_dev(f) <= 0);
|
||||
fin:
|
||||
pico_frame_discard(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int32_t
|
||||
pico_6lowpan_ll_process_in(struct pico_protocol *self, struct pico_frame *f)
|
||||
{
|
||||
int32_t i = 0, ret = 0;
|
||||
uint32_t len = 0;
|
||||
IGNORE_PARAMETER(self);
|
||||
|
||||
/* net_hdr is the pointer that is dynamically updated by the incoming
|
||||
* processing functions to always point to right after a particular
|
||||
* header, whether it's MAC, MESH, LL_SEC, ... eventually net_hdr will
|
||||
* point to 6LoWPAN header which is exactly what we want */
|
||||
f->net_hdr = f->buffer;
|
||||
|
||||
for (i = NUM_LL_EXTENSIONS - 1; i >= 0; i--) {
|
||||
ret = exts[i].in(f);
|
||||
switch (ret) {
|
||||
case FRAME_6LOWPAN_LL_RELEASE:
|
||||
/* Success, frame is somewhere else now.. */
|
||||
break;
|
||||
case FRAME_6LOWPAN_LL_DISCARD:
|
||||
/* Something went wrong, discard the frame */
|
||||
pico_frame_discard(f);
|
||||
break;
|
||||
default:
|
||||
/* Success, update link layer header length */
|
||||
len = (uint32_t)(len + (uint32_t)ret);
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine size at network layer */
|
||||
f->net_len = (uint16_t)(f->len - len);
|
||||
f->len = (uint32_t)(f->len - len);
|
||||
return pico_6lowpan_pull(f);
|
||||
}
|
||||
|
||||
/* Entry point for incoming 6LoWPAN frames, proxy for pico_stack_recv. This allows passing the link
|
||||
* layer source and destination address as well */
|
||||
int32_t pico_6lowpan_stack_recv(struct pico_device *dev, uint8_t *buffer, uint32_t len, union pico_ll_addr *src, union pico_ll_addr *dst)
|
||||
{
|
||||
int32_t ret = 0;
|
||||
ll_dbg("6LoWPAN - Stack recv called!\n");
|
||||
if (PICO_DEV_IS_NOMAC(dev)) {
|
||||
struct pico_frame *f = pico_stack_recv_new_frame(dev, buffer, len);
|
||||
if (f) {
|
||||
f->src = *src;
|
||||
f->dst = *dst;
|
||||
ret = pico_enqueue(dev->q_in, f);
|
||||
if (0 >= ret)
|
||||
pico_frame_discard(f);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
return pico_stack_recv(dev, buffer, len);
|
||||
}
|
||||
return -1; // return ERROR
|
||||
}
|
||||
|
||||
/* Proxy for pico_devloop_sendto_dev, 6LoWPAN-devices have a different interface with pico. This
|
||||
* allows passing the link layer source and destination address as well */
|
||||
int32_t pico_6lowpan_ll_sendto_dev(struct pico_device *dev, struct pico_frame *f)
|
||||
{
|
||||
/* FINAL OUTGOING POINT OF 6LOWPAN STACK */
|
||||
return ((struct pico_dev_6lowpan *)dev)->send(dev, f->start, (int32_t)f->len, f->src, f->dst);
|
||||
}
|
||||
|
||||
/* Initialisation routine for 6LoWPAN specific devices */
|
||||
int pico_dev_6lowpan_init(struct pico_dev_6lowpan *dev, const char *name, uint8_t *mac, enum pico_ll_mode ll_mode, uint16_t mtu, uint8_t nomac,
|
||||
int (* send)(struct pico_device *dev, void *_buf, int len, union pico_ll_addr src, union pico_ll_addr dst),
|
||||
int (* poll)(struct pico_device *dev, int loop_score))
|
||||
{
|
||||
struct pico_device *picodev = (struct pico_device *)dev;
|
||||
if (!dev || !send || !poll) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
picodev->mode = ll_mode;
|
||||
picodev->hostvars.lowpan_flags = PICO_6LP_FLAG_LOWPAN;
|
||||
if (nomac) {
|
||||
picodev->hostvars.lowpan_flags |= PICO_6LP_FLAG_NOMAC;
|
||||
}
|
||||
picodev->mtu = mtu;
|
||||
picodev->poll = poll;
|
||||
picodev->send = NULL;
|
||||
dev->send = send;
|
||||
|
||||
return pico_device_init(picodev, name, mac);
|
||||
}
|
||||
|
||||
|
||||
/* Push function for 6LoWPAN to call when it wants to try to send te frame to the device-driver */
|
||||
int32_t
|
||||
pico_6lowpan_ll_push(struct pico_frame *f)
|
||||
{
|
||||
uint16_t frame_size, pl_available = 0;
|
||||
int32_t i = 0;
|
||||
|
||||
if (!f || !f->dev)
|
||||
return -1;
|
||||
frame_size = (uint16_t)(f->len);
|
||||
|
||||
/* Restrict frames to be as large as the device's MTU. */
|
||||
pl_available = (uint16_t)f->dev->mtu;
|
||||
|
||||
/* Call each of the estimator functions of the additional headers to
|
||||
* determine if the frame fits inside a single 802.15.4 frame, if it doesn't
|
||||
* in the end, return the available bytes */
|
||||
for (i = 0; i < NUM_LL_EXTENSIONS; i++) {
|
||||
pl_available = (uint16_t)(pl_available - exts[i].estimate(f));
|
||||
}
|
||||
if (frame_size > pl_available)
|
||||
return pl_available;
|
||||
|
||||
/* Make sure these addresses are retrievable from the frame on processing */
|
||||
if (pico_enqueue(pico_proto_6lowpan_ll.q_out,f) > 0) {
|
||||
return 0; // Frame enqueued for later processing
|
||||
}
|
||||
return -1; // Return ERROR
|
||||
}
|
||||
|
||||
struct pico_protocol pico_proto_6lowpan_ll = {
|
||||
.name = "6lowpan_ll",
|
||||
.layer = PICO_LAYER_DATALINK,
|
||||
.alloc = pico_6lowpan_frame_alloc,
|
||||
.process_in = pico_6lowpan_ll_process_in,
|
||||
.process_out = pico_6lowpan_ll_process_out,
|
||||
.q_in = &pico_6lowpan_ll_in,
|
||||
.q_out = &pico_6lowpan_ll_out
|
||||
};
|
||||
|
||||
void pico_6lowpan_ll_init(void)
|
||||
{
|
||||
int32_t i = 0;
|
||||
|
||||
#ifdef PICO_6LOWPAN_IPHC_ENABLED
|
||||
/* We don't care about failure */
|
||||
(void)pico_timer_add(60000, ctx_lifetime_check, NULL);
|
||||
#endif
|
||||
|
||||
/* Initialize interface with 6LoWPAN link layer protocols */
|
||||
pico_6lowpan_lls[i++] = pico_6lowpan_ll_none;
|
||||
|
||||
#ifdef PICO_SUPPORT_802154
|
||||
pico_6lowpan_lls[i++] = pico_6lowpan_ll_802154;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* PICO_SUPPORT_6LOWPAN */
|
||||
Reference in New Issue
Block a user