diff --git a/tests/gnrc_ipv6_ext/Makefile b/tests/gnrc_ipv6_ext/Makefile index faddc75b76a8206ccd42a206bfd0f086fffa0b00..a5348b776923b8db5f2d80d41a7add806152b2bc 100644 --- a/tests/gnrc_ipv6_ext/Makefile +++ b/tests/gnrc_ipv6_ext/Makefile @@ -1,30 +1,52 @@ +DEVELHELP := 1 # name of your application include ../Makefile.tests_common BOARD_INSUFFICIENT_MEMORY := arduino-duemilanove arduino-mega2560 arduino-uno \ - chronos msb-430 msb-430h nucleo-f030r8 \ - nucleo-f031k6 nucleo-f042k6 nucleo-f303k8 \ + hifive1 mega-xplained msb-430 msb-430h \ + nucleo-f030r8 nucleo-f031k6 nucleo-f042k6 \ + nucleo-f070rb nucleo-f072rb nucleo-f303k8 \ nucleo-f334r8 nucleo-l031k6 nucleo-l053r8 \ - stm32f0discovery waspmote-pro + stm32f0discovery telosb thingy52 waspmote-pro \ + wsn430-v1_3b wsn430-v1_4 z1 +# chronos, mips-malta, and ruuvitag boards don't support ethos +BOARD_BLACKLIST := chronos mips-malta ruuvitag + +export TAP ?= tap0 # use Ethernet as link-layer protocol -USEMODULE += netdev_eth -USEMODULE += netdev_test +ifeq (native,$(BOARD)) + USEMODULE += netdev_tap + + TERMFLAGS ?= $(TAP) +else + USEMODULE += ethos + + ETHOS_BAUDRATE ?= 115200 + CFLAGS += -DETHOS_BAUDRATE=$(ETHOS_BAUDRATE) -DUSE_ETHOS_FOR_STDIO + TERMDEPS += ethos + TERMPROG ?= sudo $(RIOTTOOLS)/ethos/ethos + TERMFLAGS ?= $(TAP) $(PORT) $(ETHOS_BAUDRATE) +endif +USEMODULE += auto_init_gnrc_netif # Specify the mandatory networking modules for IPv6 USEMODULE += gnrc_ipv6_router_default +USEMODULE += gnrc_icmpv6_error +USEMODULE += gnrc_pktdump +USEMODULE += gnrc_pktbuf_cmd # IPv6 extension headers USEMODULE += gnrc_ipv6_ext -USEMODULE += gnrc_rpl_srh -USEMODULE += gnrc_sixlowpan_iphc_nhc -# UDP -USEMODULE += gnrc_udp +USEMODULE += od # Add also the shell, some shell commands +USEMODULE += shell +USEMODULE += shell_commands USEMODULE += ps -CFLAGS += -DGNRC_NETIF_IPV6_ADDRS_NUMOF=3 +# TEST_ON_CI_WHITELIST += all -TEST_ON_CI_WHITELIST += all +.PHONY: ethos -# Note: The test can check more things with ENABLE_DEBUG set to 1 in gnrc_ipv6.c +ethos: + $(Q)env -u CC -u CFLAGS make -C $(RIOTTOOLS)/ethos include $(RIOTBASE)/Makefile.include diff --git a/tests/gnrc_ipv6_ext/README.md b/tests/gnrc_ipv6_ext/README.md index b10e51384ed0b18c96cb928f4cc5ddb56945a428..b2ec926971ea87c2f0b00ff3f252ba7ce0528df7 100644 --- a/tests/gnrc_ipv6_ext/README.md +++ b/tests/gnrc_ipv6_ext/README.md @@ -1,30 +1,32 @@ # `gnrc_ipv6_ext` test -This test sends a packet to itself with extension headers. This is based on gnrc_networking example. +This test utilizes [scapy] to test the IPv6 Extension header parsing. -Enable debug output of `gnrc_ipv6.c` before run. When the test is run, it should show the following debug output: +It is intended to just test the basic parsing functionality. For specific +extension header types please provide a separate test application. + +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). + +``` +make flash +``` + +And run the tests using ``` -ipv6: Received (src = fd01::1, dst = fd01::2, next header = 0, length = 42) -ipv6: forward nh = 0 to other threads -ipv6: handle extension header (nh = 0) -ipv6: waiting for incoming message. -ipv6: GNRC_NETAPI_MSG_TYPE_RCV received -ipv6: Received (src = fd01::1, dst = fd01::3, next header = 0, length = 42) -ipv6: forward nh = 0 to other threads -ipv6: handle extension header (nh = 0) -ipv6: waiting for incoming message. -ipv6: GNRC_NETAPI_MSG_TYPE_RCV received -ipv6: Received (src = fd01::1, dst = fd01::2, next header = 0, length = 42) -ipv6: forward nh = 0 to other threads -ipv6: handle extension header (nh = 0) -ipv6: forward nh = 17 to other threads -ipv6: waiting for incoming message. -pkt->users: 0 +sudo make test ``` -It configures the network interface with addresses fd01::02 and fd01::03. Then it sends a packet to fd01::02 with a routing extension header containing addresses fd01::03 and fd01::02. So the packet should be forwarded from fd01::02 to fd01::03, then to fd01::02 again. +Note that root privileges are required since `scapy` needs to construct Ethernet +frames to properly communicate over the TAP interface. + +The tests succeeds if you see the string `SUCCESS`. -The packet has a Hop-by-Hop extension header that should be ignored. +If any problems are encountered (i.e. if the test prints the sting `FAILED`), +set the echo parameter in the `run()` function at the bottom of the test script +(tests/01-run.py) to `True`. The test script will then offer a more detailed +output. -The test also asserts that the packet is released. +[scapy]: https://scapy.readthedocs.io/en/latest/ diff --git a/tests/gnrc_ipv6_ext/main.c b/tests/gnrc_ipv6_ext/main.c index 3a3ad67ec2cd3bb0178d5260883cc959a880f06c..7c01c996fa7da513b99958614f51e7c1392f79c4 100644 --- a/tests/gnrc_ipv6_ext/main.c +++ b/tests/gnrc_ipv6_ext/main.c @@ -22,152 +22,55 @@ #include <stdio.h> #include "shell.h" -#include "msg.h" -#include "net/ethernet.h" -#include "net/ipv6/addr.h" -#include "net/gnrc/pkt.h" #include "net/gnrc/pktbuf.h" +#include "net/gnrc/pktdump.h" #include "net/gnrc/netreg.h" -#include "net/gnrc/netapi.h" -#include "net/gnrc/netif.h" -#include "net/gnrc/netif/conf.h" -#include "net/gnrc/netif/ethernet.h" -#include "net/gnrc/netif/hdr.h" -#include "net/netdev_test.h" -static char _netif_stack[THREAD_STACKSIZE_SMALL]; -static netdev_test_t _dev; +static char line_buf[SHELL_DEFAULT_BUFSIZE]; +static gnrc_netreg_entry_t ip_entry = GNRC_NETREG_ENTRY_INIT_PID( + 0, KERNEL_PID_UNDEF + ); -static int _get_netdev_device_type(netdev_t *netdev, void *value, size_t max_len) +static inline void _ipreg_usage(char *cmd) { - assert(max_len == sizeof(uint16_t)); - (void)netdev; - - *((uint16_t *)value) = NETDEV_TYPE_ETHERNET; - return sizeof(uint16_t); + printf("Usage: %s {reg|unreg} <protnum>", cmd); } -static int _get_netdev_max_packet_size(netdev_t *netdev, void *value, - size_t max_len) +static int _ipreg(int argc, char **argv) { - assert(max_len == sizeof(uint16_t)); - (void)netdev; - - *((uint16_t *)value) = ETHERNET_DATA_LEN; - return sizeof(uint16_t); -} - -static void _init_interface(void) -{ - gnrc_netif_t *netif; - ipv6_addr_t addr = IPV6_ADDR_UNSPECIFIED; - - netdev_test_setup(&_dev, NULL); - netdev_test_set_get_cb(&_dev, NETOPT_DEVICE_TYPE, - _get_netdev_device_type); - netdev_test_set_get_cb(&_dev, NETOPT_MAX_PACKET_SIZE, - _get_netdev_max_packet_size); - netif = gnrc_netif_ethernet_create( - _netif_stack, THREAD_STACKSIZE_SMALL, GNRC_NETIF_PRIO, - "dummy_netif", (netdev_t *)&_dev); - - addr.u8[0] = 0xfd; - addr.u8[1] = 0x01; - addr.u8[15] = 0x02; - - xtimer_usleep(500); /* wait for thread to start */ - /* add addresses fd01::02/64 and fd01::3/64 to interface */ - for (uint8_t i = 0x2; i <= 0x3; i++) { - addr.u8[15] = i; - if (gnrc_netapi_set(netif->pid, NETOPT_IPV6_ADDR, 64U << 8U, &addr, - sizeof(addr)) < 0) { - printf("error: unable to add IPv6 address fd01::%x/64 to interface %u\n", - addr.u8[15], netif->pid); + if ((argc > 2) && (strcmp("reg", argv[1]) == 0)) { + uint32_t protnum; + if (ip_entry.target.pid != KERNEL_PID_UNDEF) { + printf("Already registered to protnum %" PRIu32 "\n", + ip_entry.demux_ctx); + return 1; } + protnum = atoi(argv[2]); + gnrc_netreg_entry_init_pid(&ip_entry, protnum, gnrc_pktdump_pid); + gnrc_netreg_register(GNRC_NETTYPE_IPV6, &ip_entry); + printf("Registered to protocol number %" PRIu32 "\n", protnum); + } + else if ((argc > 1) && (strcmp("unreg", argv[1]) == 0)) { + printf("Unregistered from protocol number %" PRIu32 "\n", + ip_entry.demux_ctx); + gnrc_netreg_unregister(GNRC_NETTYPE_IPV6, &ip_entry); + gnrc_netreg_entry_init_pid(&ip_entry, 0, KERNEL_PID_UNDEF); + } + else { + _ipreg_usage(argv[0]); + return 1; } -} - -static void _send_packet_raw(void) -{ - gnrc_netif_t *iface = gnrc_netif_iter(NULL); - - gnrc_netif_hdr_t netif_hdr; - - gnrc_netif_hdr_init(&netif_hdr, 8, 8); - - netif_hdr.if_pid = iface->pid; - - uint8_t data[] = { - /* IPv6 Header */ - 0x60, 0x00, 0x00, 0x00, /* version, traffic class, flow label */ - 0x00, 0x2a, /* payload length: 42 */ - 0x00, /* next header: Hop-by-Hop Option */ - 0x10, /* hop limit: 16 */ - /* source address: fd01::1 */ - 0xfd, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, - /* destination address: fd01::2 */ - 0xfd, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, - - /* Hop-by-Hop Options Header */ - /* https://tools.ietf.org/html/rfc6553 */ - 0x2b, /* next header: IPv6-Route */ - 0x00, /* hdr ext len: 0 * 8 + 8 = 8 */ - 0x63, /* option type: RPL Option */ - 0x04, /* opt data len: 4 */ - 0x80, /* flags, Down: 1, Rank-Error: 0, Forwarding-Error: 0 */ - 0x00, /* RPLInstanceID */ - 0x80, 0x00, /* SenderRank */ - - /* RPL Routing Header */ - /* https://tools.ietf.org/html/rfc6554 */ - 0x11, /* next header: UDP */ - 0x02, /* hdr ext len: 2 * 8 + 8 = 24 */ - 0x03, /* routing type: SRH */ - 0x02, /* segments left: 2 */ - 0xef, /* ComprI: 14, ComprE: 15 */ - 0xd0, 0x00, 0x00, /* pad and reserved */ - /* address: fd01::3, fd01::2 */ - 0x00, 0x03, 0x02, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - - /* UDP (ignored) */ - 0x1f, 0x90, /* source port: 8080 */ - 0x1f, 0x90, /* destination port: 8080 */ - 0x00, 0x0a, /* length: 10 */ - 0xff, 0xff, /* checksum */ - 0x00, 0x00, /* payload */ - }; - - gnrc_pktsnip_t *netif = gnrc_pktbuf_add(NULL, - &netif_hdr, - sizeof(netif_hdr), - GNRC_NETTYPE_NETIF); - gnrc_pktsnip_t *pkt = gnrc_pktbuf_add(netif, - data, - sizeof(data), - GNRC_NETTYPE_UNDEF); - - gnrc_netapi_dispatch_receive(GNRC_NETTYPE_IPV6, GNRC_NETREG_DEMUX_CTX_ALL, pkt); - printf("pkt->users: %d\n", pkt->users); - assert(pkt->users == 0); + return 0; } +static const shell_command_t shell_commands[] = { + { "ip", "Registers pktdump to a protocol number", _ipreg }, + { NULL, NULL, NULL } +}; + int main(void) { - puts("RIOT network stack example application"); - - _init_interface(); - _send_packet_raw(); - - /* should be never reached */ + shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); return 0; } diff --git a/tests/gnrc_ipv6_ext/tests/01-run.py b/tests/gnrc_ipv6_ext/tests/01-run.py index e34a9370318d28579982ccdb54a790bbf41aa0b7..9d2f5e7d8c7d5bfe8424c5c01a02a47d350c4ffd 100755 --- a/tests/gnrc_ipv6_ext/tests/01-run.py +++ b/tests/gnrc_ipv6_ext/tests/01-run.py @@ -1,19 +1,646 @@ #!/usr/bin/env python3 -# Copyright (C) 2016 Kaspar Schleiser <kaspar@schleiser.de> -# Copyright (C) 2016 Takuo Yonezawa <Yonezawa-T2@mail.dnp.co.jp> +# 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 re +import os import sys +import subprocess + +from scapy.all import Ether, IPv6, UDP, \ + IPv6ExtHdrHopByHop, IPv6ExtHdrDestOpt, \ + IPv6ExtHdrFragment, IPv6ExtHdrRouting, \ + ICMPv6ParamProblem, \ + sendp, srp1 from testrunner import run +EXT_HDR_NH = { + IPv6ExtHdrHopByHop: 0, + IPv6ExtHdrRouting: 43, + IPv6ExtHdrFragment: 44, + # IPSec headers currently unsupported by scapy + IPv6ExtHdrDestOpt: 60, + # Mobility header currently unsupported by scapy + } + + +def pktbuf_empty(child): + child.sendline("pktbuf") + child.expect(r"packet buffer: first byte: (?P<first_byte>0x[0-9a-fA-F]+), " + r"last byte: 0x[0-9a-fA-F]+ \(size: (?P<size>\d+)\)") + first_byte = child.match.group("first_byte") + size = child.match.group("size") + child.expect( + r"~ unused: {} \(next: (\(nil\)|0), size: {}\) ~".format( + first_byte, size)) + + +def register_protnum(child, protnum): + child.sendline("ip reg %d" % protnum) + child.expect("Registered to protocol number %d" % protnum) + + +def unregister(child): + child.sendline("ip unreg") + child.expect(r"Unregistered from protocol number \d") + + +def test_empty_hop_by_hop_opt_wo_register(child, iface, hw_dst, ll_dst, ll_src): + # Try sending an empty hop-by-hop-option header + sendp(Ether(dst=hw_dst) / IPv6(dst=ll_dst, src=ll_src) / + IPv6ExtHdrHopByHop() / UDP(), iface=iface, verbose=0) + pktbuf_empty(child) + + +def test_empty_hop_by_hop_opt_w_register(child, iface, hw_dst, ll_dst, ll_src): + # Register to hop-by-hop-option header + register_protnum(child, EXT_HDR_NH[IPv6ExtHdrHopByHop]) + # Try sending an empty hop-by-hop-option header + sendp(Ether(dst=hw_dst) / IPv6(dst=ll_dst, src=ll_src) / + IPv6ExtHdrHopByHop() / UDP() / "\x01\x02", iface=iface, verbose=0) + child.expect(r"~~ SNIP 0 - size:\s+(\d+) byte, type: NETTYPE_\w+ \(\d+\)") + ipv6_payload_len = int(child.match.group(1)) + # NH = 17 (UDP), len = 0x00, PadN option (0x01) of length 0x04 + child.expect(r"00000000 11 00 01 04 00 00 00 00") + child.expect(r"~~ SNIP 1 - size:\s+40 byte, type: NETTYPE_IPV6 \(\d+\)") + child.expect_exact(r"length: {} next header: {}".format( + ipv6_payload_len, EXT_HDR_NH[IPv6ExtHdrHopByHop] + )) + child.expect_exact(r"destination address: {}".format(ll_dst)) + pktbuf_empty(child) + unregister(child) + + +def test_empty_duplicate_hop_by_hop_opt(child, iface, hw_dst, ll_dst, ll_src): + # Try sending two empty hop-by-hop-option header + p = srp1(Ether(dst=hw_dst) / IPv6(dst=ll_dst, src=ll_src) / + IPv6ExtHdrHopByHop() / IPv6ExtHdrHopByHop() / UDP() / "\x03\x04", + iface=iface, timeout=1, verbose=0) + # should return parameter problem message + assert(p is not None) + assert(ICMPv6ParamProblem in p) + assert(p[ICMPv6ParamProblem].code == 1) # unrecognized next header + assert(p[ICMPv6ParamProblem].ptr >= 40) # after IPv6 header + pktbuf_empty(child) + + +def test_empty_non_first_hop_by_hop_opt(child, iface, hw_dst, ll_dst, ll_src): + # Try sending empty hop-by-hop-option header after destination option + # header + p = srp1(Ether(dst=hw_dst) / IPv6(dst=ll_dst, src=ll_src) / + IPv6ExtHdrDestOpt() / IPv6ExtHdrHopByHop() / UDP() / "\x05\x06", + iface=iface, timeout=1, verbose=0) + # should return parameter problem message + assert(p is not None) + assert(ICMPv6ParamProblem in p) + assert(p[ICMPv6ParamProblem].code == 1) # unrecognized next header + assert(p[ICMPv6ParamProblem].ptr >= 40) # after IPv6 header + pktbuf_empty(child) + + +def test_empty_duplicate_non_first_hop_by_hop_opt(child, iface, hw_dst, ll_dst, + ll_src): + # Try sending empty hop-by-hop-option header after destination option + # header and another hop-by-hop-option header + p = srp1(Ether(dst=hw_dst) / IPv6(dst=ll_dst, src=ll_src) / + IPv6ExtHdrHopByHop() / IPv6ExtHdrDestOpt() / IPv6ExtHdrHopByHop() / + UDP() / "\x07\x08", + iface=iface, timeout=1, verbose=0) + # should return parameter problem message + assert(p is not None) + assert(ICMPv6ParamProblem in p) + assert(p[ICMPv6ParamProblem].code == 1) # unrecognized next header + assert(p[ICMPv6ParamProblem].ptr >= 48) # after IPv6 header and HopByHopOpt + pktbuf_empty(child) + + +def test_empty_routing_header_wo_register(child, iface, hw_dst, ll_dst, ll_src): + # Try sending an empty routing header + sendp(Ether(dst=hw_dst) / IPv6(dst=ll_dst, src=ll_src) / + IPv6ExtHdrRouting() / UDP(), iface=iface, verbose=0) + pktbuf_empty(child) + + +def test_empty_routing_header_w_register(child, iface, hw_dst, ll_dst, ll_src): + # Register to routing header + register_protnum(child, EXT_HDR_NH[IPv6ExtHdrRouting]) + # Try sending an empty routing header + sendp(Ether(dst=hw_dst) / IPv6(dst=ll_dst, src=ll_src) / + IPv6ExtHdrRouting() / UDP() / "\x01\x02", iface=iface, verbose=0) + child.expect(r"~~ SNIP 0 - size:\s+(\d+) byte, type: NETTYPE_\w+ \(\d+\)") + ipv6_payload_len = int(child.match.group(1)) + # NH = 17 (UDP), len = 0x00, routing type = 0, segments left = 0 + child.expect(r"00000000 11 00 00 00 00 00 00 00") + child.expect(r"~~ SNIP 1 - size:\s+40 byte, type: NETTYPE_IPV6 \(\d+\)") + child.expect_exact(r"length: {} next header: {}".format( + ipv6_payload_len, EXT_HDR_NH[IPv6ExtHdrRouting] + )) + child.expect_exact(r"destination address: {}".format(ll_dst)) + pktbuf_empty(child) + unregister(child) + + +def test_empty_fragment_header_wo_register(child, iface, hw_dst, ll_dst, ll_src): + # Try sending an empty fragment header + sendp(Ether(dst=hw_dst) / IPv6(dst=ll_dst, src=ll_src) / + IPv6ExtHdrFragment() / UDP(), iface=iface, verbose=0) + pktbuf_empty(child) + + +def test_empty_fragment_header_w_register(child, iface, hw_dst, ll_dst, ll_src): + # Register to fragment header + register_protnum(child, EXT_HDR_NH[IPv6ExtHdrFragment]) + # Try sending an empty fragment header + sendp(Ether(dst=hw_dst) / IPv6(dst=ll_dst, src=ll_src) / + IPv6ExtHdrFragment() / UDP() / "\x01\x02", iface=iface, verbose=0) + child.expect(r"~~ SNIP 0 - size:\s+(\d+) byte, type: NETTYPE_\w+ \(\d+\)") + ipv6_payload_len = int(child.match.group(1)) + # NH = 17 (UDP), reserved = 0x00, fragment offset = 0, res = 0, M = 0 + child.expect(r"00000000 11 00 00 00 00 00 00 00") + child.expect(r"~~ SNIP 1 - size:\s+40 byte, type: NETTYPE_IPV6 \(\d+\)") + child.expect_exact(r"length: {} next header: {}".format( + ipv6_payload_len, EXT_HDR_NH[IPv6ExtHdrFragment] + )) + child.expect_exact(r"destination address: {}".format(ll_dst)) + pktbuf_empty(child) + unregister(child) + + +def test_empty_dest_opt_wo_register(child, iface, hw_dst, ll_dst, ll_src): + # Try sending an empty Destination-Option header + sendp(Ether(dst=hw_dst) / IPv6(dst=ll_dst, src=ll_src) / + IPv6ExtHdrDestOpt() / UDP(), iface=iface, verbose=0) + pktbuf_empty(child) + + +def test_empty_dest_opt_w_register(child, iface, hw_dst, ll_dst, ll_src): + # Register to Destination-Option header + register_protnum(child, EXT_HDR_NH[IPv6ExtHdrDestOpt]) + # Try sending an empty Destination-Option header + sendp(Ether(dst=hw_dst) / IPv6(dst=ll_dst, src=ll_src) / + IPv6ExtHdrDestOpt() / UDP() / "\x01\x02", iface=iface, verbose=0) + child.expect(r"~~ SNIP 0 - size:\s+(\d+) byte, type: NETTYPE_\w+ \(\d+\)") + ipv6_payload_len = int(child.match.group(1)) + # NH = 17 (UDP), len = 0x00, PadN option (0x01) of length 0x04 + child.expect(r"00000000 11 00 01 04 00 00 00 00") + child.expect(r"~~ SNIP 1 - size:\s+40 byte, type: NETTYPE_IPV6 \(\d+\)") + child.expect_exact(r"length: {} next header: {}".format( + ipv6_payload_len, EXT_HDR_NH[IPv6ExtHdrDestOpt] + )) + child.expect_exact(r"destination address: {}".format(ll_dst)) + pktbuf_empty(child) + unregister(child) + + +def test_empty_mixed1_w_hop_opt_registered(child, iface, hw_dst, ll_dst, ll_src): + # Register to hop-by-hop-option header + register_protnum(child, EXT_HDR_NH[IPv6ExtHdrHopByHop]) + # Try sending a packet with a number of extension headers in recommended + # order: https://tools.ietf.org/html/rfc8200#section-4.1 + sendp(Ether(dst=hw_dst) / IPv6(dst=ll_dst, src=ll_src) / + IPv6ExtHdrHopByHop() / IPv6ExtHdrDestOpt() / IPv6ExtHdrRouting() / + IPv6ExtHdrFragment() / IPv6ExtHdrDestOpt() / UDP() / "\x01\x02", + iface=iface, verbose=0) + # Hop-by-hop option with payload + child.expect(r"~~ SNIP 0 - size:\s+(\d+) byte, type: NETTYPE_\w+ \(\d+\)") + ipv6_payload_len = int(child.match.group(1)) + # NH = IPv6ExtHdrDestOpt, len = 0x00, PadN option (0x01) of length 0x04 + # NH = IPv6ExtHdrRouting, len = 0x00, PadN option (0x01) of length 0x04 + child.expect(r"00000000 {:02X} 00 01 04 00 00 00 00 " + r"{:02X} 00 01 04 00 00 00 00".format( + EXT_HDR_NH[IPv6ExtHdrDestOpt], + EXT_HDR_NH[IPv6ExtHdrRouting] + )) + # NH = IPv6ExtHdrFragment, len = 0x00, routing type = 0, segments left = 0 + # NH = IPv6ExtHdrDestOpt, reserved = 0x00, fragment offset = 0, res = 0, M = 0 + child.expect(r"00000010 {:02X} 00 00 00 00 00 00 00 " + r"{:02X} 00 00 00 00 00 00 00".format( + EXT_HDR_NH[IPv6ExtHdrFragment], + EXT_HDR_NH[IPv6ExtHdrDestOpt] + )) + # NH = 17 (UDP), len = 0x00, PadN option (0x01) of length 0x04 + child.expect(r"00000020 11 00 01 04 00 00 00 00") + # IPv6 header + child.expect(r"~~ SNIP 1 - size:\s+40 byte, type: NETTYPE_IPV6 \(\d+\)") + child.expect_exact(r"length: {} next header: {}".format( + ipv6_payload_len, EXT_HDR_NH[IPv6ExtHdrHopByHop] + )) + child.expect_exact(r"destination address: {}".format(ll_dst)) + pktbuf_empty(child) + unregister(child) + + +def test_empty_mixed1_w_rt_hdr_registered(child, iface, hw_dst, ll_dst, ll_src): + # Register to routing header + register_protnum(child, EXT_HDR_NH[IPv6ExtHdrRouting]) + # Try sending a packet with a number of extension headers in recommended + # order: https://tools.ietf.org/html/rfc8200#section-4.1 + sendp(Ether(dst=hw_dst) / IPv6(dst=ll_dst, src=ll_src) / + IPv6ExtHdrHopByHop() / IPv6ExtHdrDestOpt() / IPv6ExtHdrRouting() / + IPv6ExtHdrFragment() / IPv6ExtHdrDestOpt() / UDP() / "\x01\x02", + iface=iface, verbose=0) + # Routing header with payload + child.expect(r"~~ SNIP 0 - size:\s+(\d+) byte, type: NETTYPE_\w+ \(\d+\)") + ipv6_payload_len = int(child.match.group(1)) + # NH = IPv6ExtHdrFragment, len = 0x00, routing type = 0, segments left = 0 + # NH = IPv6ExtHdrDestOpt, reserved = 0x00, fragment offset = 0, res = 0, M = 0 + child.expect(r"00000000 {:02X} 00 00 00 00 00 00 00 " + r"{:02X} 00 00 00 00 00 00 00".format( + EXT_HDR_NH[IPv6ExtHdrFragment], + EXT_HDR_NH[IPv6ExtHdrDestOpt] + )) + # NH = 17 (UDP), len = 0x00, PadN option (0x01) of length 0x04 + child.expect(r"00000010 11 00 01 04 00 00 00 00") + # Destination option 1 + child.expect(r"~~ SNIP 1 - size:\s+(\d+) byte, type: NETTYPE_\w+ \(\d+\)") + ipv6_payload_len += int(child.match.group(1)) + # NH = IPv6ExtHdrRouting, len = 0x00, PadN option (0x01) of length 0x04 + child.expect(r"00000000 {:02X} 00 01 04 00 00 00 00".format( + EXT_HDR_NH[IPv6ExtHdrRouting])) + # Hop-by-hop option + child.expect(r"~~ SNIP 2 - size:\s+(\d+) byte, type: NETTYPE_\w+ \(\d+\)") + ipv6_payload_len += int(child.match.group(1)) + # NH = IPv6ExtHdrDestOpt, len = 0x00, PadN option (0x01) of length 0x04 + child.expect(r"00000000 {:02X} 00 01 04 00 00 00 00".format( + EXT_HDR_NH[IPv6ExtHdrDestOpt])) + # IPv6 header + child.expect(r"~~ SNIP 3 - size:\s+40 byte, type: NETTYPE_IPV6 \(\d+\)") + child.expect_exact(r"length: {} next header: {}".format( + ipv6_payload_len, EXT_HDR_NH[IPv6ExtHdrHopByHop] + )) + child.expect_exact(r"destination address: {}".format(ll_dst)) + pktbuf_empty(child) + unregister(child) + + +def test_empty_mixed1_w_frag_hdr_registered(child, iface, hw_dst, ll_dst, ll_src): + # Register to fragment header + register_protnum(child, EXT_HDR_NH[IPv6ExtHdrFragment]) + # Try sending a packet with a number of extension headers in recommended + # order: https://tools.ietf.org/html/rfc8200#section-4.1 + sendp(Ether(dst=hw_dst) / IPv6(dst=ll_dst, src=ll_src) / + IPv6ExtHdrHopByHop() / IPv6ExtHdrDestOpt() / IPv6ExtHdrRouting() / + IPv6ExtHdrFragment() / IPv6ExtHdrDestOpt() / UDP() / "\x01\x02", + iface=iface, verbose=0) + # Routing header with payload + child.expect(r"~~ SNIP 0 - size:\s+(\d+) byte, type: NETTYPE_\w+ \(\d+\)") + ipv6_payload_len = int(child.match.group(1)) + # NH = IPv6ExtHdrDestOpt, reserved = 0x00, fragment offset = 0, res = 0, M = 0 + child.expect(r"00000000 {:02X} 00 00 00 00 00 00 00 " + "11 00 01 04 00 00 00 00".format( + EXT_HDR_NH[IPv6ExtHdrDestOpt])) + child.expect(r"~~ SNIP 1 - size:\s+(\d+) byte, type: NETTYPE_\w+ \(\d+\)") + ipv6_payload_len += int(child.match.group(1)) + # NH = IPv6ExtHdrFragment, len = 0x00, routing type = 0, segments left = 0 + child.expect(r"00000000 {:02X} 00 00 00 00 00 00 00".format( + EXT_HDR_NH[IPv6ExtHdrFragment])) + # Destination option 1 + child.expect(r"~~ SNIP 2 - size:\s+(\d+) byte, type: NETTYPE_\w+ \(\d+\)") + ipv6_payload_len += int(child.match.group(1)) + # NH = IPv6ExtHdrRouting, len = 0x00, PadN option (0x01) of length 0x04 + child.expect(r"00000000 {:02X} 00 01 04 00 00 00 00".format( + EXT_HDR_NH[IPv6ExtHdrRouting])) + # Hop-by-hop option + child.expect(r"~~ SNIP 3 - size:\s+(\d+) byte, type: NETTYPE_\w+ \(\d+\)") + ipv6_payload_len += int(child.match.group(1)) + # NH = IPv6ExtHdrDestOpt, len = 0x00, PadN option (0x01) of length 0x04 + child.expect(r"00000000 {:02X} 00 01 04 00 00 00 00".format( + EXT_HDR_NH[IPv6ExtHdrDestOpt])) + # IPv6 header + child.expect(r"~~ SNIP 4 - size:\s+40 byte, type: NETTYPE_IPV6 \(\d+\)") + child.expect_exact(r"length: {} next header: {}".format( + ipv6_payload_len, EXT_HDR_NH[IPv6ExtHdrHopByHop] + )) + child.expect_exact(r"destination address: {}".format(ll_dst)) + pktbuf_empty(child) + unregister(child) + + +def test_empty_mixed1_w_dest_opt_registered(child, iface, hw_dst, ll_dst, ll_src): + # Register to destination-option header + register_protnum(child, EXT_HDR_NH[IPv6ExtHdrDestOpt]) + # Try sending a packet with a number of extension headers in recommended + # order: https://tools.ietf.org/html/rfc8200#section-4.1 + sendp(Ether(dst=hw_dst) / IPv6(dst=ll_dst, src=ll_src) / + IPv6ExtHdrHopByHop() / IPv6ExtHdrDestOpt() / IPv6ExtHdrRouting() / + IPv6ExtHdrFragment() / IPv6ExtHdrDestOpt() / UDP() / "\x01\x02", + iface=iface, verbose=0) + + # IPv6ExtHdrDestOpt is two times in the packet so pktdump will it print two + # times + # 1st print parsed up to the first IPv6ExtHdrDestOpt + # Destination option 1 with payload + child.expect(r"~~ SNIP 0 - size:\s+(\d+) byte, type: NETTYPE_\w+ \(\d+\)") + ipv6_payload_len = int(child.match.group(1)) + # NH = IPv6ExtHdrRouting, len = 0x00, PadN option (0x01) of length 0x04 + # NH = IPv6ExtHdrFragment, len = 0x00, routing type = 0, segments left = 0 + child.expect(r"00000000 {:02X} 00 01 04 00 00 00 00 " + r"{:02X} 00 00 00 00 00 00 00".format( + EXT_HDR_NH[IPv6ExtHdrRouting], + EXT_HDR_NH[IPv6ExtHdrFragment] + )) + # NH = IPv6ExtHdrDestOpt, reserved = 0x00, fragment offset = 0, res = 0, M = 0 + # NH = 17 (UDP), len = 0x00, PadN option (0x01) of length 0x04 + child.expect(r"00000010 {:02X} 00 00 00 00 00 00 00 " + r"11 00 01 04 00 00 00 00".format( + EXT_HDR_NH[IPv6ExtHdrDestOpt] + )) + # Hop-by-hop option + child.expect(r"~~ SNIP 1 - size:\s+(\d+) byte, type: NETTYPE_\w+ \(\d+\)") + ipv6_payload_len += int(child.match.group(1)) + # NH = IPv6ExtHdrDestOpt, len = 0x00, PadN option (0x01) of length 0x04 + child.expect(r"00000000 {:02X} 00 01 04 00 00 00 00".format( + EXT_HDR_NH[IPv6ExtHdrDestOpt])) + # IPv6 header + child.expect(r"~~ SNIP 2 - size:\s+40 byte, type: NETTYPE_IPV6 \(\d+\)") + child.expect_exact(r"length: {} next header: {}".format( + ipv6_payload_len, EXT_HDR_NH[IPv6ExtHdrHopByHop] + )) + child.expect_exact(r"destination address: {}".format(ll_dst)) + + # 2nd print parsed up to the second IPv6ExtHdrHopByHop + # Destination option 2 with payload + child.expect(r"~~ SNIP 0 - size:\s+(\d+) byte, type: NETTYPE_\w+ \(\d+\)") + ipv6_payload_len = int(child.match.group(1)) + # NH = 17 (UDP), len = 0x00, PadN option (0x01) of length 0x04 + child.expect(r"00000000 11 00 01 04 00 00 00 00") + # Fragment header + child.expect(r"~~ SNIP 1 - size:\s+(\d+) byte, type: NETTYPE_\w+ \(\d+\)") + ipv6_payload_len += int(child.match.group(1)) + # NH = IPv6ExtHdrDestOpt, reserved = 0x00, fragment offset = 0, res = 0, M = 0 + child.expect(r"00000000 {:02X} 00 00 00 00 00 00 00".format( + EXT_HDR_NH[IPv6ExtHdrDestOpt])) + # Routing header + child.expect(r"~~ SNIP 2 - size:\s+(\d+) byte, type: NETTYPE_\w+ \(\d+\)") + ipv6_payload_len += int(child.match.group(1)) + # NH = IPv6ExtHdrFragment, len = 0x00, routing type = 0, segments left = 0 + child.expect(r"00000000 {:02X} 00 00 00 00 00 00 00".format( + EXT_HDR_NH[IPv6ExtHdrFragment])) + # Destination option 1 + child.expect(r"~~ SNIP 3 - size:\s+(\d+) byte, type: NETTYPE_\w+ \(\d+\)") + ipv6_payload_len += int(child.match.group(1)) + # NH = IPv6ExtHdrRouting, len = 0x00, PadN option (0x01) of length 0x04 + child.expect(r"00000000 {:02X} 00 01 04 00 00 00 00".format( + EXT_HDR_NH[IPv6ExtHdrRouting])) + # Hop-by-hop option + child.expect(r"~~ SNIP 4 - size:\s+(\d+) byte, type: NETTYPE_\w+ \(\d+\)") + ipv6_payload_len += int(child.match.group(1)) + # NH = IPv6ExtHdrDestOpt, len = 0x00, PadN option (0x01) of length 0x04 + child.expect(r"00000000 {:02X} 00 01 04 00 00 00 00".format( + EXT_HDR_NH[IPv6ExtHdrDestOpt])) + # IPv6 header + child.expect(r"~~ SNIP 5 - size:\s+40 byte, type: NETTYPE_IPV6 \(\d+\)") + child.expect_exact(r"length: {} next header: {}".format( + ipv6_payload_len, EXT_HDR_NH[IPv6ExtHdrHopByHop] + )) + child.expect_exact(r"destination address: {}".format(ll_dst)) + pktbuf_empty(child) + unregister(child) + + +def test_empty_mixed2_w_hop_opt_registered(child, iface, hw_dst, ll_dst, ll_src): + # Register to hop-by-hop-option header + register_protnum(child, EXT_HDR_NH[IPv6ExtHdrHopByHop]) + # Try sending a packet with a number of extension headers in not recommended + # (but legal) order + sendp(Ether(dst=hw_dst) / IPv6(dst=ll_dst, src=ll_src) / + IPv6ExtHdrHopByHop() / IPv6ExtHdrRouting() / IPv6ExtHdrDestOpt() / + IPv6ExtHdrFragment() / UDP() / "\x01\x02", + iface=iface, verbose=0) + # Hop-by-hop option with payload + child.expect(r"~~ SNIP 0 - size:\s+(\d+) byte, type: NETTYPE_\w+ \(\d+\)") + ipv6_payload_len = int(child.match.group(1)) + # NH = IPv6ExtHdrRouting, len = 0x00, PadN option (0x01) of length 0x04 + # NH = IPv6ExtHdrDestOpt, len = 0x00, routing type = 0, segments left = 0 + child.expect(r"00000000 {:02X} 00 01 04 00 00 00 00 " + r"{:02X} 00 00 00 00 00 00 00".format( + EXT_HDR_NH[IPv6ExtHdrRouting], + EXT_HDR_NH[IPv6ExtHdrDestOpt] + )) + # NH = IPv6ExtHdrFragment, len = 0x00, PadN option (0x01) of length 0x04 + # NH = 17 (UDP), reserved = 0x00, fragment offset = 0, res = 0, M = 0 + child.expect(r"00000010 {:02X} 00 01 04 00 00 00 00 " + r"11 00 00 00 00 00 00 00".format( + EXT_HDR_NH[IPv6ExtHdrFragment] + )) + # IPv6 header + child.expect(r"~~ SNIP 1 - size:\s+40 byte, type: NETTYPE_IPV6 \(\d+\)") + child.expect_exact(r"length: {} next header: {}".format( + ipv6_payload_len, EXT_HDR_NH[IPv6ExtHdrHopByHop] + )) + child.expect_exact(r"destination address: {}".format(ll_dst)) + pktbuf_empty(child) + unregister(child) + + +def test_empty_mixed2_w_rt_hdr_registered(child, iface, hw_dst, ll_dst, ll_src): + # Register to routing header + register_protnum(child, EXT_HDR_NH[IPv6ExtHdrRouting]) + # Try sending a packet with a number of extension headers in not recommended + # (but legal) order + sendp(Ether(dst=hw_dst) / IPv6(dst=ll_dst, src=ll_src) / + IPv6ExtHdrHopByHop() / IPv6ExtHdrRouting() / IPv6ExtHdrDestOpt() / + IPv6ExtHdrFragment() / UDP() / "\x01\x02", + iface=iface, verbose=0) + # Routing header with payload + child.expect(r"~~ SNIP 0 - size:\s+(\d+) byte, type: NETTYPE_\w+ \(\d+\)") + ipv6_payload_len = int(child.match.group(1)) + # NH = IPv6ExtHdrDestOpt, len = 0x00, routing type = 0, segments left = 0 + # NH = IPv6ExtHdrFragment, len = 0x00, PadN option (0x01) of length 0x04 + child.expect(r"00000000 {:02X} 00 00 00 00 00 00 00 " + r"{:02X} 00 01 04 00 00 00 00".format( + EXT_HDR_NH[IPv6ExtHdrDestOpt], + EXT_HDR_NH[IPv6ExtHdrFragment] + )) + # NH = 17 (UDP), reserved = 0x00, fragment offset = 0, res = 0, M = 0 + child.expect(r"00000010 11 00 00 00 00 00 00 00") + # Hop-by-hop-option + child.expect(r"~~ SNIP 1 - size:\s+(\d+) byte, type: NETTYPE_\w+ \(\d+\)") + ipv6_payload_len += int(child.match.group(1)) + # NH = IPv6ExtHdrRouting, len = 0x00, PadN option (0x01) of length 0x04 + child.expect(r"00000000 {:02X} 00 01 04 00 00 00 00".format( + EXT_HDR_NH[IPv6ExtHdrRouting])) + # IPv6 header + child.expect(r"~~ SNIP 2 - size:\s+40 byte, type: NETTYPE_IPV6 \(\d+\)") + child.expect_exact(r"length: {} next header: {}".format( + ipv6_payload_len, EXT_HDR_NH[IPv6ExtHdrHopByHop] + )) + child.expect_exact(r"destination address: {}".format(ll_dst)) + pktbuf_empty(child) + unregister(child) + + +def test_empty_mixed2_w_frag_hdr_registered(child, iface, hw_dst, ll_dst, + ll_src): + # Register to fragment header + register_protnum(child, EXT_HDR_NH[IPv6ExtHdrFragment]) + # Try sending a packet with a number of extension headers in not recommended + # (but legal) order + sendp(Ether(dst=hw_dst) / IPv6(dst=ll_dst, src=ll_src) / + IPv6ExtHdrHopByHop() / IPv6ExtHdrRouting() / IPv6ExtHdrDestOpt() / + IPv6ExtHdrFragment() / UDP() / "\x01\x02", + iface=iface, verbose=0) + # Fragment header with payload + child.expect(r"~~ SNIP 0 - size:\s+(\d+) byte, type: NETTYPE_\w+ \(\d+\)") + ipv6_payload_len = int(child.match.group(1)) + # NH = 17 (UDP), reserved = 0x00, fragment offset = 0, res = 0, M = 0 + child.expect(r"00000000 11 00 00 00 00 00 00 00") + # Destination option + child.expect(r"~~ SNIP 1 - size:\s+(\d+) byte, type: NETTYPE_\w+ \(\d+\)") + ipv6_payload_len += int(child.match.group(1)) + # NH = IPv6ExtHdrFragment, len = 0x00, PadN option (0x01) of length 0x04 + child.expect(r"00000000 {:02X} 00 01 04 00 00 00 00".format( + EXT_HDR_NH[IPv6ExtHdrFragment])) + # Routing header + child.expect(r"~~ SNIP 2 - size:\s+(\d+) byte, type: NETTYPE_\w+ \(\d+\)") + ipv6_payload_len += int(child.match.group(1)) + # NH = IPv6ExtHdrDestOpt, len = 0x00, routing type = 0, segments left = 0 + child.expect(r"00000000 {:02X} 00 00 00 00 00 00 00".format( + EXT_HDR_NH[IPv6ExtHdrDestOpt])) + # Hop-by-hop-option + child.expect(r"~~ SNIP 3 - size:\s+(\d+) byte, type: NETTYPE_\w+ \(\d+\)") + ipv6_payload_len += int(child.match.group(1)) + # NH = IPv6ExtHdrRouting, len = 0x00, PadN option (0x01) of length 0x04 + child.expect(r"00000000 {:02X} 00 01 04 00 00 00 00".format( + EXT_HDR_NH[IPv6ExtHdrRouting])) + # IPv6 header + child.expect(r"~~ SNIP 4 - size:\s+40 byte, type: NETTYPE_IPV6 \(\d+\)") + child.expect_exact(r"length: {} next header: {}".format( + ipv6_payload_len, EXT_HDR_NH[IPv6ExtHdrHopByHop] + )) + child.expect_exact(r"destination address: {}".format(ll_dst)) + pktbuf_empty(child) + unregister(child) + + +def test_empty_mixed2_w_dest_opt_registered(child, iface, hw_dst, ll_dst, ll_src): + # Register to destination-option header + register_protnum(child, EXT_HDR_NH[IPv6ExtHdrDestOpt]) + # Try sending a packet with a number of extension headers in not recommended + # (but legal) order + sendp(Ether(dst=hw_dst) / IPv6(dst=ll_dst, src=ll_src) / + IPv6ExtHdrHopByHop() / IPv6ExtHdrRouting() / IPv6ExtHdrDestOpt() / + IPv6ExtHdrFragment() / UDP() / "\x01\x02", + iface=iface, verbose=0) + # Destination option with payload + child.expect(r"~~ SNIP 0 - size:\s+(\d+) byte, type: NETTYPE_\w+ \(\d+\)") + ipv6_payload_len = int(child.match.group(1)) + # NH = IPv6ExtHdrFragment, len = 0x00, PadN option (0x01) of length 0x04 + # NH = 17 (UDP), reserved = 0x00, fragment offset = 0, res = 0, M = 0 + child.expect(r"00000000 {:02X} 00 01 04 00 00 00 00 " + r"11 00 00 00 00 00 00 00".format( + EXT_HDR_NH[IPv6ExtHdrFragment])) + # Routing header + child.expect(r"~~ SNIP 1 - size:\s+(\d+) byte, type: NETTYPE_\w+ \(\d+\)") + ipv6_payload_len += int(child.match.group(1)) + + # NH = IPv6ExtHdrDestOpt, len = 0x00, routing type = 0, segments left = 0 + child.expect(r"00000000 {:02X} 00 00 00 00 00 00 00".format( + EXT_HDR_NH[IPv6ExtHdrDestOpt])) + # Hop-by-hop-option + child.expect(r"~~ SNIP 2 - size:\s+(\d+) byte, type: NETTYPE_\w+ \(\d+\)") + ipv6_payload_len += int(child.match.group(1)) + # NH = IPv6ExtHdrRouting, len = 0x00, PadN option (0x01) of length 0x04 + child.expect(r"00000000 {:02X} 00 01 04 00 00 00 00".format( + EXT_HDR_NH[IPv6ExtHdrRouting])) + # IPv6 header + child.expect(r"~~ SNIP 3 - size:\s+40 byte, type: NETTYPE_IPV6 \(\d+\)") + child.expect_exact(r"length: {} next header: {}".format( + ipv6_payload_len, EXT_HDR_NH[IPv6ExtHdrHopByHop] + )) + child.expect_exact(r"destination address: {}".format(ll_dst)) + pktbuf_empty(child) + unregister(child) + + +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<master>[^\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 (?P<lladdr>[0-9A-Fa-f:]+)/64", + "lladdr" + ) + if res is None: + raise AssertionError( + "Can't find host link-local address on interface {}".format(tap) + ) + else: + return res + + def testfunc(child): - child.expect_exact("pkt->users: 0") + tap = get_bridge(os.environ["TAP"]) + + lladdr_src = get_host_lladdr(tap) + child.sendline("ifconfig") + child.expect("HWaddr: (?P<hwaddr>[A-Fa-f:0-9]+)") + hwaddr_dst = child.match.group("hwaddr").lower() + child.expect("(?P<lladdr>fe80::[A-Fa-f:0-9]+)") + lladdr_dst = child.match.group("lladdr").lower() + + def run(func): + if child.logfile == sys.stdout: + func(child, tap, hwaddr_dst, lladdr_dst, lladdr_src) + else: + try: + func(child, tap, hwaddr_dst, lladdr_dst, lladdr_src) + print(".", end="", flush=True) + except Exception as e: + print("FAILED") + raise e + + run(test_empty_hop_by_hop_opt_wo_register) + run(test_empty_hop_by_hop_opt_w_register) + run(test_empty_duplicate_hop_by_hop_opt) + run(test_empty_non_first_hop_by_hop_opt) + run(test_empty_duplicate_non_first_hop_by_hop_opt) + run(test_empty_routing_header_wo_register) + run(test_empty_routing_header_w_register) + run(test_empty_fragment_header_wo_register) + run(test_empty_fragment_header_w_register) + run(test_empty_dest_opt_wo_register) + run(test_empty_dest_opt_w_register) + # check various registrations with recommended order to validate parsing + # (recommended order, see https://tools.ietf.org/html/rfc8200#section-4.1) + run(test_empty_mixed1_w_hop_opt_registered) + run(test_empty_mixed1_w_rt_hdr_registered) + run(test_empty_mixed1_w_frag_hdr_registered) + run(test_empty_mixed1_w_dest_opt_registered) + # other orders SHOULD also be parsed (and since checking the order is more + # complicated we are able to do that) + run(test_empty_mixed2_w_hop_opt_registered) + run(test_empty_mixed2_w_rt_hdr_registered) + run(test_empty_mixed2_w_frag_hdr_registered) + run(test_empty_mixed2_w_dest_opt_registered) + print("SUCCESS") if __name__ == "__main__": - sys.exit(run(testfunc)) + if os.geteuid() != 0: + print("\x1b[1;31mThis test requires root privileges.\n" + "It's constructing and sending Ethernet frames.\x1b[0m\n", + file=sys.stderr) + sys.exit(1) + sys.exit(run(testfunc, timeout=1, echo=False))