/* * 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 "test_utils/expect.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 _netif; 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 < CONFIG_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 < CONFIG_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 < CONFIG_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) { expect(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) { expect(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) { expect(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; expect(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; expect(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); gnrc_netif_ieee802154_create(&_netif, _mock_netif_stack, THREAD_STACKSIZE_DEFAULT, GNRC_NETIF_PRIO, "mock_netif", (netdev_t *)&_mock_dev); _mock_netif = &_netif; thread_yield_higher(); } int main(void) { _init_mock_netif(); run_unittests(); return 0; }