diff --git a/sys/net/gnrc/network_layer/sixlowpan/iphc/gnrc_sixlowpan_iphc.c b/sys/net/gnrc/network_layer/sixlowpan/iphc/gnrc_sixlowpan_iphc.c index 9cae4bf560..0cfac91c34 100644 --- a/sys/net/gnrc/network_layer/sixlowpan/iphc/gnrc_sixlowpan_iphc.c +++ b/sys/net/gnrc/network_layer/sixlowpan/iphc/gnrc_sixlowpan_iphc.c @@ -25,11 +25,15 @@ #include "net/gnrc/sixlowpan.h" #include "net/gnrc/sixlowpan/ctx.h" #include "net/gnrc/sixlowpan/frag/rb.h" +#ifdef MODULE_GNRC_SIXLOWPAN_FRAG_VRB +#include "net/gnrc/sixlowpan/frag/vrb.h" +#endif /* MODULE_GNRC_SIXLOWPAN_FRAG_VRB */ #include "net/gnrc/sixlowpan/internal.h" #include "net/sixlowpan.h" #include "utlist.h" #include "net/gnrc/nettype.h" #include "net/gnrc/udp.h" +#include "od.h" #include "net/gnrc/sixlowpan/iphc.h" @@ -92,6 +96,12 @@ #define NHC_UDP_8BIT_PORT (0xF000) #define NHC_UDP_8BIT_MASK (0xFF00) +/* currently only used with forwarding output, remove guard if more debug info + * is added */ +#ifdef MODULE_GNRC_SIXLOWPAN_FRAG_VRB +static char addr_str[IPV6_ADDR_MAX_STR_LEN]; +#endif /* MODULE_GNRC_SIXLOWPAN_FRAG_VRB */ + static inline bool _context_overlaps_iid(gnrc_sixlowpan_ctx_t *ctx, ipv6_addr_t *addr, eui64_t *iid) @@ -113,6 +123,17 @@ static inline bool _context_overlaps_iid(gnrc_sixlowpan_ctx_t *ctx, (iid->uint8[(ctx->prefix_len / 8) - 8] & byte_mask[ctx->prefix_len % 8]))); } +static gnrc_pktsnip_t *_iphc_encode(gnrc_pktsnip_t *pkt, + const gnrc_netif_hdr_t *netif_hdr, + gnrc_netif_t *netif); + +#ifdef MODULE_GNRC_SIXLOWPAN_FRAG_VRB +static gnrc_pktsnip_t *_encode_frag_for_forwarding(gnrc_pktsnip_t *decoded_pkt, + gnrc_sixlowpan_frag_vrb_t *vrbe); +static int _forward_frag(gnrc_pktsnip_t *pkt, gnrc_pktsnip_t *frag_hdr, + gnrc_sixlowpan_frag_vrb_t *vrbe, unsigned page); +#endif /* MODULE_GNRC_SIXLOWPAN_FRAG_VRB */ + #ifdef MODULE_GNRC_SIXLOWPAN_IPHC_NHC /** * @brief Decodes UDP NHC @@ -236,6 +257,9 @@ void gnrc_sixlowpan_iphc_recv(gnrc_pktsnip_t *sixlo, void *rbuf_ptr, size_t uncomp_hdr_len = sizeof(ipv6_hdr_t); gnrc_sixlowpan_ctx_t *ctx = NULL; gnrc_sixlowpan_frag_rb_t *rbuf = rbuf_ptr; +#ifdef MODULE_GNRC_SIXLOWPAN_FRAG_VRB + gnrc_sixlowpan_frag_vrb_t *vrbe = NULL; +#endif /* MODULE_GNRC_SIXLOWPAN_FRAG_VRB */ if (rbuf != NULL) { ipv6 = rbuf->pkt; @@ -559,6 +583,40 @@ void gnrc_sixlowpan_iphc_recv(gnrc_pktsnip_t *sixlo, void *rbuf_ptr, if (rbuf != NULL) { /* for a fragmented datagram we know the overall length already */ payload_len = (uint16_t)(rbuf->super.datagram_size - sizeof(ipv6_hdr_t)); +#ifdef MODULE_GNRC_SIXLOWPAN_FRAG_VRB + DEBUG("6lo iphc: VRB present, trying to create entry for dst %s\n", + ipv6_addr_to_str(addr_str, &ipv6_hdr->dst, sizeof(addr_str))); + /* re-assign IPv6 header in case realloc changed the address */ + ipv6_hdr = ipv6->data; + /* only create virtual reassembly buffer entry from IPv6 destination if + * the current first fragment is the only received fragment in the + * reassembly buffer so far and the hop-limit is larger than 1 + */ + if ((rbuf->super.current_size <= sixlo->size) && (ipv6_hdr->hl > 1U) && + /* and there is enough slack for changing compression */ + (rbuf->super.current_size <= iface->sixlo.max_frag_size) && + (vrbe = gnrc_sixlowpan_frag_vrb_from_route(&rbuf->super, iface, + ipv6))) { + /* add netif header to `ipv6` so its flags can be used when + * forwarding the fragment */ + LL_DELETE(sixlo, netif); + LL_APPEND(ipv6, netif); + /* provide space to copy remaining payload */ + if (gnrc_pktbuf_realloc_data(ipv6, uncomp_hdr_len + sixlo->size - + payload_offset) != 0) { + DEBUG("6lo iphc: no space left to copy payload\n"); + gnrc_sixlowpan_frag_vrb_rm(vrbe); + _recv_error_release(sixlo, ipv6, rbuf); + return; + } + } + /* reallocate to copy complete payload */ + else if (gnrc_pktbuf_realloc_data(ipv6, rbuf->super.datagram_size) != 0) { + DEBUG("6lo iphc: no space left to reassemble payload\n"); + _recv_error_release(sixlo, ipv6, rbuf); + return; + } +#endif /* MODULE_GNRC_SIXLOWPAN_FRAG_VRB */ } else { /* set IPv6 header payload length field to the length of whatever is left @@ -580,6 +638,30 @@ void gnrc_sixlowpan_iphc_recv(gnrc_pktsnip_t *sixlo, void *rbuf_ptr, sixlo->size - payload_offset); if (rbuf != NULL) { rbuf->super.current_size += (uncomp_hdr_len - payload_offset); +#ifdef MODULE_GNRC_SIXLOWPAN_FRAG_VRB + if (vrbe != NULL) { + int res = -1; + DEBUG("6lo iphc: found route, trying to forward\n"); + ipv6_hdr->hl--; + vrbe->super.current_size = rbuf->super.current_size; + if ((ipv6 = _encode_frag_for_forwarding(ipv6, vrbe))) { + if ((res = _forward_frag(ipv6, sixlo->next, vrbe, page)) == 0) { + DEBUG("6lo iphc: successfully recompressed and forwarded " + "1st fragment\n"); + /* empty list, as it should be in VRB now */ + rbuf->super.ints = NULL; + } + } + if ((ipv6 == NULL) || (res < 0)) { + gnrc_sixlowpan_frag_vrb_rm(vrbe); + } + gnrc_pktbuf_release(sixlo); + /* don't remove `rbuf->pkt` (aka ipv6) as it was forwarded */ + gnrc_sixlowpan_frag_rb_remove(rbuf); + return; + } + DEBUG("6lo iphc: no route found, reassemble datagram normally\n"); +#endif /* MODULE_GNRC_SIXLOWPAN_FRAG_VRB */ } else { LL_DELETE(sixlo, netif); @@ -590,6 +672,76 @@ void gnrc_sixlowpan_iphc_recv(gnrc_pktsnip_t *sixlo, void *rbuf_ptr, return; } +#ifdef MODULE_GNRC_SIXLOWPAN_FRAG_VRB +static gnrc_pktsnip_t *_encode_frag_for_forwarding(gnrc_pktsnip_t *decoded_pkt, + gnrc_sixlowpan_frag_vrb_t *vrbe) +{ + gnrc_pktsnip_t *res; + gnrc_netif_hdr_t *netif_hdr; + + /* mark IPv6 header to allow for next header compression */ + res = gnrc_pktbuf_mark(decoded_pkt, sizeof(ipv6_hdr_t), GNRC_NETTYPE_IPV6); + if (res == NULL) { + DEBUG("6lo iphc: unable to mark IPv6 header for forwarding\n"); + gnrc_pktbuf_release(decoded_pkt); + return NULL; + } + res = gnrc_pktbuf_reverse_snips(decoded_pkt); + if (res == NULL) { + DEBUG("6lo iphc: unable to reverse packet for forwarding\n"); + /* decoded_pkt is released in gnrc_pktbuf_reverse_snips() */ + return NULL; + } + /* set netif header from VRB for correct encoding */ + netif_hdr = res->data; + /* _iphc_encode only checks the destination address, so leave src + * untouched */ + netif_hdr->dst_l2addr_len = vrbe->super.dst_len; + gnrc_netif_hdr_set_dst_addr(netif_hdr, vrbe->super.dst, + vrbe->super.dst_len); + gnrc_netif_hdr_set_netif(netif_hdr, vrbe->out_netif); + decoded_pkt = res; + if ((res = _iphc_encode(decoded_pkt, netif_hdr, vrbe->out_netif))) { + return res; + } + else { + DEBUG("6lo iphc: unable to compress packet for forwarding\n"); + gnrc_pktbuf_release(decoded_pkt); + return NULL; + } +} + +static int _forward_frag(gnrc_pktsnip_t *pkt, gnrc_pktsnip_t *frag_hdr, + gnrc_sixlowpan_frag_vrb_t *vrbe, unsigned page) +{ + /* remove rewritten netif header (forwarding implementation must do this + * anyway) */ + pkt = gnrc_pktbuf_remove_snip(pkt, pkt); + /* the following is just debug output for testing without any forwarding + * scheme */ + DEBUG("6lo iphc: Do not know how to forward fragment from (%s, %u) ", + gnrc_netif_addr_to_str(vrbe->super.src, vrbe->super.src_len, + addr_str), vrbe->super.tag); + DEBUG("to (%s, %u)\n", + gnrc_netif_addr_to_str(vrbe->super.dst, vrbe->super.dst_len, + addr_str), vrbe->out_tag); +#if ENABLE_DEBUG && defined(MODULE_OD) + DEBUG("Original fragmentation header:\n"); + od_hex_dump(frag_hdr->data, frag_hdr->size, OD_WIDTH_DEFAULT); + DEBUG("IPHC headers + payload:\n"); + frag_hdr = pkt; + while (frag_hdr) { + od_hex_dump(frag_hdr->data, frag_hdr->size, OD_WIDTH_DEFAULT); + frag_hdr = frag_hdr->next; + } +#endif + gnrc_pktbuf_release(pkt); + (void)frag_hdr; + (void)page; + return -ENOTSUP; +} +#endif /* MODULE_GNRC_SIXLOWPAN_FRAG_VRB */ + #ifdef MODULE_GNRC_SIXLOWPAN_IPHC_NHC static inline size_t iphc_nhc_udp_encode(uint8_t *nhc_data, const gnrc_pktsnip_t *udp) @@ -655,22 +807,19 @@ static inline bool _compressible(gnrc_pktsnip_t *hdr) } } -void gnrc_sixlowpan_iphc_send(gnrc_pktsnip_t *pkt, void *ctx, unsigned page) +static gnrc_pktsnip_t *_iphc_encode(gnrc_pktsnip_t *pkt, + const gnrc_netif_hdr_t *netif_hdr, + gnrc_netif_t *iface) { assert(pkt != NULL); - gnrc_netif_hdr_t *netif_hdr = pkt->data; ipv6_hdr_t *ipv6_hdr; - gnrc_netif_t *iface = gnrc_netif_hdr_get_netif(netif_hdr); uint8_t *iphc_hdr; gnrc_sixlowpan_ctx_t *src_ctx = NULL, *dst_ctx = NULL; gnrc_pktsnip_t *dispatch, *ptr = pkt->next; bool addr_comp = false; size_t dispatch_size = 0; - /* datagram size before compression */ - size_t orig_datagram_size = gnrc_pkt_len(pkt->next); uint16_t inline_pos = SIXLOWPAN_IPHC_HDR_LEN; - (void)ctx; assert(iface != NULL); dispatch = NULL; /* use dispatch as temporary pointer for prev */ /* determine maximum dispatch size and write protect all headers until @@ -680,10 +829,7 @@ void gnrc_sixlowpan_iphc_send(gnrc_pktsnip_t *pkt, void *ctx, unsigned page) if (tmp == NULL) { DEBUG("6lo iphc: unable to write protect compressible header\n"); - if (addr_comp) { /* addr_comp was used as release indicator */ - gnrc_pktbuf_release(pkt); - } - return; + return NULL; } ptr = tmp; if (dispatch == NULL) { @@ -716,8 +862,7 @@ void gnrc_sixlowpan_iphc_send(gnrc_pktsnip_t *pkt, void *ctx, unsigned page) if (dispatch == NULL) { DEBUG("6lo iphc: error allocating dispatch space\n"); - gnrc_pktbuf_release(pkt); - return; + return NULL; } iphc_hdr = dispatch->data; @@ -844,8 +989,7 @@ void gnrc_sixlowpan_iphc_send(gnrc_pktsnip_t *pkt, void *ctx, unsigned page) if (gnrc_netif_ipv6_get_iid(iface, &iid) < 0) { DEBUG("6lo iphc: could not get interface's IID\n"); gnrc_netif_release(iface); - gnrc_pktbuf_release(pkt); - return; + return NULL; } gnrc_netif_release(iface); @@ -963,8 +1107,7 @@ void gnrc_sixlowpan_iphc_send(gnrc_pktsnip_t *pkt, void *ctx, unsigned page) if (gnrc_netif_hdr_ipv6_iid_from_dst(iface, netif_hdr, &iid) < 0) { DEBUG("6lo iphc: could not get destination's IID\n"); - gnrc_pktbuf_release(pkt); - return; + return NULL; } if ((ipv6_hdr->dst.u64[1].u64 == iid.uint64.u64) || @@ -1012,7 +1155,7 @@ void gnrc_sixlowpan_iphc_send(gnrc_pktsnip_t *pkt, void *ctx, unsigned page) if (udp == NULL) { DEBUG("gnrc_sixlowpan_iphc_encode: unable to mark UDP header\n"); gnrc_pktbuf_release(dispatch); - return; + return NULL; } } gnrc_pktbuf_remove_snip(pkt, udp); @@ -1033,8 +1176,24 @@ void gnrc_sixlowpan_iphc_send(gnrc_pktsnip_t *pkt, void *ctx, unsigned page) /* insert dispatch into packet */ dispatch->next = pkt->next; pkt->next = dispatch; + return pkt; +} - gnrc_sixlowpan_multiplex_by_size(pkt, orig_datagram_size, iface, page); +void gnrc_sixlowpan_iphc_send(gnrc_pktsnip_t *pkt, void *ctx, unsigned page) +{ + gnrc_netif_hdr_t *netif_hdr = pkt->data; + gnrc_netif_t *netif = gnrc_netif_hdr_get_netif(netif_hdr); + gnrc_pktsnip_t *tmp; + /* datagram size before compression */ + size_t orig_datagram_size = gnrc_pkt_len(pkt->next); + + (void)ctx; + if ((tmp = _iphc_encode(pkt, pkt->data, netif))) { + gnrc_sixlowpan_multiplex_by_size(tmp, orig_datagram_size, netif, page); + } + else { + gnrc_pktbuf_release(pkt); + } } /** @} */ diff --git a/tests/gnrc_sixlowpan_iphc_w_vrb/Makefile b/tests/gnrc_sixlowpan_iphc_w_vrb/Makefile new file mode 100644 index 0000000000..c5468203b4 --- /dev/null +++ b/tests/gnrc_sixlowpan_iphc_w_vrb/Makefile @@ -0,0 +1,15 @@ +include ../Makefile.tests_common + +USEMODULE += embunit +USEMODULE += gnrc_ipv6_nib_6ln +USEMODULE += gnrc_sixlowpan_iphc +USEMODULE += gnrc_sixlowpan_frag +USEMODULE += gnrc_sixlowpan_frag_vrb +USEMODULE += netdev_ieee802154 +USEMODULE += netdev_test +USEMODULE += od + +# we don't need all this packet buffer space so reduce it a little +CFLAGS += -DTEST_SUITES -DGNRC_PKTBUF_SIZE=2048 + +include $(RIOTBASE)/Makefile.include diff --git a/tests/gnrc_sixlowpan_iphc_w_vrb/Makefile.ci b/tests/gnrc_sixlowpan_iphc_w_vrb/Makefile.ci new file mode 100644 index 0000000000..d792290668 --- /dev/null +++ b/tests/gnrc_sixlowpan_iphc_w_vrb/Makefile.ci @@ -0,0 +1,22 @@ +BOARD_INSUFFICIENT_MEMORY := \ + arduino-duemilanove \ + arduino-leonardo \ + arduino-mega2560 \ + arduino-nano \ + arduino-uno \ + atmega328p \ + chronos \ + i-nucleo-lrwan1 \ + msb-430 \ + msb-430h \ + nucleo-f030r8 \ + nucleo-f031k6 \ + nucleo-f042k6 \ + nucleo-l031k6 \ + nucleo-l053r8 \ + stm32f030f4-demo \ + stm32f0discovery \ + stm32l0538-disco \ + telosb \ + waspmote-pro \ + # diff --git a/tests/gnrc_sixlowpan_iphc_w_vrb/main.c b/tests/gnrc_sixlowpan_iphc_w_vrb/main.c new file mode 100644 index 0000000000..3fb71a3d30 --- /dev/null +++ b/tests/gnrc_sixlowpan_iphc_w_vrb/main.c @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2019 Freie Universität Berlin + * + * 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. + */ + +/** + * @ingroup tests + * @{ + * + * @file + * @brief Tests 6LoWPAN fragmentation handling of gnrc stack. + * + * @author Martine S. Lenders + * + * @} + */ + +#include "embUnit.h" +#include "net/gnrc.h" +#include "net/gnrc/netif/ieee802154.h" +#include "net/gnrc/sixlowpan.h" +#include "net/gnrc/sixlowpan/frag/rb.h" +#include "net/gnrc/sixlowpan/frag/vrb.h" +#include "net/gnrc/ipv6/nib.h" +#include "net/netdev_test.h" +#include "thread.h" +#include "xtimer.h" + +#define TEST_DST { 0x5a, 0x9d, 0x93, 0x86, 0x22, 0x08, 0x65, 0x79 } +#define TEST_SRC { 0x2a, 0xab, 0xdc, 0x15, 0x54, 0x01, 0x64, 0x79 } +#define TEST_6LO_PAYLOAD { \ + /* 6LoWPAN, Src: 2001:db8::1, Dest: 2001:db8::2 + * Fragmentation Header + * 1100 0... = Pattern: First fragment (0x18) + * Datagram size: 188 + * Datagram tag: 0x000f + * IPHC Header + * 011. .... = Pattern: IP header compression (0x03) + * ...1 1... .... .... = Version, traffic class, and flow label compressed (0x3) + * .... .0.. .... .... = Next header: Inline + * .... ..10 .... .... = Hop limit: 64 (0x2) + * .... .... 0... .... = Context identifier extension: False + * .... .... .0.. .... = Source address compression: Stateless + * .... .... ..00 .... = Source address mode: Inline (0x0000) + * .... .... .... 0... = Multicast address compression: False + * .... .... .... .0.. = Destination address compression: Stateless + * .... .... .... ..00 = Destination address mode: Inline (0x0000) + * Next header: ICMPv6 (0x3a) + * Source: 2001:db8::1 + * Destination: 2001:db8::2 */ \ + 0xc0, 0xbc, 0x00, 0x0f, \ + /* IPHC Header + * 011. .... = Pattern: IP header compression (0x03) + * ...1 1... .... .... = Version, traffic class, and flow label compressed (0x3) + * .... .0.. .... .... = Next header: Inline + * .... ..10 .... .... = Hop limit: 64 (0x2) + * .... .... 0... .... = Context identifier extension: False + * .... .... .0.. .... = Source address compression: Stateless + * .... .... ..00 .... = Source address mode: Inline (0x0000) + * .... .... .... 0... = Multicast address compression: False + * .... .... .... .0.. = Destination address compression: Stateless + * .... .... .... ..00 = Destination address mode: Inline (0x0000) + * Next header: ICMPv6 (0x3a) */ \ + 0x7a, 0x00, 0x3a, \ + /* Source: 2001:db8::1 */ \ + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, \ + /* Destination: 2001:db8::2 */ \ + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, \ + /* Internet Control Message Protocol v6 + * Type: Echo (ping) request (128) + * Code: 0 + * Checksum: 0x8ea0 + * Identifier: 0x238f + * Sequence: 2 + * [No response seen] + * Data (140 bytes) + * Data: 9d4bb21c5353535353535353535353535353535353535353… + */ \ + 0x80, 0x00, 0x8e, 0xa0, 0x23, 0x8f, 0x00, 0x02, \ + 0x9d, 0x4b, 0xb2, 0x1c, 0x53, 0x53, 0x53, 0x53, \ + 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, \ + 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, \ + 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, \ + 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, \ + 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, \ + } +#define TEST_TAG (0x000f) +#define TEST_TGT_IPV6 { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x48, 0x3d, 0x1d, 0x0c, 0x98, 0x31, 0x58, 0xae } + +const uint8_t _test_src[] = TEST_SRC; +const uint8_t _test_dst[] = TEST_DST; +const uint8_t _test_6lo_payload[] = TEST_6LO_PAYLOAD; +const ipv6_addr_t _test_tgt_ipv6 = { .u8 = TEST_TGT_IPV6 }; + +static char _mock_netif_stack[THREAD_STACKSIZE_DEFAULT]; +static netdev_test_t _mock_dev; +static gnrc_netif_t *_mock_netif; + +void _set_up(void) +{ + /* Add default route for the VRB entry created from */ + gnrc_ipv6_nib_ft_add(NULL, 0, &_test_tgt_ipv6, _mock_netif->pid, 0); +} + +void _tear_down(void) +{ + gnrc_ipv6_nib_ft_del(NULL, 0); + gnrc_sixlowpan_frag_rb_reset(); + gnrc_sixlowpan_frag_vrb_reset(); +} + +gnrc_pktsnip_t *_create_fragment(void) +{ + gnrc_pktsnip_t *res = gnrc_netif_hdr_build(_test_src, sizeof(_test_src), + _test_dst, sizeof(_test_dst)); + if (res == NULL) { + return NULL; + } + gnrc_netif_hdr_set_netif(res->data, _mock_netif); + res = gnrc_pktbuf_add(res, _test_6lo_payload, sizeof(_test_6lo_payload), + GNRC_NETTYPE_SIXLOWPAN); + return res; +} + +static unsigned _dispatch_to_6lowpan(gnrc_pktsnip_t *pkt) +{ + unsigned res = gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN, + GNRC_NETREG_DEMUX_CTX_ALL, + pkt); + thread_yield_higher(); + return res; +} + +static bool _rb_is_empty(void) +{ + const gnrc_sixlowpan_frag_rb_t *rb = gnrc_sixlowpan_frag_rb_array(); + unsigned res = 0; + + for (unsigned i = 0; i < GNRC_SIXLOWPAN_FRAG_RBUF_SIZE; i++) { + res += gnrc_sixlowpan_frag_rb_entry_empty(&rb[i]); + } + return res; +} + +static void _test_no_vrbe_but_rbe_exists(void) +{ + const gnrc_sixlowpan_frag_rb_t *rb = gnrc_sixlowpan_frag_rb_array(); + unsigned rbs = 0; + + /* VRB entry does not exist */ + TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get(_test_src, + sizeof(_test_src), + TEST_TAG)); + /* and one reassembly buffer entry exists with the source and tag exists */ + for (unsigned i = 0; i < GNRC_SIXLOWPAN_FRAG_RBUF_SIZE; i++) { + if (!gnrc_sixlowpan_frag_rb_entry_empty(&rb[i])) { + rbs++; + TEST_ASSERT_EQUAL_INT(sizeof(_test_src), rb[i].super.src_len); + TEST_ASSERT_EQUAL_INT(0, memcmp(rb[i].super.src, _test_src, + rb[i].super.src_len)); + TEST_ASSERT_EQUAL_INT(TEST_TAG, rb[i].super.tag); + /* release packet for packet buffer check */ + gnrc_pktbuf_release(rb[i].pkt); + + } + } + TEST_ASSERT_EQUAL_INT(1, rbs); +} + +static void test_recv__success(void) +{ + gnrc_pktsnip_t *pkt = _create_fragment(); + + TEST_ASSERT_NOT_NULL(pkt); + TEST_ASSERT_EQUAL_INT(1, _dispatch_to_6lowpan(pkt)); + /* A VRB entry exists was created but deleted due to -ENOTSUP being + * returned by gnrc_sixlowpan_iphc.c:_forward_frag() + * but the reassembly buffer is empty */ + TEST_ASSERT(_rb_is_empty()); + TEST_ASSERT(gnrc_pktbuf_is_empty()); +} + +static void test_recv__no_route(void) +{ + gnrc_pktsnip_t *pkt = _create_fragment(); + + gnrc_ipv6_nib_ft_del(NULL, 0); + TEST_ASSERT_NOT_NULL(pkt); + TEST_ASSERT_EQUAL_INT(1, _dispatch_to_6lowpan(pkt)); + _test_no_vrbe_but_rbe_exists(); + TEST_ASSERT(gnrc_pktbuf_is_empty()); +} + +static void test_recv__vrb_full(void) +{ + gnrc_pktsnip_t *pkt = _create_fragment(); + gnrc_sixlowpan_frag_rb_base_t base = { + .src = TEST_SRC, + .src_len = sizeof(_test_src), + .tag = TEST_TAG, + }; + + TEST_ASSERT_NOT_NULL(pkt); + /* Fill up VRB */ + for (unsigned i = 0; i < GNRC_SIXLOWPAN_FRAG_VRB_SIZE; i++) { + base.tag++; + base.arrival = xtimer_now_usec(); + TEST_ASSERT_NOT_NULL(gnrc_sixlowpan_frag_vrb_add(&base, _mock_netif, + _test_dst, + sizeof(_test_dst))); + } + TEST_ASSERT_EQUAL_INT(1, _dispatch_to_6lowpan(pkt)); + _test_no_vrbe_but_rbe_exists(); + TEST_ASSERT(gnrc_pktbuf_is_empty()); +} + +static void test_recv__pkt_held(void) +{ + gnrc_pktsnip_t *pkt = _create_fragment(); + + TEST_ASSERT_NOT_NULL(pkt); + gnrc_pktbuf_hold(pkt, 1); + TEST_ASSERT_EQUAL_INT(1, _dispatch_to_6lowpan(pkt)); + /* A VRB entry exists was created but deleted due to -ENOTSUP being + * returned by gnrc_sixlowpan_iphc.c:_forward_frag() + * but the reassembly buffer is empty */ + TEST_ASSERT(_rb_is_empty()); + gnrc_pktbuf_release(pkt); + TEST_ASSERT(gnrc_pktbuf_is_empty()); +} + +static void run_unittests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_recv__success), + new_TestFixture(test_recv__no_route), + new_TestFixture(test_recv__vrb_full), + new_TestFixture(test_recv__pkt_held), + }; + + EMB_UNIT_TESTCALLER(sixlo_iphc_vrb_tests, _set_up, _tear_down, fixtures); + TESTS_START(); + TESTS_RUN((Test *)&sixlo_iphc_vrb_tests); + TESTS_END(); +} + +static int _get_netdev_device_type(netdev_t *netdev, void *value, size_t max_len) +{ + assert(max_len == sizeof(uint16_t)); + (void)netdev; + + *((uint16_t *)value) = NETDEV_TYPE_IEEE802154; + return sizeof(uint16_t); +} + +static int _get_netdev_proto(netdev_t *netdev, void *value, size_t max_len) +{ + assert(max_len == sizeof(gnrc_nettype_t)); + (void)netdev; + + *((gnrc_nettype_t *)value) = GNRC_NETTYPE_SIXLOWPAN; + return sizeof(gnrc_nettype_t); +} + +static int _get_netdev_max_pdu_size(netdev_t *netdev, void *value, + size_t max_len) +{ + assert(max_len == sizeof(uint16_t)); + (void)netdev; + + *((uint16_t *)value) = sizeof(_test_6lo_payload); + return sizeof(uint16_t); +} + +static int _get_netdev_src_len(netdev_t *netdev, void *value, size_t max_len) +{ + (void)netdev; + assert(max_len == sizeof(uint16_t)); + *((uint16_t *)value) = sizeof(_test_dst); + return sizeof(uint16_t); +} + +static int _get_netdev_addr_long(netdev_t *netdev, void *value, size_t max_len) +{ + (void)netdev; + assert(max_len >= sizeof(_test_dst)); + memcpy(value, _test_dst, sizeof(_test_dst)); + return sizeof(_test_dst); +} + +static void _init_mock_netif(void) +{ + netdev_test_setup(&_mock_dev, NULL); + netdev_test_set_get_cb(&_mock_dev, NETOPT_DEVICE_TYPE, + _get_netdev_device_type); + netdev_test_set_get_cb(&_mock_dev, NETOPT_PROTO, + _get_netdev_proto); + netdev_test_set_get_cb(&_mock_dev, NETOPT_MAX_PDU_SIZE, + _get_netdev_max_pdu_size); + netdev_test_set_get_cb(&_mock_dev, NETOPT_SRC_LEN, + _get_netdev_src_len); + netdev_test_set_get_cb(&_mock_dev, NETOPT_ADDRESS_LONG, + _get_netdev_addr_long); + _mock_netif = gnrc_netif_ieee802154_create( + _mock_netif_stack, THREAD_STACKSIZE_DEFAULT, GNRC_NETIF_PRIO, + "mock_netif", (netdev_t *)&_mock_dev); + thread_yield_higher(); +} + +int main(void) +{ + _init_mock_netif(); + run_unittests(); + return 0; +} diff --git a/tests/gnrc_sixlowpan_iphc_w_vrb/tests/01-run.py b/tests/gnrc_sixlowpan_iphc_w_vrb/tests/01-run.py new file mode 100755 index 0000000000..f6bb07dcc4 --- /dev/null +++ b/tests/gnrc_sixlowpan_iphc_w_vrb/tests/01-run.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2019 Freie Universität Berlin +# +# 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. + +import sys +from testrunner import run + + +def testfunc(child): + debug_enabled = child.expect([ + r"OK \(\d+ tests\)", + r"6lo iphc: VRB present, trying to create entry for dst 2001:db8::2", + ]) + if debug_enabled: + # only import scapy dependency if DEBUG_ENABLE=1 + from scapy.all import SixLoWPAN, LoWPANFragmentationFirst, \ + LoWPAN_IPHC, IPv6, ICMPv6EchoRequest + + # test_recv__success + child.expect_exact("6lo iphc: found route, trying to forward") + child.expect( + r"6lo iphc: Do not know how to forward fragment from " + r"\((?P[a-fA-F0-9:]+), (?P\d+)\) to " + r"\((?P[a-fA-F0-9:]+), (?P\d+)\)" + ) + vrb_src_tag = int(child.match.group("vrb_src_tag")) + child.expect_exact("Original fragmentation header:") + child.expect("(?P( [A-Fa-f0-9]{2})+)") + frag = SixLoWPAN(bytes.fromhex(child.match.group("hex"))) + assert LoWPANFragmentationFirst in frag + assert frag[LoWPANFragmentationFirst].datagramTag == vrb_src_tag + stream = "" + child.expect_exact("IPHC headers + payload:") + for _ in range(7): + child.expect("(?P( [A-Fa-f0-9]{2})+)") + stream += child.match.group("hex") + iphc = SixLoWPAN(bytes.fromhex(stream)) + assert LoWPAN_IPHC in iphc + assert IPv6 in iphc # The IPHC header after compression + assert ICMPv6EchoRequest in iphc + # check against fields of original fragment specified in main.c + assert iphc[LoWPAN_IPHC].tf == 0x3 + assert iphc[LoWPAN_IPHC].nh == 0b0 # Inline (ICMPv6) + assert iphc[LoWPAN_IPHC].hlim == 0b00 # Inline as it was decremented + assert not iphc[LoWPAN_IPHC].cid + assert iphc[LoWPAN_IPHC].sac == 0b0 # Stateless + assert iphc[LoWPAN_IPHC].sam == 0b00 # Inline + assert not iphc[LoWPAN_IPHC].m # not multicast + assert iphc[LoWPAN_IPHC].dac == 0b0 # Stateless + assert iphc[LoWPAN_IPHC].dam == 0b00 # Inline + assert iphc[IPv6].src == "2001:db8::1" + assert iphc[IPv6].dst == "2001:db8::2" + assert iphc[IPv6].hlim == (64 - 1) # hop limit was decremented + assert iphc[ICMPv6EchoRequest].code == 0 + assert iphc[ICMPv6EchoRequest].cksum == 0x8ea0 + assert iphc[ICMPv6EchoRequest].id == 0x238f + assert iphc[ICMPv6EchoRequest].seq == 2 + assert iphc[ICMPv6EchoRequest].data.startswith( + bytes.fromhex("9d4bb21c5353535353535353535353535353535353535353") + ) + + # test_recv__no_route + child.expect_exact( + "6lo iphc: VRB present, trying to create entry for dst 2001:db8::2" + ) + child.expect_exact( + "6lo iphc: no route found, reassemble datagram normally" + ) + + # test_recv__vrb_full + child.expect_exact( + "6lo iphc: VRB present, trying to create entry for dst 2001:db8::2" + ) + child.expect_exact( + "6lo iphc: no route found, reassemble datagram normally" + ) + + # test_recv__pkt_held + child.expect_exact("6lo iphc: found route, trying to forward") + child.expect( + r"6lo iphc: Do not know how to forward fragment from " + r"\((?P[a-fA-F0-9:]+), (?P\d+)\) to " + r"\((?P[a-fA-F0-9:]+), (?P\d+)\)" + ) + child.expect_exact("Original fragmentation header:") + child.expect_exact("IPHC headers + payload:") + child.expect(r"OK \((\d+) tests\)") + assert int(child.match.group(1)) >= 4 + + +if __name__ == "__main__": + sys.exit(run(testfunc))