From b31d0dbf065636d05a0f445e65118469e1f4630e Mon Sep 17 00:00:00 2001 From: "Martine S. Lenders" Date: Mon, 4 Nov 2019 16:35:48 +0100 Subject: [PATCH] tests: provide test application for IPHC+VRB behavior --- tests/gnrc_sixlowpan_iphc_w_vrb/Makefile | 15 + tests/gnrc_sixlowpan_iphc_w_vrb/Makefile.ci | 22 ++ tests/gnrc_sixlowpan_iphc_w_vrb/main.c | 321 ++++++++++++++++++ .../gnrc_sixlowpan_iphc_w_vrb/tests/01-run.py | 96 ++++++ 4 files changed, 454 insertions(+) create mode 100644 tests/gnrc_sixlowpan_iphc_w_vrb/Makefile create mode 100644 tests/gnrc_sixlowpan_iphc_w_vrb/Makefile.ci create mode 100644 tests/gnrc_sixlowpan_iphc_w_vrb/main.c create mode 100755 tests/gnrc_sixlowpan_iphc_w_vrb/tests/01-run.py diff --git a/tests/gnrc_sixlowpan_iphc_w_vrb/Makefile b/tests/gnrc_sixlowpan_iphc_w_vrb/Makefile new file mode 100644 index 0000000000000..c5468203b425a --- /dev/null +++ b/tests/gnrc_sixlowpan_iphc_w_vrb/Makefile @@ -0,0 +1,15 @@ +include ../Makefile.tests_common + +USEMODULE += embunit +USEMODULE += gnrc_ipv6_nib_6ln +USEMODULE += gnrc_sixlowpan_iphc +USEMODULE += gnrc_sixlowpan_frag +USEMODULE += gnrc_sixlowpan_frag_vrb +USEMODULE += netdev_ieee802154 +USEMODULE += netdev_test +USEMODULE += od + +# we don't need all this packet buffer space so reduce it a little +CFLAGS += -DTEST_SUITES -DGNRC_PKTBUF_SIZE=2048 + +include $(RIOTBASE)/Makefile.include diff --git a/tests/gnrc_sixlowpan_iphc_w_vrb/Makefile.ci b/tests/gnrc_sixlowpan_iphc_w_vrb/Makefile.ci new file mode 100644 index 0000000000000..d792290668293 --- /dev/null +++ b/tests/gnrc_sixlowpan_iphc_w_vrb/Makefile.ci @@ -0,0 +1,22 @@ +BOARD_INSUFFICIENT_MEMORY := \ + arduino-duemilanove \ + arduino-leonardo \ + arduino-mega2560 \ + arduino-nano \ + arduino-uno \ + atmega328p \ + chronos \ + i-nucleo-lrwan1 \ + msb-430 \ + msb-430h \ + nucleo-f030r8 \ + nucleo-f031k6 \ + nucleo-f042k6 \ + nucleo-l031k6 \ + nucleo-l053r8 \ + stm32f030f4-demo \ + stm32f0discovery \ + stm32l0538-disco \ + telosb \ + waspmote-pro \ + # diff --git a/tests/gnrc_sixlowpan_iphc_w_vrb/main.c b/tests/gnrc_sixlowpan_iphc_w_vrb/main.c new file mode 100644 index 0000000000000..3fb71a3d30c1e --- /dev/null +++ b/tests/gnrc_sixlowpan_iphc_w_vrb/main.c @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2019 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup tests + * @{ + * + * @file + * @brief Tests 6LoWPAN fragmentation handling of gnrc stack. + * + * @author Martine S. Lenders + * + * @} + */ + +#include "embUnit.h" +#include "net/gnrc.h" +#include "net/gnrc/netif/ieee802154.h" +#include "net/gnrc/sixlowpan.h" +#include "net/gnrc/sixlowpan/frag/rb.h" +#include "net/gnrc/sixlowpan/frag/vrb.h" +#include "net/gnrc/ipv6/nib.h" +#include "net/netdev_test.h" +#include "thread.h" +#include "xtimer.h" + +#define TEST_DST { 0x5a, 0x9d, 0x93, 0x86, 0x22, 0x08, 0x65, 0x79 } +#define TEST_SRC { 0x2a, 0xab, 0xdc, 0x15, 0x54, 0x01, 0x64, 0x79 } +#define TEST_6LO_PAYLOAD { \ + /* 6LoWPAN, Src: 2001:db8::1, Dest: 2001:db8::2 + * Fragmentation Header + * 1100 0... = Pattern: First fragment (0x18) + * Datagram size: 188 + * Datagram tag: 0x000f + * IPHC Header + * 011. .... = Pattern: IP header compression (0x03) + * ...1 1... .... .... = Version, traffic class, and flow label compressed (0x3) + * .... .0.. .... .... = Next header: Inline + * .... ..10 .... .... = Hop limit: 64 (0x2) + * .... .... 0... .... = Context identifier extension: False + * .... .... .0.. .... = Source address compression: Stateless + * .... .... ..00 .... = Source address mode: Inline (0x0000) + * .... .... .... 0... = Multicast address compression: False + * .... .... .... .0.. = Destination address compression: Stateless + * .... .... .... ..00 = Destination address mode: Inline (0x0000) + * Next header: ICMPv6 (0x3a) + * Source: 2001:db8::1 + * Destination: 2001:db8::2 */ \ + 0xc0, 0xbc, 0x00, 0x0f, \ + /* IPHC Header + * 011. .... = Pattern: IP header compression (0x03) + * ...1 1... .... .... = Version, traffic class, and flow label compressed (0x3) + * .... .0.. .... .... = Next header: Inline + * .... ..10 .... .... = Hop limit: 64 (0x2) + * .... .... 0... .... = Context identifier extension: False + * .... .... .0.. .... = Source address compression: Stateless + * .... .... ..00 .... = Source address mode: Inline (0x0000) + * .... .... .... 0... = Multicast address compression: False + * .... .... .... .0.. = Destination address compression: Stateless + * .... .... .... ..00 = Destination address mode: Inline (0x0000) + * Next header: ICMPv6 (0x3a) */ \ + 0x7a, 0x00, 0x3a, \ + /* Source: 2001:db8::1 */ \ + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, \ + /* Destination: 2001:db8::2 */ \ + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, \ + /* Internet Control Message Protocol v6 + * Type: Echo (ping) request (128) + * Code: 0 + * Checksum: 0x8ea0 + * Identifier: 0x238f + * Sequence: 2 + * [No response seen] + * Data (140 bytes) + * Data: 9d4bb21c5353535353535353535353535353535353535353… + */ \ + 0x80, 0x00, 0x8e, 0xa0, 0x23, 0x8f, 0x00, 0x02, \ + 0x9d, 0x4b, 0xb2, 0x1c, 0x53, 0x53, 0x53, 0x53, \ + 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, \ + 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, \ + 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, \ + 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, \ + 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, \ + } +#define TEST_TAG (0x000f) +#define TEST_TGT_IPV6 { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x48, 0x3d, 0x1d, 0x0c, 0x98, 0x31, 0x58, 0xae } + +const uint8_t _test_src[] = TEST_SRC; +const uint8_t _test_dst[] = TEST_DST; +const uint8_t _test_6lo_payload[] = TEST_6LO_PAYLOAD; +const ipv6_addr_t _test_tgt_ipv6 = { .u8 = TEST_TGT_IPV6 }; + +static char _mock_netif_stack[THREAD_STACKSIZE_DEFAULT]; +static netdev_test_t _mock_dev; +static gnrc_netif_t *_mock_netif; + +void _set_up(void) +{ + /* Add default route for the VRB entry created from */ + gnrc_ipv6_nib_ft_add(NULL, 0, &_test_tgt_ipv6, _mock_netif->pid, 0); +} + +void _tear_down(void) +{ + gnrc_ipv6_nib_ft_del(NULL, 0); + gnrc_sixlowpan_frag_rb_reset(); + gnrc_sixlowpan_frag_vrb_reset(); +} + +gnrc_pktsnip_t *_create_fragment(void) +{ + gnrc_pktsnip_t *res = gnrc_netif_hdr_build(_test_src, sizeof(_test_src), + _test_dst, sizeof(_test_dst)); + if (res == NULL) { + return NULL; + } + gnrc_netif_hdr_set_netif(res->data, _mock_netif); + res = gnrc_pktbuf_add(res, _test_6lo_payload, sizeof(_test_6lo_payload), + GNRC_NETTYPE_SIXLOWPAN); + return res; +} + +static unsigned _dispatch_to_6lowpan(gnrc_pktsnip_t *pkt) +{ + unsigned res = gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN, + GNRC_NETREG_DEMUX_CTX_ALL, + pkt); + thread_yield_higher(); + return res; +} + +static bool _rb_is_empty(void) +{ + const gnrc_sixlowpan_frag_rb_t *rb = gnrc_sixlowpan_frag_rb_array(); + unsigned res = 0; + + for (unsigned i = 0; i < GNRC_SIXLOWPAN_FRAG_RBUF_SIZE; i++) { + res += gnrc_sixlowpan_frag_rb_entry_empty(&rb[i]); + } + return res; +} + +static void _test_no_vrbe_but_rbe_exists(void) +{ + const gnrc_sixlowpan_frag_rb_t *rb = gnrc_sixlowpan_frag_rb_array(); + unsigned rbs = 0; + + /* VRB entry does not exist */ + TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get(_test_src, + sizeof(_test_src), + TEST_TAG)); + /* and one reassembly buffer entry exists with the source and tag exists */ + for (unsigned i = 0; i < GNRC_SIXLOWPAN_FRAG_RBUF_SIZE; i++) { + if (!gnrc_sixlowpan_frag_rb_entry_empty(&rb[i])) { + rbs++; + TEST_ASSERT_EQUAL_INT(sizeof(_test_src), rb[i].super.src_len); + TEST_ASSERT_EQUAL_INT(0, memcmp(rb[i].super.src, _test_src, + rb[i].super.src_len)); + TEST_ASSERT_EQUAL_INT(TEST_TAG, rb[i].super.tag); + /* release packet for packet buffer check */ + gnrc_pktbuf_release(rb[i].pkt); + + } + } + TEST_ASSERT_EQUAL_INT(1, rbs); +} + +static void test_recv__success(void) +{ + gnrc_pktsnip_t *pkt = _create_fragment(); + + TEST_ASSERT_NOT_NULL(pkt); + TEST_ASSERT_EQUAL_INT(1, _dispatch_to_6lowpan(pkt)); + /* A VRB entry exists was created but deleted due to -ENOTSUP being + * returned by gnrc_sixlowpan_iphc.c:_forward_frag() + * but the reassembly buffer is empty */ + TEST_ASSERT(_rb_is_empty()); + TEST_ASSERT(gnrc_pktbuf_is_empty()); +} + +static void test_recv__no_route(void) +{ + gnrc_pktsnip_t *pkt = _create_fragment(); + + gnrc_ipv6_nib_ft_del(NULL, 0); + TEST_ASSERT_NOT_NULL(pkt); + TEST_ASSERT_EQUAL_INT(1, _dispatch_to_6lowpan(pkt)); + _test_no_vrbe_but_rbe_exists(); + TEST_ASSERT(gnrc_pktbuf_is_empty()); +} + +static void test_recv__vrb_full(void) +{ + gnrc_pktsnip_t *pkt = _create_fragment(); + gnrc_sixlowpan_frag_rb_base_t base = { + .src = TEST_SRC, + .src_len = sizeof(_test_src), + .tag = TEST_TAG, + }; + + TEST_ASSERT_NOT_NULL(pkt); + /* Fill up VRB */ + for (unsigned i = 0; i < GNRC_SIXLOWPAN_FRAG_VRB_SIZE; i++) { + base.tag++; + base.arrival = xtimer_now_usec(); + TEST_ASSERT_NOT_NULL(gnrc_sixlowpan_frag_vrb_add(&base, _mock_netif, + _test_dst, + sizeof(_test_dst))); + } + TEST_ASSERT_EQUAL_INT(1, _dispatch_to_6lowpan(pkt)); + _test_no_vrbe_but_rbe_exists(); + TEST_ASSERT(gnrc_pktbuf_is_empty()); +} + +static void test_recv__pkt_held(void) +{ + gnrc_pktsnip_t *pkt = _create_fragment(); + + TEST_ASSERT_NOT_NULL(pkt); + gnrc_pktbuf_hold(pkt, 1); + TEST_ASSERT_EQUAL_INT(1, _dispatch_to_6lowpan(pkt)); + /* A VRB entry exists was created but deleted due to -ENOTSUP being + * returned by gnrc_sixlowpan_iphc.c:_forward_frag() + * but the reassembly buffer is empty */ + TEST_ASSERT(_rb_is_empty()); + gnrc_pktbuf_release(pkt); + TEST_ASSERT(gnrc_pktbuf_is_empty()); +} + +static void run_unittests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_recv__success), + new_TestFixture(test_recv__no_route), + new_TestFixture(test_recv__vrb_full), + new_TestFixture(test_recv__pkt_held), + }; + + EMB_UNIT_TESTCALLER(sixlo_iphc_vrb_tests, _set_up, _tear_down, fixtures); + TESTS_START(); + TESTS_RUN((Test *)&sixlo_iphc_vrb_tests); + TESTS_END(); +} + +static int _get_netdev_device_type(netdev_t *netdev, void *value, size_t max_len) +{ + assert(max_len == sizeof(uint16_t)); + (void)netdev; + + *((uint16_t *)value) = NETDEV_TYPE_IEEE802154; + return sizeof(uint16_t); +} + +static int _get_netdev_proto(netdev_t *netdev, void *value, size_t max_len) +{ + assert(max_len == sizeof(gnrc_nettype_t)); + (void)netdev; + + *((gnrc_nettype_t *)value) = GNRC_NETTYPE_SIXLOWPAN; + return sizeof(gnrc_nettype_t); +} + +static int _get_netdev_max_pdu_size(netdev_t *netdev, void *value, + size_t max_len) +{ + assert(max_len == sizeof(uint16_t)); + (void)netdev; + + *((uint16_t *)value) = sizeof(_test_6lo_payload); + return sizeof(uint16_t); +} + +static int _get_netdev_src_len(netdev_t *netdev, void *value, size_t max_len) +{ + (void)netdev; + assert(max_len == sizeof(uint16_t)); + *((uint16_t *)value) = sizeof(_test_dst); + return sizeof(uint16_t); +} + +static int _get_netdev_addr_long(netdev_t *netdev, void *value, size_t max_len) +{ + (void)netdev; + assert(max_len >= sizeof(_test_dst)); + memcpy(value, _test_dst, sizeof(_test_dst)); + return sizeof(_test_dst); +} + +static void _init_mock_netif(void) +{ + netdev_test_setup(&_mock_dev, NULL); + netdev_test_set_get_cb(&_mock_dev, NETOPT_DEVICE_TYPE, + _get_netdev_device_type); + netdev_test_set_get_cb(&_mock_dev, NETOPT_PROTO, + _get_netdev_proto); + netdev_test_set_get_cb(&_mock_dev, NETOPT_MAX_PDU_SIZE, + _get_netdev_max_pdu_size); + netdev_test_set_get_cb(&_mock_dev, NETOPT_SRC_LEN, + _get_netdev_src_len); + netdev_test_set_get_cb(&_mock_dev, NETOPT_ADDRESS_LONG, + _get_netdev_addr_long); + _mock_netif = gnrc_netif_ieee802154_create( + _mock_netif_stack, THREAD_STACKSIZE_DEFAULT, GNRC_NETIF_PRIO, + "mock_netif", (netdev_t *)&_mock_dev); + thread_yield_higher(); +} + +int main(void) +{ + _init_mock_netif(); + run_unittests(); + return 0; +} diff --git a/tests/gnrc_sixlowpan_iphc_w_vrb/tests/01-run.py b/tests/gnrc_sixlowpan_iphc_w_vrb/tests/01-run.py new file mode 100755 index 0000000000000..f6bb07dcc4492 --- /dev/null +++ b/tests/gnrc_sixlowpan_iphc_w_vrb/tests/01-run.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2019 Freie Universität Berlin +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. + +import sys +from testrunner import run + + +def testfunc(child): + debug_enabled = child.expect([ + r"OK \(\d+ tests\)", + r"6lo iphc: VRB present, trying to create entry for dst 2001:db8::2", + ]) + if debug_enabled: + # only import scapy dependency if DEBUG_ENABLE=1 + from scapy.all import SixLoWPAN, LoWPANFragmentationFirst, \ + LoWPAN_IPHC, IPv6, ICMPv6EchoRequest + + # test_recv__success + child.expect_exact("6lo iphc: found route, trying to forward") + child.expect( + r"6lo iphc: Do not know how to forward fragment from " + r"\((?P[a-fA-F0-9:]+), (?P\d+)\) to " + r"\((?P[a-fA-F0-9:]+), (?P\d+)\)" + ) + vrb_src_tag = int(child.match.group("vrb_src_tag")) + child.expect_exact("Original fragmentation header:") + child.expect("(?P( [A-Fa-f0-9]{2})+)") + frag = SixLoWPAN(bytes.fromhex(child.match.group("hex"))) + assert LoWPANFragmentationFirst in frag + assert frag[LoWPANFragmentationFirst].datagramTag == vrb_src_tag + stream = "" + child.expect_exact("IPHC headers + payload:") + for _ in range(7): + child.expect("(?P( [A-Fa-f0-9]{2})+)") + stream += child.match.group("hex") + iphc = SixLoWPAN(bytes.fromhex(stream)) + assert LoWPAN_IPHC in iphc + assert IPv6 in iphc # The IPHC header after compression + assert ICMPv6EchoRequest in iphc + # check against fields of original fragment specified in main.c + assert iphc[LoWPAN_IPHC].tf == 0x3 + assert iphc[LoWPAN_IPHC].nh == 0b0 # Inline (ICMPv6) + assert iphc[LoWPAN_IPHC].hlim == 0b00 # Inline as it was decremented + assert not iphc[LoWPAN_IPHC].cid + assert iphc[LoWPAN_IPHC].sac == 0b0 # Stateless + assert iphc[LoWPAN_IPHC].sam == 0b00 # Inline + assert not iphc[LoWPAN_IPHC].m # not multicast + assert iphc[LoWPAN_IPHC].dac == 0b0 # Stateless + assert iphc[LoWPAN_IPHC].dam == 0b00 # Inline + assert iphc[IPv6].src == "2001:db8::1" + assert iphc[IPv6].dst == "2001:db8::2" + assert iphc[IPv6].hlim == (64 - 1) # hop limit was decremented + assert iphc[ICMPv6EchoRequest].code == 0 + assert iphc[ICMPv6EchoRequest].cksum == 0x8ea0 + assert iphc[ICMPv6EchoRequest].id == 0x238f + assert iphc[ICMPv6EchoRequest].seq == 2 + assert iphc[ICMPv6EchoRequest].data.startswith( + bytes.fromhex("9d4bb21c5353535353535353535353535353535353535353") + ) + + # test_recv__no_route + child.expect_exact( + "6lo iphc: VRB present, trying to create entry for dst 2001:db8::2" + ) + child.expect_exact( + "6lo iphc: no route found, reassemble datagram normally" + ) + + # test_recv__vrb_full + child.expect_exact( + "6lo iphc: VRB present, trying to create entry for dst 2001:db8::2" + ) + child.expect_exact( + "6lo iphc: no route found, reassemble datagram normally" + ) + + # test_recv__pkt_held + child.expect_exact("6lo iphc: found route, trying to forward") + child.expect( + r"6lo iphc: Do not know how to forward fragment from " + r"\((?P[a-fA-F0-9:]+), (?P\d+)\) to " + r"\((?P[a-fA-F0-9:]+), (?P\d+)\)" + ) + child.expect_exact("Original fragmentation header:") + child.expect_exact("IPHC headers + payload:") + child.expect(r"OK \((\d+) tests\)") + assert int(child.match.group(1)) >= 4 + + +if __name__ == "__main__": + sys.exit(run(testfunc))