1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

tests: provide test application for IPHC+VRB behavior

This commit is contained in:
Martine S. Lenders 2019-11-04 16:35:48 +01:00
parent a689f51e9a
commit 3273602f24
No known key found for this signature in database
GPG Key ID: CCD317364F63286F
5 changed files with 456 additions and 0 deletions

View File

@ -717,6 +717,8 @@ 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);
/* the following is just debug output for testing without any forwarding
* scheme */
DEBUG("6lo iphc: Do not know how to forward fragment from (%s, %u) ",
gnrc_netif_addr_to_str(vrbe->super.src, vrbe->super.src_len,
addr_str), vrbe->super.tag);

View File

@ -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

View File

@ -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 \
#

View File

@ -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 <m.lenders@fu-berlin.de>
*
* @}
*/
#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;
}

View File

@ -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<vrb_src>[a-fA-F0-9:]+), (?P<vrb_src_tag>\d+)\) to "
r"\((?P<vrb_dst>[a-fA-F0-9:]+), (?P<vrb_dst_tag>\d+)\)"
)
vrb_src_tag = int(child.match.group("vrb_src_tag"))
child.expect_exact("Original fragmentation header:")
child.expect("(?P<hex>( [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<hex>( [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<vrb_src>[a-fA-F0-9:]+), (?P<vrb_src_tag>\d+)\) to "
r"\((?P<vrb_dst>[a-fA-F0-9:]+), (?P<vrb_dst_tag>\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))