/* * Copyright (C) 2015 Martine Lenders * * 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 #include "sched.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/frag/rb.h" #ifdef MODULE_GNRC_SIXLOWPAN_FRAG_SFR #include "net/gnrc/sixlowpan/frag/sfr.h" #endif /* MODULE_GNRC_SIXLOWPAN_FRAG_SFR */ #include "net/gnrc/sixlowpan/iphc.h" #include "net/gnrc/netif.h" #include "net/sixlowpan.h" #define ENABLE_DEBUG 0 #include "debug.h" static kernel_pid_t _pid = KERNEL_PID_UNDEF; static char _stack[GNRC_SIXLOWPAN_STACK_SIZE + DEBUG_EXTRA_STACKSIZE]; /* 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; } kernel_pid_t gnrc_sixlowpan_get_pid(void) { return _pid; } void gnrc_sixlowpan_dispatch_recv(gnrc_pktsnip_t *pkt, void *context, unsigned page) { gnrc_nettype_t type; (void)context; (void)page; #ifndef MODULE_GNRC_IPV6 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; break; } } #else /* MODULE_GNRC_IPV6 */ /* just assume normal IPv6 traffic */ type = GNRC_NETTYPE_IPV6; #endif /* MODULE_GNRC_IPV6 */ 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_netif_send(gnrc_netif_get_by_pid(hdr->if_pid), pkt) < 1) { DEBUG("6lo: unable to send %p over interface %u\n", (void *)pkt, hdr->if_pid); gnrc_pktbuf_release(pkt); } } void gnrc_sixlowpan_multiplex_by_size(gnrc_pktsnip_t *pkt, size_t orig_datagram_size, gnrc_netif_t *netif, unsigned page) { assert(pkt != NULL); assert(netif != NULL); size_t datagram_size = gnrc_pkt_len(pkt->next); DEBUG("6lo: iface->sixlo.max_frag_size = %u for interface %i\n", netif->sixlo.max_frag_size, netif->pid); if ((netif->sixlo.max_frag_size == 0) || (datagram_size <= netif->sixlo.max_frag_size)) { DEBUG("6lo: Dispatch for sending\n"); gnrc_sixlowpan_dispatch_send(pkt, NULL, page); } #if defined(MODULE_GNRC_SIXLOWPAN_FRAG) || defined(MODULE_GNRC_SIXLOWPAN_FRAG_SFR) else if (orig_datagram_size <= SIXLOWPAN_FRAG_MAX_LEN) { DEBUG("6lo: Send fragmented (%u > %u)\n", (unsigned int)datagram_size, netif->sixlo.max_frag_size); gnrc_sixlowpan_frag_fb_t *fbuf; #ifdef MODULE_GNRC_SIXLOWPAN_FRAG_SFR bool sfr = gnrc_sixlowpan_frag_sfr_netif(netif); #else /* MODULE_GNRC_SIXLOWPAN_FRAG_SFR */ bool sfr = false; #endif /* MODULE_GNRC_SIXLOWPAN_FRAG_SFR */ fbuf = gnrc_sixlowpan_frag_fb_get(); if (fbuf == NULL) { DEBUG("6lo: Not enough resources to fragment packet. " "Dropping packet\n"); gnrc_pktbuf_release_error(pkt, ENOMEM); return; } fbuf->pkt = pkt; fbuf->datagram_size = orig_datagram_size; fbuf->tag = gnrc_sixlowpan_frag_fb_next_tag(); /* Sending the first fragment has an offset==0 */ fbuf->offset = 0; #ifdef MODULE_GNRC_SIXLOWPAN_FRAG_HINT fbuf->hint.fragsz = 0; #endif if (!sfr) { #ifdef MODULE_GNRC_SIXLOWPAN_FRAG gnrc_sixlowpan_frag_send(pkt, fbuf, page); #endif } #ifdef MODULE_GNRC_SIXLOWPAN_FRAG_SFR else { fbuf->sfr.cur_seq = 0U; fbuf->sfr.frags_sent = 0U; gnrc_sixlowpan_frag_sfr_send(pkt, fbuf, page); } #endif /* MODULE_GNRC_SIXLOWPAN_FRAG_SFR */ } #endif /* defined(MODULE_GNRC_SIXLOWPAN_FRAG) || defined(MODULE_GNRC_SIXLOWPAN_FRAG_SFR) */ else { (void)orig_datagram_size; DEBUG("6lo: packet too big (%u > %u)\n", (unsigned int)datagram_size, netif->sixlo.max_frag_size); gnrc_pktbuf_release_error(pkt, EMSGSIZE); } } 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); #if defined(MODULE_CCN_LITE) payload->type = GNRC_NETTYPE_CCN; #elif defined(MODULE_GNRC_IPV6) payload->type = GNRC_NETTYPE_IPV6; #else payload->type = GNRC_NETTYPE_UNDEF; #endif } #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_FRAG_SFR else if (sixlowpan_sfr_is((sixlowpan_sfr_t *)dispatch)) { DEBUG("6lo: received 6LoWPAN recoverable fragment\n"); gnrc_sixlowpan_frag_sfr_recv(pkt, NULL, 0); return; } #endif /* MODULE_GNRC_SIXLOWPAN_FRAG_SFR */ #ifdef MODULE_GNRC_SIXLOWPAN_IPHC else if (sixlowpan_iphc_is(dispatch)) { DEBUG("6lo: received 6LoWPAN IPHC compressed datagram\n"); gnrc_sixlowpan_iphc_recv(pkt, NULL, 0); return; } #endif else { DEBUG("6lo: dispatch %02x... 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_pktsnip_t *tmp; gnrc_netif_t *netif; /* 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; } #ifdef MODULE_GNRC_IPV6 if ((pkt->next == NULL) || (pkt->next->type != GNRC_NETTYPE_IPV6)) { DEBUG("6lo: Sending packet has no IPv6 header\n"); gnrc_pktbuf_release(pkt); return; } #endif tmp = gnrc_pktbuf_start_write(pkt); if (tmp == NULL) { DEBUG("6lo: no space left in packet buffer\n"); gnrc_pktbuf_release(pkt); return; } pkt = tmp; netif = gnrc_netif_hdr_get_netif(pkt->data); datagram_size = gnrc_pkt_len(pkt->next); if (netif == NULL) { DEBUG("6lo: Can not get 6LoWPAN specific interface information.\n"); gnrc_pktbuf_release(pkt); return; } if (IS_USED(MODULE_GNRC_SIXLOWPAN_IPHC) && netif->flags & GNRC_NETIF_FLAGS_6LO_HC) { gnrc_sixlowpan_frag_fb_t *fbuf; if (IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_HINT) && IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_MINFWD)) { /* prepare for sending with IPHC slack in first fragment */ fbuf = gnrc_sixlowpan_frag_fb_get(); if (fbuf != NULL) { fbuf->pkt = pkt; fbuf->datagram_size = datagram_size; fbuf->tag = gnrc_sixlowpan_frag_fb_next_tag(); fbuf->offset = 0; /* fbuf->hint only exists with the `gnrc_sixlowpan_frag_hint` * module, so despite already specifying that this `if` block * only works with `IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_HINT)` * above, we need to add a pre-processor `#if` here */ #if IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_HINT) fbuf->hint.fragsz = 0; #endif } else { DEBUG("6lo: Not enough resources to fragment packet. " "Dropping packet\n"); gnrc_pktbuf_release(pkt); return; } } else { fbuf = NULL; } gnrc_sixlowpan_iphc_send(pkt, fbuf, 0); return; } if (!_add_uncompr_disp(pkt)) { /* adding uncompressed dispatch failed */ DEBUG("6lo: no space left in packet buffer\n"); gnrc_pktbuf_release(pkt); return; } gnrc_sixlowpan_multiplex_by_size(pkt, datagram_size, netif, 0); } #ifdef MODULE_GNRC_SIXLOWPAN_FRAG_FB static void _continue_fragmenting(gnrc_sixlowpan_frag_fb_t *fbuf) { #ifdef MODULE_GNRC_SIXLOWPAN_FRAG_SFR if (fbuf->pkt == NULL) { /* In case the timer fired before the entry was removed */ return; } gnrc_netif_t *netif = gnrc_netif_hdr_get_netif(fbuf->pkt->data); assert(netif != NULL); if (gnrc_sixlowpan_frag_sfr_netif(netif)) { gnrc_sixlowpan_frag_sfr_send(NULL, fbuf, 0); return; } #endif /* MODULE_GNRC_SIXLOWPAN_FRAG_SFR */ #ifdef MODULE_GNRC_SIXLOWPAN_FRAG gnrc_sixlowpan_frag_send(NULL, fbuf, 0); #else /* MODULE_GNRC_SIXLOWPAN_FRAG */ (void)fbuf; DEBUG("6lo: No fragmentation implementation available to sent\n"); assert(false); #endif /* MODULE_GNRC_SIXLOWPAN_FRAG */ } #endif /* MODULE_GNRC_SIXLOWPAN_FRAG_FB */ 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, thread_getpid()); (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; #ifdef MODULE_GNRC_SIXLOWPAN_FRAG_SFR gnrc_sixlowpan_frag_sfr_init(); #endif /* 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_FB case GNRC_SIXLOWPAN_FRAG_FB_SND_MSG: DEBUG("6lo: send fragmented event received\n"); _continue_fragmenting(msg.content.ptr); break; #endif /* MODULE_GNRC_SIXLOWPAN_FRAG_FB */ #ifdef MODULE_GNRC_SIXLOWPAN_FRAG_RB case GNRC_SIXLOWPAN_FRAG_RB_GC_MSG: DEBUG("6lo: garbage collect reassembly buffer event received\n"); gnrc_sixlowpan_frag_rb_gc(); break; #endif #ifdef MODULE_GNRC_SIXLOWPAN_FRAG_SFR case GNRC_SIXLOWPAN_FRAG_SFR_ARQ_TIMEOUT_MSG: DEBUG("6lo sfr: ARQ timeout received\n"); gnrc_sixlowpan_frag_sfr_arq_timeout(msg.content.ptr); break; case GNRC_SIXLOWPAN_FRAG_SFR_INTER_FRAG_GAP_MSG: DEBUG("6lo sfr: sending next scheduled frame\n"); gnrc_sixlowpan_frag_sfr_inter_frame_gap(); break; #endif default: DEBUG("6lo: operation not supported\n"); break; } } return NULL; } /** @} */