diff --git a/tests/gnrc_ipv6_fwd_w_sub/Makefile b/tests/gnrc_ipv6_fwd_w_sub/Makefile new file mode 100644 index 0000000000..d3c7c07423 --- /dev/null +++ b/tests/gnrc_ipv6_fwd_w_sub/Makefile @@ -0,0 +1,27 @@ +DEVELHELP := 1 +include ../Makefile.tests_common + +BOARD_INSUFFICIENT_MEMORY := arduino-duemilanove arduino-leonardo \ + arduino-mega2560 arduino-nano arduino-uno chronos \ + i-nucleo-lrwan1 msb-430 msb-430h nucleo-f030r8 \ + nucleo-f031k6 nucleo-f042k6 nucleo-l031k6 \ + nucleo-l053r8 stm32f0discovery telosb \ + waspmote-pro wsn430-v1_3b wsn430-v1_4 z1 + +USEMODULE += gnrc_ipv6_router_default +USEMODULE += gnrc_netif +USEMODULE += gnrc_pktbuf_cmd +USEMODULE += netdev_eth +USEMODULE += netdev_test +USEMODULE += od +USEMODULE += ps +USEMODULE += shell +USEMODULE += shell_commands +USEMODULE += xtimer + +CFLAGS += -DGNRC_PKTBUF_SIZE=512 +CFLAGS += -DTEST_SUITES + +TEST_ON_CI_WHITELIST += all + +include $(RIOTBASE)/Makefile.include diff --git a/tests/gnrc_ipv6_fwd_w_sub/README.md b/tests/gnrc_ipv6_fwd_w_sub/README.md new file mode 100644 index 0000000000..681c6f4739 --- /dev/null +++ b/tests/gnrc_ipv6_fwd_w_sub/README.md @@ -0,0 +1,10 @@ +# Regression test for forwarding IPv6 packets with a sniffing subscriber + +This only tests if a router can sniff its own forwarded IPv6 traffic. It is +**not** a full IPv6 test suite. + +## Usage + +``` +BOARD='' make flash test +``` diff --git a/tests/gnrc_ipv6_fwd_w_sub/common.h b/tests/gnrc_ipv6_fwd_w_sub/common.h new file mode 100644 index 0000000000..33620c4cee --- /dev/null +++ b/tests/gnrc_ipv6_fwd_w_sub/common.h @@ -0,0 +1,48 @@ +/* + * 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 (0xce) +#define _LL1 (0xab) +#define _LL2 (0xfe) +#define _LL3 (0xad) +#define _LL4 (0xf7) +#define _LL5 (0x26) + +extern gnrc_netif_t *_mock_netif; + +void _tests_init(void); + + +#ifdef __cplusplus +} +#endif + +#endif /* COMMON_H */ +/** @} */ diff --git a/tests/gnrc_ipv6_fwd_w_sub/main.c b/tests/gnrc_ipv6_fwd_w_sub/main.c new file mode 100644 index 0000000000..12583fc9ba --- /dev/null +++ b/tests/gnrc_ipv6_fwd_w_sub/main.c @@ -0,0 +1,202 @@ +/* + * 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 Regression test to test subscribing to IPv6 packets while + * forwarding + * + * @author Martine S. Lenders + * + * @} + */ + +#include +#include +#include + +#include "kernel_types.h" +#include "msg.h" +#include "net/ethernet/hdr.h" +#include "net/ipv6/addr.h" +#include "net/udp.h" +#include "net/gnrc.h" +#include "net/gnrc.h" +#include "net/gnrc/ipv6/nib.h" +#include "net/netdev_test.h" +#include "od.h" +#include "sched.h" +#include "shell.h" +#include "thread.h" +#include "xtimer.h" + +#include "common.h" + +#define DUMPER_QUEUE_SIZE (16) +#define NBR_MAC { 0x57, 0x44, 0x33, 0x22, 0x11, 0x00, } +#define NBR_LINK_LOCAL { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x55, 0x44, 0x33, 0xff, 0xfe, 0x22, 0x11, 0x00, } +#define DST { 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0xab, 0xcd, \ + 0x55, 0x44, 0x33, 0xff, 0xfe, 0x22, 0x11, 0x00, } +#define DST_PFX_LEN (64U) +/* IPv6 header + payload: version+TC FL: 0 plen: 16 NH:17 HL:64 */ +#define L2_PAYLOAD { 0x60, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x40, \ + /* source: random address */ \ + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0xef, 0x01, \ + 0x02, 0xca, 0x4b, 0xef, 0xf4, 0xc2, 0xde, 0x01, \ + /* destination: DST */ \ + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0xab, 0xcd, \ + 0x55, 0x44, 0x33, 0xff, 0xfe, 0x22, 0x11, 0x00, \ + /* random payload of length 16 */ \ + 0x54, 0xb8, 0x59, 0xaf, 0x3a, 0xb4, 0x5c, 0x85, \ + 0x1e, 0xce, 0xe2, 0xeb, 0x05, 0x4e, 0xa3, 0x85, } + +static const uint8_t _nbr_mac[] = NBR_MAC; +static const ipv6_addr_t _nbr_link_local = { .u8 = NBR_LINK_LOCAL }; +static const ipv6_addr_t _dst = { .u8 = DST }; +static const uint8_t _l2_payload[] = L2_PAYLOAD; +static gnrc_netreg_entry_t _dumper; +static msg_t _dumper_queue[DUMPER_QUEUE_SIZE]; +static char _dumper_stack[THREAD_STACKSIZE_MAIN]; + +static int _run_test(int argc, char **argv); + +static const shell_command_t shell_commands[] = { + { "run_test", "runs the regression test", _run_test }, + { NULL, NULL, NULL } +}; + +static void *_dumper_thread(void *arg) +{ + (void)arg; + msg_init_queue(_dumper_queue, DUMPER_QUEUE_SIZE); + + while (1) { + msg_t msg; + + msg_receive(&msg); + if (msg.type == GNRC_NETAPI_MSG_TYPE_RCV) { + gnrc_pktsnip_t *pkt = msg.content.ptr; + + /* wait a bit to give IPv6 time to handle the packet */ + xtimer_usleep(500); + /* dump pkt. Should be equal to _l2_payloa*/ + puts("I got a subscription!"); + od_hex_dump(pkt->data, pkt->size, OD_WIDTH_DEFAULT); + gnrc_pktbuf_release(pkt); + } + else if (msg.type == GNRC_NETAPI_MSG_TYPE_SND) { + /* we are not interested in sent packets from the node itself; + * just release it */ + gnrc_pktbuf_release(msg.content.ptr); + } + } + + return NULL; +} + +static int _dump_etherframe(netdev_t *dev, const iolist_t *iolist) +{ + static uint8_t outbuf[sizeof(ethernet_hdr_t) + sizeof(_l2_payload)]; + size_t outbuf_len = 0U; + + (void)dev; + while (iolist) { + if ((outbuf_len + iolist->iol_len) > sizeof(outbuf)) { + printf("Ignoring packet: %u > %u\n", + (unsigned)(outbuf_len + iolist->iol_len), + (unsigned)sizeof(outbuf)); + /* ignore larger packets */ + return outbuf_len; + } + memcpy(&outbuf[outbuf_len], iolist->iol_base, iolist->iol_len); + outbuf_len += iolist->iol_len; + iolist = iolist->iol_next; + } + + puts("Forwarded Ethernet frame:"); + od_hex_dump(outbuf, outbuf_len, OD_WIDTH_DEFAULT); + return outbuf_len; +} + +static gnrc_pktsnip_t *_build_recvd_pkt(void) +{ + gnrc_pktsnip_t *netif; + gnrc_pktsnip_t *pkt; + gnrc_netif_hdr_t *netif_hdr; + + netif = gnrc_netif_hdr_build(NULL, 0, NULL, 0); + assert(netif); + netif_hdr = netif->data; + netif_hdr->if_pid = _mock_netif->pid; + pkt = gnrc_pktbuf_add(netif, _l2_payload, sizeof(_l2_payload), + GNRC_NETTYPE_IPV6); + assert(pkt); + return pkt; +} + +static int _run_test(int argc, char **argv) +{ + int subscribers; + (void)argc; + (void)argv; + if (_dumper.target.pid <= KERNEL_PID_UNDEF) { + gnrc_netreg_entry_init_pid(&_dumper, GNRC_NETREG_DEMUX_CTX_ALL, + thread_create(_dumper_stack, + sizeof(_dumper_stack), + THREAD_PRIORITY_MAIN - 1, 0, + _dumper_thread, NULL, + "dumper")); + assert(_dumper.target.pid > KERNEL_PID_UNDEF); + /* give dumper thread time to run */ + xtimer_usleep(200); + } + /* activate dumping of sent ethernet frames */ + netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev, + _dump_etherframe); + /* first, test forwarding without subscription */ + subscribers = gnrc_netapi_dispatch_receive(GNRC_NETTYPE_IPV6, + GNRC_NETREG_DEMUX_CTX_ALL, + _build_recvd_pkt()); + /* only IPv6 should be subscribed at the moment */ + assert(subscribers == 1); + /* subscribe dumper thread for any IPv6 packets */ + gnrc_netreg_register(GNRC_NETTYPE_IPV6, &_dumper); + /* now test forwarding with subscription */ + subscribers = gnrc_netapi_dispatch_receive(GNRC_NETTYPE_IPV6, + GNRC_NETREG_DEMUX_CTX_ALL, + _build_recvd_pkt()); + /* assert 2 subscribers: IPv6 and gnrc_pktdump as registered above */ + assert(subscribers == 2); + return 0; +} + +int main(void) +{ + int res; + + /* initialize mock interface */ + _tests_init(); + /* define neighbor to forward to */ + res = gnrc_ipv6_nib_nc_set(&_nbr_link_local, _mock_netif->pid, + _nbr_mac, sizeof(_nbr_mac)); + assert(res == 0); + /* set route to neighbor */ + res = gnrc_ipv6_nib_ft_add(&_dst, DST_PFX_LEN, &_nbr_link_local, + _mock_netif->pid, 0); + assert(res == 0); + /* start shell */ + char line_buf[SHELL_DEFAULT_BUFSIZE]; + shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); + + /* should be never reached */ + return 0; +} diff --git a/tests/gnrc_ipv6_fwd_w_sub/mockup_netif.c b/tests/gnrc_ipv6_fwd_w_sub/mockup_netif.c new file mode 100644 index 0000000000..0ede3603c8 --- /dev/null +++ b/tests/gnrc_ipv6_fwd_w_sub/mockup_netif.c @@ -0,0 +1,80 @@ +/* + * 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 "net/gnrc.h" +#include "net/ethernet.h" +#include "net/gnrc/ipv6/nib.h" +#include "net/gnrc/netif/ethernet.h" +#include "net/netdev_test.h" +#include "thread.h" + +gnrc_netif_t *_mock_netif = NULL; + +static netdev_test_t _mock_netdev; +static char _mock_netif_stack[THREAD_STACKSIZE_MAIN]; + +static 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_ETHERNET; + return sizeof(uint16_t); +} + +static 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) = ETHERNET_DATA_LEN; + return sizeof(uint16_t); +} + +static int _get_address(netdev_t *dev, void *value, size_t max_len) +{ + static const uint8_t addr[] = { _LL0, _LL1, _LL2, _LL3, _LL4, _LL5 }; + + (void)dev; + assert(max_len >= sizeof(addr)); + memcpy(value, addr, sizeof(addr)); + return sizeof(addr); +} + +void _tests_init(void) +{ + 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_MAX_PDU_SIZE, + _get_max_packet_size); + netdev_test_set_get_cb(&_mock_netdev, NETOPT_ADDRESS, + _get_address); + _mock_netif = gnrc_netif_ethernet_create( + _mock_netif_stack, THREAD_STACKSIZE_DEFAULT, GNRC_NETIF_PRIO, + "mockup_eth", &_mock_netdev.netdev + ); + 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); + /* we do not want to test for SLAAC here so just assure the configured + * address is valid */ + assert(!ipv6_addr_is_unspecified(&_mock_netif->ipv6.addrs[0])); + _mock_netif->ipv6.addrs_flags[0] &= ~GNRC_NETIF_IPV6_ADDRS_FLAGS_STATE_MASK; + _mock_netif->ipv6.addrs_flags[0] |= GNRC_NETIF_IPV6_ADDRS_FLAGS_STATE_VALID; +} + +/** @} */ diff --git a/tests/gnrc_ipv6_fwd_w_sub/tests/01-run.py b/tests/gnrc_ipv6_fwd_w_sub/tests/01-run.py new file mode 100755 index 0000000000..be26456e31 --- /dev/null +++ b/tests/gnrc_ipv6_fwd_w_sub/tests/01-run.py @@ -0,0 +1,44 @@ +#!/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): + child.sendline("run_test") + child.expect(r"Forwarded Ethernet frame:") + child.expect(r"00000000 57 44 33 22 11 00 CE AB FE AD F7 26 86 DD 60 00") + child.expect(r"00000010 00 00 00 10 11 3F 20 01 0D B8 00 00 EF 01 02 CA") + child.expect(r"00000020 4B EF F4 C2 DE 01 20 01 0D B8 00 00 AB CD 55 44") + child.expect(r"00000030 33 FF FE 22 11 00 54 B8 59 AF 3A B4 5C 85 1E CE") + child.expect(r"00000040 E2 EB 05 4E A3 85") + child.expect(r"Forwarded Ethernet frame:") + child.expect(r"00000000 57 44 33 22 11 00 CE AB FE AD F7 26 86 DD 60 00") + child.expect(r"00000010 00 00 00 10 11 3F 20 01 0D B8 00 00 EF 01 02 CA") + child.expect(r"00000020 4B EF F4 C2 DE 01 20 01 0D B8 00 00 AB CD 55 44") + child.expect(r"00000030 33 FF FE 22 11 00 54 B8 59 AF 3A B4 5C 85 1E CE") + child.expect(r"00000040 E2 EB 05 4E A3 85") + child.expect(r"I got a subscription!") + child.expect(r"00000000 60 00 00 00 00 10 11 40 20 01 0D B8 00 00 EF 01") + child.expect(r"00000010 02 CA 4B EF F4 C2 DE 01 20 01 0D B8 00 00 AB CD") + child.expect(r"00000020 55 44 33 FF FE 22 11 00 54 B8 59 AF 3A B4 5C 85") + child.expect(r"00000030 1E CE E2 EB 05 4E A3 85") + child.sendline("pktbuf") + child.expect(r"packet buffer: first byte: (0x[0-9a-fA-F]+), " + r"last byte: 0x[0-9a-fA-F]+ \(size: (\d+)\)") + start_addr = child.match.group(1) + size = child.match.group(2) + child.expect(r" position of last byte used: \d+") + child.expect(r"~ unused: {} \(next: (\(nil\)|0(x0+)?), size: +{}\) ~" + .format(start_addr, size)) + + +if __name__ == "__main__": + sys.exit(run(testfunc, timeout=5, echo=True))