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