Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F109183164
D39445.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
41 KB
Referenced Files
None
Subscribers
None
D39445.diff
View Options
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
Details
Attached
Mime Type
text/plain
Expires
Sun, Feb 2, 8:23 PM (11 h, 26 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
16411907
Default Alt Text
D39445.diff (41 KB)
Attached To
Mode
D39445: tests: convert forwarding tests to python
Attached
Detach File
Event Timeline
Log In to Comment