Page MenuHomeFreeBSD

D39445.diff
No OneTemporary

D39445.diff

diff --git a/tests/atf_python/sys/net/tools.py b/tests/atf_python/sys/net/tools.py
--- a/tests/atf_python/sys/net/tools.py
+++ b/tests/atf_python/sys/net/tools.py
@@ -30,13 +30,15 @@
def set_sysctl(cls, oid, val):
cls.get_output("sysctl {}={}".format(oid, val))
+ @classmethod
+ def get_netstat_raw(cls, params: str):
+ out = cls.get_output("{} {} --libxo json".format(cls.NETSTAT_PATH, params))
+ return json.loads(out)
+
@classmethod
def get_routes(cls, family: str, fibnum: int = 0):
family_key = {"inet": "-4", "inet6": "-6"}.get(family)
- out = cls.get_output(
- "{} {} -rnW -F {} --libxo json".format(cls.NETSTAT_PATH, family_key, fibnum)
- )
- js = json.loads(out)
+ js = cls.get_netstat_raw("{} -rnW -F {}".format(family_key, fibnum))
js = js["statistics"]["route-information"]["route-table"]["rt-family"]
if js:
return js[0]["rt-entry"]
@@ -46,10 +48,7 @@
@classmethod
def get_nhops(cls, family: str, fibnum: int = 0):
family_key = {"inet": "-4", "inet6": "-6"}.get(family)
- out = cls.get_output(
- "{} {} -onW -F {} --libxo json".format(cls.NETSTAT_PATH, family_key, fibnum)
- )
- js = json.loads(out)
+ js = cls.get_netstat_raw("{} -onW -F {}".format(family_key, fibnum))
js = js["statistics"]["route-nhop-information"]["nhop-table"]["rt-family"]
if js:
return js[0]["nh-entry"]
diff --git a/tests/atf_python/sys/net/vnet.py b/tests/atf_python/sys/net/vnet.py
--- a/tests/atf_python/sys/net/vnet.py
+++ b/tests/atf_python/sys/net/vnet.py
@@ -69,11 +69,29 @@
d = self.addr_map["inet6"]
return d[next(iter(d))]
+ @property
+ def first_ipv6_ll(self):
+ out = self.run_cmd("ifconfig {} inet6".format(self.name))
+ addrs = [l.strip() for l in out.splitlines() if "inet6" in l]
+ for addr in addrs:
+ parts = addr.split()
+ addr = parts[1]
+ plen = parts[3]
+ if addr.startswith("fe80"):
+ addr = addr.split("%")[0]
+ ll_addr = ipaddress.ip_interface("{}%{}".format(addr, plen))
+ return ll_addr
+
@property
def first_ipv4(self):
d = self.addr_map["inet"]
return d[next(iter(d))]
+ @property
+ def mac(self):
+ out = self.run_cmd("ifconfig {} ether".format(self.name))
+ return [l.strip() for l in out.splitlines() if "ether" in l][0].split()[1]
+
def set_vnet(self, vnet_name: str):
self.vnet_name = vnet_name
diff --git a/tests/sys/common/Makefile b/tests/sys/common/Makefile
--- a/tests/sys/common/Makefile
+++ b/tests/sys/common/Makefile
@@ -4,7 +4,6 @@
TESTSDIR= ${TESTSBASE}/sys/common
${PACKAGE}FILES+= vnet.subr
${PACKAGE}FILES+= divert.py
-${PACKAGE}FILES+= sender.py
${PACKAGE}FILES+= net_receiver.py
${PACKAGE}FILESMODE_divert.py=0555
diff --git a/tests/sys/common/sender.py b/tests/sys/common/sender.py
deleted file mode 100755
--- a/tests/sys/common/sender.py
+++ /dev/null
@@ -1,203 +0,0 @@
-#!/usr/bin/env python
-# -
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (c) 2020 Alexander V. Chernikov
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-# 1. Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-# SUCH DAMAGE.
-#
-# $FreeBSD$
-#
-
-
-from functools import partial
-import socket
-import logging
-logging.getLogger("scapy").setLevel(logging.CRITICAL)
-import scapy.all as sc
-import argparse
-import time
-
-
-def parse_args():
- parser = argparse.ArgumentParser(description='divert socket tester')
- parser.add_argument('--dip', type=str, help='destination packet IP')
- parser.add_argument('--sip', type=str, help='source packet IP')
- parser.add_argument('--dmac', type=str, help='packet dst mac')
- parser.add_argument('--smac', type=str, help='packet src mac')
- parser.add_argument('--iface', type=str, help='interface to use')
- parser.add_argument('--test_name', type=str, required=True,
- help='test name to run')
- return parser.parse_args()
-
-
-def send_packet(args, pkt):
- sc.sendp(pkt, iface=args.iface, verbose=False)
-
-
-def is_icmp6_echo_request(pkt):
- return pkt.type == 0x86DD and pkt.payload.nh == 58 and \
- pkt.payload.payload.type == 128
-
-
-def check_forwarded_ip_packet(orig_pkt, fwd_pkt):
- """
- Checks that forwarded ICMP packet @fwd_ptk is the same as
- @orig_pkt. Assumes router-on-the-stick forwarding behaviour:
- * src/dst macs are swapped
- * TTL is decremented
- """
- # Check ether fields
- assert orig_pkt.src == fwd_pkt.dst
- assert orig_pkt.dst == fwd_pkt.src
- assert len(orig_pkt) == len(fwd_pkt)
- # Check IP
- fwd_ip = fwd_pkt[sc.IP]
- orig_ip = orig_pkt[sc.IP]
- assert orig_ip.src == orig_ip.src
- assert orig_ip.dst == fwd_ip.dst
- assert orig_ip.ttl == fwd_ip.ttl + 1
- # Check ICMP
- fwd_icmp = fwd_ip[sc.ICMP]
- orig_icmp = orig_ip[sc.ICMP]
- assert bytes(orig_ip.payload) == bytes(fwd_ip.payload)
-
-
-def fwd_ip_icmp_fast(args):
- """
- Sends ICMP packet via args.iface interface.
- Receives and checks the forwarded packet.
- Assumes forwarding router decrements TTL
- """
-
- def filter_f(x):
- return x.src == args.dmac and x.type == 0x0800
-
- e = sc.Ether(src=args.smac, dst=args.dmac)
- ip = sc.IP(src=args.sip, dst=args.dip)
- icmp = sc.ICMP(type='echo-request')
- pkt = e / ip / icmp
-
- send_cb = partial(send_packet, args, pkt)
- packets = sc.sniff(iface=args.iface, started_callback=send_cb,
- stop_filter=filter_f, lfilter=filter_f, timeout=5)
- assert len(packets) > 0
- fwd_pkt = packets[-1]
- try:
- check_forwarded_ip_packet(pkt, fwd_pkt)
- except Exception as e:
- print('Original packet:')
- pkt.show()
- print('Forwarded packet:')
- fwd_pkt.show()
- for a_packet in packets:
- a_packet.summary()
- raise Exception from e
-
-
-def fwd_ip_icmp_slow(args):
- """
- Sends ICMP packet via args.iface interface.
- Forces slow path processing by introducing IP option.
- Receives and checks the forwarded packet.
- Assumes forwarding router decrements TTL
- """
-
- def filter_f(x):
- return x.src == args.dmac and x.type == 0x0800
-
- e = sc.Ether(src=args.smac, dst=args.dmac)
- # Add IP option to switch to 'normal' IP processing
- stream_id = sc.IPOption_Stream_Id(security=0xFFFF)
- ip = sc.IP(src=args.sip, dst=args.dip,
- options=[sc.IPOption_Stream_Id(security=0xFFFF)])
- icmp = sc.ICMP(type='echo-request')
- pkt = e / ip / icmp
-
- send_cb = partial(send_packet, args, pkt)
- packets = sc.sniff(iface=args.iface, started_callback=send_cb,
- stop_filter=filter_f, lfilter=filter_f, timeout=5)
- assert len(packets) > 0
- check_forwarded_ip_packet(pkt, packets[-1])
-
-
-def check_forwarded_ip6_packet(orig_pkt, fwd_pkt):
- """
- Checks that forwarded ICMP packet @fwd_ptk is the same as
- @orig_pkt. Assumes router-on-the-stick forwarding behaviour:
- * src/dst macs are swapped
- * TTL is decremented
- """
- # Check ether fields
- assert orig_pkt.src == fwd_pkt.dst
- assert orig_pkt.dst == fwd_pkt.src
- assert len(orig_pkt) == len(fwd_pkt)
- # Check IP
- fwd_ip = fwd_pkt[sc.IPv6]
- orig_ip = orig_pkt[sc.IPv6]
- assert orig_ip.src == orig_ip.src
- assert orig_ip.dst == fwd_ip.dst
- assert orig_ip.hlim == fwd_ip.hlim + 1
- # Check ICMPv6
- assert bytes(orig_ip.payload) == bytes(fwd_ip.payload)
-
-
-def fwd_ip6_icmp(args):
- """
- Sends ICMPv6 packet via args.iface interface.
- Receives and checks the forwarded packet.
- Assumes forwarding router decrements TTL
- """
-
- def filter_f(x):
- return x.src == args.dmac and is_icmp6_echo_request(x)
-
- e = sc.Ether(src=args.smac, dst=args.dmac)
- ip = sc.IPv6(src=args.sip, dst=args.dip)
- icmp = sc.ICMPv6EchoRequest()
- pkt = e / ip / icmp
-
- send_cb = partial(send_packet, args, pkt)
- packets = sc.sniff(iface=args.iface, started_callback=send_cb,
- stop_filter=filter_f, lfilter=filter_f, timeout=5)
- assert len(packets) > 0
- fwd_pkt = packets[-1]
- try:
- check_forwarded_ip6_packet(pkt, fwd_pkt)
- except Exception as e:
- print('Original packet:')
- pkt.show()
- print('Forwarded packet:')
- fwd_pkt.show()
- for idx, a_packet in enumerate(packets):
- print('{}: {}'.format(idx, a_packet.summary()))
- raise Exception from e
-
-
-def main():
- args = parse_args()
- test_ptr = globals()[args.test_name]
- test_ptr(args)
-
-
-if __name__ == '__main__':
- main()
diff --git a/tests/sys/netinet/Makefile b/tests/sys/netinet/Makefile
--- a/tests/sys/netinet/Makefile
+++ b/tests/sys/netinet/Makefile
@@ -7,6 +7,7 @@
TESTS_SUBDIRS+= libalias
+ATF_TESTS_PYTEST=test_ip_forward.py
ATF_TESTS_C= ip_reass_test \
so_reuseport_lb_test \
socket_afinet \
diff --git a/tests/sys/netinet/forward.sh b/tests/sys/netinet/forward.sh
deleted file mode 100755
--- a/tests/sys/netinet/forward.sh
+++ /dev/null
@@ -1,273 +0,0 @@
-#!/usr/bin/env atf-sh
-#-
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (c) 2020 Alexander V. Chernikov
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-# 1. Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-# SUCH DAMAGE.
-#
-# $FreeBSD$
-#
-
-. $(atf_get_srcdir)/../common/vnet.subr
-
-atf_test_case "fwd_ip_icmp_iface_fast_success" "cleanup"
-fwd_ip_icmp_iface_fast_success_head() {
-
- atf_set descr 'Test valid IPv4 on-stick fastforwarding to iface'
- atf_set require.user root
- atf_set require.progs scapy
-}
-
-fwd_ip_icmp_iface_fast_success_body() {
-
- vnet_init
-
- ip4a="192.0.2.1"
- ip4b="192.0.2.2"
- plen=29
- src_ip="192.0.2.3"
-
- script_name="../common/sender.py"
-
- epair=$(vnet_mkepair)
- ifconfig ${epair}a up
- ifconfig ${epair}a inet ${ip4a}/${plen}
-
- jname="v4t-fwd_ip_icmp_iface_fast_success"
- vnet_mkjail ${jname} ${epair}b
- jexec ${jname} ifconfig ${epair}b up
- jexec ${jname} ifconfig ${epair}b inet ${ip4b}/${plen}
-
- # Get router ip/mac
- jail_ip=${ip4b}
- jail_mac=`jexec ${jname} ifconfig ${epair}b ether | awk '$1~/ether/{print$2}'`
-
- our_mac=`ifconfig ${epair}a ether | awk '$1~/ether/{print$2}'`
-
- jexec ${jname} sysctl net.inet.ip.forwarding=1
- # As we're doing router-on-the-stick, turn sending IP redirects off:
- jexec ${jname} sysctl net.inet.ip.redirect=0
-
- # echo "LOCAL: ${local_ip} ${local_mac}"
- # echo "REMOTE: ${remote_rtr_ip} ${remote_rtr_mac}"
-
- atf_check -s exit:0 $(atf_get_srcdir)/${script_name} \
- --test_name fwd_ip_icmp_fast \
- --smac ${our_mac} --dmac ${jail_mac} \
- --sip ${src_ip} --dip ${ip4a} \
- --iface ${epair}a
-
- # check counters are valid
- atf_check -o match:'1 packet forwarded \(1 packet fast forwarded\)' jexec ${jname} netstat -sp ip
-}
-
-fwd_ip_icmp_iface_fast_success_cleanup() {
-
- vnet_cleanup
-}
-
-atf_test_case "fwd_ip_icmp_gw_fast_success" "cleanup"
-fwd_ip_icmp_gw_fast_success_head() {
-
- atf_set descr 'Test valid IPv4 on-stick fastforwarding to gw'
- atf_set require.user root
- atf_set require.progs scapy
-}
-
-fwd_ip_icmp_gw_fast_success_body() {
-
- vnet_init
-
- ip4a="192.0.2.1"
- ip4b="192.0.2.2"
- plen=29
- src_ip="192.0.2.3"
- dst_ip="192.0.2.4"
-
- script_name="../common/sender.py"
-
- epair=$(vnet_mkepair)
- ifconfig ${epair}a up
- ifconfig ${epair}a inet ${ip4a}/${plen}
-
- jname="v4t-fwd_ip_icmp_gw_fast_success"
- vnet_mkjail ${jname} ${epair}b
- jexec ${jname} ifconfig ${epair}b up
- jexec ${jname} ifconfig ${epair}b inet ${ip4b}/${plen}
-
- # Get router ip/mac
- jail_ip=${ip4b}
- jail_mac=`jexec ${jname} ifconfig ${epair}b ether | awk '$1~/ether/{print$2}'`
-
- our_mac=`ifconfig ${epair}a ether | awk '$1~/ether/{print$2}'`
-
- jexec ${jname} sysctl net.inet.ip.forwarding=1
- # As we're doing router-on-the-stick, turn sending IP redirects off:
- jexec ${jname} sysctl net.inet.ip.redirect=0
-
- # Add host route
- jexec ${jname} route -4 add -host ${dst_ip} ${ip4a}
-
- # echo "LOCAL: ${local_ip} ${local_mac}"
- # echo "REMOTE: ${remote_rtr_ip} ${remote_rtr_mac}"
-
- atf_check -s exit:0 $(atf_get_srcdir)/${script_name} \
- --test_name fwd_ip_icmp_fast \
- --smac ${our_mac} --dmac ${jail_mac} \
- --sip ${src_ip} --dip ${dst_ip} \
- --iface ${epair}a
-
- # check counters are valid
- atf_check -o match:'1 packet forwarded \(1 packet fast forwarded\)' jexec ${jname} netstat -sp ip
-}
-
-fwd_ip_icmp_gw_fast_success_cleanup() {
-
- vnet_cleanup
-}
-
-atf_test_case "fwd_ip_icmp_iface_slow_success" "cleanup"
-fwd_ip_icmp_iface_slow_success_head() {
-
- atf_set descr 'Test valid IPv4 on-stick "slow" forwarding to iface'
- atf_set require.user root
- atf_set require.progs scapy
-}
-
-fwd_ip_icmp_iface_slow_success_body() {
-
- vnet_init
-
- ip4a="192.0.2.1"
- ip4b="192.0.2.2"
- plen=29
- src_ip="192.0.2.3"
-
- script_name="../common/sender.py"
-
- epair=$(vnet_mkepair)
- ifconfig ${epair}a up
- ifconfig ${epair}a inet ${ip4a}/${plen}
-
- jname="v4t-fwd_ip_icmp_iface_slow_success"
- vnet_mkjail ${jname} ${epair}b
- jexec ${jname} ifconfig ${epair}b up
- jexec ${jname} ifconfig ${epair}b inet ${ip4b}/${plen}
-
- # Get router ip/mac
- jail_ip=${ip4b}
- jail_mac=`jexec ${jname} ifconfig ${epair}b ether | awk '$1~/ether/{print$2}'`
-
- our_mac=`ifconfig ${epair}a ether | awk '$1~/ether/{print$2}'`
-
- jexec ${jname} sysctl net.inet.ip.forwarding=1
- # As we're doing router-on-the-stick, turn sending IP redirects off:
- jexec ${jname} sysctl net.inet.ip.redirect=0
-
- # Generate packet with options to force slow-path
- atf_check -s exit:0 $(atf_get_srcdir)/${script_name} \
- --test_name fwd_ip_icmp_slow \
- --smac ${our_mac} --dmac ${jail_mac} \
- --sip ${src_ip} --dip ${ip4a} \
- --iface ${epair}a
-
- # check counters are valid
- atf_check -o match:'1 packet forwarded \(0 packets fast forwarded\)' jexec ${jname} netstat -sp ip
-}
-
-fwd_ip_icmp_iface_slow_success_cleanup() {
-
- vnet_cleanup
-}
-
-atf_test_case "fwd_ip_icmp_gw_slow_success" "cleanup"
-fwd_ip_icmp_gw_slow_success_head() {
-
- atf_set descr 'Test valid IPv4 on-stick "slow" forwarding to gw'
- atf_set require.user root
- atf_set require.progs scapy
-}
-
-fwd_ip_icmp_gw_slow_success_body() {
-
- vnet_init
-
- ip4a="192.0.2.1"
- ip4b="192.0.2.2"
- plen=29
- src_ip="192.0.2.3"
- dst_ip="192.0.2.4"
-
- script_name="../common/sender.py"
-
- epair=$(vnet_mkepair)
- ifconfig ${epair}a up
- ifconfig ${epair}a inet ${ip4a}/${plen}
-
- jname="v4t-fwd_ip_icmp_gw_slow_success"
- vnet_mkjail ${jname} ${epair}b
- jexec ${jname} ifconfig ${epair}b up
- jexec ${jname} ifconfig ${epair}b inet ${ip4b}/${plen}
-
- # Get router ip/mac
- jail_ip=${ip4b}
- jail_mac=`jexec ${jname} ifconfig ${epair}b ether | awk '$1~/ether/{print$2}'`
-
- our_mac=`ifconfig ${epair}a ether | awk '$1~/ether/{print$2}'`
-
- jexec ${jname} sysctl net.inet.ip.forwarding=1
- # As we're doing router-on-the-stick, turn sending IP redirects off:
- jexec ${jname} sysctl net.inet.ip.redirect=0
-
- # Add host route
- jexec ${jname} route -4 add -host ${dst_ip} ${ip4a}
-
- # echo "LOCAL: ${local_ip} ${local_mac}"
- # echo "REMOTE: ${remote_rtr_ip} ${remote_rtr_mac}"
-
- atf_check -s exit:0 $(atf_get_srcdir)/${script_name} \
- --test_name fwd_ip_icmp_fast \
- --smac ${our_mac} --dmac ${jail_mac} \
- --sip ${src_ip} --dip ${dst_ip} \
- --iface ${epair}a
-
- # check counters are valid
- atf_check -o match:'1 packet forwarded \(1 packet fast forwarded\)' jexec ${jname} netstat -sp ip
-}
-
-fwd_ip_icmp_gw_slow_success_cleanup() {
-
- vnet_cleanup
-}
-
-atf_init_test_cases()
-{
-
- atf_add_test_case "fwd_ip_icmp_iface_fast_success"
- atf_add_test_case "fwd_ip_icmp_gw_fast_success"
- atf_add_test_case "fwd_ip_icmp_iface_slow_success"
- atf_add_test_case "fwd_ip_icmp_gw_slow_success"
-}
-
-# end
-
diff --git a/tests/sys/netinet/test_ip_forward.py b/tests/sys/netinet/test_ip_forward.py
new file mode 100644
--- /dev/null
+++ b/tests/sys/netinet/test_ip_forward.py
@@ -0,0 +1,139 @@
+from functools import partial
+
+import pytest
+from atf_python.sys.net.tools import ToolsHelper
+from atf_python.sys.net.vnet import VnetTestTemplate
+
+sc = None
+
+
+class TestIPForward(VnetTestTemplate):
+ TOPOLOGY = {
+ "vnet1": {"ifaces": ["if1"]},
+ "vnet2": {"ifaces": ["if1"]},
+ "if1": {"prefixes4": [("192.0.2.1/24", "192.0.2.2/24")]},
+ }
+
+ def setup_method(self, method):
+ global sc
+ if sc is None:
+ import scapy.all as _sc
+
+ sc = _sc
+ super().setup_method(method)
+
+ def enable_forwarding(self):
+ ToolsHelper.set_sysctl("net.inet.ip.forwarding", 1)
+
+ def disable_redirects(self):
+ ToolsHelper.set_sysctl("net.inet.ip.redirect", 0)
+
+ def _send_frame(self, iface_name, pkt):
+ sc.sendp(pkt, iface=iface_name, verbose=False)
+
+ @classmethod
+ def _check_forwarded_ip_packet(cls, orig_pkt, fwd_pkt):
+ """
+ Checks that forwarded ICMP packet @fwd_ptk is the same as
+ @orig_pkt. Assumes router-on-the-stick forwarding behaviour:
+ * src/dst macs are swapped
+ * TTL is decremented
+ """
+ # Check ether fields
+ assert orig_pkt.src == fwd_pkt.dst
+ assert orig_pkt.dst == fwd_pkt.src
+ assert len(orig_pkt) == len(fwd_pkt)
+ # Check IP
+ fwd_ip = fwd_pkt[sc.IP]
+ orig_ip = orig_pkt[sc.IP]
+ assert orig_ip.src == orig_ip.src
+ assert orig_ip.dst == fwd_ip.dst
+ assert orig_ip.ttl == fwd_ip.ttl + 1
+ # Check ICMP
+ fwd_icmp = fwd_ip[sc.ICMP]
+ orig_icmp = orig_ip[sc.ICMP]
+ assert bytes(orig_ip.payload) == bytes(fwd_ip.payload)
+
+ @classmethod
+ def check_forwarded_ip_packets(cls, orig_pkt, fwd_packets):
+ assert len(fwd_packets) > 0
+ fwd_pkt = fwd_packets[-1]
+ try:
+ cls._check_forwarded_ip_packet(orig_pkt, fwd_pkt)
+ except Exception:
+ print("Original packet:")
+ orig_pkt.show()
+ print("Forwarded packet:")
+ fwd_pkt.show()
+ for idx, a_packet in enumerate(fwd_packets):
+ print("{}: {}".format(idx, a_packet.summary()))
+ raise
+
+ def vnet2_handler(self, vnet):
+ self.enable_forwarding()
+
+ ip_type, gw_addr, fwd_type = self.wait_object(vnet.pipe)
+
+ if fwd_type == "fast":
+ # As we're doing router-on-the-stick, turn sending IP redirects off
+ # to enable the fast path
+ self.disable_redirects()
+
+ if ip_type != "if":
+ gw = str(gw_addr.ip)
+ ToolsHelper.print_output("route add -4 -host 192.0.2.4 {}".format(gw))
+
+ # Send our MAC so the other jail can form a packet
+ self.send_object(vnet.pipe, vnet.iface_alias_map["if1"].mac)
+ # Wait for the request to send statistics
+ self.wait_object(vnet.pipe)
+ js = ToolsHelper.get_netstat_raw("-sp ip")
+ self.send_object(vnet.pipe, js)
+
+ @pytest.mark.parametrize("fwd_type", ["fast", "slow"])
+ @pytest.mark.parametrize("ip_type", ["if", "gw"])
+ def test_success(self, ip_type, fwd_type):
+ "Test valid IPv4 global unicast fast-forwarding to IP/interface gw"
+ second_vnet = self.vnet_map["vnet2"]
+ iface = self.vnet.iface_alias_map["if1"]
+
+ if ip_type == "gw":
+ gw = iface.first_ipv4
+ target_ipv4 = "192.0.2.4"
+ elif ip_type == "if":
+ gw = ""
+ target_ipv4 = str(iface.first_ipv4.ip)
+
+ # Send the desired GW and forwarding type
+ self.send_object(second_vnet.pipe, (ip_type, gw, fwd_type))
+ # Wait till the second vnet inits & provide us with its mac
+ dst_mac = self.wait_object(second_vnet.pipe)
+
+ # Prepare the packet to output to the forwarding vnet
+ e = sc.Ether(src=iface.mac, dst=dst_mac)
+ if fwd_type == "slow":
+ # Add IP option to switch to 'normal' IP processing
+ ip_opts = [sc.IPOption_Stream_Id(security=0xFFFF)]
+ ip = sc.IP(src=str(iface.first_ipv4.ip), dst=target_ipv4, options=ip_opts)
+ else:
+ ip = sc.IP(src=str(iface.first_ipv4.ip), dst=target_ipv4)
+ icmp = sc.ICMP(type="echo-request")
+ pkt = e / ip / icmp
+ send_cb = partial(self._send_frame, iface.name, pkt)
+
+ def filter_f(x):
+ return x.src == dst_mac and x.type == 0x0800
+
+ fwd_packets = sc.sniff(
+ iface=iface.name,
+ started_callback=send_cb,
+ stop_filter=filter_f,
+ lfilter=filter_f,
+ timeout=5,
+ )
+ self.check_forwarded_ip_packets(pkt, fwd_packets)
+
+ # Finally, check if the forwarder has properly recorded stats
+ self.send_object(second_vnet.pipe, "")
+ js = self.wait_object(second_vnet.pipe)
+ assert js["statistics"]["ip"]["forwarded-packets"] == 1
diff --git a/tests/sys/netinet6/Makefile b/tests/sys/netinet6/Makefile
--- a/tests/sys/netinet6/Makefile
+++ b/tests/sys/netinet6/Makefile
@@ -6,6 +6,7 @@
FILESDIR= ${TESTSDIR}
ATF_TESTS_PYTEST= test_ip6_output.py
+ATF_TESTS_PYTEST+= test_ip6_forward.py
ATF_TESTS_SH= \
exthdr \
mld \
diff --git a/tests/sys/netinet6/forward6.sh b/tests/sys/netinet6/forward6.sh
deleted file mode 100755
--- a/tests/sys/netinet6/forward6.sh
+++ /dev/null
@@ -1,482 +0,0 @@
-#!/usr/bin/env atf-sh
-#-
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (c) 2020 Alexander V. Chernikov
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-# 1. Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-# SUCH DAMAGE.
-#
-# $FreeBSD$
-#
-
-. $(atf_get_srcdir)/../common/vnet.subr
-
-atf_test_case "fwd_ip6_gu_icmp_iface_fast_success" "cleanup"
-fwd_ip6_gu_icmp_iface_fast_success_head() {
-
- atf_set descr 'Test valid IPv6 global unicast fast-forwarding to interface'
- atf_set require.user root
- atf_set require.progs scapy
-}
-
-fwd_ip6_gu_icmp_iface_fast_success_body() {
-
- ids=65529
- id=`printf "%x" ${ids}`
- if [ $$ -gt 65535 ]; then
- xl=`printf "%x" $(($$ - 65535))`
- yl="1"
- else
- xl=`printf "%x" $$`
- yl=""
- fi
-
- vnet_init
-
- ip6a="2001:db8:6666:0000:${yl}:${id}:1:${xl}"
- ip6b="2001:db8:6666:0000:${yl}:${id}:2:${xl}"
- plen=96
-
- src_ip="2001:db8:6666:0000:${yl}:${id}:3:${xl}"
-
- script_name="../common/sender.py"
-
- epair=$(vnet_mkepair)
- ifconfig ${epair}a up
- ifconfig ${epair}a inet6 ${ip6a}/${plen}
-
- jname="v6t-${id}-${yl}-${xl}"
- vnet_mkjail ${jname} ${epair}b
- jexec ${jname} ifconfig ${epair}b up
- jexec ${jname} ifconfig ${epair}b inet6 ${ip6b}/${plen}
-
- jail_mac=`jexec ${jname} ifconfig ${epair}b ether | awk '$1~/ether/{print$2}'`
-
- our_mac=`ifconfig ${epair}a ether | awk '$1~/ether/{print$2}'`
-
- # wait for DAD to complete
- while [ `jexec ${jname} ifconfig ${epair}b inet6 | grep -c tentative` != "0" ]; do
- sleep 0.1
- done
- while [ `ifconfig ${epair}a inet6 | grep -c tentative` != "0" ]; do
- sleep 0.1
- done
-
- jexec ${jname} sysctl net.inet6.ip6.forwarding=1
- # As we're doing router-on-the-stick, turn sending IP redirects off:
- jexec ${jname} sysctl net.inet6.ip6.redirect=0
-
- atf_check -s exit:0 $(atf_get_srcdir)/${script_name} \
- --test_name fwd_ip6_icmp \
- --smac ${our_mac} --dmac ${jail_mac} \
- --sip ${src_ip} --dip ${ip6a} \
- --iface ${epair}a
-
- # check counters are valid
- atf_check -o match:'1 packet forwarded' jexec ${jname} netstat -sp ip6
-}
-
-fwd_ip6_gu_icmp_iface_fast_success_cleanup() {
-
- vnet_cleanup
-}
-
-atf_test_case "fwd_ip6_gu_icmp_gw_gu_fast_success" "cleanup"
-fwd_ip6_gu_icmp_gw_gu_fast_success_head() {
-
- atf_set descr 'Test valid IPv6 global unicast fast-forwarding to GU gw'
- atf_set require.user root
- atf_set require.progs scapy
-}
-
-fwd_ip6_gu_icmp_gw_gu_fast_success_body() {
-
- ids=65528
- id=`printf "%x" ${ids}`
- if [ $$ -gt 65535 ]; then
- xl=`printf "%x" $(($$ - 65535))`
- yl="1"
- else
- xl=`printf "%x" $$`
- yl=""
- fi
-
- vnet_init
-
- ip6a="2001:db8:6666:0000:${yl}:${id}:1:${xl}"
- ip6b="2001:db8:6666:0000:${yl}:${id}:2:${xl}"
- plen=96
-
- src_ip="2001:db8:6666:0000:${yl}:${id}:3:${xl}"
- dst_ip="2001:db8:6666:0000:${yl}:${id}:4:${xl}"
-
- script_name="../common/sender.py"
-
- epair=$(vnet_mkepair)
- ifconfig ${epair}a up
- ifconfig ${epair}a inet6 ${ip6a}/${plen}
-
- jname="v6t-${id}-${yl}-${xl}"
- vnet_mkjail ${jname} ${epair}b
- jexec ${jname} ifconfig ${epair}b up
- jexec ${jname} ifconfig ${epair}b inet6 ${ip6b}/${plen}
-
- jail_mac=`jexec ${jname} ifconfig ${epair}b ether | awk '$1~/ether/{print$2}'`
-
- our_mac=`ifconfig ${epair}a ether | awk '$1~/ether/{print$2}'`
-
- # wait for DAD to complete
- while [ `jexec ${jname} ifconfig ${epair}b inet6 | grep -c tentative` != "0" ]; do
- sleep 0.1
- done
- while [ `ifconfig ${epair}a inet6 | grep -c tentative` != "0" ]; do
- sleep 0.1
- done
-
- # Add static route back to us
- jexec ${jname} route add -6 -host ${dst_ip} ${ip6a}
-
- jexec ${jname} sysctl net.inet6.ip6.forwarding=1
- # As we're doing router-on-the-stick, turn sending IP redirects off:
- jexec ${jname} sysctl net.inet6.ip6.redirect=0
-
- atf_check -s exit:0 $(atf_get_srcdir)/${script_name} \
- --test_name fwd_ip6_icmp \
- --smac ${our_mac} --dmac ${jail_mac} \
- --sip ${src_ip} --dip ${dst_ip} \
- --iface ${epair}a
-
- # check counters are valid
- atf_check -o match:'1 packet forwarded' jexec ${jname} netstat -sp ip6
-}
-
-fwd_ip6_gu_icmp_gw_gu_fast_success_cleanup() {
-
- vnet_cleanup
-}
-
-atf_test_case "fwd_ip6_gu_icmp_gw_ll_fast_success" "cleanup"
-fwd_ip6_gu_icmp_gw_ll_fast_success_head() {
-
- atf_set descr 'Test valid IPv6 global unicast fast-forwarding to LL gw'
- atf_set require.user root
- atf_set require.progs scapy
-}
-
-fwd_ip6_gu_icmp_gw_ll_fast_success_body() {
-
- ids=65527
- id=`printf "%x" ${ids}`
- if [ $$ -gt 65535 ]; then
- xl=`printf "%x" $(($$ - 65535))`
- yl="1"
- else
- xl=`printf "%x" $$`
- yl=""
- fi
-
- vnet_init
-
- ip6a="2001:db8:6666:0000:${yl}:${id}:1:${xl}"
- ip6b="2001:db8:6666:0000:${yl}:${id}:2:${xl}"
- plen=96
-
- src_ip="2001:db8:6666:0000:${yl}:${id}:3:${xl}"
- dst_ip="2001:db8:6666:0000:${yl}:${id}:4:${xl}"
-
- script_name="../common/sender.py"
-
- epair=$(vnet_mkepair)
- ifconfig ${epair}a up
- ifconfig ${epair}a inet6 ${ip6a}/${plen}
-
- jname="v6t-${id}-${yl}-${xl}"
- vnet_mkjail ${jname} ${epair}b
- jexec ${jname} ifconfig ${epair}b up
- jexec ${jname} ifconfig ${epair}b inet6 ${ip6b}/${plen}
-
- jail_mac=`jexec ${jname} ifconfig ${epair}b ether | awk '$1~/ether/{print$2}'`
-
- our_mac=`ifconfig ${epair}a ether | awk '$1~/ether/{print$2}'`
- our_ll_ip=`ifconfig ${epair}a inet6 | awk '$1~/inet6/&& $2~/^fe80:/{print$2}' | awk -F% '{print$1}'`
-
- # wait for DAD to complete
- while [ `jexec ${jname} ifconfig ${epair}b inet6 | grep -c tentative` != "0" ]; do
- sleep 0.1
- done
- while [ `ifconfig ${epair}a inet6 | grep -c tentative` != "0" ]; do
- sleep 0.1
- done
-
- # Add static route back to us
- atf_check -s exit:0 -o ignore jexec ${jname} route add -6 -host ${dst_ip} ${our_ll_ip}%${epair}b
-
- jexec ${jname} sysctl net.inet6.ip6.forwarding=1
- # As we're doing router-on-the-stick, turn sending IP redirects off:
- jexec ${jname} sysctl net.inet6.ip6.redirect=0
-
- atf_check -s exit:0 $(atf_get_srcdir)/${script_name} \
- --test_name fwd_ip6_icmp \
- --smac ${our_mac} --dmac ${jail_mac} \
- --sip ${src_ip} --dip ${dst_ip} \
- --iface ${epair}a
-
- # check counters are valid
- atf_check -o match:'1 packet forwarded' jexec ${jname} netstat -sp ip6
-}
-
-fwd_ip6_gu_icmp_gw_ll_fast_success_cleanup() {
-
- vnet_cleanup
-}
-
-atf_test_case "fwd_ip6_gu_icmp_iface_slow_success" "cleanup"
-fwd_ip6_gu_icmp_iface_slow_success_head() {
-
- atf_set descr 'Test valid IPv6 global unicast fast-forwarding to interface'
- atf_set require.user root
- atf_set require.progs scapy
-}
-
-fwd_ip6_gu_icmp_iface_slow_success_body() {
-
- ids=65526
- id=`printf "%x" ${ids}`
- if [ $$ -gt 65535 ]; then
- xl=`printf "%x" $(($$ - 65535))`
- yl="1"
- else
- xl=`printf "%x" $$`
- yl=""
- fi
-
- vnet_init
-
- ip6a="2001:db8:6666:0000:${yl}:${id}:1:${xl}"
- ip6b="2001:db8:6666:0000:${yl}:${id}:2:${xl}"
- plen=96
-
- src_ip="2001:db8:6666:0000:${yl}:${id}:3:${xl}"
-
- script_name="../common/sender.py"
-
- epair=$(vnet_mkepair)
- ifconfig ${epair}a up
- ifconfig ${epair}a inet6 ${ip6a}/${plen}
-
- jname="v6t-${id}-${yl}-${xl}"
- vnet_mkjail ${jname} ${epair}b
- jexec ${jname} ifconfig ${epair}b up
- jexec ${jname} ifconfig ${epair}b inet6 ${ip6b}/${plen}
-
- jail_mac=`jexec ${jname} ifconfig ${epair}b ether | awk '$1~/ether/{print$2}'`
-
- our_mac=`ifconfig ${epair}a ether | awk '$1~/ether/{print$2}'`
-
- # wait for DAD to complete
- while [ `jexec ${jname} ifconfig ${epair}b inet6 | grep -c tentative` != "0" ]; do
- sleep 0.1
- done
- while [ `ifconfig ${epair}a inet6 | grep -c tentative` != "0" ]; do
- sleep 0.1
- done
-
- jexec ${jname} sysctl net.inet6.ip6.forwarding=1
- # Do not turn off route redirects to ensure slow path is on
-
- atf_check -s exit:0 $(atf_get_srcdir)/${script_name} \
- --test_name fwd_ip6_icmp \
- --smac ${our_mac} --dmac ${jail_mac} \
- --sip ${src_ip} --dip ${ip6a} \
- --iface ${epair}a
-
- # check counters are valid
- atf_check -o match:'1 packet forwarded' jexec ${jname} netstat -sp ip6
-}
-
-fwd_ip6_gu_icmp_iface_slow_success_cleanup() {
-
- vnet_cleanup
-}
-
-atf_test_case "fwd_ip6_gu_icmp_gw_gu_slow_success" "cleanup"
-fwd_ip6_gu_icmp_gw_gu_slow_success_head() {
-
- atf_set descr 'Test valid IPv6 global unicast fast-forwarding to GU gw'
- atf_set require.user root
- atf_set require.progs scapy
-}
-
-fwd_ip6_gu_icmp_gw_gu_slow_success_body() {
-
- ids=65525
- id=`printf "%x" ${ids}`
- if [ $$ -gt 65535 ]; then
- xl=`printf "%x" $(($$ - 65535))`
- yl="1"
- else
- xl=`printf "%x" $$`
- yl=""
- fi
-
- vnet_init
-
- ip6a="2001:db8:6666:0000:${yl}:${id}:1:${xl}"
- ip6b="2001:db8:6666:0000:${yl}:${id}:2:${xl}"
- plen=96
-
- src_ip="2001:db8:6666:0000:${yl}:${id}:3:${xl}"
- dst_ip="2001:db8:6666:0000:${yl}:${id}:4:${xl}"
-
- script_name="../common/sender.py"
-
- epair=$(vnet_mkepair)
- ifconfig ${epair}a up
- ifconfig ${epair}a inet6 ${ip6a}/${plen}
-
- jname="v6t-${id}-${yl}-${xl}"
- vnet_mkjail ${jname} ${epair}b
- jexec ${jname} ifconfig ${epair}b up
- jexec ${jname} ifconfig ${epair}b inet6 ${ip6b}/${plen}
-
- jail_mac=`jexec ${jname} ifconfig ${epair}b ether | awk '$1~/ether/{print$2}'`
-
- our_mac=`ifconfig ${epair}a ether | awk '$1~/ether/{print$2}'`
-
- # wait for DAD to complete
- while [ `jexec ${jname} ifconfig ${epair}b inet6 | grep -c tentative` != "0" ]; do
- sleep 0.1
- done
- while [ `ifconfig ${epair}a inet6 | grep -c tentative` != "0" ]; do
- sleep 0.1
- done
-
- # Add static route back to us
- jexec ${jname} route add -6 -host ${dst_ip} ${ip6a}
-
- jexec ${jname} sysctl net.inet6.ip6.forwarding=1
- # Do not turn off route redirects to ensure slow path is on
-
- # atf_check -s exit:0
- $(atf_get_srcdir)/${script_name} \
- --test_name fwd_ip6_icmp \
- --smac ${our_mac} --dmac ${jail_mac} \
- --sip ${src_ip} --dip ${dst_ip} \
- --iface ${epair}a
- jexec ${jname} netstat -sp ip6
-
- # check counters are valid
- atf_check -o match:'1 packet forwarded' jexec ${jname} netstat -sp ip6
-}
-
-fwd_ip6_gu_icmp_gw_gu_slow_success_cleanup() {
-
- vnet_cleanup
-}
-
-atf_test_case "fwd_ip6_gu_icmp_gw_ll_slow_success" "cleanup"
-fwd_ip6_gu_icmp_gw_ll_slow_success_head() {
-
- atf_set descr 'Test valid IPv6 global unicast fast-forwarding to LL gw'
- atf_set require.user root
- atf_set require.progs scapy
-}
-
-fwd_ip6_gu_icmp_gw_ll_slow_success_body() {
-
- ids=65524
- id=`printf "%x" ${ids}`
- if [ $$ -gt 65535 ]; then
- xl=`printf "%x" $(($$ - 65535))`
- yl="1"
- else
- xl=`printf "%x" $$`
- yl=""
- fi
-
- vnet_init
-
- ip6a="2001:db8:6666:0000:${yl}:${id}:1:${xl}"
- ip6b="2001:db8:6666:0000:${yl}:${id}:2:${xl}"
- plen=96
-
- src_ip="2001:db8:6666:0000:${yl}:${id}:3:${xl}"
- dst_ip="2001:db8:6666:0000:${yl}:${id}:4:${xl}"
-
- script_name="../common/sender.py"
-
- epair=$(vnet_mkepair)
- ifconfig ${epair}a up
- ifconfig ${epair}a inet6 ${ip6a}/${plen}
-
- jname="v6t-${id}-${yl}-${xl}"
- vnet_mkjail ${jname} ${epair}b
- jexec ${jname} ifconfig ${epair}b up
- jexec ${jname} ifconfig ${epair}b inet6 ${ip6b}/${plen}
-
- jail_mac=`jexec ${jname} ifconfig ${epair}b ether | awk '$1~/ether/{print$2}'`
-
- our_mac=`ifconfig ${epair}a ether | awk '$1~/ether/{print$2}'`
- our_ll_ip=`ifconfig ${epair}a inet6 | awk '$1~/inet6/&& $2~/^fe80:/{print$2}' | awk -F% '{print$1}'`
-
- # wait for DAD to complete
- while [ `jexec ${jname} ifconfig ${epair}b inet6 | grep -c tentative` != "0" ]; do
- sleep 0.1
- done
- while [ `ifconfig ${epair}a inet6 | grep -c tentative` != "0" ]; do
- sleep 0.1
- done
-
- # Add static route back to us
- atf_check -s exit:0 -o ignore jexec ${jname} route add -6 -host ${dst_ip} ${our_ll_ip}%${epair}b
-
- jexec ${jname} sysctl net.inet6.ip6.forwarding=1
- # Do not turn off route redirects to ensure slow path is on
-
- atf_check -s exit:0 $(atf_get_srcdir)/${script_name} \
- --test_name fwd_ip6_icmp \
- --smac ${our_mac} --dmac ${jail_mac} \
- --sip ${src_ip} --dip ${dst_ip} \
- --iface ${epair}a
-
- # check counters are valid
- atf_check -o match:'1 packet forwarded' jexec ${jname} netstat -sp ip6
-}
-
-fwd_ip6_gu_icmp_gw_ll_slow_success_cleanup() {
-
- vnet_cleanup
-}
-
-atf_init_test_cases()
-{
-
- atf_add_test_case "fwd_ip6_gu_icmp_iface_fast_success"
- atf_add_test_case "fwd_ip6_gu_icmp_gw_gu_fast_success"
- atf_add_test_case "fwd_ip6_gu_icmp_gw_ll_fast_success"
- atf_add_test_case "fwd_ip6_gu_icmp_iface_slow_success"
- atf_add_test_case "fwd_ip6_gu_icmp_gw_gu_slow_success"
- atf_add_test_case "fwd_ip6_gu_icmp_gw_ll_slow_success"
-}
-
-# end
-
diff --git a/tests/sys/netinet6/test_ip6_forward.py b/tests/sys/netinet6/test_ip6_forward.py
new file mode 100644
--- /dev/null
+++ b/tests/sys/netinet6/test_ip6_forward.py
@@ -0,0 +1,138 @@
+from functools import partial
+
+import pytest
+import scapy.all as sc
+from atf_python.sys.net.tools import ToolsHelper
+from atf_python.sys.net.vnet import VnetTestTemplate
+
+
+def is_icmp6_echo_request(pkt):
+ return (
+ pkt.type == 0x86DD and pkt.payload.nh == 58 and pkt.payload.payload.type == 128
+ )
+
+
+class TestIP6Forward(VnetTestTemplate):
+ TOPOLOGY = {
+ "vnet1": {"ifaces": ["if1", "if2"]},
+ "vnet2": {"ifaces": ["if1", "if2"]},
+ "if1": {"prefixes6": [("2001:db8:a::1/64", "2001:db8:a::2/64")]},
+ "if2": {"prefixes6": [("2001:db8:b::1/64", "2001:db8:b::2/64")]},
+ }
+
+ def enable_forwarding(self):
+ ToolsHelper.set_sysctl("net.inet6.ip6.forwarding", 1)
+
+ def disable_redirects(self):
+ ToolsHelper.set_sysctl("net.inet6.ip6.redirect", 0)
+
+ def _send_frame(self, iface_name, pkt):
+ sc.sendp(pkt, iface=iface_name, verbose=False)
+
+ @staticmethod
+ def _check_forwarded_frame(orig_pkt, fwd_pkt):
+ assert orig_pkt.src == fwd_pkt.dst
+ assert orig_pkt.dst == fwd_pkt.src
+ assert len(orig_pkt) == len(fwd_pkt)
+
+ @classmethod
+ def _check_forwarded_ip6_packet(cls, orig_pkt, fwd_pkt):
+ """
+ Checks that forwarded ICMP packet @fwd_pkt is the same as
+ @orig_pkt. Assumes router-on-the-stick forwarding behaviour:
+ * src/dst macs are swapped
+ * TTL is decremented
+ """
+ # Check ether fields
+ cls._check_forwarded_frame(orig_pkt, fwd_pkt)
+ # Check IP
+ fwd_ip = fwd_pkt[sc.IPv6]
+ orig_ip = orig_pkt[sc.IPv6]
+ assert orig_ip.src == orig_ip.src
+ assert orig_ip.dst == fwd_ip.dst
+ assert orig_ip.hlim == fwd_ip.hlim + 1
+ # Check ICMPv6
+ assert bytes(orig_ip.payload) == bytes(fwd_ip.payload)
+
+ @classmethod
+ def check_forwarded_ip6_packets(cls, orig_pkt, fwd_packets):
+ assert len(fwd_packets) > 0
+ fwd_pkt = fwd_packets[-1]
+ try:
+ cls._check_forwarded_ip6_packet(orig_pkt, fwd_pkt)
+ except Exception:
+ print("Original packet:")
+ orig_pkt.show()
+ print("Forwarded packet:")
+ fwd_pkt.show()
+ for idx, a_packet in enumerate(fwd_packets):
+ print("{}: {}".format(idx, a_packet.summary()))
+ raise
+
+ def vnet2_handler(self, vnet):
+ self.enable_forwarding()
+
+ ip_type, gw_addr, fwd_type = self.wait_object(vnet.pipe)
+
+ if fwd_type == "fast":
+ # As we're doing router-on-the-stick, turn sending IP redirects off
+ # to enable the fast path
+ self.disable_redirects()
+
+ if ip_type != "if":
+ gw = str(gw_addr.ip)
+ if gw_addr.is_link_local:
+ gw = "{}%{}".format(gw, vnet.iface_alias_map["if1"].name)
+ ToolsHelper.print_output("route add -6 -net 2001:db8:f::/64 {}".format(gw))
+
+ # Send our MAC so the other jail can form a packet
+ self.send_object(vnet.pipe, vnet.iface_alias_map["if1"].mac)
+ # Wait for the request to send statistics
+ self.wait_object(vnet.pipe)
+ js = ToolsHelper.get_netstat_raw("-sp ip6")
+ self.send_object(vnet.pipe, js)
+
+ @pytest.mark.parametrize("fwd_type", ["fast", "slow"])
+ @pytest.mark.parametrize("ip_type", ["if", "gu", "ll"])
+ def test_success(self, ip_type, fwd_type):
+ "Test valid IPv6 global unicast fast-forwarding to GU/LL/interface gw"
+ second_vnet = self.vnet_map["vnet2"]
+ iface = self.vnet.iface_alias_map["if1"]
+
+ target_ipv6 = "2001:db8:f::1"
+ if ip_type == "gu":
+ gw = iface.first_ipv6
+ elif ip_type == "ll":
+ gw = iface.first_ipv6_ll
+ elif ip_type == "if":
+ gw = ""
+ target_ipv6 = str(iface.first_ipv6.ip)
+
+ # Send the desired GW and forwarding type
+ self.send_object(second_vnet.pipe, (ip_type, gw, fwd_type))
+ # Wait till the second vnet inits & provide us with its mac
+ dst_mac = self.wait_object(second_vnet.pipe)
+
+ # Prepare the packet to output to the forwarding vnet
+ e = sc.Ether(src=iface.mac, dst=dst_mac)
+ ip = sc.IPv6(src=str(iface.first_ipv6.ip), dst=target_ipv6)
+ icmp = sc.ICMPv6EchoRequest()
+ pkt = e / ip / icmp
+ send_cb = partial(self._send_frame, iface.name, pkt)
+
+ def filter_f(x):
+ return x.src == dst_mac and is_icmp6_echo_request(x)
+
+ fwd_packets = sc.sniff(
+ iface=iface.name,
+ started_callback=send_cb,
+ stop_filter=filter_f,
+ lfilter=filter_f,
+ timeout=5,
+ )
+ self.check_forwarded_ip6_packets(pkt, fwd_packets)
+
+ # Finally, check if the forwarder has properly recorded stats
+ self.send_object(second_vnet.pipe, "")
+ js = self.wait_object(second_vnet.pipe)
+ assert js["statistics"]["ip6"]["forwarded-packets"] == 1

File Metadata

Mime Type
text/plain
Expires
Mon, Feb 3, 8:37 AM (22 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
16411907
Default Alt Text
D39445.diff (41 KB)

Event Timeline