diff --git a/tests/gnrc_ipv6_nib_dns/Makefile b/tests/gnrc_ipv6_nib_dns/Makefile new file mode 100644 index 0000000000..ea4515e19d --- /dev/null +++ b/tests/gnrc_ipv6_nib_dns/Makefile @@ -0,0 +1,45 @@ +include ../Makefile.tests_common + +RIOTBASE ?= $(CURDIR)/../.. + +export TAP ?= tap0 + +USEMODULE += sock_dns +USEMODULE += gnrc_sock_udp +USEMODULE += gnrc_ipv6_default +USEMODULE += gnrc_ipv6_nib_dns +# use Ethernet as link-layer protocol +ifeq (native,$(BOARD)) + TERMFLAGS ?= $(TAP) +else + ETHOS_BAUDRATE ?= 115200 + CFLAGS += -DETHOS_BAUDRATE=$(ETHOS_BAUDRATE) + TERMDEPS += ethos + TERMPROG ?= sudo $(RIOTTOOLS)/ethos/ethos + TERMFLAGS ?= $(TAP) $(PORT) $(ETHOS_BAUDRATE) +endif +USEMODULE += auto_init_gnrc_netif + +USEMODULE += shell +USEMODULE += shell_commands + +USEMODULE += posix_inet + +LOW_MEMORY_BOARDS := nucleo-f334r8 msb-430 msb-430h + +ifeq ($(BOARD),$(filter $(BOARD),$(LOW_MEMORY_BOARDS))) + CFLAGS += -DGNRC_PKTBUF_SIZE=512 -DCONFIG_GNRC_NETIF_IPV6_ADDRS_NUMOF=2 \ + -DGNRC_NETIF_IPV6_GROUPS_NUMOF=2 -DGNRC_IPV6_NIB_NUMOF=1 \ + -DNRC_IPV6_NIB_OFFL_NUMOF=1 +endif + +# The test requires some setup and to be run as root +# So it cannot currently be run +TEST_ON_CI_BLACKLIST += all + +.PHONY: ethos + +ethos: + $(Q)env -u CC -u CFLAGS make -C $(RIOTTOOLS)/ethos + +include $(RIOTBASE)/Makefile.include diff --git a/tests/gnrc_ipv6_nib_dns/Makefile.board.dep b/tests/gnrc_ipv6_nib_dns/Makefile.board.dep new file mode 100644 index 0000000000..b595b8605c --- /dev/null +++ b/tests/gnrc_ipv6_nib_dns/Makefile.board.dep @@ -0,0 +1,6 @@ +# Put board specific dependencies here +ifeq (native,$(BOARD)) + USEMODULE += netdev_tap +else + USEMODULE += stdio_ethos +endif diff --git a/tests/gnrc_ipv6_nib_dns/Makefile.ci b/tests/gnrc_ipv6_nib_dns/Makefile.ci new file mode 100644 index 0000000000..af68750ef4 --- /dev/null +++ b/tests/gnrc_ipv6_nib_dns/Makefile.ci @@ -0,0 +1,28 @@ +BOARD_INSUFFICIENT_MEMORY := \ + arduino-duemilanove \ + arduino-leonardo \ + arduino-mega2560 \ + arduino-nano \ + arduino-uno \ + atmega1284p \ + atmega328p \ + derfmega128 \ + i-nucleo-lrwan1 \ + mega-xplained \ + microduino-corerf \ + msb-430 \ + msb-430h \ + nucleo-f030r8 \ + nucleo-f031k6 \ + nucleo-f042k6 \ + nucleo-f303k8 \ + nucleo-l031k6 \ + nucleo-l053r8 \ + stm32f030f4-demo \ + stm32f0discovery \ + stm32l0538-disco \ + telosb \ + waspmote-pro \ + wsn430-v1_3b \ + wsn430-v1_4 \ + # diff --git a/tests/gnrc_ipv6_nib_dns/README.md b/tests/gnrc_ipv6_nib_dns/README.md new file mode 100644 index 0000000000..9d164d6c15 --- /dev/null +++ b/tests/gnrc_ipv6_nib_dns/README.md @@ -0,0 +1,23 @@ +# Overview + +This test utilizes [scapy] to test GNRC's RDNSS option implementation for +router advertisements + +To test, compile and flash the application to any board of your liking (since +`ethos` is used to communicate with non-native boards it really doesn't matter +as long as the application fits and the board is supported). + + make flash + +And run the tests using + +``` +sudo make test +``` + +Note that root privileges are required since `scapy` needs to construct Ethernet +frames to properly communicate over the TAP interface. + +The test passes on success, on failure an exception is shown. + +[scapy]: https://scapy.readthedocs.io/en/latest/ diff --git a/tests/gnrc_ipv6_nib_dns/main.c b/tests/gnrc_ipv6_nib_dns/main.c new file mode 100644 index 0000000000..af7e2e2a6d --- /dev/null +++ b/tests/gnrc_ipv6_nib_dns/main.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2017 Kaspar Schleiser + * + * 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 sock DNS client test application + * + * @author Kaspar Schleiser + * + * @} + */ + +#include +#include + +#include + +#include "net/sock/dns.h" +#include "shell.h" + +#define MAIN_QUEUE_SIZE (8) +static int _dns(int argc, char **argv); + +static const shell_command_t _shell_commands[] = { + { "dns", "configures and requests a DNS server", _dns }, + { NULL, NULL, NULL }, +}; +static char _shell_buffer[SHELL_DEFAULT_BUFSIZE]; + +static void _usage(char *cmd) +{ + printf("usage: %s server\n", cmd); +} + +static int _dns_server(int argc, char **argv) +{ + int res = (argc < 2); + + if (!res) { + if (sock_dns_server.port > 0) { + char addrstr[INET6_ADDRSTRLEN]; + + inet_ntop(AF_INET6, sock_dns_server.addr.ipv6, addrstr, + sizeof(addrstr)); + printf("DNS server: [%s%%%d]:%u\n", addrstr, sock_dns_server.netif, + sock_dns_server.port); + } + else { + puts("DNS server: -"); + } + } + else { + _usage(argv[0]); + } + return res; +} + +static int _dns(int argc, char **argv) +{ + if ((argc > 1) && (strcmp(argv[1], "server") == 0)) { + return _dns_server(argc, argv); + } + else { + _usage(argv[0]); + return 1; + } +} + +int main(void) +{ + /* start shell */ + shell_run(_shell_commands, _shell_buffer, sizeof(_shell_buffer)); + return 0; +} diff --git a/tests/gnrc_ipv6_nib_dns/tests/01-run.py b/tests/gnrc_ipv6_nib_dns/tests/01-run.py new file mode 100755 index 0000000000..2b16ecbdc3 --- /dev/null +++ b/tests/gnrc_ipv6_nib_dns/tests/01-run.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2018 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 os +import re +import sys +import subprocess + +from scapy.all import Ether, IPv6, ICMPv6ND_RA, ICMPv6NDOptRDNSS, sendp +from testrunner import run + + +def check_and_search_output(cmd, pattern, res_group, *args, **kwargs): + output = subprocess.check_output(cmd, *args, **kwargs).decode("utf-8") + for line in output.splitlines(): + m = re.search(pattern, line) + if m is not None: + return m.group(res_group) + return None + + +def get_bridge(tap): + res = check_and_search_output( + ["bridge", "link"], + r"{}.+master\s+(?P[^\s]+)".format(tap), + "master" + ) + return tap if res is None else res + + +def get_host_lladdr(tap): + res = check_and_search_output( + ["ip", "addr", "show", "dev", tap, "scope", "link"], + r"inet6\s+(?P[0-9A-Fa-f:]+)/\d+", + "lladdr" + ) + if res is None: + raise AssertionError( + "Can't find host link-local address on interface {}".format(tap) + ) + else: + return res + + +def dns_server(child): + child.sendline("dns server") + res = child.expect([r"DNS server: -", + r"DNS server: \[([0-9a-f:]+)%\d+\]:(\d+)"]) + if res > 0: + return child.match.group(1), int(child.match.group(2)) + else: + return None + + +DNS_ADDR = "2001:db8:affe::dead:beef" +DNS_PORT = 53 + + +def testfunc(child): + tap = get_bridge(os.environ["TAP"]) + lladdr_src = get_host_lladdr(tap) + child.sendline("ifconfig") + child.expect(r"HWaddr: (?P[A-Fa-f:0-9]+)\s") + hwaddr_dst = child.match.group("hwaddr").lower() + child.expect(r"(?Pfe80::[A-Fa-f:0-9]+)\s") + lladdr_dst = child.match.group("lladdr").lower() + + assert dns_server(child) is None + sendp(Ether(dst=hwaddr_dst) / IPv6(dst=lladdr_dst, src=lladdr_src) / + ICMPv6ND_RA() / ICMPv6NDOptRDNSS(dns=[DNS_ADDR]), + iface=tap) + assert dns_server(child) == (DNS_ADDR, DNS_PORT) + + +if __name__ == "__main__": + sys.exit(run(testfunc, timeout=1, echo=True))