tests: add test case for pointer confusion

When subscribing to IPv6 packets on a router for sniffing, the NETIF
header is released prematurely, because of a wrong
`gnrc_pktbuf_start_write()` call. This test aims to reproduce this
error case.
This commit is contained in:
Martine S. Lenders 2019-06-25 19:39:17 +02:00
parent 56085b10a0
commit b8269316e6
6 changed files with 411 additions and 0 deletions

View File

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

View File

@ -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='<your choice>' make flash test
```

View File

@ -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 <m.lenders@fu-berlin.de>
*/
#ifndef COMMON_H
#define COMMON_H
#include <stdio.h>
#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 */
/** @} */

View File

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

View File

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

View File

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