From f1af602952e50adc421a442a8f19b17074387a4c Mon Sep 17 00:00:00 2001 From: "Martine S. Lenders" Date: Tue, 12 Nov 2019 11:13:54 +0100 Subject: [PATCH 01/10] gnrc_sixlowpan_frag_fb: add missing `frag/hint.h` include --- sys/include/net/gnrc/sixlowpan/frag/fb.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sys/include/net/gnrc/sixlowpan/frag/fb.h b/sys/include/net/gnrc/sixlowpan/frag/fb.h index 4bcef54626d9..d9a9009d779a 100644 --- a/sys/include/net/gnrc/sixlowpan/frag/fb.h +++ b/sys/include/net/gnrc/sixlowpan/frag/fb.h @@ -25,6 +25,9 @@ #include "msg.h" #include "net/gnrc/pkt.h" +#ifdef MODULE_GNRC_SIXLOWPAN_FRAG_HINT +#include "net/gnrc/sixlowpan/frag/hint.h" +#endif /* MODULE_GNRC_SIXLOWPAN_FRAG_HINT */ #ifdef __cplusplus extern "C" { From 4ce2d01cb992e2b1bca9a306d6784b5cf057174f Mon Sep 17 00:00:00 2001 From: Martine Lenders Date: Tue, 12 Feb 2019 12:24:51 +0100 Subject: [PATCH 02/10] gnrc_sixlowpan_frag: initial import of minimal forwarding See https://tools.ietf.org/html/draft-ietf-6lo-minimal-fragment-04 --- sys/Makefile.dep | 7 ++ sys/include/net/gnrc/sixlowpan/frag/minfwd.h | 66 ++++++++++++++ sys/net/gnrc/Makefile | 3 + .../sixlowpan/frag/minfwd/Makefile | 3 + .../frag/minfwd/gnrc_sixlowpan_frag_minfwd.c | 89 +++++++++++++++++++ 5 files changed, 168 insertions(+) create mode 100644 sys/include/net/gnrc/sixlowpan/frag/minfwd.h create mode 100644 sys/net/gnrc/network_layer/sixlowpan/frag/minfwd/Makefile create mode 100644 sys/net/gnrc/network_layer/sixlowpan/frag/minfwd/gnrc_sixlowpan_frag_minfwd.c diff --git a/sys/Makefile.dep b/sys/Makefile.dep index 34f0b3ca2390..90383e3fa439 100644 --- a/sys/Makefile.dep +++ b/sys/Makefile.dep @@ -292,6 +292,13 @@ ifneq (,$(filter gnrc_sixlowpan_frag_fb,$(USEMODULE))) USEMODULE += core_msg endif +ifneq (,$(filter gnrc_sixlowpan_frag_minfwd,$(USEMODULE))) + USEMODULE += gnrc_netif_pktq + USEMODULE += gnrc_sixlowpan_frag + USEMODULE += gnrc_sixlowpan_frag_hint + USEMODULE += gnrc_sixlowpan_frag_vrb +endif + ifneq (,$(filter gnrc_sixlowpan_frag_rb,$(USEMODULE))) USEMODULE += xtimer endif diff --git a/sys/include/net/gnrc/sixlowpan/frag/minfwd.h b/sys/include/net/gnrc/sixlowpan/frag/minfwd.h new file mode 100644 index 000000000000..af8705899b7d --- /dev/null +++ b/sys/include/net/gnrc/sixlowpan/frag/minfwd.h @@ -0,0 +1,66 @@ +/* + * 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. + */ + +/** + * @defgroup net_gnrc_sixlowpan_frag_minfwd Minimal fragment forwarding + * @ingroup net_gnrc_sixlowpan_frag + * @brief Provides minimal fragment forwarding using the VRB + * @see [RFC 8930](https://tools.ietf.org/html/rfc8930) + * @see @ref net_gnrc_sixlowpan_frag_vrb + * @experimental + * @{ + * + * @file + * @brief Minimal fragment forwarding definitions + * + * @author Martine Lenders + */ +#ifndef NET_GNRC_SIXLOWPAN_FRAG_MINFWD_H +#define NET_GNRC_SIXLOWPAN_FRAG_MINFWD_H + +#include + +#include "net/gnrc/pkt.h" +#include "net/gnrc/netif.h" +#include "net/gnrc/sixlowpan/frag.h" +#include "net/gnrc/sixlowpan/frag/vrb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Forwards a fragment according to a VRB entry + * + * @param[in] pkt The fragment to forward (without fragmentation header). + * Is consumed by this function. + * @param[in] frag The originally received fragmentation header. + * @param[in] vrbe Virtual reassembly buffer containing the forwarding + * information. Removed when datagram was completely + * forwarded. + * @param[in] page Current 6Lo dispatch parsing page. + * + * @pre `vrbe != NULL` + * @pre `pkt != NULL` + * @pre `frag != NULL` + * + * @return 0 on success. + * @return -ENOMEM, when packet buffer is too full to prepare packet for + * forwarding. + */ +int gnrc_sixlowpan_frag_minfwd_forward(gnrc_pktsnip_t *pkt, + const sixlowpan_frag_n_t *frag, + gnrc_sixlowpan_frag_vrb_t *vrbe, + unsigned page); + +#ifdef __cplusplus +} +#endif + +#endif /* NET_GNRC_SIXLOWPAN_FRAG_MINFWD_H */ +/** @} */ diff --git a/sys/net/gnrc/Makefile b/sys/net/gnrc/Makefile index 2322bed62ec5..dc63650d80d3 100644 --- a/sys/net/gnrc/Makefile +++ b/sys/net/gnrc/Makefile @@ -100,6 +100,9 @@ endif ifneq (,$(filter gnrc_sixlowpan_frag_fb,$(USEMODULE))) DIRS += network_layer/sixlowpan/frag/fb endif +ifneq (,$(filter gnrc_sixlowpan_frag_minfwd,$(USEMODULE))) + DIRS += network_layer/sixlowpan/frag/minfwd +endif ifneq (,$(filter gnrc_sixlowpan_frag_rb,$(USEMODULE))) DIRS += network_layer/sixlowpan/frag/rb endif diff --git a/sys/net/gnrc/network_layer/sixlowpan/frag/minfwd/Makefile b/sys/net/gnrc/network_layer/sixlowpan/frag/minfwd/Makefile new file mode 100644 index 000000000000..6db089d5b09b --- /dev/null +++ b/sys/net/gnrc/network_layer/sixlowpan/frag/minfwd/Makefile @@ -0,0 +1,3 @@ +MODULE := gnrc_sixlowpan_frag_minfwd + +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/gnrc/network_layer/sixlowpan/frag/minfwd/gnrc_sixlowpan_frag_minfwd.c b/sys/net/gnrc/network_layer/sixlowpan/frag/minfwd/gnrc_sixlowpan_frag_minfwd.c new file mode 100644 index 000000000000..86a0983972a1 --- /dev/null +++ b/sys/net/gnrc/network_layer/sixlowpan/frag/minfwd/gnrc_sixlowpan_frag_minfwd.c @@ -0,0 +1,89 @@ +/* + * 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. + */ + +/** + * @{ + * + * @file + * @author Martine Lenders + */ + +#include +#include + +#include "net/gnrc/netif/hdr.h" +#include "net/gnrc/pktbuf.h" +#include "net/gnrc/sixlowpan/internal.h" +#include "utlist.h" + +#include "net/gnrc/sixlowpan/frag/minfwd.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +static gnrc_pktsnip_t *_netif_hdr_from_vrbe(const gnrc_sixlowpan_frag_vrb_t *vrbe) +{ + gnrc_pktsnip_t *res = gnrc_netif_hdr_build(NULL, 0, vrbe->super.dst, + vrbe->super.dst_len); + if (res == NULL) { + DEBUG("6lo minfwd: can't allocate netif header for forwarding.\n"); + return NULL; + } + gnrc_netif_hdr_set_netif(res->data, vrbe->out_netif); + return res; +} + +static inline bool _is_last_frag(const gnrc_sixlowpan_frag_vrb_t *vrbe) +{ + return (vrbe->super.current_size >= vrbe->super.datagram_size); +} + +int gnrc_sixlowpan_frag_minfwd_forward(gnrc_pktsnip_t *pkt, + const sixlowpan_frag_n_t *frag, + gnrc_sixlowpan_frag_vrb_t *vrbe, + unsigned page) +{ + sixlowpan_frag_t *new; + gnrc_pktsnip_t *tmp; + const size_t fragsnip_size = sizeof(sixlowpan_frag_t) + + ((sixlowpan_frag_1_is((sixlowpan_frag_t *)frag)) + ? 0U : sizeof(frag->offset)); + + assert(vrbe != NULL); + assert(pkt != NULL); + assert(frag != NULL); + if ((tmp = gnrc_pktbuf_add(pkt, frag, fragsnip_size, + GNRC_NETTYPE_SIXLOWPAN)) == NULL) { + DEBUG("6lo minfwd: unable to allocate new fragmentation header.\n"); + gnrc_pktbuf_release(pkt); + return -ENOMEM; + } + pkt = tmp; + new = pkt->data; + new->tag = byteorder_htons(vrbe->out_tag); + tmp = _netif_hdr_from_vrbe(vrbe); + if (tmp == NULL) { + gnrc_pktbuf_release(pkt); + return -ENOMEM; + } + if (_is_last_frag(vrbe)) { + DEBUG("6lo minfwd: current_size (%u) >= datagram_size (%u)\n", + vrbe->super.current_size, vrbe->super.datagram_size); + gnrc_sixlowpan_frag_vrb_rm(vrbe); + } + else { + gnrc_netif_hdr_t *netif_hdr = tmp->data; + + netif_hdr->flags |= GNRC_NETIF_HDR_FLAGS_MORE_DATA; + } + pkt = gnrc_pkt_prepend(pkt, tmp); + gnrc_sixlowpan_dispatch_send(pkt, NULL, page); + return 0; +} + +/** @} */ From 8564a86989be8e6e5dfd4f775b764ca66ce2d952 Mon Sep 17 00:00:00 2001 From: Martine Lenders Date: Mon, 25 Feb 2019 20:03:00 +0100 Subject: [PATCH 03/10] tests: introduce tests for gnrc_sixlowpan_frag_minfwd --- tests/gnrc_sixlowpan_frag_minfwd/Makefile | 14 + tests/gnrc_sixlowpan_frag_minfwd/Makefile.ci | 37 ++ tests/gnrc_sixlowpan_frag_minfwd/common.h | 51 ++ tests/gnrc_sixlowpan_frag_minfwd/main.c | 587 ++++++++++++++++++ .../gnrc_sixlowpan_frag_minfwd/mockup_netif.c | 113 ++++ .../tests/01-run.py | 19 + 6 files changed, 821 insertions(+) create mode 100644 tests/gnrc_sixlowpan_frag_minfwd/Makefile create mode 100644 tests/gnrc_sixlowpan_frag_minfwd/Makefile.ci create mode 100644 tests/gnrc_sixlowpan_frag_minfwd/common.h create mode 100644 tests/gnrc_sixlowpan_frag_minfwd/main.c create mode 100644 tests/gnrc_sixlowpan_frag_minfwd/mockup_netif.c create mode 100755 tests/gnrc_sixlowpan_frag_minfwd/tests/01-run.py diff --git a/tests/gnrc_sixlowpan_frag_minfwd/Makefile b/tests/gnrc_sixlowpan_frag_minfwd/Makefile new file mode 100644 index 000000000000..9994b7baf2e1 --- /dev/null +++ b/tests/gnrc_sixlowpan_frag_minfwd/Makefile @@ -0,0 +1,14 @@ +include ../Makefile.tests_common + +USEMODULE += gnrc_ipv6_router_default +USEMODULE += gnrc_sixlowpan_frag_minfwd +USEMODULE += gnrc_sixlowpan_iphc +USEMODULE += gnrc_ipv6_nib +USEMODULE += gnrc_netif +USEMODULE += embunit +USEMODULE += netdev_ieee802154 +USEMODULE += netdev_test + +CFLAGS += -DTEST_SUITES + +include $(RIOTBASE)/Makefile.include diff --git a/tests/gnrc_sixlowpan_frag_minfwd/Makefile.ci b/tests/gnrc_sixlowpan_frag_minfwd/Makefile.ci new file mode 100644 index 000000000000..b92e6e211e42 --- /dev/null +++ b/tests/gnrc_sixlowpan_frag_minfwd/Makefile.ci @@ -0,0 +1,37 @@ +BOARD_INSUFFICIENT_MEMORY := \ + arduino-duemilanove \ + arduino-leonardo \ + arduino-mega2560 \ + arduino-nano \ + arduino-uno \ + atmega1284p \ + atmega328p \ + derfmega128 \ + hifive1 \ + hifive1b \ + i-nucleo-lrwan1 \ + im880b \ + mega-xplained \ + microduino-corerf \ + msb-430 \ + msb-430h \ + nucleo-f030r8 \ + nucleo-f031k6 \ + nucleo-f042k6 \ + nucleo-f070rb \ + nucleo-f072rb \ + nucleo-f303k8 \ + nucleo-f334r8 \ + nucleo-l011k4 \ + nucleo-l031k6 \ + nucleo-l053r8 \ + saml10-xpro \ + saml11-xpro \ + stk3200 \ + stm32f030f4-demo \ + stm32f0discovery \ + stm32l0538-disco \ + telosb \ + waspmote-pro \ + z1 \ + # diff --git a/tests/gnrc_sixlowpan_frag_minfwd/common.h b/tests/gnrc_sixlowpan_frag_minfwd/common.h new file mode 100644 index 000000000000..5d8b4833b8d6 --- /dev/null +++ b/tests/gnrc_sixlowpan_frag_minfwd/common.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2017 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. + */ + +/** + * @defgroup tests_gnrc_ipv6_nib Common header for GNRC's NIB tests + * @ingroup tests + * @brief Common definitions for GNRC's NIB tests + * @{ + * + * @file + * + * @author Martine Lenders + */ +#ifndef COMMON_H +#define COMMON_H + +#include + +#include "net/gnrc.h" +#include "net/gnrc/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define _LL0 (0xb8) +#define _LL1 (0x8c) +#define _LL2 (0xcc) +#define _LL3 (0xba) +#define _LL4 (0xef) +#define _LL5 (0x9a) +#define _LL6 (0x67) +#define _LL7 (0x42) + +extern gnrc_netif_t *_mock_netif; + +void _tests_init(void); +void _common_set_up(void); + + +#ifdef __cplusplus +} +#endif + +#endif /* COMMON_H */ +/** @} */ diff --git a/tests/gnrc_sixlowpan_frag_minfwd/main.c b/tests/gnrc_sixlowpan_frag_minfwd/main.c new file mode 100644 index 000000000000..8ef95ff10dee --- /dev/null +++ b/tests/gnrc_sixlowpan_frag_minfwd/main.c @@ -0,0 +1,587 @@ +/* + * 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 minimal forwarding + * + * @author Martine Lenders + * + * @} + */ + +#include +#include + +#include "cib.h" +#include "common.h" +#include "embUnit.h" +#include "embUnit/embUnit.h" +#include "mutex.h" +#include "net/ipv6.h" +#include "net/gnrc.h" +#include "net/gnrc/ipv6/nib.h" +#include "net/gnrc/ipv6/nib/nc.h" +#include "net/gnrc/ipv6/nib/ft.h" +#include "net/gnrc/sixlowpan/frag.h" +#include "net/gnrc/sixlowpan/frag/rb.h" +#include "net/gnrc/sixlowpan/frag/minfwd.h" +#include "net/gnrc/sixlowpan/iphc.h" +#include "net/netdev_test.h" +/* for debugging _target_buf */ +#include "od.h" +#include "utlist.h" + +#define SEND_PACKET_TIMEOUT (500U) + +#define LOC_L2 { _LL0, _LL1, _LL2, _LL3, _LL4, _LL5, _LL6, _LL7 } +#define LOC_LL { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + _LL0 ^ 0x2, _LL1, _LL2, _LL3, _LL4, _LL5, _LL6, _LL7 \ + } +#define LOC_GB { 0x20, 0x01, 0x0d, 0xb8, 0xd3, 0x35, 0x91, 0x7e, \ + _LL0 ^ 0x2, _LL1, _LL2, _LL3, _LL4, _LL5, _LL6, _LL7 \ + } +#define REM_L2 { _LL0, _LL1, _LL2, _LL3, _LL4, _LL5, _LL6, _LL7 + 1 } +#define REM_LL { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + _LL0 ^ 0x2, _LL1, _LL2, _LL3, _LL4, _LL5, _LL6, _LL7 + 1 \ + } +#define REM_GB { 0x20, 0x01, 0x0d, 0xb8, 0xd3, 0x35, 0x91, 0x7e, \ + _LL0 ^ 0x2, _LL1, _LL2, _LL3, _LL4, _LL5, _LL6, _LL7 + 1 \ + } + +#define LOC_GB_PFX_LEN (64U) +#define REM_GB_PFX_LEN (64U) +#define TEST_1ST_FRAG_UNCOMP_SIZE (80U) +#define TEST_1ST_FRAG_UNCOMP_PAYLOAD_POS (4U) +#define TEST_1ST_FRAG_UNCOMP_IPV6_HDR_POS (5U) +#define TEST_1ST_FRAG_UNCOMP_IPV6_PAYLOAD_SIZE (40U) +#define TEST_1ST_FRAG_UNCOMP_IPV6_PAYLOAD_POS (45U) +#define TEST_1ST_FRAG_UNCOMP_UDP_PAYLOAD_SIZE (32U) +#define TEST_1ST_FRAG_UNCOMP_UDP_PAYLOAD_POS (53U) +#define TEST_1ST_FRAG_COMP_EXP_OFFSET (6U) +#define TEST_NTH_FRAG_SIZE (32U) +#define TEST_NTH_FRAG_OFFSET_POS (4U) +#define TEST_NTH_FRAG_PAYLOAD_POS (5U) + +enum { + FIRST_FRAGMENT = 0, + FIRST_FRAGMENT_REST, + NTH_FRAGMENT +}; + +static const uint8_t _test_1st_frag_uncomp[] = { + 0xc4, 0xd0, /* 1st fragment | datagram size: 1232 */ + 0x67, 0x9d, /* tag: 0x679d */ + 0x41, /* uncompressed IPv6 */ + /* IPv6 header: payload length = 1192, + * next header = UDP (17), hop limit = 65 */ + 0x60, 0x00, 0x00, 0x00, 0x04, 0xa8, 0x11, 0x41, + /* Source: 2001:db8:d6c3:acf:dc71:2b85:82f:75fb */ + 0x20, 0x01, 0x0d, 0xb8, 0xd6, 0xc3, 0x0a, 0xcf, + 0xdc, 0x71, 0x2b, 0x85, 0x08, 0x2f, 0x75, 0xfb, + /* Destination: REM_GB */ + 0x20, 0x01, 0x0d, 0xb8, 0xd3, 0x35, 0x91, 0x7e, + _LL0 ^ 0x2, _LL1, _LL2, _LL3, _LL4, _LL5, _LL6, _LL7 + 1, + /* UDP source: 0xf0b4, UDP destination: 0xf0ba, + * length: 1192, (random) checksum: 0x47b8 */ + 0xf0, 0xb4, 0xf0, 0xba, 0x04, 0xa8, 0x47, 0xb8, + /* (random) payload of length 32 */ + 0xba, 0xb3, 0x6e, 0x4f, 0xd8, 0x23, 0x40, 0xf3, + 0xfb, 0xb9, 0x05, 0xbf, 0xbe, 0x19, 0xf6, 0xa2, + 0xc7, 0x6e, 0x09, 0xf9, 0xba, 0x70, 0x3a, 0x38, + 0xd5, 0x2f, 0x08, 0x85, 0xb8, 0xc1, 0x1a, 0x31, + }; +static const uint8_t _test_nth_frag[] = { + 0xe4, 0xd0, /* n-th fragment | datagram size: 1232 */ + 0x67, 0x9d, /* tag: 0x679d */ + 0x96, /* offset: 1200 (divided by 8) */ + /* payload of length 32 */ + 0x54, 0x26, 0x63, 0xab, 0x31, 0x0b, 0xa4, 0x4e, + 0x6e, 0xa9, 0x09, 0x02, 0x15, 0xbb, 0x24, 0xa9, + 0x56, 0x44, 0x4a, 0x84, 0xd1, 0x83, 0xb9, 0xdb, + 0x0e, 0x0d, 0xd6, 0x6a, 0x83, 0x31, 0x1d, 0x94, + }; +static const ipv6_addr_t _rem_ll = { .u8 = REM_LL }; +static const uint8_t _rem_l2[] = REM_L2; +static const gnrc_sixlowpan_frag_rb_base_t _vrbe_base = { + .src = { 0xde, 0x71, 0x2b, 0x85, 0x08, 0x2f, 0x75, 0xfb }, + .src_len = IEEE802154_LONG_ADDRESS_LEN, + .dst = LOC_L2, + .dst_len = IEEE802154_LONG_ADDRESS_LEN, + .tag = 0x679d, + .datagram_size = 1232U, + .current_size = 0U, + }; +static uint8_t _target_buf[128U]; +static uint8_t _target_buf_len; +/* to protect _target_buf and _target_buf_len */ +/* to wait for new data in _target_buf */ +static mutex_t _target_buf_filled = MUTEX_INIT_LOCKED; +static mutex_t _target_buf_barrier = MUTEX_INIT; + +static gnrc_pktsnip_t *_create_ipv6_hdr(const ipv6_hdr_t *hdr); +static int _set_route_and_nce(const ipv6_addr_t *route, unsigned pfx_len); +static size_t _wait_for_packet(size_t exp_size); +static void _check_vrbe_values(gnrc_sixlowpan_frag_vrb_t *vrbe, + size_t mhr_len, int frag_type); +static void _check_1st_frag_uncomp(size_t mhr_len, uint8_t exp_hl_diff); +static int _mock_netdev_send(netdev_t *dev, const iolist_t *iolist); + +static void _set_up(void) +{ + /* reset data-structures */ + gnrc_sixlowpan_frag_rb_reset(); + gnrc_sixlowpan_frag_vrb_reset(); + gnrc_pktbuf_init(); + memset(_mock_netif->ipv6.addrs, 0, sizeof(_mock_netif->ipv6.addrs)); + memset(_mock_netif->ipv6.addrs_flags, 0, + sizeof(_mock_netif->ipv6.addrs_flags)); + gnrc_ipv6_nib_init(); + gnrc_ipv6_nib_init_iface(_mock_netif); + /* re-init for syncing */ + mutex_init(&_target_buf_filled); + mutex_lock(&_target_buf_filled); + mutex_init(&_target_buf_barrier); +} + +static void _tear_down(void) +{ + netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev, NULL); + mutex_unlock(&_target_buf_barrier); + /* wait in case mutex in _mock_netdev_send was already entered */ + mutex_lock(&_target_buf_barrier); + memset(_target_buf, 0, sizeof(_target_buf)); + _target_buf_len = 0; + mutex_unlock(&_target_buf_barrier); +} + +static void test_minfwd_vrbe_from_route__success__given_netif(void) +{ + gnrc_sixlowpan_frag_vrb_t *vrbe; + static gnrc_pktsnip_t *ipv6_snip; + static const ipv6_hdr_t ipv6_hdr = { + .dst = { .u8 = REM_GB } + }; + + TEST_ASSERT_EQUAL_INT(0, _set_route_and_nce(&ipv6_hdr.dst, REM_GB_PFX_LEN)); + TEST_ASSERT_NOT_NULL((ipv6_snip = _create_ipv6_hdr(&ipv6_hdr))); + TEST_ASSERT_NOT_NULL((vrbe = gnrc_sixlowpan_frag_vrb_from_route( + &_vrbe_base, _mock_netif, ipv6_snip))); + gnrc_pktbuf_release(ipv6_snip); + TEST_ASSERT_EQUAL_INT(_vrbe_base.current_size, vrbe->super.current_size); + TEST_ASSERT(_mock_netif == vrbe->out_netif); + TEST_ASSERT_EQUAL_INT(sizeof(_rem_l2), vrbe->super.dst_len); + TEST_ASSERT_MESSAGE(memcmp(_rem_l2, vrbe->super.dst, sizeof(_rem_l2)) == 0, + "_rem_l2 != vrbe->super.dst"); +} + +static void test_minfwd_vrbe_from_route__success__no_netif(void) +{ + gnrc_sixlowpan_frag_vrb_t *vrbe; + static gnrc_pktsnip_t *ipv6_snip; + static const ipv6_hdr_t ipv6_hdr = { + .dst = { .u8 = REM_GB } + }; + + TEST_ASSERT_EQUAL_INT(0, _set_route_and_nce(&ipv6_hdr.dst, REM_GB_PFX_LEN)); + TEST_ASSERT_NOT_NULL((ipv6_snip = _create_ipv6_hdr(&ipv6_hdr))); + TEST_ASSERT_NOT_NULL((vrbe = gnrc_sixlowpan_frag_vrb_from_route( + &_vrbe_base, NULL, ipv6_snip))); + gnrc_pktbuf_release(ipv6_snip); + TEST_ASSERT_EQUAL_INT(_vrbe_base.current_size, vrbe->super.current_size); + TEST_ASSERT(_mock_netif == vrbe->out_netif); + TEST_ASSERT_EQUAL_INT(sizeof(_rem_l2), vrbe->super.dst_len); + TEST_ASSERT_MESSAGE(memcmp(_rem_l2, vrbe->super.dst, sizeof(_rem_l2)) == 0, + "_rem_l2 != vrbe->super.dst"); +} + +static void test_minfwd_vrbe_from_route__no_route1(void) +{ + static gnrc_pktsnip_t *ipv6_snip; + static const ipv6_hdr_t ipv6_hdr = { + .dst = { .u8 = REM_GB } + }; + + TEST_ASSERT_NOT_NULL((ipv6_snip = _create_ipv6_hdr(&ipv6_hdr))); + TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_from_route( + &_vrbe_base, NULL, ipv6_snip)); + gnrc_pktbuf_release(ipv6_snip); +} + +static void test_minfwd_vrbe_from_route__no_route2(void) +{ + static gnrc_pktsnip_t *snip; + /* fantasy header */ + static const uint8_t hdr[] = { + 0x40, 0xa9, 0xf4, 0xde, 0x6c, 0x87, 0x50, 0x9a, 0x54, 0x1f, + 0x79, 0xde, 0x6e, 0xd2, 0xb0, 0x82, 0x5c, 0x16, 0xdc, 0xd7 + }; + + TEST_ASSERT_NOT_NULL((snip = gnrc_pktbuf_add(NULL, hdr, sizeof(hdr), + GNRC_NETTYPE_TEST))); + TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_from_route(&_vrbe_base, NULL, snip)); + gnrc_pktbuf_release(snip); +} + +static void test_minfwd_vrbe_from_route__local_addr(void) +{ + static gnrc_pktsnip_t *ipv6_snip; + static ipv6_hdr_t ipv6_hdr = { + .dst = { .u8 = LOC_GB } + }; + + /* add address to interface */ + TEST_ASSERT_EQUAL_INT( + sizeof(ipv6_addr_t), + gnrc_netif_ipv6_addr_add(_mock_netif, &ipv6_hdr.dst, + LOC_GB_PFX_LEN, + GNRC_NETIF_IPV6_ADDRS_FLAGS_STATE_VALID) + ); + TEST_ASSERT_NOT_NULL((ipv6_snip = _create_ipv6_hdr(&ipv6_hdr))); + TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_from_route( + &_vrbe_base, NULL, ipv6_snip)); + gnrc_pktbuf_release(ipv6_snip); +} + +static void test_minfwd_vrbe_from_route__vrb_full(void) +{ + static gnrc_pktsnip_t *ipv6_snip; + static ipv6_hdr_t ipv6_hdr = { + .dst = { .u8 = REM_GB } + }; + gnrc_sixlowpan_frag_rb_base_t base = _vrbe_base; + + TEST_ASSERT_EQUAL_INT(0, _set_route_and_nce(&ipv6_hdr.dst, REM_GB_PFX_LEN)); + /* fill up VRB */ + for (unsigned i = 0; i < GNRC_SIXLOWPAN_FRAG_VRB_SIZE; i++) { + TEST_ASSERT_NOT_NULL(gnrc_sixlowpan_frag_vrb_add(&base, + _mock_netif, + _rem_l2, + sizeof(_rem_l2))); + base.tag++; + } + TEST_ASSERT_NOT_NULL((ipv6_snip = _create_ipv6_hdr(&ipv6_hdr))); + TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_from_route( + &base, NULL, ipv6_snip)); + gnrc_pktbuf_release(ipv6_snip); +} + +static void test_minfwd_forward__success__1st_frag_sixlo(void) +{ + gnrc_sixlowpan_frag_vrb_t *vrbe = gnrc_sixlowpan_frag_vrb_add( + &_vrbe_base, _mock_netif, _rem_l2, sizeof(_rem_l2) + ); + gnrc_pktsnip_t *pkt, *frag; + size_t mhr_len; + + TEST_ASSERT_NOT_NULL((pkt = gnrc_pktbuf_add(NULL, _test_1st_frag_uncomp, + sizeof(_test_1st_frag_uncomp), + GNRC_NETTYPE_SIXLOWPAN))); + /* separate fragment header from payload */ + TEST_ASSERT_NOT_NULL((frag = gnrc_pktbuf_mark(pkt, sizeof(sixlowpan_frag_t), + GNRC_NETTYPE_SIXLOWPAN))); + LL_DELETE(pkt, frag); + netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev, + _mock_netdev_send); + TEST_ASSERT_EQUAL_INT(0, gnrc_sixlowpan_frag_minfwd_forward(pkt, + frag->data, + vrbe, + 0)); + gnrc_pktbuf_release(frag); /* delete separated fragment header */ + TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_1st_frag_uncomp)))); + TEST_ASSERT(gnrc_pktbuf_is_sane()); + TEST_ASSERT(gnrc_pktbuf_is_empty()); + _check_vrbe_values(vrbe, mhr_len, FIRST_FRAGMENT); + TEST_ASSERT(_target_buf[0] & IEEE802154_FCF_FRAME_PEND); + _check_1st_frag_uncomp(mhr_len, 0U); + /* VRB entry should not have been removed */ + TEST_ASSERT_NOT_NULL(gnrc_sixlowpan_frag_vrb_get(_vrbe_base.src, + _vrbe_base.src_len, + _vrbe_base.tag)); +} + +static void test_minfwd_forward__success__nth_frag_incomplete(void) +{ + gnrc_sixlowpan_frag_vrb_t *vrbe = gnrc_sixlowpan_frag_vrb_add( + &_vrbe_base, _mock_netif, _rem_l2, sizeof(_rem_l2) + ); + gnrc_pktsnip_t *pkt, *frag; + size_t mhr_len; + + TEST_ASSERT_NOT_NULL((pkt = gnrc_pktbuf_add(NULL, _test_nth_frag, + sizeof(_test_nth_frag), + GNRC_NETTYPE_SIXLOWPAN))); + /* separate fragment header from payload */ + TEST_ASSERT_NOT_NULL((frag = gnrc_pktbuf_mark(pkt, + sizeof(sixlowpan_frag_n_t), + GNRC_NETTYPE_SIXLOWPAN))); + LL_DELETE(pkt, frag); + netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev, + _mock_netdev_send); + TEST_ASSERT_EQUAL_INT(0, gnrc_sixlowpan_frag_minfwd_forward(pkt, + frag->data, + vrbe, + 0)); + gnrc_pktbuf_release(frag); /* delete separated fragment header */ + TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_nth_frag)))); + TEST_ASSERT(gnrc_pktbuf_is_sane()); + TEST_ASSERT(gnrc_pktbuf_is_empty()); + _check_vrbe_values(vrbe, mhr_len, NTH_FRAGMENT); + TEST_ASSERT(_target_buf[0] & IEEE802154_FCF_FRAME_PEND); + TEST_ASSERT_MESSAGE( + memcmp(&_test_nth_frag[TEST_NTH_FRAG_PAYLOAD_POS], + &_target_buf[mhr_len + sizeof(sixlowpan_frag_n_t)], + TEST_NTH_FRAG_SIZE) == 0, + "unexpected forwarded packet payload" + ); + /* VRB entry should not have been removed */ + TEST_ASSERT_NOT_NULL(gnrc_sixlowpan_frag_vrb_get(_vrbe_base.src, + _vrbe_base.src_len, + _vrbe_base.tag)); +} + +static void test_minfwd_forward__success__nth_frag_complete(void) +{ + gnrc_sixlowpan_frag_vrb_t *vrbe = gnrc_sixlowpan_frag_vrb_add( + &_vrbe_base, _mock_netif, _rem_l2, sizeof(_rem_l2) + ); + gnrc_pktsnip_t *pkt, *frag; + + TEST_ASSERT_NOT_NULL((pkt = gnrc_pktbuf_add(NULL, _test_nth_frag, + sizeof(_test_nth_frag), + GNRC_NETTYPE_SIXLOWPAN))); + /* separate fragment header from payload */ + TEST_ASSERT_NOT_NULL((frag = gnrc_pktbuf_mark(pkt, + sizeof(sixlowpan_frag_n_t), + GNRC_NETTYPE_SIXLOWPAN))); + LL_DELETE(pkt, frag); + /* simulate current_size only missing the created fragment */ + vrbe->super.current_size = _vrbe_base.datagram_size; + netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev, + _mock_netdev_send); + TEST_ASSERT_EQUAL_INT(0, gnrc_sixlowpan_frag_minfwd_forward(pkt, + frag->data, + vrbe, + 0)); + gnrc_pktbuf_release(frag); /* delete separated fragment header */ + TEST_ASSERT(_wait_for_packet(sizeof(_test_nth_frag))); + TEST_ASSERT(gnrc_pktbuf_is_sane()); + TEST_ASSERT(gnrc_pktbuf_is_empty()); + TEST_ASSERT(!(_target_buf[0] & IEEE802154_FCF_FRAME_PEND)); + /* VRB entry should have been removed since + * vrbe->super.current_size became vrbe->super.datagram_size */ + TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get(_vrbe_base.src, + _vrbe_base.src_len, + _vrbe_base.tag)); +} + +static void test_minfwd_forward__ENOMEM__netif_hdr_build_fail(void) +{ + gnrc_sixlowpan_frag_vrb_t *vrbe = gnrc_sixlowpan_frag_vrb_add( + &_vrbe_base, _mock_netif, _rem_l2, sizeof(_rem_l2) + ); + gnrc_pktsnip_t *pkt, *frag, *filled_space; + + TEST_ASSERT_NOT_NULL((filled_space = gnrc_pktbuf_add( + NULL, NULL, + /* 115U == 2 * sizeof(gnrc_pktsnip_t) + movement due to mark */ + CONFIG_GNRC_PKTBUF_SIZE - sizeof(_test_nth_frag) - 115U, + GNRC_NETTYPE_UNDEF + ))); + TEST_ASSERT_NOT_NULL((pkt = gnrc_pktbuf_add(NULL, _test_nth_frag, + sizeof(_test_nth_frag), + GNRC_NETTYPE_SIXLOWPAN))); + /* separate fragment header from payload */ + TEST_ASSERT_NOT_NULL((frag = gnrc_pktbuf_mark(pkt, + sizeof(sixlowpan_frag_n_t), + GNRC_NETTYPE_SIXLOWPAN))); + LL_DELETE(pkt, frag); + + netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev, + _mock_netdev_send); + TEST_ASSERT_EQUAL_INT(-ENOMEM, gnrc_sixlowpan_frag_minfwd_forward(pkt, + frag->data, + vrbe, + 0)); + gnrc_pktbuf_release(frag); /* delete separated fragment header */ + gnrc_pktbuf_release(filled_space); + TEST_ASSERT(gnrc_pktbuf_is_sane()); + TEST_ASSERT(gnrc_pktbuf_is_empty()); +} + +static Test *tests_gnrc_sixlowpan_frag_minfwd_api(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_minfwd_vrbe_from_route__success__given_netif), + new_TestFixture(test_minfwd_vrbe_from_route__success__no_netif), + new_TestFixture(test_minfwd_vrbe_from_route__no_route1), + new_TestFixture(test_minfwd_vrbe_from_route__no_route2), + new_TestFixture(test_minfwd_vrbe_from_route__local_addr), + new_TestFixture(test_minfwd_vrbe_from_route__vrb_full), + new_TestFixture(test_minfwd_forward__success__1st_frag_sixlo), + new_TestFixture(test_minfwd_forward__success__nth_frag_incomplete), + new_TestFixture(test_minfwd_forward__success__nth_frag_complete), + new_TestFixture(test_minfwd_forward__ENOMEM__netif_hdr_build_fail), + }; + + EMB_UNIT_TESTCALLER(tests, _set_up, _tear_down, fixtures); + + return (Test *)&tests; +} + +int main(void) +{ + _tests_init(); + + TESTS_START(); + TESTS_RUN(tests_gnrc_sixlowpan_frag_minfwd_api()); + TESTS_END(); + return 0; +} + +static gnrc_pktsnip_t *_create_ipv6_hdr(const ipv6_hdr_t *hdr) +{ + return gnrc_pktbuf_add(NULL, hdr, sizeof(*hdr), GNRC_NETTYPE_IPV6); +} + +static int _set_route_and_nce(const ipv6_addr_t *route, unsigned pfx_len) +{ + /* add neighbor cache entry */ + if (gnrc_ipv6_nib_nc_set(&_rem_ll, _mock_netif->pid, + _rem_l2, sizeof(_rem_l2)) < 0) { + return -1; + } + /* and route to neighbor */ + if (gnrc_ipv6_nib_ft_add(route, pfx_len, &_rem_ll, _mock_netif->pid, + 0) < 0) { + return -1; + } + return 0; +} + +static size_t _wait_for_packet(size_t exp_size) +{ + size_t mhr_len; + + xtimer_mutex_lock_timeout(&_target_buf_filled, + SEND_PACKET_TIMEOUT); + while ((mhr_len = ieee802154_get_frame_hdr_len(_target_buf))) { + if (IS_USED(MODULE_OD) && _target_buf_len > 0) { + puts("Sent packet: "); + od_hex_dump(_target_buf, _target_buf_len, OD_WIDTH_DEFAULT); + } + if (exp_size == (_target_buf_len - mhr_len)) { + /* found expected packet */ + break; + } + /* let packets in again at the device */ + mutex_unlock(&_target_buf_barrier); + /* wait for next packet */ + if (xtimer_mutex_lock_timeout(&_target_buf_filled, + SEND_PACKET_TIMEOUT) < 0) { + return 0; + } + } + return mhr_len; +} + +static void _check_vrbe_values(gnrc_sixlowpan_frag_vrb_t *vrbe, + size_t mhr_len, int frag_type) +{ + uint8_t target_buf_dst[IEEE802154_LONG_ADDRESS_LEN]; + sixlowpan_frag_t *frag_hdr = (sixlowpan_frag_t *)&_target_buf[mhr_len]; + le_uint16_t tmp; + + TEST_ASSERT_EQUAL_INT(vrbe->super.dst_len, + ieee802154_get_dst(_target_buf, + target_buf_dst, + &tmp)); + TEST_ASSERT_MESSAGE(memcmp(vrbe->super.dst, target_buf_dst, + vrbe->super.dst_len) == 0, + "vrbe->out_dst != target_buf_dst"); + + TEST_ASSERT_EQUAL_INT(vrbe->super.datagram_size, + byteorder_ntohs(frag_hdr->disp_size) & + SIXLOWPAN_FRAG_SIZE_MASK); + TEST_ASSERT_EQUAL_INT(vrbe->out_tag, + byteorder_ntohs(frag_hdr->tag)); + switch (frag_type) { + case FIRST_FRAGMENT: { + TEST_ASSERT_EQUAL_INT( + SIXLOWPAN_FRAG_1_DISP, + _target_buf[mhr_len] & SIXLOWPAN_FRAG_DISP_MASK + ); + break; + } + case FIRST_FRAGMENT_REST: + case NTH_FRAGMENT: { + sixlowpan_frag_n_t *frag_n_hdr = (sixlowpan_frag_n_t *)&_target_buf[mhr_len]; + uint8_t exp_offset = (frag_type == FIRST_FRAGMENT_REST) + ? TEST_1ST_FRAG_COMP_EXP_OFFSET + : _test_nth_frag[TEST_NTH_FRAG_OFFSET_POS]; + + TEST_ASSERT_EQUAL_INT( + SIXLOWPAN_FRAG_N_DISP, + _target_buf[mhr_len] & SIXLOWPAN_FRAG_DISP_MASK + ); + TEST_ASSERT_EQUAL_INT(exp_offset, frag_n_hdr->offset); + break; + } + default: + TEST_ASSERT_MESSAGE(false, "Unexpected frag_type"); + break; + } +} + +static void _check_1st_frag_uncomp(size_t mhr_len, uint8_t exp_hl_diff) +{ + static const ipv6_hdr_t *exp_ipv6_hdr = (ipv6_hdr_t *)&_test_1st_frag_uncomp[ + TEST_1ST_FRAG_UNCOMP_IPV6_HDR_POS + ]; + ipv6_hdr_t *ipv6_hdr; + + TEST_ASSERT_EQUAL_INT( + SIXLOWPAN_UNCOMP, + _target_buf[mhr_len + TEST_1ST_FRAG_UNCOMP_PAYLOAD_POS] + ); + ipv6_hdr = (ipv6_hdr_t *)&_target_buf[ + mhr_len + TEST_1ST_FRAG_UNCOMP_IPV6_HDR_POS + ]; + TEST_ASSERT_EQUAL_INT(exp_ipv6_hdr->v_tc_fl.u32, ipv6_hdr->v_tc_fl.u32); + TEST_ASSERT_EQUAL_INT(exp_ipv6_hdr->len.u16, ipv6_hdr->len.u16); + TEST_ASSERT_EQUAL_INT(exp_ipv6_hdr->nh, ipv6_hdr->nh); + /* hop-limit shall be decremented by 1 */ + TEST_ASSERT_EQUAL_INT(exp_ipv6_hdr->hl - exp_hl_diff, ipv6_hdr->hl); + TEST_ASSERT(ipv6_addr_equal(&exp_ipv6_hdr->src, &ipv6_hdr->src)); + TEST_ASSERT(ipv6_addr_equal(&exp_ipv6_hdr->dst, &ipv6_hdr->dst)); + TEST_ASSERT_MESSAGE( + memcmp(&_test_1st_frag_uncomp[TEST_1ST_FRAG_UNCOMP_IPV6_PAYLOAD_POS], + ipv6_hdr + 1, TEST_1ST_FRAG_UNCOMP_IPV6_PAYLOAD_SIZE) == 0, + "unexpected forwarded packet payload" + ); +} + +static int _mock_netdev_send(netdev_t *dev, const iolist_t *iolist) +{ + (void)dev; + mutex_lock(&_target_buf_barrier); + _target_buf_len = 0; + for (const iolist_t *ptr = iolist; ptr != NULL; ptr = ptr->iol_next) { + if ((_target_buf_len + iolist->iol_len) > sizeof(_target_buf)) { + return -ENOBUFS; + } + memcpy(&_target_buf[_target_buf_len], ptr->iol_base, ptr->iol_len); + _target_buf_len += ptr->iol_len; + } + /* wake-up test thread */ + mutex_unlock(&_target_buf_filled); + return _target_buf_len; +} diff --git a/tests/gnrc_sixlowpan_frag_minfwd/mockup_netif.c b/tests/gnrc_sixlowpan_frag_minfwd/mockup_netif.c new file mode 100644 index 000000000000..342559f9238d --- /dev/null +++ b/tests/gnrc_sixlowpan_frag_minfwd/mockup_netif.c @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2017 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. + */ + +/** + * @{ + * + * @file + * @author Martine Lenders + */ + +#include "common.h" +#include "msg.h" +#include "net/gnrc.h" +#include "net/ethernet.h" +#include "net/gnrc/ipv6/nib.h" +#include "net/gnrc/netif/ieee802154.h" +#include "net/gnrc/netif/internal.h" +#include "net/netdev_test.h" +#include "sched.h" +#include "thread.h" + +#define _MSG_QUEUE_SIZE (2) + +gnrc_netif_t *_mock_netif = NULL; + +static netdev_test_t _mock_netdev; +static char _mock_netif_stack[THREAD_STACKSIZE_DEFAULT]; +static msg_t _main_msg_queue[_MSG_QUEUE_SIZE]; +static gnrc_netif_t _netif; + +void _common_set_up(void) +{ + assert(_mock_netif != NULL); + gnrc_ipv6_nib_init(); + gnrc_netif_acquire(_mock_netif); + gnrc_ipv6_nib_init_iface(_mock_netif); + gnrc_netif_release(_mock_netif); +} + +int _get_device_type(netdev_t *dev, void *value, size_t max_len) +{ + (void)dev; + assert(max_len == sizeof(uint16_t)); + *((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); +} + +int _get_max_packet_size(netdev_t *dev, void *value, size_t max_len) +{ + (void)dev; + assert(max_len == sizeof(uint16_t)); + *((uint16_t *)value) = 102U; + return sizeof(uint16_t); +} + +int _get_src_len(netdev_t *dev, void *value, size_t max_len) +{ + (void)dev; + assert(max_len == sizeof(uint16_t)); + *((uint16_t *)value) = IEEE802154_LONG_ADDRESS_LEN; + return sizeof(uint16_t); +} + +int _get_address_long(netdev_t *dev, void *value, size_t max_len) +{ + static const uint8_t addr[] = { _LL0, _LL1, _LL2, _LL3, + _LL4, _LL5, _LL6, _LL7 }; + + (void)dev; + assert(max_len >= sizeof(addr)); + memcpy(value, addr, sizeof(addr)); + return sizeof(addr); +} + +void _tests_init(void) +{ + int res; + + msg_init_queue(_main_msg_queue, _MSG_QUEUE_SIZE); + netdev_test_setup(&_mock_netdev, 0); + netdev_test_set_get_cb(&_mock_netdev, NETOPT_DEVICE_TYPE, + _get_device_type); + netdev_test_set_get_cb(&_mock_netdev, NETOPT_PROTO, + _get_netdev_proto); + netdev_test_set_get_cb(&_mock_netdev, NETOPT_MAX_PACKET_SIZE, + _get_max_packet_size); + netdev_test_set_get_cb(&_mock_netdev, NETOPT_SRC_LEN, + _get_src_len); + netdev_test_set_get_cb(&_mock_netdev, NETOPT_ADDRESS_LONG, + _get_address_long); + res = gnrc_netif_ieee802154_create( + &_netif, _mock_netif_stack, THREAD_STACKSIZE_DEFAULT, + GNRC_NETIF_PRIO, "mockup_wpan", &_mock_netdev.netdev.netdev + ); + assert(res == 0); + _mock_netif = &_netif; +} + +/** @} */ diff --git a/tests/gnrc_sixlowpan_frag_minfwd/tests/01-run.py b/tests/gnrc_sixlowpan_frag_minfwd/tests/01-run.py new file mode 100755 index 000000000000..6d8b056e2d59 --- /dev/null +++ b/tests/gnrc_sixlowpan_frag_minfwd/tests/01-run.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2016 Kaspar Schleiser +# Copyright (C) 2016 Takuo Yonezawa +# +# 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): + child.expect(r"OK \(\d+ tests\)") + + +if __name__ == "__main__": + sys.exit(run(testfunc)) From df1171a069429841428ec86d9577f7764b07b563 Mon Sep 17 00:00:00 2001 From: Martine Lenders Date: Mon, 25 Feb 2019 20:30:30 +0100 Subject: [PATCH 04/10] gnrc_sixlowpan_frag_rb: make rbuf pointer generic --- .../frag/rb/gnrc_sixlowpan_frag_rb.c | 47 ++++++++++--------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/sys/net/gnrc/network_layer/sixlowpan/frag/rb/gnrc_sixlowpan_frag_rb.c b/sys/net/gnrc/network_layer/sixlowpan/frag/rb/gnrc_sixlowpan_frag_rb.c index 2ba5f625cb42..4b7f78261def 100644 --- a/sys/net/gnrc/network_layer/sixlowpan/frag/rb/gnrc_sixlowpan_frag_rb.c +++ b/sys/net/gnrc/network_layer/sixlowpan/frag/rb/gnrc_sixlowpan_frag_rb.c @@ -221,7 +221,12 @@ static size_t _6lo_frag_size(gnrc_pktsnip_t *pkt, size_t offset, uint8_t *data) static int _rbuf_add(gnrc_netif_hdr_t *netif_hdr, gnrc_pktsnip_t *pkt, size_t offset, unsigned page) { - gnrc_sixlowpan_frag_rb_t *entry; + union { + gnrc_sixlowpan_frag_rb_base_t *super; + gnrc_sixlowpan_frag_rb_t *rbuf; + } entry; + const uint8_t *src = gnrc_netif_hdr_get_src_addr(netif_hdr); + const uint8_t *dst = gnrc_netif_hdr_get_dst_addr(netif_hdr); uint8_t *data; size_t frag_size; int res; @@ -236,29 +241,27 @@ static int _rbuf_add(gnrc_netif_hdr_t *netif_hdr, gnrc_pktsnip_t *pkt, datagram_tag = sixlowpan_frag_datagram_tag(pkt->data); gnrc_sixlowpan_frag_rb_gc(); - res = _rbuf_get(gnrc_netif_hdr_get_src_addr(netif_hdr), netif_hdr->src_l2addr_len, - gnrc_netif_hdr_get_dst_addr(netif_hdr), netif_hdr->dst_l2addr_len, - datagram_size, datagram_tag, page); - - if (res < 0) { + if ((res = _rbuf_get(src, netif_hdr->src_l2addr_len, + dst, netif_hdr->dst_l2addr_len, + datagram_size, datagram_tag, page)) < 0) { DEBUG("6lo rbuf: reassembly buffer full.\n"); gnrc_pktbuf_release(pkt); return RBUF_ADD_ERROR; } - entry = &rbuf[res]; - if ((offset + frag_size) > entry->super.datagram_size) { + entry.rbuf = &rbuf[res]; + if ((offset + frag_size) > entry.super->datagram_size) { DEBUG("6lo rfrag: fragment too big for resulting datagram, discarding datagram\n"); - gnrc_pktbuf_release(entry->pkt); + gnrc_pktbuf_release(entry.rbuf->pkt); gnrc_pktbuf_release(pkt); - gnrc_sixlowpan_frag_rb_remove(entry); + gnrc_sixlowpan_frag_rb_remove(entry.rbuf); return RBUF_ADD_ERROR; } - switch (_check_fragments(&entry->super, frag_size, offset)) { + switch (_check_fragments(entry.super, frag_size, offset)) { case RBUF_ADD_REPEAT: DEBUG("6lo rfrag: overlapping intervals, discarding datagram\n"); - gnrc_pktbuf_release(entry->pkt); - gnrc_sixlowpan_frag_rb_remove(entry); + gnrc_pktbuf_release(entry.rbuf->pkt); + gnrc_sixlowpan_frag_rb_remove(entry.rbuf); return RBUF_ADD_REPEAT; case RBUF_ADD_DUPLICATE: gnrc_pktbuf_release(pkt); @@ -267,9 +270,9 @@ static int _rbuf_add(gnrc_netif_hdr_t *netif_hdr, gnrc_pktsnip_t *pkt, break; } - if (_rbuf_update_ints(&entry->super, offset, frag_size)) { + if (_rbuf_update_ints(entry.super, offset, frag_size)) { DEBUG("6lo rbuf: add fragment data\n"); - entry->super.current_size += (uint16_t)frag_size; + entry.super->current_size += (uint16_t)frag_size; if (offset == 0) { #ifdef MODULE_GNRC_SIXLOWPAN_IPHC if (sixlowpan_iphc_is(data)) { @@ -279,17 +282,17 @@ static int _rbuf_add(gnrc_netif_hdr_t *netif_hdr, gnrc_pktsnip_t *pkt, if (frag_hdr == NULL) { DEBUG("6lo rbuf: unable to mark fragment header. " "aborting reassembly.\n"); - gnrc_pktbuf_release(entry->pkt); + gnrc_pktbuf_release(entry.rbuf->pkt); gnrc_pktbuf_release(pkt); - gnrc_sixlowpan_frag_rb_remove(entry); + gnrc_sixlowpan_frag_rb_remove(entry.rbuf); return RBUF_ADD_ERROR; } else { DEBUG("6lo rbuf: handing over to IPHC reception.\n"); /* `pkt` released in IPHC */ - gnrc_sixlowpan_iphc_recv(pkt, entry, 0); + gnrc_sixlowpan_iphc_recv(pkt, entry.rbuf, 0); /* check if entry was deleted in IPHC (error case) */ - if (gnrc_sixlowpan_frag_rb_entry_empty(entry)) { + if (gnrc_sixlowpan_frag_rb_entry_empty(entry.rbuf)) { res = RBUF_ADD_ERROR; } return res; @@ -302,13 +305,13 @@ static int _rbuf_add(gnrc_netif_hdr_t *netif_hdr, gnrc_pktsnip_t *pkt, data++; } } - memcpy(((uint8_t *)entry->pkt->data) + offset, data, + memcpy(((uint8_t *)entry.rbuf->pkt->data) + offset, data, frag_size); } else { /* no space left in rbuf interval buffer*/ - gnrc_pktbuf_release(entry->pkt); - gnrc_sixlowpan_frag_rb_remove(entry); + gnrc_pktbuf_release(entry.rbuf->pkt); + gnrc_sixlowpan_frag_rb_remove(entry.rbuf); res = RBUF_ADD_ERROR; } /* no errors and not consumed => release packet */ From 7fe07e35a3439234a4458362625d7be645a06843 Mon Sep 17 00:00:00 2001 From: Martine Lenders Date: Tue, 26 Feb 2019 13:31:21 +0100 Subject: [PATCH 05/10] gnrc_sixlowpan_frag_rb: add VRB and use minfwd handling When a VRB entry exists use minfwd to forward. When a route exist for the first fragment received in reassembly create a virtual reassembly buffer entry. --- .../frag/rb/gnrc_sixlowpan_frag_rb.c | 192 +++++++++++++++++- 1 file changed, 187 insertions(+), 5 deletions(-) diff --git a/sys/net/gnrc/network_layer/sixlowpan/frag/rb/gnrc_sixlowpan_frag_rb.c b/sys/net/gnrc/network_layer/sixlowpan/frag/rb/gnrc_sixlowpan_frag_rb.c index 4b7f78261def..80016bd0616e 100644 --- a/sys/net/gnrc/network_layer/sixlowpan/frag/rb/gnrc_sixlowpan_frag_rb.c +++ b/sys/net/gnrc/network_layer/sixlowpan/frag/rb/gnrc_sixlowpan_frag_rb.c @@ -26,9 +26,8 @@ #ifdef MODULE_GNRC_SIXLOWPAN_FRAG_STATS #include "net/gnrc/sixlowpan/frag/stats.h" #endif /* MODULE_GNRC_SIXLOWPAN_FRAG_STATS */ -#ifdef MODULE_GNRC_SIXLOWPAN_FRAG_VRB +#include "net/gnrc/sixlowpan/frag/minfwd.h" #include "net/gnrc/sixlowpan/frag/vrb.h" -#endif /* MODULE_GNRC_SIXLOWPAN_FRAG_VRB */ #include "net/sixlowpan.h" #include "thread.h" #include "xtimer.h" @@ -49,8 +48,14 @@ #ifndef RBUF_INT_SIZE /* same as ((int) ceil((double) N / D)) */ #define DIV_CEIL(N, D) (((N) + (D) - 1) / (D)) +#if IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_MINFWD) +#define RBUF_INT_SIZE (DIV_CEIL(IPV6_MIN_MTU, GNRC_SIXLOWPAN_FRAG_SIZE) * \ + (CONFIG_GNRC_SIXLOWPAN_FRAG_RBUF_SIZE + \ + CONFIG_GNRC_SIXLOWPAN_FRAG_VRB_SIZE)) +#else /* IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_MINFWD) */ #define RBUF_INT_SIZE (DIV_CEIL(IPV6_MIN_MTU, GNRC_SIXLOWPAN_FRAG_SIZE) * \ CONFIG_GNRC_SIXLOWPAN_FRAG_RBUF_SIZE) +#endif /* IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_MINFWD) */ #endif static gnrc_sixlowpan_frag_rb_int_t rbuf_int[RBUF_INT_SIZE]; @@ -91,8 +96,19 @@ enum { RBUF_ADD_ERROR = -1, RBUF_ADD_REPEAT = -2, RBUF_ADD_DUPLICATE = -3, + RBUF_ADD_FORWARDED = -4, }; +static bool _check_hdr(gnrc_pktsnip_t *hdr, unsigned page); +static void _adapt_hdr(gnrc_pktsnip_t *hdr, unsigned page); +static int _forward_frag(gnrc_pktsnip_t *pkt, size_t frag_hdr_size, + gnrc_sixlowpan_frag_vrb_t *vrbe, unsigned page); +static int _forward_uncomp(gnrc_pktsnip_t *pkt, + gnrc_sixlowpan_frag_rb_t *rbuf, + gnrc_sixlowpan_frag_vrb_t *vrbe, + unsigned page); +static int _rbuf_resize_for_reassembly(gnrc_sixlowpan_frag_rb_t *rbuf); + static int _check_fragments(gnrc_sixlowpan_frag_rb_base_t *entry, size_t frag_size, size_t offset) { @@ -224,6 +240,7 @@ static int _rbuf_add(gnrc_netif_hdr_t *netif_hdr, gnrc_pktsnip_t *pkt, union { gnrc_sixlowpan_frag_rb_base_t *super; gnrc_sixlowpan_frag_rb_t *rbuf; + gnrc_sixlowpan_frag_vrb_t *vrb; } entry; const uint8_t *src = gnrc_netif_hdr_get_src_addr(netif_hdr); const uint8_t *dst = gnrc_netif_hdr_get_dst_addr(netif_hdr); @@ -241,9 +258,44 @@ static int _rbuf_add(gnrc_netif_hdr_t *netif_hdr, gnrc_pktsnip_t *pkt, datagram_tag = sixlowpan_frag_datagram_tag(pkt->data); gnrc_sixlowpan_frag_rb_gc(); - if ((res = _rbuf_get(src, netif_hdr->src_l2addr_len, - dst, netif_hdr->dst_l2addr_len, - datagram_size, datagram_tag, page)) < 0) { + /* only check VRB for subsequent frags, first frags create and not get VRB + * entries below */ + if (IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_MINFWD) && + (offset > 0) && + sixlowpan_frag_n_is(pkt->data) && + (entry.vrb = gnrc_sixlowpan_frag_vrb_get(src, netif_hdr->src_l2addr_len, + datagram_tag)) != NULL) { + DEBUG("6lo rbuf minfwd: VRB entry found, trying to forward\n"); + switch (_check_fragments(entry.super, frag_size, offset)) { + case RBUF_ADD_REPEAT: + DEBUG("6lo rbuf minfwd: overlap found; dropping VRB\n"); + gnrc_sixlowpan_frag_vrb_rm(entry.vrb); + /* we don't repeat for VRB */ + gnrc_pktbuf_release(pkt); + return RBUF_ADD_ERROR; + case RBUF_ADD_DUPLICATE: + DEBUG("6lo rbuf minfwd: not forwarding duplicate\n"); + gnrc_pktbuf_release(pkt); + return RBUF_ADD_FORWARDED; + default: + break; + } + res = RBUF_ADD_ERROR; + if (_rbuf_update_ints(entry.super, offset, frag_size)) { + DEBUG("6lo rbuf minfwd: trying to forward fragment\n"); + entry.super->current_size += (uint16_t)frag_size; + if (_forward_frag(pkt, sizeof(sixlowpan_frag_n_t), entry.vrb, + page) < 0) { + DEBUG("6lo rbuf minfwd: unable to forward fragment\n"); + return RBUF_ADD_ERROR; + } + res = RBUF_ADD_FORWARDED; + } + return res; + } + else if ((res = _rbuf_get(src, netif_hdr->src_l2addr_len, + dst, netif_hdr->dst_l2addr_len, + datagram_size, datagram_tag, page)) < 0) { DEBUG("6lo rbuf: reassembly buffer full.\n"); gnrc_pktbuf_release(pkt); return RBUF_ADD_ERROR; @@ -303,6 +355,39 @@ static int _rbuf_add(gnrc_netif_hdr_t *netif_hdr, gnrc_pktsnip_t *pkt, if (data[0] == SIXLOWPAN_UNCOMP) { DEBUG("6lo rbuf: detected uncompressed datagram\n"); data++; + if (IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_MINFWD) && + /* only try minimal forwarding when fragment is the only + * fragment in reassembly buffer yet */ + sixlowpan_frag_1_is(pkt->data) && + (entry.super->current_size == frag_size)) { + gnrc_sixlowpan_frag_vrb_t *vrbe; + gnrc_pktsnip_t tmp = { + .data = data, + .size = frag_size, + .users = 1, + }; + + if (_check_hdr(&tmp, page) && + (vrbe = gnrc_sixlowpan_frag_vrb_from_route( + entry.super, + gnrc_netif_hdr_get_netif(netif_hdr), + &tmp))) { + _adapt_hdr(&tmp, page); + return _forward_uncomp(pkt, rbuf, vrbe, page); + } + } + } + } + if (IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_MINFWD)) { + /* all cases to try forwarding with minfwd above failed so just do + * normal reassembly. For the `minfwd` case however, we need to + * resize `entry.rbuf->pkt`, since we kept the packet allocation + * with fragment forwarding as minimal as possible in + * `_rbuf_get()` */ + res = _rbuf_resize_for_reassembly(entry.rbuf); + if (res == RBUF_ADD_ERROR) { + gnrc_pktbuf_release(pkt); + return res; } } memcpy(((uint8_t *)entry.rbuf->pkt->data) + offset, data, @@ -504,7 +589,11 @@ static int _rbuf_get(const void *src, size_t src_len, default: reass_type = GNRC_NETTYPE_UNDEF; } +#ifdef MODULE_GNRC_SIXLOWPAN_FRAG_VRB + res->pkt = gnrc_pktbuf_add(NULL, NULL, 0, reass_type); +#else /* MODULE_GNRC_SIXLOWPAN_FRAG_VRB */ res->pkt = gnrc_pktbuf_add(NULL, NULL, size, reass_type); +#endif /* MODULE_GNRC_SIXLOWPAN_FRAG_VRB */ if (res->pkt == NULL) { DEBUG("6lo rfrag: can not allocate reassembly buffer space.\n"); return -1; @@ -642,5 +731,98 @@ int gnrc_sixlowpan_frag_rb_dispatch_when_complete(gnrc_sixlowpan_frag_rb_t *rbuf return res; } +static bool _check_hdr(gnrc_pktsnip_t *hdr, unsigned page) +{ + switch (page) { +#if IS_USED(MODULE_GNRC_NETTYPE_IPV6) + case 0: { + ipv6_hdr_t *ipv6_hdr = hdr->data; + + if (ipv6_hdr->hl <= 1) { + DEBUG("6lo rbuf minfwd: minimal hop-limit reached\n"); + return false; + } + hdr->type = GNRC_NETTYPE_IPV6; + break; + } +#endif + default: + hdr->type = GNRC_NETTYPE_UNDEF; + break; + } + return true; +} + +static void _adapt_hdr(gnrc_pktsnip_t *hdr, unsigned page) +{ + switch (page) { +#if IS_USED(MODULE_GNRC_NETTYPE_IPV6) + case 0: { + ipv6_hdr_t *ipv6_hdr = hdr->data; + + ipv6_hdr->hl--; + break; + } +#endif + default: + (void)hdr; + break; + } +} + +static int _forward_frag(gnrc_pktsnip_t *pkt, size_t frag_hdr_size, + gnrc_sixlowpan_frag_vrb_t *vrbe, unsigned page) +{ + int res = -ENOTSUP; + + if (IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_MINFWD)) { + gnrc_pktsnip_t *frag = gnrc_pktbuf_mark(pkt, frag_hdr_size, + GNRC_NETTYPE_SIXLOWPAN); + if (frag == NULL) { + gnrc_pktbuf_release(pkt); + res = -ENOMEM; + } + else { + pkt = gnrc_pkt_delete(pkt, frag); + frag->next = NULL; + /* remove netif header */ + gnrc_pktbuf_remove_snip(pkt, pkt->next); + res = gnrc_sixlowpan_frag_minfwd_forward(pkt, frag->data, vrbe, + page); + gnrc_pktbuf_release(frag); + } + } + return res; +} + +static int _forward_uncomp(gnrc_pktsnip_t *pkt, + gnrc_sixlowpan_frag_rb_t *rbuf, + gnrc_sixlowpan_frag_vrb_t *vrbe, + unsigned page) +{ + DEBUG("6lo rbuf minfwd: found route, trying to forward\n"); + int res = _forward_frag(pkt, sizeof(sixlowpan_frag_t), + vrbe, page); + + /* prevent intervals from being deleted (they are in the + * VRB now) */ + rbuf->super.ints = NULL; + gnrc_pktbuf_release(rbuf->pkt); + gnrc_sixlowpan_frag_rb_remove(rbuf); + return (res == 0) ? RBUF_ADD_SUCCESS : RBUF_ADD_ERROR; +} + +static int _rbuf_resize_for_reassembly(gnrc_sixlowpan_frag_rb_t *rbuf) +{ + DEBUG("6lo rbuf: just do normal reassembly\n"); + if (gnrc_pktbuf_realloc_data(rbuf->pkt, + rbuf->super.datagram_size) != 0) { + DEBUG("6lo rbuf minfwd: can't allocate packet data\n"); + gnrc_pktbuf_release(rbuf->pkt); + gnrc_sixlowpan_frag_rb_remove(rbuf); + return RBUF_ADD_ERROR; + } + return RBUF_ADD_SUCCESS; +} /** @} */ From 383790af6370a2987cfd017e06cdf02e49842cf3 Mon Sep 17 00:00:00 2001 From: Martine Lenders Date: Tue, 26 Feb 2019 13:37:55 +0100 Subject: [PATCH 06/10] tests: add gnrc_sixlowpan_frag_minfwd tests for rbuf integration --- tests/gnrc_sixlowpan_frag_minfwd/main.c | 366 ++++++++++++++++++++++++ 1 file changed, 366 insertions(+) diff --git a/tests/gnrc_sixlowpan_frag_minfwd/main.c b/tests/gnrc_sixlowpan_frag_minfwd/main.c index 8ef95ff10dee..e72243b680bf 100644 --- a/tests/gnrc_sixlowpan_frag_minfwd/main.c +++ b/tests/gnrc_sixlowpan_frag_minfwd/main.c @@ -110,6 +110,7 @@ static const uint8_t _test_nth_frag[] = { 0x0e, 0x0d, 0xd6, 0x6a, 0x83, 0x31, 0x1d, 0x94, }; static const ipv6_addr_t _rem_ll = { .u8 = REM_LL }; +static const ipv6_addr_t _rem_gb = { .u8 = REM_GB }; static const uint8_t _rem_l2[] = REM_L2; static const gnrc_sixlowpan_frag_rb_base_t _vrbe_base = { .src = { 0xde, 0x71, 0x2b, 0x85, 0x08, 0x2f, 0x75, 0xfb }, @@ -128,11 +129,14 @@ static mutex_t _target_buf_filled = MUTEX_INIT_LOCKED; static mutex_t _target_buf_barrier = MUTEX_INIT; static gnrc_pktsnip_t *_create_ipv6_hdr(const ipv6_hdr_t *hdr); +static gnrc_pktsnip_t *_create_recv_frag(const void *frag_data, + size_t frag_size); static int _set_route_and_nce(const ipv6_addr_t *route, unsigned pfx_len); static size_t _wait_for_packet(size_t exp_size); static void _check_vrbe_values(gnrc_sixlowpan_frag_vrb_t *vrbe, size_t mhr_len, int frag_type); static void _check_1st_frag_uncomp(size_t mhr_len, uint8_t exp_hl_diff); +static const gnrc_sixlowpan_frag_rb_t *_first_non_empty_rbuf(void); static int _mock_netdev_send(netdev_t *dev, const iolist_t *iolist); static void _set_up(void) @@ -417,6 +421,319 @@ static void test_minfwd_forward__ENOMEM__netif_hdr_build_fail(void) TEST_ASSERT(gnrc_pktbuf_is_empty()); } +static void test_sixlo_recv__1st_frag_uncomp(void) +{ + gnrc_sixlowpan_frag_vrb_t *vrbe; + gnrc_pktsnip_t *frag; + size_t mhr_len; + + TEST_ASSERT_NOT_NULL( + (frag = _create_recv_frag(_test_1st_frag_uncomp, + sizeof(_test_1st_frag_uncomp))) + ); + /* configure route to destination of IP header in frag */ + TEST_ASSERT_EQUAL_INT(0, _set_route_and_nce(&_rem_gb, REM_GB_PFX_LEN)); + netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev, + _mock_netdev_send); + TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN, + GNRC_NETREG_DEMUX_CTX_ALL, + frag)); + TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_1st_frag_uncomp)))); + TEST_ASSERT(gnrc_pktbuf_is_sane()); + TEST_ASSERT(gnrc_pktbuf_is_empty()); + /* reassembly buffer remains empty */ + TEST_ASSERT_NULL(_first_non_empty_rbuf()); + /* but there was a VRB entry created */ + TEST_ASSERT_NOT_NULL((vrbe = gnrc_sixlowpan_frag_vrb_get( + _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag + ))); + _check_vrbe_values(vrbe, mhr_len, FIRST_FRAGMENT); + TEST_ASSERT_EQUAL_INT(TEST_1ST_FRAG_UNCOMP_SIZE, + vrbe->super.current_size); + /* only the received fragment is registered */ + TEST_ASSERT_NOT_NULL(vrbe->super.ints); + TEST_ASSERT_NULL(vrbe->super.ints->next); + TEST_ASSERT(_target_buf[0] & IEEE802154_FCF_FRAME_PEND); + _check_1st_frag_uncomp(mhr_len, 1U); +} + +static void test_sixlo_recv__1st_frag_uncomp__no_route(void) +{ + const gnrc_sixlowpan_frag_rb_t *rbuf; + gnrc_pktsnip_t *frag; + + TEST_ASSERT_NOT_NULL( + (frag = _create_recv_frag(_test_1st_frag_uncomp, + sizeof(_test_1st_frag_uncomp))) + ); + TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN, + GNRC_NETREG_DEMUX_CTX_ALL, + frag)); + /* should time out */ + TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_1st_frag_uncomp))); + /* normal reassembly should have started */ + /* reassembly buffer entry should have been created */ + TEST_ASSERT_NOT_NULL((rbuf = _first_non_empty_rbuf())); + /* and VRB remains empty */ + TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get( + _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag + )); + TEST_ASSERT_EQUAL_INT(_vrbe_base.datagram_size, rbuf->pkt->size); + gnrc_pktbuf_release(rbuf->pkt); + TEST_ASSERT(gnrc_pktbuf_is_sane()); + TEST_ASSERT(gnrc_pktbuf_is_empty()); + /* This is normal reassembly so the rest should have be tested in the + * test for normal reassembly ;-) */ +} + +static void test_sixlo_recv__1st_frag_uncomp__after_nth_frag(void) +{ + const gnrc_sixlowpan_frag_rb_t *rbuf; + gnrc_pktsnip_t *frag; + + TEST_ASSERT_NOT_NULL( + (frag = _create_recv_frag(_test_nth_frag, sizeof(_test_nth_frag))) + ); + /* configure route to destination of IP header in frag */ + TEST_ASSERT_EQUAL_INT(0, _set_route_and_nce(&_rem_gb, REM_GB_PFX_LEN)); + netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev, + _mock_netdev_send); + TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN, + GNRC_NETREG_DEMUX_CTX_ALL, + frag)); + + /* should time out */ + TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_nth_frag))); + /* reassembly buffer entry should have been created */ + TEST_ASSERT_NOT_NULL(_first_non_empty_rbuf()); + /* and VRB remains empty */ + TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get( + _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag + )); + TEST_ASSERT_NOT_NULL( + (frag = _create_recv_frag(_test_1st_frag_uncomp, + sizeof(_test_1st_frag_uncomp))) + ); + TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN, + GNRC_NETREG_DEMUX_CTX_ALL, + frag)); + /* should time out */ + TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_1st_frag_uncomp))); + /* reassembly buffer entry should still exist */ + TEST_ASSERT_NOT_NULL((rbuf = _first_non_empty_rbuf())); + /* and VRB still remains empty */ + TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get( + _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag + )); + TEST_ASSERT_EQUAL_INT(_vrbe_base.datagram_size, rbuf->pkt->size); + gnrc_pktbuf_release(rbuf->pkt); + TEST_ASSERT(gnrc_pktbuf_is_sane()); + TEST_ASSERT(gnrc_pktbuf_is_empty()); + /* This is normal reassembly so the rest should have be tested in the + * test for normal reassembly ;-) */ +} + +static void test_sixlo_recv__nth_frag(void) +{ + gnrc_sixlowpan_frag_vrb_t *vrbe; + gnrc_pktsnip_t *frag; + size_t mhr_len; + + TEST_ASSERT_NOT_NULL( + (frag = _create_recv_frag(_test_nth_frag, sizeof(_test_nth_frag))) + ); + TEST_ASSERT_NOT_NULL( + (vrbe = gnrc_sixlowpan_frag_vrb_add(&_vrbe_base, _mock_netif, + _rem_l2, sizeof(_rem_l2))) + ); + netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev, + _mock_netdev_send); + TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN, + GNRC_NETREG_DEMUX_CTX_ALL, + frag)); + TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_nth_frag)))); + /* reassembly buffer remains empty */ + TEST_ASSERT_NULL(_first_non_empty_rbuf()); + TEST_ASSERT(gnrc_pktbuf_is_sane()); + TEST_ASSERT(gnrc_pktbuf_is_empty()); + _check_vrbe_values(vrbe, mhr_len, NTH_FRAGMENT); + TEST_ASSERT(_target_buf[0] & IEEE802154_FCF_FRAME_PEND); + TEST_ASSERT_MESSAGE( + memcmp(&_test_nth_frag[TEST_NTH_FRAG_PAYLOAD_POS], + &_target_buf[mhr_len + sizeof(sixlowpan_frag_n_t)], + TEST_NTH_FRAG_SIZE) == 0, + "unexpected forwarded packet payload" + ); + /* VRB entry should not have been removed */ + TEST_ASSERT_NOT_NULL(gnrc_sixlowpan_frag_vrb_get(_vrbe_base.src, + _vrbe_base.src_len, + _vrbe_base.tag)); +} + +static void test_sixlo_recv__nth_frag__datagram_complete(void) +{ + gnrc_sixlowpan_frag_vrb_t *vrbe; + gnrc_pktsnip_t *frag; + size_t mhr_len; + + TEST_ASSERT_NOT_NULL( + (frag = _create_recv_frag(_test_nth_frag, sizeof(_test_nth_frag))) + ); + TEST_ASSERT_NOT_NULL( + (vrbe = gnrc_sixlowpan_frag_vrb_add(&_vrbe_base, _mock_netif, + _rem_l2, sizeof(_rem_l2))) + ); + /* simulate current_size only missing the created fragment */ + vrbe->super.current_size = _vrbe_base.datagram_size - TEST_NTH_FRAG_SIZE; + netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev, + _mock_netdev_send); + TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN, + GNRC_NETREG_DEMUX_CTX_ALL, + frag)); + TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_nth_frag)))); + /* reassembly buffer remains empty */ + TEST_ASSERT_NULL(_first_non_empty_rbuf()); + /* VRB entry should have been removed */ + TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get( + _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag + )); + TEST_ASSERT(gnrc_pktbuf_is_sane()); + TEST_ASSERT(gnrc_pktbuf_is_empty()); + /* VRB entry should have been removed, so don't check */ + TEST_ASSERT(!(_target_buf[0] & IEEE802154_FCF_FRAME_PEND)); + TEST_ASSERT_MESSAGE( + memcmp(&_test_nth_frag[TEST_NTH_FRAG_PAYLOAD_POS], + &_target_buf[mhr_len + sizeof(sixlowpan_frag_n_t)], + TEST_NTH_FRAG_SIZE) == 0, + "unexpected forwarded packet payload" + ); +} + +static void test_sixlo_recv__nth_frag__no_vrbe(void) +{ + const gnrc_sixlowpan_frag_rb_t *rbuf; + gnrc_pktsnip_t *frag; + + TEST_ASSERT_NOT_NULL( + (frag = _create_recv_frag(_test_nth_frag, sizeof(_test_nth_frag))) + ); + netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev, + _mock_netdev_send); + TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN, + GNRC_NETREG_DEMUX_CTX_ALL, + frag)); + /* should time out */ + TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_nth_frag))); + /* normal reassembly should have started */ + /* reassembly buffer entry should have been created */ + TEST_ASSERT_NOT_NULL((rbuf = _first_non_empty_rbuf())); + /* and VRB remains empty */ + TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get( + _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag + )); + TEST_ASSERT_EQUAL_INT(_vrbe_base.datagram_size, rbuf->pkt->size); + gnrc_pktbuf_release(rbuf->pkt); + TEST_ASSERT(gnrc_pktbuf_is_sane()); + TEST_ASSERT(gnrc_pktbuf_is_empty()); + /* This is normal reassembly so the rest should have be tested in the + * test for normal reassembly ;-) */ +} + +static void test_sixlo_recv__nth_frag__duplicate(void) +{ + gnrc_sixlowpan_frag_vrb_t *vrbe; + gnrc_pktsnip_t *frag; + uint16_t exp_current_size; + + TEST_ASSERT_NOT_NULL( + (frag = _create_recv_frag(_test_nth_frag, sizeof(_test_nth_frag))) + ); + TEST_ASSERT_NOT_NULL( + (vrbe = gnrc_sixlowpan_frag_vrb_add(&_vrbe_base, _mock_netif, + _rem_l2, sizeof(_rem_l2))) + ); + netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev, + _mock_netdev_send); + TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN, + GNRC_NETREG_DEMUX_CTX_ALL, + frag)); + TEST_ASSERT(_wait_for_packet(sizeof(_test_nth_frag))); + /* reassembly buffer remains empty */ + TEST_ASSERT_NULL(_first_non_empty_rbuf()); + /* VRB entry should not have been removed */ + TEST_ASSERT_NOT_NULL(gnrc_sixlowpan_frag_vrb_get(_vrbe_base.src, + _vrbe_base.src_len, + _vrbe_base.tag)); + /* rest was already tested */ + exp_current_size = vrbe->super.current_size; + + /* generate and receive duplicate */ + TEST_ASSERT_NOT_NULL( + (frag = _create_recv_frag(_test_nth_frag, sizeof(_test_nth_frag))) + ); + TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN, + GNRC_NETREG_DEMUX_CTX_ALL, + frag)); + _target_buf_len = 0; + /* should time out */ + TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_nth_frag))); + TEST_ASSERT(gnrc_pktbuf_is_sane()); + TEST_ASSERT(gnrc_pktbuf_is_empty()); + /* reassembly buffer remains empty */ + TEST_ASSERT_NULL(_first_non_empty_rbuf()); + /* VRB entry should not have been removed */ + TEST_ASSERT_NOT_NULL(gnrc_sixlowpan_frag_vrb_get(_vrbe_base.src, + _vrbe_base.src_len, + _vrbe_base.tag)); + TEST_ASSERT_EQUAL_INT(exp_current_size, vrbe->super.current_size); +} + +static void test_sixlo_recv__nth_frag__overlap(void) +{ + gnrc_sixlowpan_frag_vrb_t *vrbe; + gnrc_pktsnip_t *frag; + sixlowpan_frag_n_t *frag_hdr; + + TEST_ASSERT_NOT_NULL( + (frag = _create_recv_frag(_test_nth_frag, sizeof(_test_nth_frag))) + ); + TEST_ASSERT_NOT_NULL( + (vrbe = gnrc_sixlowpan_frag_vrb_add(&_vrbe_base, _mock_netif, + _rem_l2, sizeof(_rem_l2))) + ); + netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev, + _mock_netdev_send); + TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN, + GNRC_NETREG_DEMUX_CTX_ALL, + frag)); + TEST_ASSERT(_wait_for_packet(sizeof(_test_nth_frag))); + /* reassembly buffer remains empty */ + TEST_ASSERT_NULL(_first_non_empty_rbuf()); + /* rest was already tested */ + + /* generate and receive overlapping fragment */ + TEST_ASSERT_NOT_NULL( + (frag = _create_recv_frag(_test_nth_frag, sizeof(_test_nth_frag))) + ); + frag_hdr = frag->data; + /* move offset to simulate overlap*/ + frag_hdr->offset--; + TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN, + GNRC_NETREG_DEMUX_CTX_ALL, + frag)); + _target_buf_len = 0; + /* should time out */ + TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_nth_frag))); + TEST_ASSERT(gnrc_pktbuf_is_sane()); + TEST_ASSERT(gnrc_pktbuf_is_empty()); + /* reassembly buffer remains empty */ + TEST_ASSERT_NULL(_first_non_empty_rbuf()); + /* VRB entry was removed due to overlap error */ + TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get( + _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag + )); +} + static Test *tests_gnrc_sixlowpan_frag_minfwd_api(void) { EMB_UNIT_TESTFIXTURES(fixtures) { @@ -437,12 +754,31 @@ static Test *tests_gnrc_sixlowpan_frag_minfwd_api(void) return (Test *)&tests; } +static Test *tests_gnrc_sixlowpan_frag_minfwd_integration(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_sixlo_recv__1st_frag_uncomp), + new_TestFixture(test_sixlo_recv__1st_frag_uncomp__no_route), + new_TestFixture(test_sixlo_recv__1st_frag_uncomp__after_nth_frag), + new_TestFixture(test_sixlo_recv__nth_frag), + new_TestFixture(test_sixlo_recv__nth_frag__datagram_complete), + new_TestFixture(test_sixlo_recv__nth_frag__no_vrbe), + new_TestFixture(test_sixlo_recv__nth_frag__duplicate), + new_TestFixture(test_sixlo_recv__nth_frag__overlap), + }; + + EMB_UNIT_TESTCALLER(tests, _set_up, _tear_down, fixtures); + + return (Test *)&tests; +} + int main(void) { _tests_init(); TESTS_START(); TESTS_RUN(tests_gnrc_sixlowpan_frag_minfwd_api()); + TESTS_RUN(tests_gnrc_sixlowpan_frag_minfwd_integration()); TESTS_END(); return 0; } @@ -452,6 +788,24 @@ static gnrc_pktsnip_t *_create_ipv6_hdr(const ipv6_hdr_t *hdr) return gnrc_pktbuf_add(NULL, hdr, sizeof(*hdr), GNRC_NETTYPE_IPV6); } +static gnrc_pktsnip_t *_create_recv_frag(const void *frag_data, + size_t frag_size) +{ + gnrc_pktsnip_t *netif; + gnrc_netif_hdr_t *netif_hdr; + + netif = gnrc_netif_hdr_build(_vrbe_base.src, _vrbe_base.src_len, + _vrbe_base.dst, _vrbe_base.dst_len); + if (netif == NULL) { + return NULL; + } + netif_hdr = netif->data; + netif_hdr->if_pid = _mock_netif->pid; + netif_hdr->flags = GNRC_NETIF_HDR_FLAGS_MORE_DATA; + return gnrc_pktbuf_add(netif, frag_data, frag_size, + GNRC_NETTYPE_SIXLOWPAN); +} + static int _set_route_and_nce(const ipv6_addr_t *route, unsigned pfx_len) { /* add neighbor cache entry */ @@ -569,6 +923,18 @@ static void _check_1st_frag_uncomp(size_t mhr_len, uint8_t exp_hl_diff) ); } +static const gnrc_sixlowpan_frag_rb_t *_first_non_empty_rbuf(void) +{ + const gnrc_sixlowpan_frag_rb_t *rbuf = gnrc_sixlowpan_frag_rb_array(); + + for (unsigned i = 0; i < GNRC_SIXLOWPAN_FRAG_RBUF_SIZE; i++) { + if (!gnrc_sixlowpan_frag_rb_entry_empty(&rbuf[i])) { + return rbuf; + } + } + return NULL; +} + static int _mock_netdev_send(netdev_t *dev, const iolist_t *iolist) { (void)dev; From d9be79b266d7e80c7e4bdb68245a45c1784e4739 Mon Sep 17 00:00:00 2001 From: Martine Lenders Date: Tue, 26 Feb 2019 14:09:36 +0100 Subject: [PATCH 07/10] gnrc_sixlowpan_iphc/minfwd: put only IPHC dispatch in first fragment This is only the case when minfwd is compiled in. --- sys/include/net/gnrc/sixlowpan/frag/minfwd.h | 34 ++++++++++++++++++ sys/include/net/gnrc/sixlowpan/iphc.h | 6 ++++ .../frag/minfwd/gnrc_sixlowpan_frag_minfwd.c | 33 +++++++++++++++++ .../network_layer/sixlowpan/gnrc_sixlowpan.c | 36 ++++++++++++++++--- .../sixlowpan/iphc/gnrc_sixlowpan_iphc.c | 19 +++++++++- 5 files changed, 123 insertions(+), 5 deletions(-) diff --git a/sys/include/net/gnrc/sixlowpan/frag/minfwd.h b/sys/include/net/gnrc/sixlowpan/frag/minfwd.h index af8705899b7d..df9a93ecf04e 100644 --- a/sys/include/net/gnrc/sixlowpan/frag/minfwd.h +++ b/sys/include/net/gnrc/sixlowpan/frag/minfwd.h @@ -28,6 +28,7 @@ #include "net/gnrc/pkt.h" #include "net/gnrc/netif.h" #include "net/gnrc/sixlowpan/frag.h" +#include "net/gnrc/sixlowpan/frag/fb.h" #include "net/gnrc/sixlowpan/frag/vrb.h" #ifdef __cplusplus @@ -58,6 +59,39 @@ int gnrc_sixlowpan_frag_minfwd_forward(gnrc_pktsnip_t *pkt, gnrc_sixlowpan_frag_vrb_t *vrbe, unsigned page); +/** + * @brief Fragments a packet with just the IPHC (and padding payload to get + * to 8 byte) as the first fragment + * + * @pre `(frag_msg != NULL)` + * @pre `(pkt != NULL) && (pkt->type == GNRC_NETTYPE_NETIF)` + * @pre `(pkt->next != NULL) && (pkt->next->type == GNRC_NETTYPE_SIXLOWPAN)` + * + * @param[in] pkt The compressed packet to be sent. Must be in + * send order with a packet snip of type + * @ref GNRC_NETTYPE_NETIF first, + * @ref GNRC_NETTYPE_SIXLOWPAN (the IPHC + * header including NHC) second, and 0 or more + * snips of payload. + * @param[in] orig_datagram_size The size of the @p pkt before compression + * (without @ref GNRC_NETTYPE_NETIF snip). + * This can differ from @p frag_msg's + * gnrc_sixlowpan_msg_frag_t::datagram_size + * as it might just be a fragment in forwarding + * that is re-compressed in @p pkt. + * @param[in] ipv6_addr The (uncompressed) destination address of + * @p pkt. + * @param[in] fbuf A fragmentation buffer entry. + * + * @return 0, when fragmentation was successful + * @return -1, on error. @p pkt is **not** released in that case and *should* + * be handled by normal fragmentation. + */ +int gnrc_sixlowpan_frag_minfwd_frag_iphc(gnrc_pktsnip_t *pkt, + size_t orig_datagram_size, + const ipv6_addr_t *ipv6_addr, + gnrc_sixlowpan_frag_fb_t *fbuf); + #ifdef __cplusplus } #endif diff --git a/sys/include/net/gnrc/sixlowpan/iphc.h b/sys/include/net/gnrc/sixlowpan/iphc.h index ee9a5508dfb3..deb5bd7a6ab5 100644 --- a/sys/include/net/gnrc/sixlowpan/iphc.h +++ b/sys/include/net/gnrc/sixlowpan/iphc.h @@ -52,6 +52,12 @@ void gnrc_sixlowpan_iphc_recv(gnrc_pktsnip_t *pkt, void *ctx, unsigned page); * @param[in] pkt A 6LoWPAN frame with an uncompressed IPv6 header to send. * Will be translated to an 6LoWPAN IPHC frame. * @param[in] ctx Context for the packet. May be NULL. + * If not NULL it is expected to be of type @ref + * gnrc_sixlowpan_frag_fb_t to provide initial information for + * possible fragmentation after compression (see + * net_gnrc_sixlowpan_frag_hint). This function might change + * the content of that. Depending on the compile configuration + * it might be ignored completely. * @param[in] page Current 6Lo dispatch parsing page. * */ diff --git a/sys/net/gnrc/network_layer/sixlowpan/frag/minfwd/gnrc_sixlowpan_frag_minfwd.c b/sys/net/gnrc/network_layer/sixlowpan/frag/minfwd/gnrc_sixlowpan_frag_minfwd.c index 86a0983972a1..0c51abfa452c 100644 --- a/sys/net/gnrc/network_layer/sixlowpan/frag/minfwd/gnrc_sixlowpan_frag_minfwd.c +++ b/sys/net/gnrc/network_layer/sixlowpan/frag/minfwd/gnrc_sixlowpan_frag_minfwd.c @@ -86,4 +86,37 @@ int gnrc_sixlowpan_frag_minfwd_forward(gnrc_pktsnip_t *pkt, return 0; } +int gnrc_sixlowpan_frag_minfwd_frag_iphc(gnrc_pktsnip_t *pkt, + size_t orig_datagram_size, + const ipv6_addr_t *ipv6_dst, + gnrc_sixlowpan_frag_fb_t *fbuf) +{ + gnrc_netif_t *netif; + int res = -1; + + assert(fbuf != NULL); + assert((pkt != NULL) && (pkt->type == GNRC_NETTYPE_NETIF)); + assert((pkt->next != NULL) && (pkt->next->type == GNRC_NETTYPE_SIXLOWPAN)); + netif = gnrc_netif_hdr_get_netif(pkt->data); + + if (!ipv6_addr_is_link_local(ipv6_dst) && + (fbuf->datagram_size > netif->sixlo.max_frag_size)) { + fbuf->pkt = pkt; /* packet might have been rewritten */ + /* put slack of IPHC in first fragment */ + fbuf->hint.fragsz = pkt->next->size; + fbuf->hint.fragsz_uncomp = orig_datagram_size - + gnrc_pkt_len(pkt->next->next); + gnrc_sixlowpan_frag_send(NULL, fbuf, 0); + res = 0; + } + else { + /* we don't forward link-local so free fbuf again */ + DEBUG("6lo minfwd: link-local address is not forwarded or " + "no fragmentation necessary (%u < %u)\n", + fbuf->datagram_size, netif->sixlo.max_frag_size); + fbuf->pkt = NULL; + } + return res; +} + /** @} */ diff --git a/sys/net/gnrc/network_layer/sixlowpan/gnrc_sixlowpan.c b/sys/net/gnrc/network_layer/sixlowpan/gnrc_sixlowpan.c index e4649a5de714..3a63eab09a90 100644 --- a/sys/net/gnrc/network_layer/sixlowpan/gnrc_sixlowpan.c +++ b/sys/net/gnrc/network_layer/sixlowpan/gnrc_sixlowpan.c @@ -293,12 +293,40 @@ static void _send(gnrc_pktsnip_t *pkt) return; } -#ifdef MODULE_GNRC_SIXLOWPAN_IPHC - if (netif->flags & GNRC_NETIF_FLAGS_6LO_HC) { - gnrc_sixlowpan_iphc_send(pkt, NULL, 0); + 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; } -#endif if (!_add_uncompr_disp(pkt)) { /* adding uncompressed dispatch failed */ DEBUG("6lo: no space left in packet buffer\n"); 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 bfe6fe7fc412..91ba02bb06cf 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 @@ -26,6 +26,7 @@ #include "net/gnrc/sixlowpan.h" #include "net/gnrc/sixlowpan/ctx.h" #include "net/gnrc/sixlowpan/frag/rb.h" +#include "net/gnrc/sixlowpan/frag/minfwd.h" #ifdef MODULE_GNRC_SIXLOWPAN_FRAG_VRB #include "net/gnrc/sixlowpan/frag/vrb.h" #endif /* MODULE_GNRC_SIXLOWPAN_FRAG_VRB */ @@ -915,6 +916,11 @@ static int _forward_frag(gnrc_pktsnip_t *pkt, gnrc_pktsnip_t *frag_hdr, /* remove rewritten netif header (forwarding implementation must do this * anyway) */ pkt = gnrc_pktbuf_remove_snip(pkt, pkt); + if (IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_MINFWD) && + sixlowpan_frag_is(frag_hdr->data)) { + return gnrc_sixlowpan_frag_minfwd_forward(pkt, frag_hdr->data, vrbe, + page); + } /* 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) ", @@ -1609,9 +1615,20 @@ void gnrc_sixlowpan_iphc_send(gnrc_pktsnip_t *pkt, void *ctx, unsigned page) gnrc_pktsnip_t *tmp; /* datagram size before compression */ size_t orig_datagram_size = gnrc_pkt_len(pkt->next); + ipv6_hdr_t *ipv6_hdr = pkt->next->data; + ipv6_addr_t dst; + + if (IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_MINFWD)) { + dst = ipv6_hdr->dst; /* copying original destination address */ + } - (void)ctx; if ((tmp = _iphc_encode(pkt, pkt->data, netif))) { + if (IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_MINFWD) && (ctx != NULL) && + (gnrc_sixlowpan_frag_minfwd_frag_iphc(tmp, orig_datagram_size, &dst, + ctx) == 0)) { + DEBUG("6lo iphc minfwd: putting slack in first fragment\n"); + return; + } gnrc_sixlowpan_multiplex_by_size(tmp, orig_datagram_size, netif, page); } else { From d8c5a222388f48356cf610a4640e79becc373f71 Mon Sep 17 00:00:00 2001 From: Martine Lenders Date: Tue, 26 Feb 2019 14:13:45 +0100 Subject: [PATCH 08/10] tests: add gnrc_sixlowpan_frag_minfwd tests for IPHC sending --- tests/gnrc_sixlowpan_frag_minfwd/main.c | 340 ++++++++++++++++++ .../gnrc_sixlowpan_frag_minfwd/mockup_netif.c | 10 + 2 files changed, 350 insertions(+) diff --git a/tests/gnrc_sixlowpan_frag_minfwd/main.c b/tests/gnrc_sixlowpan_frag_minfwd/main.c index e72243b680bf..dff59f796b7a 100644 --- a/tests/gnrc_sixlowpan_frag_minfwd/main.c +++ b/tests/gnrc_sixlowpan_frag_minfwd/main.c @@ -70,6 +70,20 @@ #define TEST_NTH_FRAG_SIZE (32U) #define TEST_NTH_FRAG_OFFSET_POS (4U) #define TEST_NTH_FRAG_PAYLOAD_POS (5U) +#define TEST_SEND_DATAGRAM_SIZE (148U) +#define TEST_SEND_DATAGRAM_TAG (0x22ddU) +#define TEST_SEND_LL_PAYLOAD_POS (4U) +#define TEST_SEND_LL_PAYLOAD_SIZE (7U) +#define TEST_SEND_FRAG1_PAYLOAD_POS (4U) +#define TEST_SEND_FRAG1_PAYLOAD_SIZE (35U) +#define TEST_SEND_FRAG2_OFFSET_POS (4U) +#define TEST_SEND_FRAG2_OFFSET (5U) +#define TEST_SEND_FRAG2_PAYLOAD_POS (5U) +#define TEST_SEND_FRAG2_PAYLOAD_SIZE (96U) +#define TEST_SEND_FRAG3_OFFSET_POS (4U) +#define TEST_SEND_FRAG3_OFFSET (17U) +#define TEST_SEND_FRAG3_PAYLOAD_POS (5U) +#define TEST_SEND_FRAG3_PAYLOAD_SIZE (12U) enum { FIRST_FRAGMENT = 0, @@ -109,6 +123,86 @@ static const uint8_t _test_nth_frag[] = { 0x56, 0x44, 0x4a, 0x84, 0xd1, 0x83, 0xb9, 0xdb, 0x0e, 0x0d, 0xd6, 0x6a, 0x83, 0x31, 0x1d, 0x94, }; +static const uint8_t _test_send_ipv6[] = { + /* IPv6 header: payload length = 108, + * next header = ICMPv6 (58), hop limit = 64 */ + 0x60, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x3a, 0x40, + /* Source: LOC_GB */ + 0x20, 0x01, 0x0d, 0xb8, 0xd3, 0x35, 0x91, 0x7e, + _LL0 ^ 0x2, _LL1, _LL2, _LL3, _LL4, _LL5, _LL6, _LL7, + /* Destination: REM_GB */ + 0x20, 0x01, 0x0d, 0xb8, 0xd3, 0x35, 0x91, 0x7e, + _LL0 ^ 0x2, _LL1, _LL2, _LL3, _LL4, _LL5, _LL6, _LL7 + 1, + }; +static const uint8_t _test_send_icmpv6[] = { + /* ICMPv6 Echo request (128), Code 0, (Random) checksum: 0x7269, + * random identifier: 0x59be, random sequence number: 15804 */ + 0x80, 0x00, 0x72, 0x69, 0x59, 0xbe, 0x3d, 0xbc, + /* random payload */ + 0x49, 0x19, 0xe8, 0x0b, 0x25, 0xbb, 0x00, 0x13, + 0x45, 0x85, 0xbd, 0x4a, 0xbb, 0xf1, 0x3d, 0xe3, + 0x36, 0xff, 0x52, 0xea, 0xe8, 0xec, 0xec, 0x82, + 0x94, 0x5f, 0xa4, 0x30, 0x1f, 0x46, 0x28, 0xc7, + 0x41, 0xff, 0x50, 0x84, 0x00, 0x41, 0xc7, 0x8d, + 0xb0, 0xdc, 0x18, 0xff, 0xcd, 0xfa, 0xa7, 0x72, + 0x4b, 0xcf, 0x7c, 0xf7, 0x7c, 0x8b, 0x65, 0x78, + 0xb0, 0xa8, 0xe7, 0x8f, 0xbc, 0x1e, 0xba, 0x4a, + 0x92, 0x13, 0x81, 0x5e, 0x23, 0xd1, 0xde, 0x09, + 0x84, 0x8a, 0xd0, 0xe2, 0xdd, 0x01, 0xc8, 0xd7, + 0x08, 0x4c, 0xd8, 0xc2, 0x21, 0x5c, 0x21, 0xb9, + 0x43, 0xea, 0x52, 0xbd, 0x6a, 0x9a, 0xac, 0x48, + 0x94, 0x98, 0xd1, 0x95 + }; +static const uint8_t _test_send_ll[] = { + 0xc0, 0x94, /* 1st fragment | datagram size: TEST_SEND_DATAGRAM_SIZE */ + 0x22, 0xdd, /* tag: TEST_SEND_DATAGRAM_TAG */ + /* IPHC: TF: 0b11, NH: 0b0 (inline), HLIM: 0b10 (64), CID: 0b0, + * Source: uncompressed (SAC: 0b0, SAM: 0b11), + * Destination: uncompressed (M:0, DAC: 0b0, DAM: 0b11) */ + 0x7a, 0x33, + /* Next header: ICMPv6 (58) */ + 0x3a, + }; +static const uint8_t _test_send_frag1[] = { + 0xc0, 0x94, /* 1st fragment | datagram size: TEST_SEND_DATAGRAM_SIZE */ + 0x22, 0xdd, /* tag: TEST_SEND_DATAGRAM_TAG */ + /* IPHC: TF: 0b11, NH: 0b0 (inline), HLIM: 0b10 (64), CID: 0b0, + * Source: uncompressed (SAC: 0b0, SAM: 0b00), + * Destination: uncompressed (M:0, DAC: 0b0, DAM: 0b00) */ + 0x7a, 0x00, + /* Next header: ICMPv6 (58) */ + 0x3a, + /* (uncompressed) Source: LOC_GB */ + 0x20, 0x01, 0x0d, 0xb8, 0xd3, 0x35, 0x91, 0x7e, + _LL0 ^ 0x2, _LL1, _LL2, _LL3, _LL4, _LL5, _LL6, _LL7, + /* (uncompressed) Destination: REM_GB */ + 0x20, 0x01, 0x0d, 0xb8, 0xd3, 0x35, 0x91, 0x7e, + _LL0 ^ 0x2, _LL1, _LL2, _LL3, _LL4, _LL5, _LL6, _LL7 + 1, + }; +static const uint8_t _test_send_frag2[] = { + 0xe0, 0x94, /* nth fragment | datagram size: TEST_SEND_DATAGRAM_SIZE */ + 0x22, 0xdd, /* tag: TEST_SEND_DATAGRAM_TAG */ + 0x05, /* offset: 40 (divided by 8) */ + 0x80, 0x00, 0x72, 0x69, 0x59, 0xbe, 0x3d, 0xbc, + 0x49, 0x19, 0xe8, 0x0b, 0x25, 0xbb, 0x00, 0x13, + 0x45, 0x85, 0xbd, 0x4a, 0xbb, 0xf1, 0x3d, 0xe3, + 0x36, 0xff, 0x52, 0xea, 0xe8, 0xec, 0xec, 0x82, + 0x94, 0x5f, 0xa4, 0x30, 0x1f, 0x46, 0x28, 0xc7, + 0x41, 0xff, 0x50, 0x84, 0x00, 0x41, 0xc7, 0x8d, + 0xb0, 0xdc, 0x18, 0xff, 0xcd, 0xfa, 0xa7, 0x72, + 0x4b, 0xcf, 0x7c, 0xf7, 0x7c, 0x8b, 0x65, 0x78, + 0xb0, 0xa8, 0xe7, 0x8f, 0xbc, 0x1e, 0xba, 0x4a, + 0x92, 0x13, 0x81, 0x5e, 0x23, 0xd1, 0xde, 0x09, + 0x84, 0x8a, 0xd0, 0xe2, 0xdd, 0x01, 0xc8, 0xd7, + 0x08, 0x4c, 0xd8, 0xc2, 0x21, 0x5c, 0x21, 0xb9, + }; +static const uint8_t _test_send_frag3[] = { + 0xe0, 0x94, /* nth fragment | datagram size: TEST_ASSERT_EQUAL_INT */ + 0x22, 0xdd, /* tag: TEST_SEND_DATAGRAM_TAG */ + 0x11, /* offset: 136 (divided by 8) */ + 0x43, 0xea, 0x52, 0xbd, 0x6a, 0x9a, 0xac, 0x48, + 0x94, 0x98, 0xd1, 0x95 + }; static const ipv6_addr_t _rem_ll = { .u8 = REM_LL }; static const ipv6_addr_t _rem_gb = { .u8 = REM_GB }; static const uint8_t _rem_l2[] = REM_L2; @@ -132,10 +226,14 @@ static gnrc_pktsnip_t *_create_ipv6_hdr(const ipv6_hdr_t *hdr); static gnrc_pktsnip_t *_create_recv_frag(const void *frag_data, size_t frag_size); static int _set_route_and_nce(const ipv6_addr_t *route, unsigned pfx_len); +static gnrc_pktsnip_t *_create_send_datagram(bool compressed, bool payload); static size_t _wait_for_packet(size_t exp_size); static void _check_vrbe_values(gnrc_sixlowpan_frag_vrb_t *vrbe, size_t mhr_len, int frag_type); static void _check_1st_frag_uncomp(size_t mhr_len, uint8_t exp_hl_diff); +static void _check_send_frag1(size_t mhr_len, bool check_tag); +static void _check_send_frag2(size_t mhr_len, bool check_tag); +static void _check_send_frag3(size_t mhr_len, bool check_tag); static const gnrc_sixlowpan_frag_rb_t *_first_non_empty_rbuf(void); static int _mock_netdev_send(netdev_t *dev, const iolist_t *iolist); @@ -421,6 +519,113 @@ static void test_minfwd_forward__ENOMEM__netif_hdr_build_fail(void) TEST_ASSERT(gnrc_pktbuf_is_empty()); } +static void test_minfwd_frag_iphc__success(void) +{ + gnrc_sixlowpan_frag_fb_t *fbuf; + gnrc_pktsnip_t *pkt; + size_t mhr_len; + + TEST_ASSERT_NOT_NULL((pkt = _create_send_datagram(true, true))); + + TEST_ASSERT_NOT_NULL((fbuf = gnrc_sixlowpan_frag_fb_get())); + fbuf->datagram_size = TEST_SEND_DATAGRAM_SIZE; + fbuf->tag = TEST_SEND_DATAGRAM_TAG; + + netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev, + _mock_netdev_send); + TEST_ASSERT_EQUAL_INT( + 0, gnrc_sixlowpan_frag_minfwd_frag_iphc(pkt, + TEST_SEND_DATAGRAM_SIZE, + &_rem_gb, fbuf) + ); + TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_send_frag1)))); + _check_send_frag1(mhr_len, true); + TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_send_frag2)))); + _check_send_frag2(mhr_len, true); + TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_send_frag3)))); + _check_send_frag3(mhr_len, true); + TEST_ASSERT(gnrc_pktbuf_is_sane()); + TEST_ASSERT(gnrc_pktbuf_is_empty()); +} + +static void test_minfwd_frag_iphc__no_frag(void) +{ + gnrc_sixlowpan_frag_fb_t *fbuf; + gnrc_pktsnip_t *pkt, *netif; + gnrc_netif_hdr_t *netif_hdr; + + pkt = gnrc_pktbuf_add(NULL, + &_test_send_frag1[TEST_SEND_FRAG1_PAYLOAD_POS], + TEST_SEND_FRAG1_PAYLOAD_SIZE, + GNRC_NETTYPE_SIXLOWPAN); + TEST_ASSERT_NOT_NULL(pkt); + netif = gnrc_netif_hdr_build(_vrbe_base.src, _vrbe_base.src_len, + _vrbe_base.dst, _vrbe_base.dst_len); + TEST_ASSERT_NOT_NULL(netif); + netif_hdr = netif->data; + TEST_ASSERT_NOT_NULL(netif_hdr); + netif_hdr->if_pid = _mock_netif->pid; + LL_PREPEND(pkt, netif); + + TEST_ASSERT_NOT_NULL((fbuf = gnrc_sixlowpan_frag_fb_get())); + fbuf->datagram_size = TEST_SEND_FRAG1_PAYLOAD_SIZE; + fbuf->tag = TEST_SEND_DATAGRAM_TAG; + + netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev, + _mock_netdev_send); + TEST_ASSERT_EQUAL_INT( + -1, gnrc_sixlowpan_frag_minfwd_frag_iphc(pkt, + TEST_SEND_DATAGRAM_SIZE, + &_rem_gb, fbuf) + ); + /* should time out (as the packet should be handled by normal fragmentation) + * now */ + TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_send_frag1))); + TEST_ASSERT_NULL(fbuf->pkt); + gnrc_pktbuf_release(pkt); + TEST_ASSERT(gnrc_pktbuf_is_sane()); + TEST_ASSERT(gnrc_pktbuf_is_empty()); +} + +static void test_minfwd_frag_iphc__ll_dst(void) +{ + gnrc_sixlowpan_frag_fb_t *fbuf; + gnrc_pktsnip_t *pkt, *netif; + gnrc_netif_hdr_t *netif_hdr; + + pkt = gnrc_pktbuf_add(NULL, + &_test_send_ll[TEST_SEND_LL_PAYLOAD_POS], + TEST_SEND_LL_PAYLOAD_SIZE, + GNRC_NETTYPE_SIXLOWPAN); + TEST_ASSERT_NOT_NULL(pkt); + netif = gnrc_netif_hdr_build(_vrbe_base.src, _vrbe_base.src_len, + _vrbe_base.dst, _vrbe_base.dst_len); + TEST_ASSERT_NOT_NULL(netif); + netif_hdr = netif->data; + TEST_ASSERT_NOT_NULL(netif_hdr); + netif_hdr->if_pid = _mock_netif->pid; + LL_PREPEND(pkt, netif); + + TEST_ASSERT_NOT_NULL((fbuf = gnrc_sixlowpan_frag_fb_get())); + fbuf->datagram_size = TEST_SEND_FRAG1_PAYLOAD_SIZE; + fbuf->tag = TEST_SEND_DATAGRAM_TAG; + + netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev, + _mock_netdev_send); + TEST_ASSERT_EQUAL_INT( + -1, gnrc_sixlowpan_frag_minfwd_frag_iphc(pkt, + TEST_SEND_DATAGRAM_SIZE, + &_rem_ll, fbuf) + ); + /* should time out (as the packet should be handled by normal fragmentation) + * now */ + TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_send_frag1))); + TEST_ASSERT_NULL(fbuf->pkt); + gnrc_pktbuf_release(pkt); + TEST_ASSERT(gnrc_pktbuf_is_sane()); + TEST_ASSERT(gnrc_pktbuf_is_empty()); +} + static void test_sixlo_recv__1st_frag_uncomp(void) { gnrc_sixlowpan_frag_vrb_t *vrbe; @@ -734,6 +939,29 @@ static void test_sixlo_recv__nth_frag__overlap(void) )); } +static void test_sixlo_send(void) +{ + gnrc_pktsnip_t *pkt; + size_t mhr_len; + + TEST_ASSERT_NOT_NULL((pkt = _create_send_datagram(false, true))); + + netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev, + _mock_netdev_send); + TEST_ASSERT(0 < gnrc_netapi_dispatch_send(GNRC_NETTYPE_SIXLOWPAN, + GNRC_NETREG_DEMUX_CTX_ALL, + pkt)); + TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_send_frag1)))); + /* tags are generated by the stack so don't check */ + _check_send_frag1(mhr_len, false); + TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_send_frag2)))); + _check_send_frag2(mhr_len, false); + TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_send_frag3)))); + _check_send_frag3(mhr_len, false); + TEST_ASSERT(gnrc_pktbuf_is_sane()); + TEST_ASSERT(gnrc_pktbuf_is_empty()); +} + static Test *tests_gnrc_sixlowpan_frag_minfwd_api(void) { EMB_UNIT_TESTFIXTURES(fixtures) { @@ -747,6 +975,9 @@ static Test *tests_gnrc_sixlowpan_frag_minfwd_api(void) new_TestFixture(test_minfwd_forward__success__nth_frag_incomplete), new_TestFixture(test_minfwd_forward__success__nth_frag_complete), new_TestFixture(test_minfwd_forward__ENOMEM__netif_hdr_build_fail), + new_TestFixture(test_minfwd_frag_iphc__success), + new_TestFixture(test_minfwd_frag_iphc__no_frag), + new_TestFixture(test_minfwd_frag_iphc__ll_dst), }; EMB_UNIT_TESTCALLER(tests, _set_up, _tear_down, fixtures); @@ -765,6 +996,7 @@ static Test *tests_gnrc_sixlowpan_frag_minfwd_integration(void) new_TestFixture(test_sixlo_recv__nth_frag__no_vrbe), new_TestFixture(test_sixlo_recv__nth_frag__duplicate), new_TestFixture(test_sixlo_recv__nth_frag__overlap), + new_TestFixture(test_sixlo_send), }; EMB_UNIT_TESTCALLER(tests, _set_up, _tear_down, fixtures); @@ -821,6 +1053,46 @@ static int _set_route_and_nce(const ipv6_addr_t *route, unsigned pfx_len) return 0; } +static gnrc_pktsnip_t *_create_send_datagram(bool compressed, bool payload) +{ + gnrc_pktsnip_t *pkt1 = NULL, *pkt2; + gnrc_netif_hdr_t *netif_hdr; + + if (payload) { + pkt1 = gnrc_pktbuf_add(NULL, _test_send_icmpv6, sizeof(_test_send_icmpv6), + GNRC_NETTYPE_ICMPV6); + if (pkt1 == NULL) { + return NULL; + } + } + if (compressed) { + /* Use IPHC header from expected data */ + pkt2 = gnrc_pktbuf_add(pkt1, + &_test_send_frag1[TEST_SEND_FRAG1_PAYLOAD_POS], + TEST_SEND_FRAG1_PAYLOAD_SIZE, + GNRC_NETTYPE_SIXLOWPAN); + } + else { + pkt2 = gnrc_pktbuf_add(pkt1, _test_send_ipv6, sizeof(_test_send_ipv6), + GNRC_NETTYPE_IPV6); + } + if (pkt2 == NULL) { + return NULL; + } + pkt1 = gnrc_netif_hdr_build(_vrbe_base.src, _vrbe_base.src_len, + _vrbe_base.dst, _vrbe_base.dst_len); + if (pkt1 == NULL) { + return NULL; + } + netif_hdr = pkt1->data; + if (netif_hdr == NULL) { + return NULL; + } + netif_hdr->if_pid = _mock_netif->pid; + LL_PREPEND(pkt2, pkt1); + return pkt2; +} + static size_t _wait_for_packet(size_t exp_size) { size_t mhr_len; @@ -923,6 +1195,74 @@ static void _check_1st_frag_uncomp(size_t mhr_len, uint8_t exp_hl_diff) ); } +static void _check_send_frag_datagram_fields(size_t mhr_len, bool check_tag) +{ + sixlowpan_frag_t *frag_hdr = (sixlowpan_frag_t *)&_target_buf[mhr_len]; + + TEST_ASSERT_EQUAL_INT(TEST_SEND_DATAGRAM_SIZE, + byteorder_ntohs(frag_hdr->disp_size) & + SIXLOWPAN_FRAG_SIZE_MASK); + if (check_tag) { + TEST_ASSERT_EQUAL_INT(TEST_SEND_DATAGRAM_TAG, + byteorder_ntohs(frag_hdr->tag)); + } +} + +static void _check_send_frag1(size_t mhr_len, bool check_tag) +{ + TEST_ASSERT_EQUAL_INT( + SIXLOWPAN_FRAG_1_DISP, + _target_buf[mhr_len] & SIXLOWPAN_FRAG_DISP_MASK + ); + _check_send_frag_datagram_fields(mhr_len, check_tag); + TEST_ASSERT_MESSAGE( + memcmp(&_test_send_frag1[TEST_SEND_FRAG1_PAYLOAD_POS], + &_target_buf[TEST_SEND_FRAG1_PAYLOAD_POS + mhr_len], + TEST_SEND_FRAG1_PAYLOAD_SIZE) == 0, + "unexpected IPHC header" + ); +} + +static void _check_send_frag2(size_t mhr_len, bool check_tag) +{ + sixlowpan_frag_n_t *frag_hdr; + + TEST_ASSERT_EQUAL_INT( + SIXLOWPAN_FRAG_N_DISP, + _target_buf[mhr_len] & SIXLOWPAN_FRAG_DISP_MASK + ); + frag_hdr = (sixlowpan_frag_n_t *)&_target_buf[mhr_len]; + _check_send_frag_datagram_fields(mhr_len, check_tag); + TEST_ASSERT_EQUAL_INT(TEST_SEND_FRAG2_OFFSET, + frag_hdr->offset); + TEST_ASSERT_MESSAGE( + memcmp(&_test_send_frag2[TEST_SEND_FRAG2_PAYLOAD_POS], + &_target_buf[TEST_SEND_FRAG2_PAYLOAD_POS + mhr_len], + TEST_SEND_FRAG2_PAYLOAD_SIZE) == 0, + "unexpected send packet payload" + ); +} + +static void _check_send_frag3(size_t mhr_len, bool check_tag) +{ + sixlowpan_frag_n_t *frag_hdr; + + TEST_ASSERT_EQUAL_INT( + SIXLOWPAN_FRAG_N_DISP, + _target_buf[mhr_len] & SIXLOWPAN_FRAG_DISP_MASK + ); + frag_hdr = (sixlowpan_frag_n_t *)&_target_buf[mhr_len]; + _check_send_frag_datagram_fields(mhr_len, check_tag); + TEST_ASSERT_EQUAL_INT(TEST_SEND_FRAG3_OFFSET, + frag_hdr->offset); + TEST_ASSERT_MESSAGE( + memcmp(&_test_send_frag3[TEST_SEND_FRAG3_PAYLOAD_POS], + &_target_buf[TEST_SEND_FRAG3_PAYLOAD_POS + mhr_len], + TEST_SEND_FRAG3_PAYLOAD_SIZE) == 0, + "unexpected send packet payload" + ); +} + static const gnrc_sixlowpan_frag_rb_t *_first_non_empty_rbuf(void) { const gnrc_sixlowpan_frag_rb_t *rbuf = gnrc_sixlowpan_frag_rb_array(); diff --git a/tests/gnrc_sixlowpan_frag_minfwd/mockup_netif.c b/tests/gnrc_sixlowpan_frag_minfwd/mockup_netif.c index 342559f9238d..0a04575fa802 100644 --- a/tests/gnrc_sixlowpan_frag_minfwd/mockup_netif.c +++ b/tests/gnrc_sixlowpan_frag_minfwd/mockup_netif.c @@ -86,6 +86,14 @@ int _get_address_long(netdev_t *dev, void *value, size_t max_len) return sizeof(addr); } +int _get_proto(netdev_t *dev, void *value, size_t max_len) +{ + (void)dev; + assert(max_len == sizeof(gnrc_nettype_t)); + *((gnrc_nettype_t *)value) = GNRC_NETTYPE_SIXLOWPAN; + return sizeof(gnrc_nettype_t); +} + void _tests_init(void) { int res; @@ -102,6 +110,8 @@ void _tests_init(void) _get_src_len); netdev_test_set_get_cb(&_mock_netdev, NETOPT_ADDRESS_LONG, _get_address_long); + netdev_test_set_get_cb(&_mock_netdev, NETOPT_PROTO, + _get_proto); res = gnrc_netif_ieee802154_create( &_netif, _mock_netif_stack, THREAD_STACKSIZE_DEFAULT, GNRC_NETIF_PRIO, "mockup_wpan", &_mock_netdev.netdev.netdev From 27e123608fdeacdfe71223375171662eb38ef602 Mon Sep 17 00:00:00 2001 From: Martine Lenders Date: Tue, 26 Feb 2019 14:16:07 +0100 Subject: [PATCH 09/10] gnrc_sixlowpan_iphc: forward received fragments --- .../frag/rb/gnrc_sixlowpan_frag_rb.c | 23 +++++++++++++++---- .../sixlowpan/iphc/gnrc_sixlowpan_iphc.c | 7 ++++++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/sys/net/gnrc/network_layer/sixlowpan/frag/rb/gnrc_sixlowpan_frag_rb.c b/sys/net/gnrc/network_layer/sixlowpan/frag/rb/gnrc_sixlowpan_frag_rb.c index 80016bd0616e..fee17cc9e35a 100644 --- a/sys/net/gnrc/network_layer/sixlowpan/frag/rb/gnrc_sixlowpan_frag_rb.c +++ b/sys/net/gnrc/network_layer/sixlowpan/frag/rb/gnrc_sixlowpan_frag_rb.c @@ -589,11 +589,24 @@ static int _rbuf_get(const void *src, size_t src_len, default: reass_type = GNRC_NETTYPE_UNDEF; } -#ifdef MODULE_GNRC_SIXLOWPAN_FRAG_VRB - res->pkt = gnrc_pktbuf_add(NULL, NULL, 0, reass_type); -#else /* MODULE_GNRC_SIXLOWPAN_FRAG_VRB */ - res->pkt = gnrc_pktbuf_add(NULL, NULL, size, reass_type); -#endif /* MODULE_GNRC_SIXLOWPAN_FRAG_VRB */ + if (IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_VRB)) { + if (IS_USED(MODULE_GNRC_SIXLOWPAN_IPHC)) { + /* only allocate enough space to decompress IPv6 header + * for forwarding information */ + res->pkt = gnrc_pktbuf_add(NULL, NULL, sizeof(ipv6_hdr_t), + reass_type); + } + else { + /* try fragment forwarding without IPHC. Since `res->pkt == NULL` + * is not a valid value for a reassembly buffer entry, we need to + * set it to at least a packet snip for now */ + res->pkt = gnrc_pktbuf_add(NULL, NULL, 0, reass_type); + } + } + else { + /* reassemble whole datagram without direct fragment forwarding */ + res->pkt = gnrc_pktbuf_add(NULL, NULL, size, reass_type); + } if (res->pkt == NULL) { DEBUG("6lo rfrag: can not allocate reassembly buffer space.\n"); return -1; 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 91ba02bb06cf..db3b78ec11ee 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 @@ -824,6 +824,7 @@ void gnrc_sixlowpan_iphc_recv(gnrc_pktsnip_t *sixlo, void *rbuf_ptr, payload_offset - sizeof(ipv6_hdr_t)); } if ((rbuf == NULL) && + /* (rbuf == NULL) => forwarding is not affected by this */ (gnrc_pktbuf_realloc_data(ipv6, uncomp_hdr_len + payload_len) != 0)) { DEBUG("6lo iphc: no space left to copy payload\n"); _recv_error_release(sixlo, ipv6, rbuf); @@ -852,6 +853,12 @@ void gnrc_sixlowpan_iphc_recv(gnrc_pktsnip_t *sixlo, void *rbuf_ptr, } } if ((ipv6 == NULL) || (res < 0)) { + /* TODO: There is a potential to fall-back to classic reassembly + * when ipv6 != NULL. However, since `ipv6` was reversed in + * `_encode_frag_for_forwarding`, that step needs to be reversed + * or a version of the old ipv6 needs to be held in the buffer. + * For now, just drop the packet all together in an error case + */ gnrc_sixlowpan_frag_vrb_rm(vrbe); } gnrc_pktbuf_release(sixlo); From e70a42c076ae58c4a345512058228e7124308e00 Mon Sep 17 00:00:00 2001 From: Martine Lenders Date: Tue, 26 Feb 2019 14:16:43 +0100 Subject: [PATCH 10/10] tests: add gnrc_sixlowpan_frag_minfwd tests for IPHC reception --- tests/gnrc_sixlowpan_frag_minfwd/Makefile | 5 + tests/gnrc_sixlowpan_frag_minfwd/app.config | 3 + tests/gnrc_sixlowpan_frag_minfwd/main.c | 400 +++++++++++++++++++- 3 files changed, 406 insertions(+), 2 deletions(-) create mode 100644 tests/gnrc_sixlowpan_frag_minfwd/app.config diff --git a/tests/gnrc_sixlowpan_frag_minfwd/Makefile b/tests/gnrc_sixlowpan_frag_minfwd/Makefile index 9994b7baf2e1..f2e7fa3f7ac8 100644 --- a/tests/gnrc_sixlowpan_frag_minfwd/Makefile +++ b/tests/gnrc_sixlowpan_frag_minfwd/Makefile @@ -12,3 +12,8 @@ USEMODULE += netdev_test CFLAGS += -DTEST_SUITES include $(RIOTBASE)/Makefile.include + +ifndef CONFIG_GNRC_IPV6_NIB_NO_RTR_SOL + # disable router solicitations so they don't interfere with the tests + CFLAGS += -DCONFIG_GNRC_IPV6_NIB_NO_RTR_SOL=1 +endif diff --git a/tests/gnrc_sixlowpan_frag_minfwd/app.config b/tests/gnrc_sixlowpan_frag_minfwd/app.config new file mode 100644 index 000000000000..72e21306cd7d --- /dev/null +++ b/tests/gnrc_sixlowpan_frag_minfwd/app.config @@ -0,0 +1,3 @@ +CONFIG_KCONFIG_USEMODULE_GNRC_IPV6_NIB=y +# disable router solicitations so they don't interfere with the tests +CONFIG_GNRC_IPV6_NIB_NO_RTR_SOL=y diff --git a/tests/gnrc_sixlowpan_frag_minfwd/main.c b/tests/gnrc_sixlowpan_frag_minfwd/main.c index dff59f796b7a..07148ae994a0 100644 --- a/tests/gnrc_sixlowpan_frag_minfwd/main.c +++ b/tests/gnrc_sixlowpan_frag_minfwd/main.c @@ -39,6 +39,7 @@ /* for debugging _target_buf */ #include "od.h" #include "utlist.h" +#include "xtimer.h" #define SEND_PACKET_TIMEOUT (500U) @@ -66,7 +67,12 @@ #define TEST_1ST_FRAG_UNCOMP_IPV6_PAYLOAD_POS (45U) #define TEST_1ST_FRAG_UNCOMP_UDP_PAYLOAD_SIZE (32U) #define TEST_1ST_FRAG_UNCOMP_UDP_PAYLOAD_POS (53U) +#define TEST_1ST_FRAG_COMP_FRAG_SIZE (80U) +#define TEST_1ST_FRAG_COMP_ONLY_IPHC_FRAG_SIZE (48U) +#define TEST_1ST_FRAG_COMP_SIZE (38U) +#define TEST_1ST_FRAG_COMP_PAYLOAD_POS (4U) #define TEST_1ST_FRAG_COMP_EXP_OFFSET (6U) +#define TEST_1ST_FRAG_COMP_PREV_HOP_UDP_PAYLOAD_POS (43U) #define TEST_NTH_FRAG_SIZE (32U) #define TEST_NTH_FRAG_OFFSET_POS (4U) #define TEST_NTH_FRAG_PAYLOAD_POS (5U) @@ -76,6 +82,7 @@ #define TEST_SEND_LL_PAYLOAD_SIZE (7U) #define TEST_SEND_FRAG1_PAYLOAD_POS (4U) #define TEST_SEND_FRAG1_PAYLOAD_SIZE (35U) +#define TEST_SEND_FRAG1_SIZE (40U) #define TEST_SEND_FRAG2_OFFSET_POS (4U) #define TEST_SEND_FRAG2_OFFSET (5U) #define TEST_SEND_FRAG2_PAYLOAD_POS (5U) @@ -113,6 +120,45 @@ static const uint8_t _test_1st_frag_uncomp[] = { 0xc7, 0x6e, 0x09, 0xf9, 0xba, 0x70, 0x3a, 0x38, 0xd5, 0x2f, 0x08, 0x85, 0xb8, 0xc1, 0x1a, 0x31, }; +static const uint8_t _test_1st_frag_comp[] = { + 0xc4, 0xd0, /* 1st fragment | datagram size: 1232 */ + 0x67, 0x9d, /* tag: 0x679d */ + /* IPHC: TF: 0b11, NH: 0b1 (NHC), HLIM: 0b10 (64), CID: 0b0, + * Source: uncompressed (SAC: 0b0, SAM: 0b00), + * Destination: uncompressed (M:0, DAC: 0b0, DAM: 0b00) */ + 0x7e, 0x00, + /* (uncompressed) Source: 2001:db8:d6c3:acf:dc71:2b85:82f:75fb */ + 0x20, 0x01, 0x0d, 0xb8, 0xd6, 0xc3, 0x0a, 0xcf, + 0xdc, 0x71, 0x2b, 0x85, 0x08, 0x2f, 0x75, 0xfb, + /* (uncompressed) Destination: REM_GB */ + 0x20, 0x01, 0x0d, 0xb8, 0xd3, 0x35, 0x91, 0x7e, + _LL0 ^ 0x2, _LL1, _LL2, _LL3, _LL4, _LL5, _LL6, _LL7 + 1, + /* NHC UDP: ports: 0b11 (12 bytes elided), (random) Checksum inline */ + 0xf3, 0x4a, 0x47, 0xb8, + }; +static const uint8_t _test_1st_frag_comp_prev_hop[] = { + 0xc4, 0xd0, /* 1st fragment | datagram size: 1232 */ + 0x67, 0x9d, /* tag: 0x679d */ + /* IPHC: TF: 0b11, NH: 0b1 (NHC), HLIM: 0b00 (inline), CID: 0b0, + * Source: uncompressed (SAC: 0b0, SAM: 0b00), + * Destination: uncompressed (M:0, DAC: 0b0, DAM: 0b00) */ + 0x7c, 0x00, + /* Hop Limit: 65 */ + 0x41, + /* (uncompressed) Source: 2001:db8:d6c3:acf:dc71:2b85:82f:75fb */ + 0x20, 0x01, 0x0d, 0xb8, 0xd6, 0xc3, 0x0a, 0xcf, + 0xdc, 0x71, 0x2b, 0x85, 0x08, 0x2f, 0x75, 0xfb, + /* (uncompressed) Destination: REM_GB */ + 0x20, 0x01, 0x0d, 0xb8, 0xd3, 0x35, 0x91, 0x7e, + _LL0 ^ 0x2, _LL1, _LL2, _LL3, _LL4, _LL5, _LL6, _LL7 + 1, + /* NHC UDP: ports: 0b11 (12 bytes elided), (random) Checksum inline */ + 0xf3, 0x4a, 0x47, 0xb8, + /* (random) payload of length 32 */ + 0xba, 0xb3, 0x6e, 0x4f, 0xd8, 0x23, 0x40, 0xf3, + 0xfb, 0xb9, 0x05, 0xbf, 0xbe, 0x19, 0xf6, 0xa2, + 0xc7, 0x6e, 0x09, 0xf9, 0xba, 0x70, 0x3a, 0x38, + 0xd5, 0x2f, 0x08, 0x85, 0xb8, 0xc1, 0x1a, 0x31, + }; static const uint8_t _test_nth_frag[] = { 0xe4, 0xd0, /* n-th fragment | datagram size: 1232 */ 0x67, 0x9d, /* tag: 0x679d */ @@ -163,6 +209,22 @@ static const uint8_t _test_send_ll[] = { /* Next header: ICMPv6 (58) */ 0x3a, }; +static const uint8_t _test_send_frag1_prev_hop[] = { + 0xc0, 0x94, /* 1st fragment | datagram size: TEST_SEND_DATAGRAM_SIZE */ + 0x67, 0x9d, /* tag: 0x679d */ + /* IPHC: TF: 0b11, NH: 0b0 (inline), HLIM: 0b00 (inline), CID: 0b0, + * Source: uncompressed (SAC: 0b0, SAM: 0b00), + * Destination: uncompressed (M:0, DAC: 0b0, DAM: 0b00) */ + 0x78, 0x00, + /* Next header: ICMPv6 (58), Hop Limit: 65 */ + 0x3a, 0x41, + /* (uncompressed) Source: LOC_GB */ + 0x20, 0x01, 0x0d, 0xb8, 0xd3, 0x35, 0x91, 0x7e, + _LL0 ^ 0x2, _LL1, _LL2, _LL3, _LL4, _LL5, _LL6, _LL7, + /* (uncompressed) Destination: REM_GB */ + 0x20, 0x01, 0x0d, 0xb8, 0xd3, 0x35, 0x91, 0x7e, + _LL0 ^ 0x2, _LL1, _LL2, _LL3, _LL4, _LL5, _LL6, _LL7 + 1, + }; static const uint8_t _test_send_frag1[] = { 0xc0, 0x94, /* 1st fragment | datagram size: TEST_SEND_DATAGRAM_SIZE */ 0x22, 0xdd, /* tag: TEST_SEND_DATAGRAM_TAG */ @@ -363,7 +425,8 @@ static void test_minfwd_vrbe_from_route__vrb_full(void) TEST_ASSERT_EQUAL_INT(0, _set_route_and_nce(&ipv6_hdr.dst, REM_GB_PFX_LEN)); /* fill up VRB */ - for (unsigned i = 0; i < GNRC_SIXLOWPAN_FRAG_VRB_SIZE; i++) { + for (unsigned i = 0; i < CONFIG_GNRC_SIXLOWPAN_FRAG_VRB_SIZE; i++) { + base.arrival = xtimer_now_usec(); TEST_ASSERT_NOT_NULL(gnrc_sixlowpan_frag_vrb_add(&base, _mock_netif, _rem_l2, @@ -384,6 +447,7 @@ static void test_minfwd_forward__success__1st_frag_sixlo(void) gnrc_pktsnip_t *pkt, *frag; size_t mhr_len; + vrbe->super.arrival = xtimer_now_usec(); TEST_ASSERT_NOT_NULL((pkt = gnrc_pktbuf_add(NULL, _test_1st_frag_uncomp, sizeof(_test_1st_frag_uncomp), GNRC_NETTYPE_SIXLOWPAN))); @@ -410,6 +474,45 @@ static void test_minfwd_forward__success__1st_frag_sixlo(void) _vrbe_base.tag)); } +static void test_minfwd_forward__success__1st_frag_iphc(void) +{ + gnrc_sixlowpan_frag_vrb_t *vrbe = gnrc_sixlowpan_frag_vrb_add( + &_vrbe_base, _mock_netif, _rem_l2, sizeof(_rem_l2) + ); + gnrc_pktsnip_t *pkt, *frag; + size_t mhr_len; + + vrbe->super.arrival = xtimer_now_usec(); + TEST_ASSERT_NOT_NULL((pkt = gnrc_pktbuf_add(NULL, _test_1st_frag_comp, + sizeof(_test_1st_frag_comp), + GNRC_NETTYPE_SIXLOWPAN))); + /* separate fragment header from payload */ + TEST_ASSERT_NOT_NULL((frag = gnrc_pktbuf_mark(pkt, sizeof(sixlowpan_frag_t), + GNRC_NETTYPE_SIXLOWPAN))); + LL_DELETE(pkt, frag); + netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev, + _mock_netdev_send); + TEST_ASSERT_EQUAL_INT(0, gnrc_sixlowpan_frag_minfwd_forward(pkt, + frag->data, + vrbe, + 0)); + gnrc_pktbuf_release(frag); /* delete separated fragment header */ + /* first wait and check IPHC part (we put some slack in the first fragment) */ + TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_1st_frag_comp)))); + _check_vrbe_values(vrbe, mhr_len, FIRST_FRAGMENT); + TEST_ASSERT(_target_buf[0] & IEEE802154_FCF_FRAME_PEND); + TEST_ASSERT_MESSAGE( + memcmp(&_test_1st_frag_comp[TEST_1ST_FRAG_COMP_PAYLOAD_POS], + &_target_buf[mhr_len + sizeof(sixlowpan_frag_t)], + sizeof(_test_1st_frag_comp) - sizeof(sixlowpan_frag_t)) == 0, + "unexpected IPHC header" + ); + /* VRB entry should not have been removed */ + TEST_ASSERT_NOT_NULL(gnrc_sixlowpan_frag_vrb_get(_vrbe_base.src, + _vrbe_base.src_len, + _vrbe_base.tag)); +} + static void test_minfwd_forward__success__nth_frag_incomplete(void) { gnrc_sixlowpan_frag_vrb_t *vrbe = gnrc_sixlowpan_frag_vrb_add( @@ -418,6 +521,7 @@ static void test_minfwd_forward__success__nth_frag_incomplete(void) gnrc_pktsnip_t *pkt, *frag; size_t mhr_len; + vrbe->super.arrival = xtimer_now_usec(); TEST_ASSERT_NOT_NULL((pkt = gnrc_pktbuf_add(NULL, _test_nth_frag, sizeof(_test_nth_frag), GNRC_NETTYPE_SIXLOWPAN))); @@ -457,6 +561,7 @@ static void test_minfwd_forward__success__nth_frag_complete(void) ); gnrc_pktsnip_t *pkt, *frag; + vrbe->super.arrival = xtimer_now_usec(); TEST_ASSERT_NOT_NULL((pkt = gnrc_pktbuf_add(NULL, _test_nth_frag, sizeof(_test_nth_frag), GNRC_NETTYPE_SIXLOWPAN))); @@ -492,6 +597,7 @@ static void test_minfwd_forward__ENOMEM__netif_hdr_build_fail(void) ); gnrc_pktsnip_t *pkt, *frag, *filled_space; + vrbe->super.arrival = xtimer_now_usec(); TEST_ASSERT_NOT_NULL((filled_space = gnrc_pktbuf_add( NULL, NULL, /* 115U == 2 * sizeof(gnrc_pktsnip_t) + movement due to mark */ @@ -738,6 +844,283 @@ static void test_sixlo_recv__1st_frag_uncomp__after_nth_frag(void) * test for normal reassembly ;-) */ } +static void test_sixlo_recv__1st_frag_comp(void) +{ + gnrc_sixlowpan_frag_vrb_t *vrbe; + gnrc_pktsnip_t *frag; + size_t mhr_len; + + TEST_ASSERT_NOT_NULL( + (frag = _create_recv_frag(_test_1st_frag_comp_prev_hop, + sizeof(_test_1st_frag_comp_prev_hop))) + ); + /* configure route to destination of IP header in frag */ + TEST_ASSERT_EQUAL_INT(0, _set_route_and_nce(&_rem_gb, REM_GB_PFX_LEN)); + netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev, + _mock_netdev_send); + TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN, + GNRC_NETREG_DEMUX_CTX_ALL, + frag)); + /* first wait and check IPHC part (we put some slack in the first fragment) */ + TEST_ASSERT((mhr_len = _wait_for_packet( + sizeof(_test_1st_frag_comp) + TEST_1ST_FRAG_UNCOMP_UDP_PAYLOAD_SIZE + ))); + TEST_ASSERT(gnrc_pktbuf_is_sane()); + TEST_ASSERT(gnrc_pktbuf_is_empty()); + /* reassembly buffer remains empty */ + TEST_ASSERT_NULL(_first_non_empty_rbuf()); + /* but there was a VRB entry created */ + TEST_ASSERT_NOT_NULL((vrbe = gnrc_sixlowpan_frag_vrb_get( + _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag + ))); + _check_vrbe_values(vrbe, mhr_len, FIRST_FRAGMENT); + TEST_ASSERT_EQUAL_INT(TEST_1ST_FRAG_COMP_FRAG_SIZE, + vrbe->super.current_size); + /* only the received fragment is registered */ + TEST_ASSERT_NOT_NULL(vrbe->super.ints); + TEST_ASSERT_NULL(vrbe->super.ints->next); + TEST_ASSERT(_target_buf[0] & IEEE802154_FCF_FRAME_PEND); + TEST_ASSERT_MESSAGE( + memcmp(&_test_1st_frag_comp[TEST_1ST_FRAG_COMP_PAYLOAD_POS], + &_target_buf[mhr_len + sizeof(sixlowpan_frag_t)], + TEST_1ST_FRAG_COMP_SIZE) == 0, + "unexpected IPHC header" + ); + TEST_ASSERT_MESSAGE( + memcmp(&_test_1st_frag_uncomp[TEST_1ST_FRAG_UNCOMP_UDP_PAYLOAD_POS], + &_target_buf[mhr_len + sizeof(_test_1st_frag_comp)], + TEST_1ST_FRAG_UNCOMP_UDP_PAYLOAD_SIZE) == 0, + "unexpected forwarded packet payload" + ); +} + +static void test_sixlo_recv__1st_frag_comp__only_iphc(void) +{ + gnrc_sixlowpan_frag_vrb_t *vrbe; + gnrc_pktsnip_t *frag; + size_t mhr_len; + + TEST_ASSERT_NOT_NULL( + (frag = _create_recv_frag(_test_1st_frag_comp_prev_hop, + TEST_1ST_FRAG_COMP_PREV_HOP_UDP_PAYLOAD_POS)) + ); + /* configure route to destination of IP header in frag */ + TEST_ASSERT_EQUAL_INT(0, _set_route_and_nce(&_rem_gb, REM_GB_PFX_LEN)); + netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev, + _mock_netdev_send); + TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN, + GNRC_NETREG_DEMUX_CTX_ALL, + frag)); + /* first wait and check IPHC part (we put some slack in the first fragment) */ + TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_1st_frag_comp)))); + /* reassembly buffer remains empty */ + TEST_ASSERT_NULL(_first_non_empty_rbuf()); + /* but there was a VRB entry created */ + TEST_ASSERT_NOT_NULL((vrbe = gnrc_sixlowpan_frag_vrb_get( + _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag + ))); + _check_vrbe_values(vrbe, mhr_len, FIRST_FRAGMENT); + TEST_ASSERT_EQUAL_INT(TEST_1ST_FRAG_COMP_ONLY_IPHC_FRAG_SIZE, + vrbe->super.current_size); + /* only the received fragment is registered */ + TEST_ASSERT_NOT_NULL(vrbe->super.ints); + TEST_ASSERT_NULL(vrbe->super.ints->next); + TEST_ASSERT(_target_buf[0] & IEEE802154_FCF_FRAME_PEND); + TEST_ASSERT_MESSAGE( + memcmp(&_test_1st_frag_comp[TEST_1ST_FRAG_COMP_PAYLOAD_POS], + &_target_buf[mhr_len + sizeof(sixlowpan_frag_t)], + TEST_1ST_FRAG_COMP_SIZE) == 0, + "unexpected IPHC header" + ); +} + +static void test_sixlo_recv__1st_frag_comp__only_iphc_no_nhc(void) +{ + gnrc_sixlowpan_frag_vrb_t *vrbe; + gnrc_pktsnip_t *frag; + size_t mhr_len; + + TEST_ASSERT_NOT_NULL( + (frag = _create_recv_frag(_test_send_frag1_prev_hop, + sizeof(_test_send_frag1_prev_hop))) + ); + /* configure route to destination of IP header in frag */ + TEST_ASSERT_EQUAL_INT(0, _set_route_and_nce(&_rem_gb, REM_GB_PFX_LEN)); + netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev, + _mock_netdev_send); + TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN, + GNRC_NETREG_DEMUX_CTX_ALL, + frag)); + /* first wait and check IPHC part (we put some slack in the first fragment) */ + TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_send_frag1)))); + /* reassembly buffer remains empty */ + TEST_ASSERT_NULL(_first_non_empty_rbuf()); + /* but there was a VRB entry created */ + TEST_ASSERT_NOT_NULL((vrbe = gnrc_sixlowpan_frag_vrb_get( + _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag + ))); + _check_vrbe_values(vrbe, mhr_len, FIRST_FRAGMENT); + TEST_ASSERT_EQUAL_INT(TEST_SEND_FRAG1_SIZE, vrbe->super.current_size); + /* only the received fragment is registered */ + TEST_ASSERT_NOT_NULL(vrbe->super.ints); + TEST_ASSERT_NULL(vrbe->super.ints->next); + TEST_ASSERT(_target_buf[0] & IEEE802154_FCF_FRAME_PEND); + TEST_ASSERT_MESSAGE( + memcmp(&_test_send_frag1[TEST_SEND_FRAG1_PAYLOAD_POS], + &_target_buf[mhr_len + sizeof(sixlowpan_frag_t)], + TEST_SEND_FRAG1_PAYLOAD_SIZE) == 0, + "unexpected IPHC header" + ); +} + +static void test_sixlo_recv__1st_frag_comp__no_route(void) +{ + const gnrc_sixlowpan_frag_rb_t *rbuf; + gnrc_pktsnip_t *frag; + + TEST_ASSERT_NOT_NULL( + (frag = _create_recv_frag(_test_1st_frag_comp_prev_hop, + sizeof(_test_1st_frag_comp_prev_hop))) + ); + TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN, + GNRC_NETREG_DEMUX_CTX_ALL, + frag)); + /* should time out */ + TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_1st_frag_comp))); + /* normal reassembly should have started */ + /* reassembly buffer entry should have been created */ + TEST_ASSERT_NOT_NULL((rbuf = _first_non_empty_rbuf())); + /* and VRB remains empty */ + TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get( + _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag + )); + TEST_ASSERT_EQUAL_INT(_vrbe_base.datagram_size, rbuf->pkt->size); + gnrc_pktbuf_release(rbuf->pkt); + TEST_ASSERT(gnrc_pktbuf_is_sane()); + TEST_ASSERT(gnrc_pktbuf_is_empty()); + /* This is normal reassembly so the rest should have be tested in the + * test for normal reassembly ;-) */ +} + +static void test_sixlo_recv__1st_frag_comp__no_route_only_iphc(void) +{ + const gnrc_sixlowpan_frag_rb_t *rbuf; + gnrc_pktsnip_t *frag; + + TEST_ASSERT_NOT_NULL( + (frag = _create_recv_frag(_test_1st_frag_comp_prev_hop, + TEST_1ST_FRAG_COMP_PREV_HOP_UDP_PAYLOAD_POS)) + ); + /* configure route to destination of IP header in frag */ + TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN, + GNRC_NETREG_DEMUX_CTX_ALL, + frag)); + /* should time out */ + TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_1st_frag_comp))); + /* normal reassembly should have started */ + /* reassembly buffer entry should have been created */ + TEST_ASSERT_NOT_NULL((rbuf = _first_non_empty_rbuf())); + /* and VRB remains empty */ + TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get( + _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag + )); + TEST_ASSERT_EQUAL_INT(_vrbe_base.datagram_size, rbuf->pkt->size); + gnrc_pktbuf_release(rbuf->pkt); + TEST_ASSERT(gnrc_pktbuf_is_sane()); + TEST_ASSERT(gnrc_pktbuf_is_empty()); + /* This is normal reassembly so the rest should have be tested in the + * test for normal reassembly ;-) */ +} + +static void test_sixlo_recv__1st_frag_comp__no_refrag(void) +{ + gnrc_sixlowpan_frag_fb_t *reserved[CONFIG_GNRC_SIXLOWPAN_FRAG_FB_SIZE]; + const gnrc_sixlowpan_frag_rb_t *rbuf; + gnrc_pktsnip_t *frag; + + TEST_ASSERT_NOT_NULL( + (frag = _create_recv_frag(_test_1st_frag_comp_prev_hop, + sizeof(_test_1st_frag_comp_prev_hop))) + ); + /* consume all available gnrc_sixlowpan_frag_fb_t instances so creating + * a fragment with extra slack is not possible */ + for (unsigned i = 0; i < CONFIG_GNRC_SIXLOWPAN_FRAG_FB_SIZE; i++) { + reserved[i] = gnrc_sixlowpan_frag_fb_get(); + reserved[i]->pkt = frag; + } + /* configure route to destination of IP header in frag */ + TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN, + GNRC_NETREG_DEMUX_CTX_ALL, + frag)); + /* should time out */ + TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_1st_frag_comp))); + /* normal reassembly should have started */ + /* reassembly buffer entry should have been created */ + TEST_ASSERT_NOT_NULL((rbuf = _first_non_empty_rbuf())); + /* and VRB remains empty */ + TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get( + _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag + )); + TEST_ASSERT_EQUAL_INT(_vrbe_base.datagram_size, rbuf->pkt->size); + gnrc_pktbuf_release(rbuf->pkt); + /* release all gnrc_sixlowpan_frag_fb_t instances again */ + for (unsigned i = 0; i < CONFIG_GNRC_SIXLOWPAN_FRAG_FB_SIZE; i++) { + reserved[i]->pkt = NULL; + } + TEST_ASSERT(gnrc_pktbuf_is_sane()); + TEST_ASSERT(gnrc_pktbuf_is_empty()); + /* This is normal reassembly so the rest should have be tested in the + * test for normal reassembly ;-) */ +} + +static void test_sixlo_recv__1st_frag_comp__after_nth_frag(void) +{ + const gnrc_sixlowpan_frag_rb_t *rbuf; + gnrc_pktsnip_t *frag; + + TEST_ASSERT_NOT_NULL( + (frag = _create_recv_frag(_test_nth_frag, + sizeof(_test_nth_frag))) + ); + /* configure route to destination of IP header in frag */ + TEST_ASSERT_EQUAL_INT(0, _set_route_and_nce(&_rem_gb, REM_GB_PFX_LEN)); + netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev, + _mock_netdev_send); + TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN, + GNRC_NETREG_DEMUX_CTX_ALL, + frag)); + + /* should time out */ + TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_nth_frag))); + /* reassembly buffer entry should have been created */ + TEST_ASSERT_NOT_NULL(_first_non_empty_rbuf()); + /* and VRB remains empty */ + TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get( + _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag + )); + TEST_ASSERT_NOT_NULL( + (frag = _create_recv_frag(_test_1st_frag_comp_prev_hop, + sizeof(_test_1st_frag_comp_prev_hop))) + ); + TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN, + GNRC_NETREG_DEMUX_CTX_ALL, + frag)); + /* should time out */ + TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_1st_frag_comp))); + /* reassembly buffer entry should still exist */ + TEST_ASSERT_NOT_NULL((rbuf = _first_non_empty_rbuf())); + /* and VRB still remains empty */ + TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get( + _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag + )); + TEST_ASSERT_EQUAL_INT(_vrbe_base.datagram_size, rbuf->pkt->size); + gnrc_pktbuf_release(rbuf->pkt); + TEST_ASSERT(gnrc_pktbuf_is_sane()); + TEST_ASSERT(gnrc_pktbuf_is_empty()); + /* This is normal reassembly so the rest should have be tested in the + * test for normal reassembly ;-) */ +} + static void test_sixlo_recv__nth_frag(void) { gnrc_sixlowpan_frag_vrb_t *vrbe; @@ -751,6 +1134,7 @@ static void test_sixlo_recv__nth_frag(void) (vrbe = gnrc_sixlowpan_frag_vrb_add(&_vrbe_base, _mock_netif, _rem_l2, sizeof(_rem_l2))) ); + vrbe->super.arrival = xtimer_now_usec(); netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev, _mock_netdev_send); TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN, @@ -762,6 +1146,7 @@ static void test_sixlo_recv__nth_frag(void) TEST_ASSERT(gnrc_pktbuf_is_sane()); TEST_ASSERT(gnrc_pktbuf_is_empty()); _check_vrbe_values(vrbe, mhr_len, NTH_FRAGMENT); + TEST_ASSERT_EQUAL_INT(TEST_NTH_FRAG_SIZE, vrbe->super.current_size); TEST_ASSERT(_target_buf[0] & IEEE802154_FCF_FRAME_PEND); TEST_ASSERT_MESSAGE( memcmp(&_test_nth_frag[TEST_NTH_FRAG_PAYLOAD_POS], @@ -790,6 +1175,7 @@ static void test_sixlo_recv__nth_frag__datagram_complete(void) ); /* simulate current_size only missing the created fragment */ vrbe->super.current_size = _vrbe_base.datagram_size - TEST_NTH_FRAG_SIZE; + vrbe->super.arrival = xtimer_now_usec(); netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev, _mock_netdev_send); TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN, @@ -857,6 +1243,7 @@ static void test_sixlo_recv__nth_frag__duplicate(void) (vrbe = gnrc_sixlowpan_frag_vrb_add(&_vrbe_base, _mock_netif, _rem_l2, sizeof(_rem_l2))) ); + vrbe->super.arrival = xtimer_now_usec(); netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev, _mock_netdev_send); TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN, @@ -906,6 +1293,7 @@ static void test_sixlo_recv__nth_frag__overlap(void) (vrbe = gnrc_sixlowpan_frag_vrb_add(&_vrbe_base, _mock_netif, _rem_l2, sizeof(_rem_l2))) ); + vrbe->super.arrival = xtimer_now_usec(); netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev, _mock_netdev_send); TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN, @@ -972,6 +1360,7 @@ static Test *tests_gnrc_sixlowpan_frag_minfwd_api(void) new_TestFixture(test_minfwd_vrbe_from_route__local_addr), new_TestFixture(test_minfwd_vrbe_from_route__vrb_full), new_TestFixture(test_minfwd_forward__success__1st_frag_sixlo), + new_TestFixture(test_minfwd_forward__success__1st_frag_iphc), new_TestFixture(test_minfwd_forward__success__nth_frag_incomplete), new_TestFixture(test_minfwd_forward__success__nth_frag_complete), new_TestFixture(test_minfwd_forward__ENOMEM__netif_hdr_build_fail), @@ -991,6 +1380,13 @@ static Test *tests_gnrc_sixlowpan_frag_minfwd_integration(void) new_TestFixture(test_sixlo_recv__1st_frag_uncomp), new_TestFixture(test_sixlo_recv__1st_frag_uncomp__no_route), new_TestFixture(test_sixlo_recv__1st_frag_uncomp__after_nth_frag), + new_TestFixture(test_sixlo_recv__1st_frag_comp), + new_TestFixture(test_sixlo_recv__1st_frag_comp__only_iphc), + new_TestFixture(test_sixlo_recv__1st_frag_comp__only_iphc_no_nhc), + new_TestFixture(test_sixlo_recv__1st_frag_comp__no_route), + new_TestFixture(test_sixlo_recv__1st_frag_comp__no_route_only_iphc), + new_TestFixture(test_sixlo_recv__1st_frag_comp__no_refrag), + new_TestFixture(test_sixlo_recv__1st_frag_comp__after_nth_frag), new_TestFixture(test_sixlo_recv__nth_frag), new_TestFixture(test_sixlo_recv__nth_frag__datagram_complete), new_TestFixture(test_sixlo_recv__nth_frag__no_vrbe), @@ -1267,7 +1663,7 @@ static const gnrc_sixlowpan_frag_rb_t *_first_non_empty_rbuf(void) { const gnrc_sixlowpan_frag_rb_t *rbuf = gnrc_sixlowpan_frag_rb_array(); - for (unsigned i = 0; i < GNRC_SIXLOWPAN_FRAG_RBUF_SIZE; i++) { + for (unsigned i = 0; i < CONFIG_GNRC_SIXLOWPAN_FRAG_RBUF_SIZE; i++) { if (!gnrc_sixlowpan_frag_rb_entry_empty(&rbuf[i])) { return rbuf; }