1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/sys/net/gnrc/network_layer/sixlowpan/gnrc_sixlowpan.c
Martine Lenders 64fed621d2 gnrc_sixlowpan_frag: Expose functions to finish datagram
This will be used in the IPHC refactoring to control the reassembly
buffer as a context.

I also adapted the name of `gnrc_sixlowpan_frag_gc_rbuf()` to be in
line with the rest of the newer functions.
2018-07-25 11:26:02 +02:00

394 lines
12 KiB
C

/*
* Copyright (C) 2015 Martine Lenders <mlenders@inf.fu-berlin.de>
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @{
*
* @file
*/
#include "kernel_types.h"
#include "net/gnrc.h"
#include "thread.h"
#include "utlist.h"
#include "net/gnrc/ipv6/hdr.h"
#include "net/gnrc/sixlowpan.h"
#include "net/gnrc/sixlowpan/frag.h"
#include "net/gnrc/sixlowpan/iphc.h"
#include "net/gnrc/netif.h"
#include "net/sixlowpan.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
#if ENABLE_DEBUG
/* For PRIu16 etc. */
#include <inttypes.h>
#endif
static kernel_pid_t _pid = KERNEL_PID_UNDEF;
#if ENABLE_DEBUG
static char _stack[GNRC_SIXLOWPAN_STACK_SIZE + THREAD_EXTRA_STACKSIZE_PRINTF];
#else
static char _stack[GNRC_SIXLOWPAN_STACK_SIZE];
#endif
/* handles GNRC_NETAPI_MSG_TYPE_RCV commands */
static void _receive(gnrc_pktsnip_t *pkt);
/* handles GNRC_NETAPI_MSG_TYPE_SND commands */
static void _send(gnrc_pktsnip_t *pkt);
/* Main event loop for 6LoWPAN */
static void *_event_loop(void *args);
kernel_pid_t gnrc_sixlowpan_init(void)
{
if (_pid > KERNEL_PID_UNDEF) {
return _pid;
}
_pid = thread_create(_stack, sizeof(_stack), GNRC_SIXLOWPAN_PRIO,
THREAD_CREATE_STACKTEST, _event_loop, NULL, "6lo");
return _pid;
}
void gnrc_sixlowpan_dispatch_recv(gnrc_pktsnip_t *pkt, void *context,
unsigned page)
{
gnrc_nettype_t type;
(void)context;
(void)page;
#ifdef MODULE_CCNLITE
type = GNRC_NETTYPE_UNDEF;
for (gnrc_pktsnip_t *ptr = pkt; (ptr || (type == GNRC_NETTYPE_UNDEF));
ptr = ptr->next) {
if ((ptr->next) && (ptr->next->type == GNRC_NETTYPE_NETIF)) {
type = ptr->type;
}
}
assert(network_snip);
#else /* MODULE_CCNLITE */
/* just assume normal IPv6 traffic */
type = GNRC_NETTYPE_IPV6;
#endif /* MODULE_CCNLITE */
if (!gnrc_netapi_dispatch_receive(type,
GNRC_NETREG_DEMUX_CTX_ALL, pkt)) {
DEBUG("6lo: No receivers for this packet found\n");
gnrc_pktbuf_release(pkt);
}
}
void gnrc_sixlowpan_dispatch_send(gnrc_pktsnip_t *pkt, void *context,
unsigned page)
{
(void)context;
(void)page;
assert(pkt->type == GNRC_NETTYPE_NETIF);
gnrc_netif_hdr_t *hdr = pkt->data;
if (gnrc_netapi_send(hdr->if_pid, pkt) < 1) {
DEBUG("6lo: unable to send %p over interface %u\n", (void *)pkt,
hdr->if_pid);
gnrc_pktbuf_release(pkt);
}
}
static void _receive(gnrc_pktsnip_t *pkt)
{
gnrc_pktsnip_t *payload;
uint8_t *dispatch;
/* seize payload as a temporary variable */
payload = gnrc_pktbuf_start_write(pkt); /* need to duplicate since pkt->next
* might get replaced */
if (payload == NULL) {
DEBUG("6lo: can not get write access on received packet\n");
#if defined(DEVELHELP) && ENABLE_DEBUG
gnrc_pktbuf_stats();
#endif
gnrc_pktbuf_release(pkt);
return;
}
pkt = payload; /* reset pkt from temporary variable */
payload = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_SIXLOWPAN);
if ((payload == NULL) || (payload->size < 1)) {
DEBUG("6lo: Received packet has no 6LoWPAN payload\n");
gnrc_pktbuf_release(pkt);
return;
}
dispatch = payload->data;
if (dispatch[0] == SIXLOWPAN_UNCOMP) {
gnrc_pktsnip_t *sixlowpan;
DEBUG("6lo: received uncompressed IPv6 packet\n");
payload = gnrc_pktbuf_start_write(payload);
if (payload == NULL) {
DEBUG("6lo: can not get write access on received packet\n");
#if defined(DEVELHELP) && ENABLE_DEBUG
gnrc_pktbuf_stats();
#endif
gnrc_pktbuf_release(pkt);
return;
}
/* packet is uncompressed: just mark and remove the dispatch */
sixlowpan = gnrc_pktbuf_mark(payload, sizeof(uint8_t), GNRC_NETTYPE_SIXLOWPAN);
if (sixlowpan == NULL) {
DEBUG("6lo: can not mark 6LoWPAN dispatch\n");
gnrc_pktbuf_release(pkt);
return;
}
pkt = gnrc_pktbuf_remove_snip(pkt, sixlowpan);
payload->type = GNRC_NETTYPE_IPV6;
}
#ifdef MODULE_GNRC_SIXLOWPAN_FRAG
else if (sixlowpan_frag_is((sixlowpan_frag_t *)dispatch)) {
DEBUG("6lo: received 6LoWPAN fragment\n");
gnrc_sixlowpan_frag_recv(pkt, NULL, 0);
return;
}
#endif
#ifdef MODULE_GNRC_SIXLOWPAN_IPHC
else if (sixlowpan_iphc_is(dispatch)) {
size_t dispatch_size, nh_len;
gnrc_pktsnip_t *sixlowpan;
gnrc_pktsnip_t *dec_hdr = gnrc_pktbuf_add(NULL, NULL, sizeof(ipv6_hdr_t),
GNRC_NETTYPE_IPV6);
if ((dec_hdr == NULL) ||
(dispatch_size = gnrc_sixlowpan_iphc_decode(&dec_hdr, pkt, 0, 0,
&nh_len)) == 0) {
DEBUG("6lo: error on IPHC decoding\n");
if (dec_hdr != NULL) {
gnrc_pktbuf_release(dec_hdr);
}
gnrc_pktbuf_release(pkt);
return;
}
sixlowpan = gnrc_pktbuf_mark(pkt, dispatch_size, GNRC_NETTYPE_SIXLOWPAN);
if (sixlowpan == NULL) {
DEBUG("6lo: error on marking IPHC dispatch\n");
gnrc_pktbuf_release(dec_hdr);
gnrc_pktbuf_release(pkt);
return;
}
/* Remove IPHC dispatches */
/* Insert decoded header instead */
pkt = gnrc_pktbuf_replace_snip(pkt, sixlowpan, dec_hdr);
payload->type = GNRC_NETTYPE_UNDEF;
}
#endif
else {
DEBUG("6lo: dispatch %02" PRIx8 " ... is not supported\n",
dispatch[0]);
gnrc_pktbuf_release(pkt);
return;
}
gnrc_sixlowpan_dispatch_recv(pkt, NULL, 0);
}
static inline bool _add_uncompr_disp(gnrc_pktsnip_t *pkt)
{
gnrc_pktsnip_t *sixlowpan;
uint8_t *disp;
DEBUG("6lo: Send uncompressed\n");
sixlowpan = gnrc_pktbuf_add(NULL, NULL, sizeof(uint8_t), GNRC_NETTYPE_SIXLOWPAN);
if (sixlowpan == NULL) {
return false;
}
sixlowpan->next = pkt->next;
pkt->next = sixlowpan;
disp = sixlowpan->data;
disp[0] = SIXLOWPAN_UNCOMP;
return true;
}
static void _send(gnrc_pktsnip_t *pkt)
{
gnrc_netif_hdr_t *hdr;
gnrc_pktsnip_t *pkt2;
gnrc_netif_t *iface;
/* datagram_size: pure IPv6 packet without 6LoWPAN dispatches or compression */
size_t datagram_size;
if ((pkt == NULL) || (pkt->size < sizeof(gnrc_netif_hdr_t))) {
DEBUG("6lo: Sending packet has no netif header\n");
gnrc_pktbuf_release(pkt);
return;
}
if ((pkt->next == NULL) || (pkt->next->type != GNRC_NETTYPE_IPV6)) {
DEBUG("6lo: Sending packet has no IPv6 header\n");
gnrc_pktbuf_release(pkt);
return;
}
pkt2 = gnrc_pktbuf_start_write(pkt);
if (pkt2 == NULL) {
DEBUG("6lo: no space left in packet buffer\n");
gnrc_pktbuf_release(pkt);
return;
}
hdr = pkt2->data;
iface = gnrc_netif_get_by_pid(hdr->if_pid);
datagram_size = gnrc_pkt_len(pkt2->next);
if (iface == NULL) {
DEBUG("6lo: Can not get 6LoWPAN specific interface information.\n");
gnrc_pktbuf_release(pkt);
return;
}
#ifdef MODULE_GNRC_SIXLOWPAN_IPHC
if (iface->flags & GNRC_NETIF_FLAGS_6LO_HC) {
if (!gnrc_sixlowpan_iphc_encode(pkt2)) {
DEBUG("6lo: error on IPHC encoding\n");
gnrc_pktbuf_release(pkt2);
return;
}
/* IPHC dispatch does not count on dispatch length since it _shortens_
* the datagram */
}
else {
if (!_add_uncompr_disp(pkt2)) {
/* adding uncompressed dispatch failed */
DEBUG("6lo: no space left in packet buffer\n");
gnrc_pktbuf_release(pkt2);
return;
}
}
#else
/* suppress clang-analyzer report about iface being not read */
(void) iface;
if (!_add_uncompr_disp(pkt2)) {
/* adding uncompressed dispatch failed */
DEBUG("6lo: no space left in packet buffer\n");
gnrc_pktbuf_release(pkt2);
return;
}
#endif
DEBUG("6lo: iface->sixlo.max_frag_size = %" PRIu8 " for interface %"
PRIkernel_pid "\n", iface->sixlo.max_frag_size, hdr->if_pid);
/* Note, that datagram_size cannot be used here, because the header size
* might be changed by IPHC. */
if ((iface->sixlo.max_frag_size == 0) ||
(gnrc_pkt_len(pkt2->next) <= iface->sixlo.max_frag_size)) {
DEBUG("6lo: Send SND command for %p to %" PRIu16 "\n",
(void *)pkt2, hdr->if_pid);
gnrc_sixlowpan_dispatch_send(pkt2, NULL, 0);
return;
}
#ifdef MODULE_GNRC_SIXLOWPAN_FRAG
else if (datagram_size <= SIXLOWPAN_FRAG_MAX_LEN) {
DEBUG("6lo: Send fragmented (%u > %" PRIu8 ")\n",
(unsigned int)datagram_size, iface->sixlo.max_frag_size);
gnrc_sixlowpan_msg_frag_t *fragment_msg;
fragment_msg = gnrc_sixlowpan_msg_frag_get();
if (fragment_msg == NULL) {
DEBUG("6lo: Not enough resources to fragment packet. Dropping packet\n");
gnrc_pktbuf_release(pkt2);
return;
}
fragment_msg->pid = hdr->if_pid;
fragment_msg->pkt = pkt2;
fragment_msg->datagram_size = datagram_size;
/* Sending the first fragment has an offset==0 */
fragment_msg->offset = 0;
gnrc_sixlowpan_frag_send(pkt2, fragment_msg, 0);
}
else {
DEBUG("6lo: packet too big (%u > %" PRIu16 ")\n",
(unsigned int)datagram_size, (uint16_t)SIXLOWPAN_FRAG_MAX_LEN);
gnrc_pktbuf_release(pkt2);
}
#else
(void) datagram_size;
DEBUG("6lo: packet too big (%u > %" PRIu8 ")\n",
(unsigned int)datagram_size, iface->sixlo.max_frag_size);
gnrc_pktbuf_release(pkt2);
#endif
}
static void *_event_loop(void *args)
{
msg_t msg, reply, msg_q[GNRC_SIXLOWPAN_MSG_QUEUE_SIZE];
gnrc_netreg_entry_t me_reg = GNRC_NETREG_ENTRY_INIT_PID(GNRC_NETREG_DEMUX_CTX_ALL,
sched_active_pid);
(void)args;
msg_init_queue(msg_q, GNRC_SIXLOWPAN_MSG_QUEUE_SIZE);
/* register interest in all 6LoWPAN packets */
gnrc_netreg_register(GNRC_NETTYPE_SIXLOWPAN, &me_reg);
/* preinitialize ACK */
reply.type = GNRC_NETAPI_MSG_TYPE_ACK;
/* start event loop */
while (1) {
DEBUG("6lo: waiting for incoming message.\n");
msg_receive(&msg);
switch (msg.type) {
case GNRC_NETAPI_MSG_TYPE_RCV:
DEBUG("6lo: GNRC_NETDEV_MSG_TYPE_RCV received\n");
_receive(msg.content.ptr);
break;
case GNRC_NETAPI_MSG_TYPE_SND:
DEBUG("6lo: GNRC_NETDEV_MSG_TYPE_SND received\n");
_send(msg.content.ptr);
break;
case GNRC_NETAPI_MSG_TYPE_GET:
case GNRC_NETAPI_MSG_TYPE_SET:
DEBUG("6lo: reply to unsupported get/set\n");
reply.content.value = -ENOTSUP;
msg_reply(&msg, &reply);
break;
#ifdef MODULE_GNRC_SIXLOWPAN_FRAG
case GNRC_SIXLOWPAN_MSG_FRAG_SND:
DEBUG("6lo: send fragmented event received\n");
gnrc_sixlowpan_frag_send(NULL, msg.content.ptr, 0);
break;
case GNRC_SIXLOWPAN_MSG_FRAG_GC_RBUF:
DEBUG("6lo: garbage collect reassembly buffer event received\n");
gnrc_sixlowpan_frag_rbuf_gc();
break;
#endif
default:
DEBUG("6lo: operation not supported\n");
break;
}
}
return NULL;
}
/** @} */