diff options
Diffstat (limited to 'tests/sys/net')
46 files changed, 10961 insertions, 0 deletions
diff --git a/tests/sys/net/Makefile b/tests/sys/net/Makefile new file mode 100644 index 000000000000..e390c6e8059d --- /dev/null +++ b/tests/sys/net/Makefile @@ -0,0 +1,47 @@ +PACKAGE= tests + +TESTSDIR= ${TESTSBASE}/sys/net +BINDIR= ${TESTSDIR} + +ATF_TESTS_C+= if_epair +ATF_TESTS_SH+= if_epair_test +ATF_TESTS_SH+= if_bridge_test +TEST_METADATA.if_bridge_test+= execenv="jail" +TEST_METADATA.if_bridge_test+= execenv_jail_params="vnet allow.raw_sockets" +ATF_TESTS_SH+= if_clone_test +ATF_TESTS_SH+= if_gif +ATF_TESTS_SH+= if_lagg_test +ATF_TESTS_SH+= if_stf +ATF_TESTS_SH+= if_tun_test +ATF_TESTS_SH+= if_vlan +ATF_TESTS_SH+= if_wg + +TESTS_SUBDIRS+= bpf +TESTS_SUBDIRS+= if_ovpn +TESTS_SUBDIRS+= routing + +# The netmap bridge application is used by if_wg tests. +.PATH: ${SRCTOP}/tools/tools/netmap +PROGS+= bridge +LIBADD.bridge+= netmap + +# The tests are written to be run in parallel, but doing so leads to random +# panics. I think it's because the kernel's list of interfaces isn't properly +# locked. +TEST_METADATA+= is_exclusive=true + +${PACKAGE}FILES+= \ + dhclient_pcp.conf \ + pcp.py \ + stp.py + +${PACKAGE}FILESMODE_pcp.py= 0555 +${PACKAGE}FILESMODE_stp.py= 0555 + +MAN= +PROGS+= randsleep +PROGS+= transient_tuntap + +CFLAGS+= -I${.CURDIR:H:H} + +.include <bsd.test.mk> diff --git a/tests/sys/net/bpf/Makefile b/tests/sys/net/bpf/Makefile new file mode 100644 index 000000000000..9c8a25b15d16 --- /dev/null +++ b/tests/sys/net/bpf/Makefile @@ -0,0 +1,15 @@ +.include <src.opts.mk> + +PACKAGE= tests + +TESTSDIR= ${TESTSBASE}/sys/net/bpf +BINDIR= ${TESTSDIR} + +LIBADD+= nv + +PROGS= bpf_multi_read +LIBADD.bpf_multi_read+= pcap + +ATF_TESTS_SH= bpf + +.include <bsd.test.mk> diff --git a/tests/sys/net/bpf/bpf.sh b/tests/sys/net/bpf/bpf.sh new file mode 100644 index 000000000000..2830c4862de9 --- /dev/null +++ b/tests/sys/net/bpf/bpf.sh @@ -0,0 +1,67 @@ +## +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2025 Rubicon Communications, LLC ("Netgate") +# +# 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. + +. $(atf_get_srcdir)/../../common/vnet.subr + +atf_test_case "multi_read" "cleanup" +multi_read_head() +{ + atf_set descr 'Test multiple readers on /dev/bpf' + atf_set require.user root +} + +multi_read_body() +{ + vnet_init + + epair=$(vnet_mkepair) + ifconfig ${epair}a inet 192.0.2.1/24 up + + vnet_mkjail alcatraz ${epair}b + jexec alcatraz ifconfig ${epair}b inet 192.0.2.2/24 up + + atf_check -s exit:0 -o ignore \ + ping -c 1 192.0.2.2 + + # Start a multi-thread (or multi-process) read on bpf + $(atf_get_srcdir)/bpf_multi_read ${epair}a & + + # Generate traffic + ping -f 192.0.2.2 >/dev/null 2>&1 & + + # Now let this run for 10 seconds + sleep 10 +} + +multi_read_cleanup() +{ + vnet_cleanup +} + +atf_init_test_cases() +{ + atf_add_test_case "multi_read" +} diff --git a/tests/sys/net/bpf/bpf_multi_read.c b/tests/sys/net/bpf/bpf_multi_read.c new file mode 100644 index 000000000000..3a8edd76d623 --- /dev/null +++ b/tests/sys/net/bpf/bpf_multi_read.c @@ -0,0 +1,76 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2025 Rubicon Communications, LLC (Netgate) + * + * 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. + * + */ + +#include <err.h> +#include <stdio.h> +#include <pcap.h> +#include <unistd.h> + +static void +callback(u_char *arg __unused, const struct pcap_pkthdr *hdr __unused, + const unsigned char *bytes __unused) +{ +} + +int +main(int argc, const char **argv) +{ + pcap_t *pcap; + const char *interface; + char errbuf[PCAP_ERRBUF_SIZE] = { 0 }; + int ret; + + if (argc != 2) + err(1, "Usage: %s <interface>\n", argv[0]); + + interface = argv[1]; + + pcap = pcap_create(interface, errbuf); + if (! pcap) + perror("Failed to pcap interface"); + + ret = pcap_set_snaplen(pcap, 86); + if (ret != 0) + perror("Failed to set snaplen"); + + ret = pcap_set_timeout(pcap, 100); + if (ret != 0) + perror("Failed to set timeout"); + + ret = pcap_activate(pcap); + if (ret != 0) + perror("Failed to activate"); + + /* So we have two readers on one /dev/bpf fd */ + fork(); + + printf("Interface open\n"); + pcap_loop(pcap, 0, callback, NULL); + + return (0); +} diff --git a/tests/sys/net/dhclient_pcp.conf b/tests/sys/net/dhclient_pcp.conf new file mode 100644 index 000000000000..fbd86e5bd0f8 --- /dev/null +++ b/tests/sys/net/dhclient_pcp.conf @@ -0,0 +1 @@ +vlan-pcp 6; diff --git a/tests/sys/net/if_bridge_test.sh b/tests/sys/net/if_bridge_test.sh new file mode 100755 index 000000000000..b3405fd978c8 --- /dev/null +++ b/tests/sys/net/if_bridge_test.sh @@ -0,0 +1,1481 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2020 The FreeBSD Foundation +# +# This software was developed by Kristof Provost under sponsorship +# from the FreeBSD Foundation. +# +# 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. + +. $(atf_get_srcdir)/../common/vnet.subr + +atf_test_case "bridge_transmit_ipv4_unicast" "cleanup" +bridge_transmit_ipv4_unicast_head() +{ + atf_set descr 'bridge_transmit_ipv4_unicast bridging test' + atf_set require.user root +} + +bridge_transmit_ipv4_unicast_body() +{ + vnet_init + vnet_init_bridge + + epair_alcatraz=$(vnet_mkepair) + epair_singsing=$(vnet_mkepair) + + vnet_mkjail alcatraz ${epair_alcatraz}b + vnet_mkjail singsing ${epair_singsing}b + + jexec alcatraz ifconfig ${epair_alcatraz}b 192.0.2.1/24 up + jexec singsing ifconfig ${epair_singsing}b 192.0.2.2/24 up + + bridge=$(vnet_mkbridge) + + ifconfig ${bridge} up + ifconfig ${epair_alcatraz}a up + ifconfig ${epair_singsing}a up + ifconfig ${bridge} addm ${epair_alcatraz}a + ifconfig ${bridge} addm ${epair_singsing}a + + atf_check -s exit:0 -o ignore jexec alcatraz ping -c 3 -t 1 192.0.2.2 + atf_check -s exit:0 -o ignore jexec singsing ping -c 3 -t 1 192.0.2.1 +} + +bridge_transmit_ipv4_unicast_cleanup() +{ + vnet_cleanup +} + +atf_test_case "stp" "cleanup" +stp_head() +{ + atf_set descr 'Spanning tree test' + atf_set require.user root +} + +stp_body() +{ + vnet_init + vnet_init_bridge + + epair_one=$(vnet_mkepair) + epair_two=$(vnet_mkepair) + bridge_a=$(vnet_mkbridge) + bridge_b=$(vnet_mkbridge) + + vnet_mkjail a ${bridge_a} ${epair_one}a ${epair_two}a + vnet_mkjail b ${bridge_b} ${epair_one}b ${epair_two}b + + jexec a ifconfig ${epair_one}a up + jexec a ifconfig ${epair_two}a up + jexec a ifconfig ${bridge_a} addm ${epair_one}a + jexec a ifconfig ${bridge_a} addm ${epair_two}a + + jexec b ifconfig ${epair_one}b up + jexec b ifconfig ${epair_two}b up + jexec b ifconfig ${bridge_b} addm ${epair_one}b + jexec b ifconfig ${bridge_b} addm ${epair_two}b + + jexec a ifconfig ${bridge_a} 192.0.2.1/24 + + # Enable spanning tree + jexec a ifconfig ${bridge_a} stp ${epair_one}a + jexec a ifconfig ${bridge_a} stp ${epair_two}a + jexec b ifconfig ${bridge_b} stp ${epair_one}b + jexec b ifconfig ${bridge_b} stp ${epair_two}b + + jexec b ifconfig ${bridge_b} up + jexec a ifconfig ${bridge_a} up + + # Give STP time to do its thing + sleep 5 + + a_discard=$(jexec a ifconfig ${bridge_a} | grep discarding) + b_discard=$(jexec b ifconfig ${bridge_b} | grep discarding) + + if [ -z "${a_discard}" ] && [ -z "${b_discard}" ] + then + atf_fail "STP failed to detect bridging loop" + fi + + # We must also have at least some forwarding interfaces + a_forwarding=$(jexec a ifconfig ${bridge_a} | grep forwarding) + b_forwarding=$(jexec b ifconfig ${bridge_b} | grep forwarding) + + if [ -z "${a_forwarding}" ] && [ -z "${b_forwarding}" ] + then + atf_fail "STP failed to detect bridging loop" + fi +} + +stp_cleanup() +{ + vnet_cleanup +} + +atf_test_case "stp_vlan" "cleanup" +stp_vlan_head() +{ + atf_set descr 'Spanning tree on VLAN test' + atf_set require.user root +} + +stp_vlan_body() +{ + vnet_init + vnet_init_bridge + + epair_one=$(vnet_mkepair) + epair_two=$(vnet_mkepair) + bridge_a=$(vnet_mkbridge) + bridge_b=$(vnet_mkbridge) + + vnet_mkjail a ${bridge_a} ${epair_one}a ${epair_two}a + vnet_mkjail b ${bridge_b} ${epair_one}b ${epair_two}b + + jexec a ifconfig ${epair_one}a up + jexec a ifconfig ${epair_two}a up + vlan_a_one=$(jexec a ifconfig vlan create vlandev ${epair_one}a vlan 42) + vlan_a_two=$(jexec a ifconfig vlan create vlandev ${epair_two}a vlan 42) + jexec a ifconfig ${vlan_a_one} up + jexec a ifconfig ${vlan_a_two} up + jexec a ifconfig ${bridge_a} addm ${vlan_a_one} + jexec a ifconfig ${bridge_a} addm ${vlan_a_two} + + jexec b ifconfig ${epair_one}b up + jexec b ifconfig ${epair_two}b up + vlan_b_one=$(jexec b ifconfig vlan create vlandev ${epair_one}b vlan 42) + vlan_b_two=$(jexec b ifconfig vlan create vlandev ${epair_two}b vlan 42) + jexec b ifconfig ${vlan_b_one} up + jexec b ifconfig ${vlan_b_two} up + jexec b ifconfig ${bridge_b} addm ${vlan_b_one} + jexec b ifconfig ${bridge_b} addm ${vlan_b_two} + + jexec a ifconfig ${bridge_a} 192.0.2.1/24 + + # Enable spanning tree + jexec a ifconfig ${bridge_a} stp ${vlan_a_one} + jexec a ifconfig ${bridge_a} stp ${vlan_a_two} + jexec b ifconfig ${bridge_b} stp ${vlan_b_one} + jexec b ifconfig ${bridge_b} stp ${vlan_b_two} + + jexec b ifconfig ${bridge_b} up + jexec a ifconfig ${bridge_a} up + + # Give STP time to do its thing + sleep 5 + + a_discard=$(jexec a ifconfig ${bridge_a} | grep discarding) + b_discard=$(jexec b ifconfig ${bridge_b} | grep discarding) + + if [ -z "${a_discard}" ] && [ -z "${b_discard}" ] + then + atf_fail "STP failed to detect bridging loop" + fi + + # We must also have at least some forwarding interfaces + a_forwarding=$(jexec a ifconfig ${bridge_a} | grep forwarding) + b_forwarding=$(jexec b ifconfig ${bridge_b} | grep forwarding) + + if [ -z "${a_forwarding}" ] && [ -z "${b_forwarding}" ] + then + atf_fail "STP failed to detect bridging loop" + fi +} + +stp_vlan_cleanup() +{ + vnet_cleanup +} + +atf_test_case "static" "cleanup" +static_head() +{ + atf_set descr 'Bridge static address test' + atf_set require.user root +} + +static_body() +{ + vnet_init + vnet_init_bridge + + epair=$(vnet_mkepair) + bridge=$(vnet_mkbridge) + + vnet_mkjail one ${bridge} ${epair}a + + ifconfig ${epair}b up + + jexec one ifconfig ${bridge} up + jexec one ifconfig ${epair}a up + jexec one ifconfig ${bridge} addm ${epair}a + + # Wrong interface + atf_check -s exit:1 -o ignore -e ignore \ + jexec one ifconfig ${bridge} static ${epair}b 00:01:02:03:04:05 + + # Bad address format + atf_check -s exit:1 -o ignore -e ignore \ + jexec one ifconfig ${bridge} static ${epair}a 00:01:02:03:04 + + # Correct add + atf_check -s exit:0 -o ignore \ + jexec one ifconfig ${bridge} static ${epair}a 00:01:02:03:04:05 + + # List addresses + atf_check -s exit:0 \ + -o match:"00:01:02:03:04:05 Vlan0 ${epair}a 0 flags=1<STATIC>" \ + jexec one ifconfig ${bridge} addr + + # Delete with bad address format + atf_check -s exit:1 -o ignore -e ignore \ + jexec one ifconfig ${bridge} deladdr 00:01:02:03:04 + + # Delete with unlisted address + atf_check -s exit:1 -o ignore -e ignore \ + jexec one ifconfig ${bridge} deladdr 00:01:02:03:04:06 + + # Correct delete + atf_check -s exit:0 -o ignore \ + jexec one ifconfig ${bridge} deladdr 00:01:02:03:04:05 +} + +static_cleanup() +{ + vnet_cleanup +} + +atf_test_case "vstatic" "cleanup" +vstatic_head() +{ + atf_set descr 'Bridge VLAN static address test' + atf_set require.user root +} + +vstatic_body() +{ + vnet_init + vnet_init_bridge + + epair=$(vnet_mkepair) + bridge=$(vnet_mkbridge) + + vnet_mkjail one ${bridge} ${epair}a + + ifconfig ${epair}b up + + jexec one ifconfig ${bridge} up + jexec one ifconfig ${epair}a up + jexec one ifconfig ${bridge} addm ${epair}a + + # Wrong interface + atf_check -s exit:1 -o ignore -e ignore jexec one \ + ifconfig ${bridge} static ${epair}b 00:01:02:03:04:05 vlan 10 + + # Bad address format + atf_check -s exit:1 -o ignore -e ignore jexec one \ + ifconfig ${bridge} static ${epair}a 00:01:02:03:04 vlan 10 + + # Invalid VLAN ID + atf_check -s exit:1 -o ignore -e ignore jexec one \ + ifconfig ${bridge} static ${epair}a 00:01:02:03:04:05 vlan 5000 + + # Correct add + atf_check -s exit:0 -o ignore jexec one \ + ifconfig ${bridge} static ${epair}a 00:01:02:03:04:05 vlan 10 + + # List addresses + atf_check -s exit:0 \ + -o match:"00:01:02:03:04:05 Vlan10 ${epair}a 0 flags=1<STATIC>" \ + jexec one ifconfig ${bridge} addr + + # Delete with bad address format + atf_check -s exit:1 -o ignore -e ignore jexec one \ + ifconfig ${bridge} deladdr 00:01:02:03:04 vlan 10 + + # Delete with unlisted address + atf_check -s exit:1 -o ignore -e ignore jexec one \ + ifconfig ${bridge} deladdr 00:01:02:03:04:06 vlan 10 + + # Delete with wrong vlan id + atf_check -s exit:1 -o ignore -e ignore jexec one \ + ifconfig ${bridge} deladdr 00:01:02:03:04:05 vlan 20 + + # Correct delete + atf_check -s exit:0 -o ignore jexec one \ + ifconfig ${bridge} deladdr 00:01:02:03:04:05 vlan 10 +} + +vstatic_cleanup() +{ + vnet_cleanup +} + +atf_test_case "span" "cleanup" +span_head() +{ + atf_set descr 'Bridge span test' + atf_set require.user root + atf_set require.progs python3 scapy +} + +span_body() +{ + vnet_init + vnet_init_bridge + + epair=$(vnet_mkepair) + epair_span=$(vnet_mkepair) + bridge=$(vnet_mkbridge) + + vnet_mkjail one ${bridge} ${epair}a ${epair_span}a + + ifconfig ${epair}b up + ifconfig ${epair_span}b up + + jexec one ifconfig ${bridge} up + jexec one ifconfig ${epair}a up + jexec one ifconfig ${epair_span}a up + jexec one ifconfig ${bridge} addm ${epair}a + + jexec one ifconfig ${bridge} span ${epair_span}a + jexec one ifconfig ${bridge} 192.0.2.1/24 + + # Send some traffic through the span + jexec one ping -c 1 -t 1 192.0.2.2 + + # Check that we see the traffic on the span interface + atf_check -s exit:0 \ + $(atf_get_srcdir)/../netpfil/common/pft_ping.py \ + --sendif ${epair}b \ + --to 192.0.2.2 \ + --recvif ${epair_span}b + + jexec one ifconfig ${bridge} -span ${epair_span}a + + # And no more traffic after we remove the span + atf_check -s exit:1 \ + $(atf_get_srcdir)/../netpfil/common/pft_ping.py \ + --sendif ${epair}b \ + --to 192.0.2.2 \ + --recvif ${epair_span}b +} + +span_cleanup() +{ + vnet_cleanup +} + +atf_test_case "delete_with_members" "cleanup" +delete_with_members_head() +{ + atf_set descr 'Delete a bridge which still has member interfaces' + atf_set require.user root +} + +delete_with_members_body() +{ + vnet_init + vnet_init_bridge + + bridge=$(vnet_mkbridge) + epair=$(vnet_mkepair) + + ifconfig ${bridge} 192.0.2.1/24 up + ifconfig ${epair}a up + ifconfig ${bridge} addm ${epair}a + + ifconfig ${bridge} destroy +} + +delete_with_members_cleanup() +{ + vnet_cleanup +} + +atf_test_case "mac_conflict" "cleanup" +mac_conflict_head() +{ + atf_set descr 'Ensure that bridges in different jails get different mac addresses' + atf_set require.user root +} + +mac_conflict_body() +{ + vnet_init + vnet_init_bridge + + epair=$(vnet_mkepair) + + # Ensure the bridge module is loaded so jails can use it. + tmpbridge=$(vnet_mkbridge) + + vnet_mkjail bridge_mac_conflict_one ${epair}a + vnet_mkjail bridge_mac_conflict_two ${epair}b + + jexec bridge_mac_conflict_one ifconfig bridge create + jexec bridge_mac_conflict_one ifconfig bridge0 192.0.2.1/24 up \ + addm ${epair}a + jexec bridge_mac_conflict_one ifconfig ${epair}a up + + jexec bridge_mac_conflict_two ifconfig bridge create + jexec bridge_mac_conflict_two ifconfig bridge0 192.0.2.2/24 up \ + addm ${epair}b + jexec bridge_mac_conflict_two ifconfig ${epair}b up + + atf_check -s exit:0 -o ignore \ + jexec bridge_mac_conflict_one ping -c 3 192.0.2.2 +} + +mac_conflict_cleanup() +{ + vnet_cleanup +} + +atf_test_case "inherit_mac" "cleanup" +inherit_mac_head() +{ + atf_set descr 'Bridge inherit_mac test, #216510' + atf_set require.user root +} + +inherit_mac_body() +{ + vnet_init + vnet_init_bridge + + bridge=$(vnet_mkbridge) + epair=$(vnet_mkepair) + vnet_mkjail one ${bridge} ${epair}a + + jexec one sysctl net.link.bridge.inherit_mac=1 + + # Attempt to provoke the panic described in #216510 + jexec one ifconfig ${bridge} 192.0.0.1/24 up + jexec one ifconfig ${bridge} addm ${epair}a +} + +inherit_mac_cleanup() +{ + vnet_cleanup +} + +atf_test_case "stp_validation" "cleanup" +stp_validation_head() +{ + atf_set descr 'Check STP validation' + atf_set require.user root + atf_set require.progs python3 scapy +} + +stp_validation_body() +{ + vnet_init + vnet_init_bridge + + epair_one=$(vnet_mkepair) + epair_two=$(vnet_mkepair) + bridge=$(vnet_mkbridge) + + ifconfig ${bridge} up + ifconfig ${bridge} addm ${epair_one}a addm ${epair_two}a + ifconfig ${bridge} stp ${epair_one}a stp ${epair_two}a + + ifconfig ${epair_one}a up + ifconfig ${epair_one}b up + ifconfig ${epair_two}a up + ifconfig ${epair_two}b up + + # Wait until the interfaces are no longer discarding + while ifconfig ${bridge} | grep 'state discarding' >/dev/null + do + sleep 1 + done + + # Now inject invalid STP BPDUs on epair_one and see if they're repeated + # on epair_two + atf_check -s exit:0 \ + $(atf_get_srcdir)/stp.py \ + --sendif ${epair_one}b \ + --recvif ${epair_two}b +} + +stp_validation_cleanup() +{ + vnet_cleanup +} + +atf_test_case "gif" "cleanup" +gif_head() +{ + atf_set descr 'gif as a bridge member' + atf_set require.user root +} + +gif_body() +{ + vnet_init + vnet_init_bridge + + epair=$(vnet_mkepair) + + vnet_mkjail one ${epair}a + vnet_mkjail two ${epair}b + + jexec one sysctl net.link.gif.max_nesting=2 + jexec two sysctl net.link.gif.max_nesting=2 + + jexec one ifconfig ${epair}a 192.0.2.1/24 up + jexec two ifconfig ${epair}b 192.0.2.2/24 up + + # Tunnel + gif_one=$(jexec one ifconfig gif create) + gif_two=$(jexec two ifconfig gif create) + + jexec one ifconfig ${gif_one} tunnel 192.0.2.1 192.0.2.2 + jexec one ifconfig ${gif_one} up + jexec two ifconfig ${gif_two} tunnel 192.0.2.2 192.0.2.1 + jexec two ifconfig ${gif_two} up + + bridge_one=$(jexec one ifconfig bridge create) + bridge_two=$(jexec two ifconfig bridge create) + jexec one ifconfig ${bridge_one} 198.51.100.1/24 up + jexec one ifconfig ${bridge_one} addm ${gif_one} + jexec two ifconfig ${bridge_two} 198.51.100.2/24 up + jexec two ifconfig ${bridge_two} addm ${gif_two} + + # Sanity check + atf_check -s exit:0 -o ignore \ + jexec one ping -c 1 192.0.2.2 + + # Test tunnel + atf_check -s exit:0 -o ignore \ + jexec one ping -c 1 198.51.100.2 + atf_check -s exit:0 -o ignore \ + jexec one ping -c 1 -s 1200 198.51.100.2 + atf_check -s exit:0 -o ignore \ + jexec one ping -c 1 -s 2000 198.51.100.2 + + # Higher MTU on the tunnel than on the underlying interface + jexec one ifconfig ${epair}a mtu 1000 + jexec two ifconfig ${epair}b mtu 1000 + + atf_check -s exit:0 -o ignore \ + jexec one ping -c 1 -s 1200 198.51.100.2 + atf_check -s exit:0 -o ignore \ + jexec one ping -c 1 -s 2000 198.51.100.2 + + # Assigning IP addresses on the gif tunneling interfaces + jexec one sysctl net.link.bridge.member_ifaddrs=1 + atf_check -s exit:0 -o ignore \ + jexec one ifconfig ${gif_one} 192.168.0.224/24 192.168.169.254 + atf_check -s exit:0 -o ignore \ + jexec one ifconfig ${gif_one} inet6 no_dad 2001:db8::1/64 + jexec one ifconfig ${bridge_one} deletem ${gif_one} + atf_check -s exit:0 -o ignore \ + jexec one ifconfig ${bridge_one} addm ${gif_one} + + jexec two sysctl net.link.bridge.member_ifaddrs=0 + atf_check -s exit:0 -o ignore \ + jexec two ifconfig ${gif_two} 192.168.169.254/24 192.168.0.224 + atf_check -s exit:0 -o ignore \ + jexec two ifconfig ${gif_two} inet6 no_dad 2001:db8::2/64 + jexec two ifconfig ${bridge_two} deletem ${gif_two} + atf_check -s exit:0 -o ignore \ + jexec two ifconfig ${bridge_two} addm ${gif_two} +} + +gif_cleanup() +{ + vnet_cleanup +} + +atf_test_case "mtu" "cleanup" +mtu_head() +{ + atf_set descr 'Bridge MTU changes' + atf_set require.user root +} + +get_mtu() +{ + intf=$1 + + ifconfig ${intf} | awk '$5 == "mtu" { print $6 }' +} + +check_mtu() +{ + intf=$1 + expected=$2 + + mtu=$(get_mtu $intf) + if [ "$mtu" -ne "$expected" ]; + then + atf_fail "Expected MTU of $expected on $intf but found $mtu" + fi +} + +mtu_body() +{ + vnet_init + vnet_init_bridge + + epair=$(vnet_mkepair) + gif=$(ifconfig gif create) + echo ${gif} >> created_interfaces.lst + bridge=$(vnet_mkbridge) + + atf_check -s exit:0 \ + ifconfig ${bridge} addm ${epair}a + + ifconfig ${gif} mtu 1500 + atf_check -s exit:0 \ + ifconfig ${bridge} addm ${gif} + + # Changing MTU changes it for all member interfaces + atf_check -s exit:0 \ + ifconfig ${bridge} mtu 2000 + + check_mtu ${bridge} 2000 + check_mtu ${gif} 2000 + check_mtu ${epair}a 2000 + + # Rejected MTUs mean none of the MTUs change + atf_check -s exit:1 -e ignore \ + ifconfig ${bridge} mtu 9000 + + check_mtu ${bridge} 2000 + check_mtu ${gif} 2000 + check_mtu ${epair}a 2000 + + # We're not allowed to change the MTU of a member interface + atf_check -s exit:1 -e ignore \ + ifconfig ${epair}a mtu 1900 + check_mtu ${epair}a 2000 + + # Test adding an interface with a different MTU + new_epair=$(vnet_mkepair) + check_mtu ${new_epair}a 1500 + atf_check -s exit:0 -e ignore \ + ifconfig ${bridge} addm ${new_epair}a + + check_mtu ${bridge} 2000 + check_mtu ${gif} 2000 + check_mtu ${epair}a 2000 + check_mtu ${new_epair}a 2000 +} + +mtu_cleanup() +{ + vnet_cleanup +} + +atf_test_case "vlan" "cleanup" +vlan_head() +{ + atf_set descr 'Ensure the bridge takes vlan ID into account, PR#270559' + atf_set require.user root +} + +vlan_body() +{ + vnet_init + vnet_init_bridge + + vid=1 + + epaira=$(vnet_mkepair) + epairb=$(vnet_mkepair) + + br=$(vnet_mkbridge) + + vnet_mkjail one ${epaira}b + vnet_mkjail two ${epairb}b + + ifconfig ${br} up + ifconfig ${epaira}a up + ifconfig ${epairb}a up + ifconfig ${br} addm ${epaira}a addm ${epairb}a + + jexec one ifconfig ${epaira}b up + jexec one ifconfig ${epaira}b.${vid} create + + jexec two ifconfig ${epairb}b up + jexec two ifconfig ${epairb}b.${vid} create + + # Create a MAC address conflict between an untagged and tagged interface + jexec two ifconfig ${epairb}b.${vid} ether 02:05:6e:06:28:1a + jexec one ifconfig ${epaira}b ether 02:05:6e:06:28:1a + jexec one ifconfig ${epaira}b.${vid} ether 02:05:6e:06:28:1b + + # Add ip address, will also populate $br's fowarding table, by ARP announcement + jexec one ifconfig ${epaira}b.${vid} 192.0.2.1/24 up + jexec two ifconfig ${epairb}b.${vid} 192.0.2.2/24 up + + sleep 0.5 + + ifconfig ${br} + jexec one ifconfig + jexec two ifconfig + ifconfig ${br} addr + + atf_check -s exit:0 -o ignore \ + jexec one ping -c 1 -t 1 192.0.2.2 + + # This will trigger a mac flap (by ARP announcement) + jexec one ifconfig ${epaira}b 192.0.2.1/24 up + + sleep 0.5 + + ifconfig ${br} addr + + atf_check -s exit:0 -o ignore \ + jexec one ping -c 1 -t 1 192.0.2.2 +} + +vlan_cleanup() +{ + vnet_cleanup +} + +atf_test_case "many_bridge_members" "cleanup" +many_bridge_members_head() +{ + atf_set descr 'many_bridge_members ifconfig test' + atf_set require.user root +} + +many_bridge_members_body() +{ + vnet_init + vnet_init_bridge + + bridge=$(vnet_mkbridge) + ifcount=256 + for _ in $(seq 1 $ifcount); do + epair=$(vnet_mkepair) + ifconfig "${bridge}" addm "${epair}"a + done + + atf_check -s exit:0 -o inline:"$ifcount\n" \ + sh -c "ifconfig ${bridge} | grep member: | wc -l | xargs" +} + +many_bridge_members_cleanup() +{ + vnet_cleanup +} + +atf_test_case "member_ifaddrs_enabled" "cleanup" +member_ifaddrs_enabled_head() +{ + atf_set descr 'bridge with member_ifaddrs=1' + atf_set require.user root +} + +member_ifaddrs_enabled_body() +{ + vnet_init + vnet_init_bridge + + ep=$(vnet_mkepair) + ifconfig ${ep}a inet 192.0.2.1/24 up + + vnet_mkjail one ${ep}b + jexec one sysctl net.link.bridge.member_ifaddrs=1 + jexec one ifconfig ${ep}b inet 192.0.2.2/24 up + jexec one ifconfig bridge0 create addm ${ep}b + + atf_check -s exit:0 -o ignore ping -c3 -t1 192.0.2.2 +} + +member_ifaddrs_enabled_cleanup() +{ + vnet_cleanup +} + +atf_test_case "member_ifaddrs_disabled" "cleanup" +member_ifaddrs_disabled_head() +{ + atf_set descr 'bridge with member_ifaddrs=0' + atf_set require.user root +} + +member_ifaddrs_disabled_body() +{ + vnet_init + vnet_init_bridge + + vnet_mkjail one + jexec one sysctl net.link.bridge.member_ifaddrs=0 + + bridge=$(jexec one ifconfig bridge create) + + # adding an interface with an IPv4 address + ep=$(jexec one ifconfig epair create) + jexec one ifconfig ${ep} 192.0.2.1/32 + atf_check -s exit:1 -e ignore jexec one ifconfig ${bridge} addm ${ep} + + # adding an interface with an IPv6 address + ep=$(jexec one ifconfig epair create) + jexec one ifconfig ${ep} inet6 2001:db8::1/128 + atf_check -s exit:1 -e ignore jexec one ifconfig ${bridge} addm ${ep} + + # adding an interface with an IPv6 link-local address + ep=$(jexec one ifconfig epair create) + jexec one ifconfig ${ep} inet6 -ifdisabled auto_linklocal up + atf_check -s exit:1 -e ignore jexec one ifconfig ${bridge} addm ${ep} + + # adding an IPv4 address to a member + ep=$(jexec one ifconfig epair create) + jexec one ifconfig ${bridge} addm ${ep} + atf_check -s exit:1 -e ignore jexec one ifconfig ${ep} inet 192.0.2.2/32 + + # adding an IPv6 address to a member + ep=$(jexec one ifconfig epair create) + jexec one ifconfig ${bridge} addm ${ep} + atf_check -s exit:1 -e ignore jexec one ifconfig ${ep} inet6 2001:db8::1/128 +} + +member_ifaddrs_disabled_cleanup() +{ + vnet_cleanup +} + +# +# Test kern/287150: when member_ifaddrs=0, and a physical interface which is in +# a bridge also has a vlan(4) on it, tagged packets are not correctly passed to +# vlan(4). +atf_test_case "member_ifaddrs_vlan" "cleanup" +member_ifaddrs_vlan_head() +{ + atf_set descr 'kern/287150: vlan and bridge on the same interface' + atf_set require.user root +} + +member_ifaddrs_vlan_body() +{ + vnet_init + vnet_init_bridge + + epone=$(vnet_mkepair) + eptwo=$(vnet_mkepair) + + # The first jail has an epair with an IP address on vlan 20. + vnet_mkjail one ${epone}a + atf_check -s exit:0 jexec one ifconfig ${epone}a up + atf_check -s exit:0 jexec one \ + ifconfig ${epone}a.20 create inet 192.0.2.1/24 up + + # The second jail has an epair with an IP address on vlan 20, + # which is also in a bridge. + vnet_mkjail two ${epone}b + + jexec two ifconfig + atf_check -s exit:0 -o save:bridge jexec two ifconfig bridge create + bridge=$(cat bridge) + atf_check -s exit:0 jexec two ifconfig ${bridge} addm ${epone}b up + + atf_check -s exit:0 -o ignore jexec two \ + sysctl net.link.bridge.member_ifaddrs=0 + atf_check -s exit:0 jexec two ifconfig ${epone}b up + atf_check -s exit:0 jexec two \ + ifconfig ${epone}b.20 create inet 192.0.2.2/24 up + + # Make sure the two jails can communicate over the vlan. + atf_check -s exit:0 -o ignore jexec one ping -c 3 -t 1 192.0.2.2 + atf_check -s exit:0 -o ignore jexec two ping -c 3 -t 1 192.0.2.1 +} + +member_ifaddrs_vlan_cleanup() +{ + vnet_cleanup +} + +atf_test_case "vlan_pvid" "cleanup" +vlan_pvid_head() +{ + atf_set descr 'bridge with two ports with pvid and vlanfilter set' + atf_set require.user root +} + +vlan_pvid_body() +{ + vnet_init + vnet_init_bridge + + epone=$(vnet_mkepair) + eptwo=$(vnet_mkepair) + + vnet_mkjail one ${epone}b + vnet_mkjail two ${eptwo}b + + jexec one ifconfig ${epone}b 192.0.2.1/24 up + jexec two ifconfig ${eptwo}b 192.0.2.2/24 up + + bridge=$(vnet_mkbridge) + + ifconfig ${bridge} vlanfilter up + ifconfig ${epone}a up + ifconfig ${eptwo}a up + ifconfig ${bridge} addm ${epone}a untagged 20 + ifconfig ${bridge} addm ${eptwo}a untagged 20 + + # With VLAN filtering enabled, traffic should be passed. + atf_check -s exit:0 -o ignore jexec one ping -c 3 -t 1 192.0.2.2 + atf_check -s exit:0 -o ignore jexec two ping -c 3 -t 1 192.0.2.1 + + # Removed the untagged VLAN on one port; traffic should not be passed. + ifconfig ${bridge} -ifuntagged ${epone}a + atf_check -s exit:2 -o ignore jexec one ping -c 3 -t 1 192.0.2.2 + atf_check -s exit:2 -o ignore jexec two ping -c 3 -t 1 192.0.2.1 +} + +vlan_pvid_cleanup() +{ + vnet_cleanup +} + +atf_test_case "vlan_pvid_filtered" "cleanup" +vlan_pvid_filtered_head() +{ + atf_set descr 'bridge with two ports with different pvids' + atf_set require.user root +} + +vlan_pvid_filtered_body() +{ + vnet_init + vnet_init_bridge + + epone=$(vnet_mkepair) + eptwo=$(vnet_mkepair) + + vnet_mkjail one ${epone}b + vnet_mkjail two ${eptwo}b + + atf_check -s exit:0 jexec one ifconfig ${epone}b 192.0.2.1/24 up + atf_check -s exit:0 jexec two ifconfig ${eptwo}b 192.0.2.2/24 up + + bridge=$(vnet_mkbridge) + + atf_check -s exit:0 ifconfig ${bridge} vlanfilter up + atf_check -s exit:0 ifconfig ${epone}a up + atf_check -s exit:0 ifconfig ${eptwo}a up + atf_check -s exit:0 ifconfig ${bridge} addm ${epone}a untagged 20 + atf_check -s exit:0 ifconfig ${bridge} addm ${eptwo}a untagged 30 + + atf_check -s exit:2 -o ignore jexec one ping -c 3 -t 1 192.0.2.2 + atf_check -s exit:2 -o ignore jexec two ping -c 3 -t 1 192.0.2.1 +} + +vlan_pvid_filtered_cleanup() +{ + vnet_cleanup +} + +atf_test_case "vlan_pvid_tagged" "cleanup" +vlan_pvid_tagged_head() +{ + atf_set descr 'bridge pvid with tagged frames for pvid' + atf_set require.user root +} + +vlan_pvid_tagged_body() +{ + vnet_init + vnet_init_bridge + + epone=$(vnet_mkepair) + eptwo=$(vnet_mkepair) + + vnet_mkjail one ${epone}b + vnet_mkjail two ${eptwo}b + + # Create two tagged interfaces on the appropriate VLANs + atf_check -s exit:0 jexec one ifconfig ${epone}b up + atf_check -s exit:0 jexec one ifconfig ${epone}b.20 \ + create 192.0.2.1/24 up + atf_check -s exit:0 jexec two ifconfig ${eptwo}b up + atf_check -s exit:0 jexec two ifconfig ${eptwo}b.20 \ + create 192.0.2.2/24 up + + bridge=$(vnet_mkbridge) + + atf_check -s exit:0 ifconfig ${bridge} vlanfilter up + atf_check -s exit:0 ifconfig ${epone}a up + atf_check -s exit:0 ifconfig ${eptwo}a up + atf_check -s exit:0 ifconfig ${bridge} addm ${epone}a untagged 20 + atf_check -s exit:0 ifconfig ${bridge} addm ${eptwo}a untagged 20 + + # Tagged frames should not be passed. + atf_check -s exit:2 -o ignore jexec one ping -c 3 -t 1 192.0.2.2 + atf_check -s exit:2 -o ignore jexec two ping -c 3 -t 1 192.0.2.1 +} + +vlan_pvid_tagged_cleanup() +{ + vnet_cleanup +} + +atf_test_case "vlan_pvid_1q" "cleanup" +vlan_pvid_1q_head() +{ + atf_set descr '802.1q tag addition and removal' + atf_set require.user root +} + +vlan_pvid_1q_body() +{ + vnet_init + vnet_init_bridge + + epone=$(vnet_mkepair) + eptwo=$(vnet_mkepair) + + vnet_mkjail one ${epone}b + vnet_mkjail two ${eptwo}b + + # Set up one jail with an access port, and the other with a trunk port. + # This forces the bridge to add and remove .1q tags to bridge the + # traffic. + + atf_check -s exit:0 jexec one ifconfig ${epone}b 192.0.2.1/24 up + atf_check -s exit:0 jexec two ifconfig ${eptwo}b up + atf_check -s exit:0 jexec two ifconfig ${eptwo}b.20 create 192.0.2.2/24 up + + bridge=$(vnet_mkbridge) + + atf_check -s exit:0 ifconfig ${bridge} vlanfilter up + atf_check -s exit:0 ifconfig ${bridge} addm ${epone}a untagged 20 + atf_check -s exit:0 ifconfig ${bridge} addm ${eptwo}a tagged 20 + + atf_check -s exit:0 ifconfig ${epone}a up + atf_check -s exit:0 ifconfig ${eptwo}a up + + atf_check -s exit:0 -o ignore jexec one ping -c 3 -t 1 192.0.2.2 + atf_check -s exit:0 -o ignore jexec two ping -c 3 -t 1 192.0.2.1 +} + +vlan_pvid_1q_cleanup() +{ + vnet_cleanup +} + +# +# Test vlan filtering. +# +atf_test_case "vlan_filtering" "cleanup" +vlan_filtering_head() +{ + atf_set descr 'tagged traffic with filtering' + atf_set require.user root +} + +vlan_filtering_body() +{ + vnet_init + vnet_init_bridge + + epone=$(vnet_mkepair) + eptwo=$(vnet_mkepair) + + vnet_mkjail one ${epone}b + vnet_mkjail two ${eptwo}b + + atf_check -s exit:0 jexec one ifconfig ${epone}b up + atf_check -s exit:0 jexec one ifconfig ${epone}b.20 \ + create 192.0.2.1/24 up + atf_check -s exit:0 jexec two ifconfig ${eptwo}b up + atf_check -s exit:0 jexec two ifconfig ${eptwo}b.20 \ + create 192.0.2.2/24 up + + bridge=$(vnet_mkbridge) + + atf_check -s exit:0 ifconfig ${bridge} vlanfilter up + atf_check -s exit:0 ifconfig ${epone}a up + atf_check -s exit:0 ifconfig ${eptwo}a up + atf_check -s exit:0 ifconfig ${bridge} addm ${epone}a + atf_check -s exit:0 ifconfig ${bridge} addm ${eptwo}a + + # Right now there are no VLANs on the access list, so everything + # should be blocked. + atf_check -s exit:2 -o ignore jexec one ping -c 3 -t 1 192.0.2.2 + atf_check -s exit:2 -o ignore jexec two ping -c 3 -t 1 192.0.2.1 + + # Set the untagged vlan on both ports to 20 and make sure traffic is + # still blocked. We intentionally do not pass tagged traffic for the + # untagged vlan. + atf_check -s exit:0 ifconfig ${bridge} ifuntagged ${epone}a 20 + atf_check -s exit:0 ifconfig ${bridge} ifuntagged ${eptwo}a 20 + + atf_check -s exit:2 -o ignore jexec one ping -c 3 -t 1 192.0.2.2 + atf_check -s exit:2 -o ignore jexec two ping -c 3 -t 1 192.0.2.1 + + atf_check -s exit:0 ifconfig ${bridge} -ifuntagged ${epone}a + atf_check -s exit:0 ifconfig ${bridge} -ifuntagged ${eptwo}a + + # Add VLANs 10-30 to the access list; now access should be allowed. + atf_check -s exit:0 ifconfig ${bridge} +iftagged ${epone}a 10-30 + atf_check -s exit:0 ifconfig ${bridge} +iftagged ${eptwo}a 10-30 + atf_check -s exit:0 -o ignore jexec one ping -c 3 -t 1 192.0.2.2 + atf_check -s exit:0 -o ignore jexec two ping -c 3 -t 1 192.0.2.1 + + # Remove vlan 20 from the access list, now access should be blocked + # again. + atf_check -s exit:0 ifconfig ${bridge} -iftagged ${epone}a 20 + atf_check -s exit:0 ifconfig ${bridge} -iftagged ${eptwo}a 20 + atf_check -s exit:2 -o ignore jexec one ping -c 3 -t 1 192.0.2.2 + atf_check -s exit:2 -o ignore jexec two ping -c 3 -t 1 192.0.2.1 +} + +vlan_filtering_cleanup() +{ + vnet_cleanup +} + +# +# Test the ifconfig 'iftagged' option. +# +atf_test_case "vlan_ifconfig_iftagged" "cleanup" +vlan_ifconfig_iftagged_head() +{ + atf_set descr 'test the ifconfig iftagged option' + atf_set require.user root +} + +vlan_ifconfig_iftagged_body() +{ + vnet_init + vnet_init_bridge + + ep=$(vnet_mkepair) + bridge=$(vnet_mkbridge) + atf_check -s exit:0 ifconfig ${bridge} vlanfilter up + + atf_check -s exit:0 ifconfig ${bridge} addm ${ep}a + atf_check -s exit:0 ifconfig ${ep}a up + + # To start with, no vlans should be configured. + atf_check -s exit:0 -o not-match:"tagged" ifconfig ${bridge} + + # Add vlans 100-149. + atf_check -s exit:0 ifconfig ${bridge} iftagged ${ep}a 100-149 + atf_check -s exit:0 -o match:"tagged 100-149" ifconfig ${bridge} + + # Replace the vlan list with 139-199. + atf_check -s exit:0 ifconfig ${bridge} iftagged ${ep}a 139-199 + atf_check -s exit:0 -o match:"tagged 139-199" ifconfig ${bridge} + + # Add vlans 100-170. + atf_check -s exit:0 ifconfig ${bridge} +iftagged ${ep}a 100-170 + atf_check -s exit:0 -o match:"tagged 100-199" ifconfig ${bridge} + + # Remove vlans 104, 105, and 150-159 + atf_check -s exit:0 ifconfig ${bridge} -iftagged ${ep}a 104,105,150-159 + atf_check -s exit:0 -o match:"tagged 100-103,106-149,160-199" \ + ifconfig ${bridge} + + # Remove the entire vlan list. + atf_check -s exit:0 ifconfig ${bridge} iftagged ${ep}a none + atf_check -s exit:0 -o not-match:"tagged" ifconfig ${bridge} + + # Test some invalid vlans sets. + for bad_vlan in -1 0 4096 4097 foo 0-10 4000-5000 foo-40 40-foo; do + atf_check -s exit:1 -e ignore \ + ifconfig ${bridge} iftagged "$bad_vlan" + done +} + +vlan_ifconfig_iftagged_cleanup() +{ + vnet_cleanup +} + +# +# Test a vlan(4) "SVI" interface on top of a bridge. +# +atf_test_case "vlan_svi" "cleanup" +vlan_svi_head() +{ + atf_set descr 'vlan bridge with an SVI' + atf_set require.user root +} + +vlan_svi_body() +{ + vnet_init + vnet_init_bridge + + epone=$(vnet_mkepair) + + vnet_mkjail one ${epone}b + + atf_check -s exit:0 jexec one ifconfig ${epone}b up + atf_check -s exit:0 jexec one ifconfig ${epone}b.20 \ + create 192.0.2.1/24 up + + bridge=$(vnet_mkbridge) + + atf_check -s exit:0 ifconfig ${bridge} vlanfilter up + atf_check -s exit:0 ifconfig ${epone}a up + atf_check -s exit:0 ifconfig ${bridge} addm ${epone}a tagged 20 + + svi=$(vnet_mkvlan) + atf_check -s exit:0 ifconfig ${svi} vlan 20 vlandev ${bridge} + atf_check -s exit:0 ifconfig ${svi} inet 192.0.2.2/24 up + + atf_check -s exit:0 -o ignore ping -c 3 -t 1 192.0.2.1 +} + +vlan_svi_cleanup() +{ + vnet_cleanup +} + +# +# Test QinQ (802.1ad). +# +atf_test_case "vlan_qinq" "cleanup" +vlan_qinq_head() +{ + atf_set descr 'vlan filtering with QinQ traffic' + atf_set require.user root +} + +vlan_qinq_body() +{ + vnet_init + vnet_init_bridge + + epone=$(vnet_mkepair) + eptwo=$(vnet_mkepair) + + vnet_mkjail one ${epone}b + vnet_mkjail two ${eptwo}b + + # Create a QinQ trunk between the two jails. The outer (provider) tag + # is 5, and the inner tag is 10. + + atf_check -s exit:0 jexec one ifconfig ${epone}b up + atf_check -s exit:0 jexec one \ + ifconfig ${epone}b.5 create vlanproto 802.1ad up + atf_check -s exit:0 jexec one \ + ifconfig ${epone}b.5.10 create inet 192.0.2.1/24 up + + atf_check -s exit:0 jexec two ifconfig ${eptwo}b up + atf_check -s exit:0 jexec two ifconfig \ + ${eptwo}b.5 create vlanproto 802.1ad up + atf_check -s exit:0 jexec two ifconfig \ + ${eptwo}b.5.10 create inet 192.0.2.2/24 up + + bridge=$(vnet_mkbridge) + + atf_check -s exit:0 ifconfig ${bridge} vlanfilter defqinq up + atf_check -s exit:0 ifconfig ${epone}a up + atf_check -s exit:0 ifconfig ${eptwo}a up + atf_check -s exit:0 ifconfig ${bridge} addm ${epone}a + atf_check -s exit:0 ifconfig ${bridge} addm ${eptwo}a + + # Right now there are no VLANs on the access list, so everything + # should be blocked. + atf_check -s exit:2 -o ignore jexec one ping -c 3 -t 1 192.0.2.2 + atf_check -s exit:2 -o ignore jexec two ping -c 3 -t 1 192.0.2.1 + + # Add the provider tag to the access list; now traffic should be passed. + atf_check -s exit:0 ifconfig ${bridge} +iftagged ${epone}a 5 + atf_check -s exit:0 ifconfig ${bridge} +iftagged ${eptwo}a 5 + atf_check -s exit:0 -o ignore jexec one ping -c 3 -t 1 192.0.2.2 + atf_check -s exit:0 -o ignore jexec two ping -c 3 -t 1 192.0.2.1 + + # Remove the qinq flag from one of the interfaces; traffic should + # be blocked again. + atf_check -s exit:0 ifconfig ${bridge} -qinq ${epone}a + atf_check -s exit:2 -o ignore jexec one ping -c 3 -t 1 192.0.2.2 + atf_check -s exit:2 -o ignore jexec two ping -c 3 -t 1 192.0.2.1 +} + +vlan_qinq_cleanup() +{ + vnet_cleanup +} + +# Adding a bridge SVI to a bridge should not be allowed. +atf_test_case "bridge_svi_in_bridge" "cleanup" +bridge_svi_in_bridge_head() +{ + atf_set descr 'adding a bridge SVI to a bridge is not allowed (1)' + atf_set require.user root +} + +bridge_svi_in_bridge_body() +{ + vnet_init + vnet_init_bridge + + bridge=$(vnet_mkbridge) + atf_check -s exit:0 ifconfig ${bridge}.1 create + atf_check -s exit:1 -e ignore ifconfig ${bridge} addm ${bridge}.1 +} + +bridge_svi_in_bridge_cleanup() +{ + vnet_cleanup +} + +atf_test_case "vlan_untagged" "cleanup" +vlan_untagged_head() +{ + atf_set descr 'bridge with two ports with untagged set' + atf_set require.user root +} + +vlan_untagged_body() +{ + vnet_init + vnet_init_bridge + + epone=$(vnet_mkepair) + eptwo=$(vnet_mkepair) + + vnet_mkjail one ${epone}b + vnet_mkjail two ${eptwo}b + + jexec one ifconfig ${epone}b 192.0.2.1/24 up + jexec two ifconfig ${eptwo}b 192.0.2.2/24 up + + bridge=$(vnet_mkbridge) + + ifconfig ${bridge} up + ifconfig ${epone}a up + ifconfig ${eptwo}a up + ifconfig ${bridge} addm ${epone}a untagged 20 + ifconfig ${bridge} addm ${eptwo}a untagged 30 + + # With two ports on different VLANs, traffic should not be passed. + atf_check -s exit:2 -o ignore jexec one ping -c 3 -t 1 192.0.2.2 + atf_check -s exit:2 -o ignore jexec two ping -c 3 -t 1 192.0.2.1 + + # Move the second port to VLAN 20; now traffic should be passed. + atf_check -s exit:0 ifconfig ${bridge} ifuntagged ${eptwo}a 20 + atf_check -s exit:0 -o ignore jexec one ping -c 3 -t 1 192.0.2.2 + atf_check -s exit:0 -o ignore jexec two ping -c 3 -t 1 192.0.2.1 + + # Remove the first's port untagged config, now traffic should + # not pass again. + atf_check -s exit:0 ifconfig ${bridge} -ifuntagged ${epone}a + atf_check -s exit:2 -o ignore jexec one ping -c 3 -t 1 192.0.2.2 + atf_check -s exit:2 -o ignore jexec two ping -c 3 -t 1 192.0.2.1 +} + +vlan_untagged_cleanup() +{ + vnet_cleanup +} + +atf_test_case "vlan_defuntagged" "cleanup" +vlan_defuntagged_head() +{ + atf_set descr 'defuntagged (defpvid) bridge option' + atf_set require.user root +} + +vlan_defuntagged_body() +{ + vnet_init + vnet_init_bridge + + bridge=$(vnet_mkbridge) + + # Invalid VLAN IDs + atf_check -s exit:1 -ematch:"invalid vlan id: 0" \ + ifconfig ${bridge} defuntagged 0 + atf_check -s exit:1 -ematch:"invalid vlan id: 4095" \ + ifconfig ${bridge} defuntagged 4095 + atf_check -s exit:1 -ematch:"invalid vlan id: 5000" \ + ifconfig ${bridge} defuntagged 5000 + + # Check the bridge option is set and cleared correctly + atf_check -s exit:0 -onot-match:"defuntagged=" \ + ifconfig ${bridge} + + atf_check -s exit:0 ifconfig ${bridge} defuntagged 10 + atf_check -s exit:0 -omatch:"defuntagged=10$" \ + ifconfig ${bridge} + + atf_check -s exit:0 ifconfig ${bridge} -defuntagged + atf_check -s exit:0 -onot-match:"defuntagged=" \ + ifconfig ${bridge} + + # Check the untagged option is correctly set on a member + atf_check -s exit:0 ifconfig ${bridge} defuntagged 10 + + epair=$(vnet_mkepair) + atf_check -s exit:0 ifconfig ${bridge} addm ${epair}a + + tag=$(ifconfig ${bridge} | sed -Ene \ + "/member: ${epair}a/ { N;s/.*untagged ([0-9]+).*/\\1/p;q; }") + if [ "$tag" != "10" ]; then + atf_fail "wrong untagged vlan: ${tag}" + fi +} + +vlan_defuntagged_cleanup() +{ + vnet_cleanup +} + +atf_init_test_cases() +{ + atf_add_test_case "bridge_transmit_ipv4_unicast" + atf_add_test_case "stp" + atf_add_test_case "stp_vlan" + atf_add_test_case "static" + atf_add_test_case "vstatic" + atf_add_test_case "span" + atf_add_test_case "inherit_mac" + atf_add_test_case "delete_with_members" + atf_add_test_case "mac_conflict" + atf_add_test_case "stp_validation" + atf_add_test_case "gif" + atf_add_test_case "mtu" + atf_add_test_case "vlan" + atf_add_test_case "many_bridge_members" + atf_add_test_case "member_ifaddrs_enabled" + atf_add_test_case "member_ifaddrs_disabled" + atf_add_test_case "member_ifaddrs_vlan" + atf_add_test_case "vlan_pvid" + atf_add_test_case "vlan_pvid_1q" + atf_add_test_case "vlan_pvid_filtered" + atf_add_test_case "vlan_pvid_tagged" + atf_add_test_case "vlan_filtering" + atf_add_test_case "vlan_ifconfig_iftagged" + atf_add_test_case "vlan_svi" + atf_add_test_case "vlan_qinq" + atf_add_test_case "vlan_untagged" + atf_add_test_case "vlan_defuntagged" + atf_add_test_case "bridge_svi_in_bridge" +} diff --git a/tests/sys/net/if_clone_test.sh b/tests/sys/net/if_clone_test.sh new file mode 100755 index 000000000000..864ff86a7d44 --- /dev/null +++ b/tests/sys/net/if_clone_test.sh @@ -0,0 +1,574 @@ +# +# Copyright (c) 2014 Spectra Logic Corporation +# All rights reserved. +# +# 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, +# without modification. +# 2. Redistributions in binary form must reproduce at minimum a disclaimer +# substantially similar to the "NO WARRANTY" disclaimer below +# ("Disclaimer") and any redistribution must be conditioned upon +# including a substantially similar Disclaimer requirement for further +# binary redistribution. +# +# NO WARRANTY +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. +# +# Authors: Alan Somers (Spectra Logic Corporation) +# + +# Outline: +# For each cloned interface type, do three tests +# 1) Create and destroy it +# 2) Create, up, and destroy it +# 3) Create, disable IPv6 auto address assignment, up, and destroy it + +TESTLEN=10 # seconds + +atf_test_case epair_stress cleanup +epair_stress_head() +{ + atf_set "descr" "Simultaneously create and destroy an epair(4)" + atf_set "require.user" "root" +} +epair_stress_body() +{ + do_stress "epair" +} +epair_stress_cleanup() +{ + cleanup_ifaces +} + +atf_test_case epair_up_stress cleanup +epair_up_stress_head() +{ + atf_set "descr" "Simultaneously up and detroy an epair(4)" + atf_set "require.user" "root" +} +epair_up_stress_body() +{ + do_up_stress "epair" "" "" +} +epair_up_stress_cleanup() +{ + cleanup_ifaces +} + +atf_test_case epair_destroy_race cleanup +epair_destroy_race_head() +{ + atf_set "descr" "Race if_detach() and if_vmove()" + atf_set "require.user" "root" +} +epair_destroy_race_body() +{ + for i in `seq 1 10` + do + epair_a=$(ifconfig epair create) + echo $epair_a >> devices_to_cleanup + epair_b=${epair_a%a}b + + jail -c vnet name="epair_destroy" nopersist path=/ \ + host.hostname="epair_destroy" vnet.interface="$epair_b" \ + command=sh -c "ifconfig $epair_b 192.0.2.1/24; sleep 0.1"& + pid=$! + ifconfig "$epair_a" destroy + wait $pid + done + true +} +epair_destroy_race_cleanup() +{ + cleanup_ifaces +} + +atf_test_case epair_ipv6_up_stress cleanup +epair_ipv6_up_stress_head() +{ + atf_set "descr" "Simultaneously up and destroy an epair(4) with IPv6" + atf_set "require.user" "root" +} +epair_ipv6_up_stress_body() +{ + do_up_stress "epair" "6" "" +} +epair_ipv6_up_stress_cleanup() +{ + cleanup_ifaces +} + +atf_test_case faith_stress cleanup +faith_stress_head() +{ + atf_set "descr" "Simultaneously create and destroy a faith(4)" + atf_set "require.user" "root" +} +faith_stress_body() +{ + do_stress "faith" +} +faith_stress_cleanup() +{ + cleanup_ifaces +} + +atf_test_case faith_up_stress cleanup +faith_up_stress_head() +{ + atf_set "descr" "Simultaneously up and destroy a faith(4)" + atf_set "require.user" "root" +} +faith_up_stress_body() +{ + do_up_stress "faith" "" "" +} +faith_up_stress_cleanup() +{ + cleanup_ifaces +} + +atf_test_case faith_ipv6_up_stress cleanup +faith_ipv6_up_stress_head() +{ + atf_set "descr" "Simultaneously up and destroy a faith(4) with IPv6" + atf_set "require.user" "root" +} +faith_ipv6_up_stress_body() +{ + do_up_stress "faith" "6" "" +} +faith_ipv6_up_stress_cleanup() +{ + cleanup_ifaces +} + +atf_test_case gif_stress cleanup +gif_stress_head() +{ + atf_set "descr" "Simultaneously create and destroy a gif(4)" + atf_set "require.user" "root" +} +gif_stress_body() +{ + do_stress "gif" +} +gif_stress_cleanup() +{ + cleanup_ifaces +} + +atf_test_case gif_up_stress cleanup +gif_up_stress_head() +{ + atf_set "descr" "Simultaneously up and destroy a gif(4)" + atf_set "require.user" "root" +} +gif_up_stress_body() +{ + do_up_stress "gif" "" "p2p" +} +gif_up_stress_cleanup() +{ + cleanup_ifaces +} + +atf_test_case gif_ipv6_up_stress cleanup +gif_ipv6_up_stress_head() +{ + atf_set "descr" "Simultaneously up and destroy a gif(4) with IPv6" + atf_set "require.user" "root" +} +gif_ipv6_up_stress_body() +{ + do_up_stress "gif" "6" "p2p" +} +gif_ipv6_up_stress_cleanup() +{ + cleanup_ifaces +} + +atf_test_case lo_stress cleanup +lo_stress_head() +{ + atf_set "descr" "Simultaneously create and destroy an lo(4)" + atf_set "require.user" "root" +} +lo_stress_body() +{ + do_stress "lo" +} +lo_stress_cleanup() +{ + cleanup_ifaces +} + +atf_test_case lo_up_stress cleanup +lo_up_stress_head() +{ + atf_set "descr" "Simultaneously up and destroy an lo(4)" + atf_set "require.user" "root" +} +lo_up_stress_body() +{ + do_up_stress "lo" "" "" +} +lo_up_stress_cleanup() +{ + cleanup_ifaces +} + +atf_test_case lo_ipv6_up_stress cleanup +lo_ipv6_up_stress_head() +{ + atf_set "descr" "Simultaneously up and destroy an lo(4) with IPv6" + atf_set "require.user" "root" +} +lo_ipv6_up_stress_body() +{ + do_up_stress "lo" "6" "" +} +lo_ipv6_up_stress_cleanup() +{ + cleanup_ifaces +} + +atf_test_case tap_stress cleanup +tap_stress_head() +{ + atf_set "descr" "Simultaneously create and destroy a tap(4)" + atf_set "require.user" "root" +} +tap_stress_body() +{ + do_stress "tap" +} +tap_stress_cleanup() +{ + cleanup_ifaces +} + +atf_test_case tap_up_stress cleanup +tap_up_stress_head() +{ + atf_set "descr" "Simultaneously up and destroy a tap(4)" + atf_set "require.user" "root" +} +tap_up_stress_body() +{ + do_up_stress "tap" "" "" +} +tap_up_stress_cleanup() +{ + cleanup_ifaces +} + +atf_test_case tap_ipv6_up_stress cleanup +tap_ipv6_up_stress_head() +{ + atf_set "descr" "Simultaneously up and destroy a tap(4) with IPv6" + atf_set "require.user" "root" +} +tap_ipv6_up_stress_body() +{ + do_up_stress "tap" "6" "" +} +tap_ipv6_up_stress_cleanup() +{ + cleanup_ifaces +} + +atf_test_case tun_stress cleanup +tun_stress_head() +{ + atf_set "descr" "Simultaneously create and destroy a tun(4)" + atf_set "require.user" "root" +} +tun_stress_body() +{ + do_stress "tun" +} +tun_stress_cleanup() +{ + cleanup_ifaces +} + +atf_test_case tun_up_stress cleanup +tun_up_stress_head() +{ + atf_set "descr" "Simultaneously up and destroy a tun(4)" + atf_set "require.user" "root" +} +tun_up_stress_body() +{ + do_up_stress "tun" "" "p2p" +} +tun_up_stress_cleanup() +{ + cleanup_ifaces +} + +atf_test_case tun_ipv6_up_stress cleanup +tun_ipv6_up_stress_head() +{ + atf_set "descr" "Simultaneously up and destroy a tun(4) with IPv6" + atf_set "require.user" "root" +} +tun_ipv6_up_stress_body() +{ + do_up_stress "tun" "6" "p2p" +} +tun_ipv6_up_stress_cleanup() +{ + cleanup_ifaces +} + +atf_test_case vlan_stress cleanup +vlan_stress_head() +{ + atf_set "descr" "Simultaneously create and destroy a vlan(4)" + atf_set "require.user" "root" +} +vlan_stress_body() +{ + do_stress "vlan" +} +vlan_stress_cleanup() +{ + cleanup_ifaces +} + +atf_test_case vlan_up_stress cleanup +vlan_up_stress_head() +{ + atf_set "descr" "Simultaneously up and destroy a vlan(4)" + atf_set "require.user" "root" +} +vlan_up_stress_body() +{ + do_up_stress "vlan" "" "" +} +vlan_up_stress_cleanup() +{ + cleanup_ifaces +} + +atf_test_case vlan_ipv6_up_stress cleanup +vlan_ipv6_up_stress_head() +{ + atf_set "descr" "Simultaneously up and destroy a vlan(4) with IPv6" + atf_set "require.user" "root" +} +vlan_ipv6_up_stress_body() +{ + do_up_stress "vlan" "6" "" +} +vlan_ipv6_up_stress_cleanup() +{ + cleanup_ifaces +} + +atf_test_case vmnet_stress cleanup +vmnet_stress_head() +{ + atf_set "descr" "Simultaneously create and destroy a vmnet(4)" + atf_set "require.user" "root" +} +vmnet_stress_body() +{ + do_stress "vmnet" +} +vmnet_stress_cleanup() +{ + cleanup_ifaces +} + +atf_test_case vmnet_up_stress cleanup +vmnet_up_stress_head() +{ + atf_set "descr" "Simultaneously up and destroy a vmnet(4)" + atf_set "require.user" "root" +} +vmnet_up_stress_body() +{ + do_up_stress "vmnet" "" "" +} +vmnet_up_stress_cleanup() +{ + cleanup_ifaces +} + +atf_test_case vmnet_ipv6_up_stress cleanup +vmnet_ipv6_up_stress_head() +{ + atf_set "descr" "Simultaneously up and destroy a vmnet(4) with IPv6" + atf_set "require.user" "root" +} +vmnet_ipv6_up_stress_body() +{ + do_up_stress "vmnet" "6" "" +} +vmnet_ipv6_up_stress_cleanup() +{ + cleanup_ifaces +} + +atf_init_test_cases() +{ + atf_add_test_case epair_ipv6_up_stress + atf_add_test_case epair_stress + atf_add_test_case epair_up_stress + atf_add_test_case epair_destroy_race + atf_add_test_case faith_ipv6_up_stress + atf_add_test_case faith_stress + atf_add_test_case faith_up_stress + atf_add_test_case gif_ipv6_up_stress + atf_add_test_case gif_stress + atf_add_test_case gif_up_stress + # Don't test lagg; it has its own test program + atf_add_test_case lo_ipv6_up_stress + atf_add_test_case lo_stress + atf_add_test_case lo_up_stress + atf_add_test_case tap_ipv6_up_stress + atf_add_test_case tap_stress + atf_add_test_case tap_up_stress + atf_add_test_case tun_ipv6_up_stress + atf_add_test_case tun_stress + atf_add_test_case tun_up_stress + atf_add_test_case vlan_ipv6_up_stress + atf_add_test_case vlan_stress + atf_add_test_case vlan_up_stress + atf_add_test_case vmnet_ipv6_up_stress + atf_add_test_case vmnet_stress + atf_add_test_case vmnet_up_stress +} + +do_stress() +{ + local IFACE CREATOR_PID DESTROYER_PID + + IFACE=`get_iface $1` + + # First thread: create the interface + while true; do + ifconfig ${IFACE%a} create 2>/dev/null && \ + echo -n . >> creator_count.txt + done & + CREATOR_PID=$! + + # Second thread: destroy the lagg + while true; do + ifconfig $IFACE destroy 2>/dev/null && \ + echo -n . >> destroyer_count.txt + done & + DESTROYER_PID=$! + + sleep ${TESTLEN} + kill $CREATOR_PID + kill $DESTROYER_PID + echo "Created ${IFACE%a} `stat -f %z creator_count.txt` times." + echo "Destroyed it `stat -f %z destroyer_count.txt` times." +} + +# Implement the up stress tests +# Parameters +# $1 Interface class, etc "lo" or "tap" +# $2 "6" to enable IPv6 auto address assignment, anything else otherwise +# $3 p2p for point to point interfaces, anything else for normal interfaces +do_up_stress() +{ + local ADDR DSTADDR MASK MEAN_SLEEP_SECONDS MAX_SLEEP_USECS \ + IFACE IPV6 P2P SRCDIR LOOP_PID ipv6_cmd up_cmd + + # Configure the interface to use an RFC5737 nonrouteable addresses + ADDR="192.0.2.2" + DSTADDR="192.0.2.3" + MASK="24" + # ifconfig takes about 10ms to run. To increase race coverage, + # randomly delay the two commands relative to each other by 5ms either + # way. + MEAN_SLEEP_SECONDS=.005 + MAX_SLEEP_USECS=10000 + + IFACE=`get_iface $1` + IPV6=$2 + P2P=$3 + + SRCDIR=$( atf_get_srcdir ) + if [ "$IPV6" = 6 ]; then + ipv6_cmd="true" + else + ipv6_cmd="ifconfig $IFACE inet6 ifdisabled" + fi + if [ "$P2P" = "p2p" ]; then + up_cmd="ifconfig $IFACE up ${ADDR}/${MASK} ${DSTADDR}" + else + up_cmd="ifconfig $IFACE up ${ADDR}/${MASK}" + fi + while true; do + eval "$ipv6_cmd" + { sleep ${MEAN_SLEEP_SECONDS} && \ + eval "$up_cmd" 2> /dev/null && + echo -n . >> up_count.txt ; } & + { ${SRCDIR}/randsleep ${MAX_SLEEP_USECS} && \ + ifconfig $IFACE destroy && + echo -n . >> destroy_count.txt ; } & + wait + ifconfig ${IFACE%a} create + done & + LOOP_PID=$! + + sleep ${TESTLEN} + kill $LOOP_PID + echo "Upped ${IFACE} `stat -f %z up_count.txt` times." + echo "Destroyed it `stat -f %z destroy_count.txt` times." +} + +# Creates a new cloned interface, registers it for cleanup, and echoes it +# params: $1 Interface class name (tap, gif, etc) +get_iface() +{ + local CLASS DEV N + + CLASS=$1 + N=0 + while ! ifconfig ${CLASS}${N} create > /dev/null 2>&1; do + if [ "$N" -ge 8 ]; then + atf_skip "Could not create a ${CLASS} interface" + else + N=$(($N + 1)) + fi + done + if [ ${CLASS} = "epair" ]; then + DEV=${CLASS}${N}a + else + DEV=${CLASS}${N} + fi + # Record the device so we can clean it up later + echo ${DEV} >> "devices_to_cleanup" + echo ${DEV} +} + + +cleanup_ifaces() +{ + local DEV + + for DEV in `cat "devices_to_cleanup"`; do + ifconfig ${DEV} destroy + done + true +} diff --git a/tests/sys/net/if_epair.c b/tests/sys/net/if_epair.c new file mode 100644 index 000000000000..5ee4a48aea86 --- /dev/null +++ b/tests/sys/net/if_epair.c @@ -0,0 +1,75 @@ +/*- + * Copyright (c) 2020 Kristof Provost <kp@FreeBSD.org> + * + * 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. + */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/linker.h> +#include <sys/module.h> +#include <sys/socket.h> +#include <sys/types.h> + +#include <net/if.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <strings.h> + +#include <atf-c.h> +#include "freebsd_test_suite/macros.h" + +ATF_TC(params); +ATF_TC_HEAD(params, tc) +{ + atf_tc_set_md_var(tc, "require.user", "root"); + atf_tc_set_md_var(tc, "require.kmods", "if_epair"); +} + +ATF_TC_BODY(params, tc) +{ + struct ifreq ifr; + int s; + + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) + atf_tc_fail("Failed to create socket"); + + bzero(&ifr, sizeof(ifr)); + ifr.ifr_data = (caddr_t)-1; + (void) strlcpy(ifr.ifr_name, "epair", sizeof(ifr.ifr_name)); + + if (ioctl(s, SIOCIFCREATE2, &ifr) < 0) + atf_tc_fail("Failed to create interface"); + + if (ioctl(s, SIOCIFDESTROY, &ifr) < 0) + atf_tc_fail("Failed to destroy interface"); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, params); + + return (atf_no_error()); +} diff --git a/tests/sys/net/if_epair_test.sh b/tests/sys/net/if_epair_test.sh new file mode 100644 index 000000000000..e1d0c0266b2d --- /dev/null +++ b/tests/sys/net/if_epair_test.sh @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2023 Rubicon Communications, LLC (Netgate) +# +# 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. + +. $(atf_get_srcdir)/../common/vnet.subr + +atf_test_case "pcp" "cleanup" +pcp_head() +{ + atf_set descr 'Test PCP over if_epair. PR#270736' + atf_set require.user root +} + +pcp_body() +{ + vnet_init + + j="if_epair_test_pcp" + + epair=$(vnet_mkepair) + + vnet_mkjail ${j}one ${epair}a + vnet_mkjail ${j}two ${epair}b + + jexec ${j}one ifconfig ${epair}a 192.0.2.1/24 pcp 3 up + jexec ${j}two ifconfig ${epair}b 192.0.2.2/24 up + + atf_check -s exit:0 -o ignore \ + jexec ${j}one ping -c 1 192.0.2.2 + + # Now set a different PCP. This used to lead to double tagging and failed pin. + atf_check -s exit:0 -o ignore \ + jexec ${j}one ping -C5 -c 1 192.0.2.2 +} + +pcp_cleanup() +{ + vnet_cleanup +} + +atf_init_test_cases() +{ + atf_add_test_case "pcp" +} diff --git a/tests/sys/net/if_gif.sh b/tests/sys/net/if_gif.sh new file mode 100644 index 000000000000..bff88f9e75b6 --- /dev/null +++ b/tests/sys/net/if_gif.sh @@ -0,0 +1,365 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2021 Rubicon Communications, LLC (Netgate) +# +# 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. + +. $(atf_get_srcdir)/../common/vnet.subr + +atf_test_case "4in4" "cleanup" +4in4_head() +{ + atf_set descr 'IPv4 in IPv4 tunnel' + atf_set require.user root +} + +4in4_body() +{ + vnet_init + if ! kldstat -q -m if_gif; then + atf_skip "This test requires if_gif" + fi + + epair=$(vnet_mkepair) + + vnet_mkjail one ${epair}a + jexec one ifconfig ${epair}a 192.0.2.1/24 up + gone=$(jexec one ifconfig gif create) + jexec one ifconfig $gone tunnel 192.0.2.1 192.0.2.2 + jexec one ifconfig $gone inet 198.51.100.1/24 198.51.100.2 up + + vnet_mkjail two ${epair}b + jexec two ifconfig ${epair}b 192.0.2.2/24 up + gtwo=$(jexec two ifconfig gif create) + jexec two ifconfig $gtwo tunnel 192.0.2.2 192.0.2.1 + jexec two ifconfig $gtwo inet 198.51.100.2/24 198.51.100.1 up + + # Sanity check + atf_check -s exit:0 -o ignore \ + jexec one ping -c 1 192.0.2.2 + + # Tunnel test + atf_check -s exit:0 -o ignore \ + jexec one ping -c 1 198.51.100.2 + atf_check -s exit:0 -o ignore \ + jexec two ping -c 1 198.51.100.1 +} + +4in4_cleanup() +{ + vnet_cleanup +} + +atf_test_case "6in4" "cleanup" +6in4_head() +{ + atf_set descr 'IPv6 in IPv4 tunnel' + atf_set require.user root +} + +6in4_body() +{ + vnet_init + if ! kldstat -q -m if_gif; then + atf_skip "This test requires if_gif" + fi + + epair=$(vnet_mkepair) + + vnet_mkjail one ${epair}a + jexec one ifconfig ${epair}a 192.0.2.1/24 up + gone=$(jexec one ifconfig gif create) + jexec one ifconfig $gone tunnel 192.0.2.1 192.0.2.2 + jexec one ifconfig $gone inet6 no_dad 2001:db8:1::1/64 up + + vnet_mkjail two ${epair}b + jexec two ifconfig ${epair}b 192.0.2.2/24 up + gtwo=$(jexec two ifconfig gif create) + jexec two ifconfig $gtwo tunnel 192.0.2.2 192.0.2.1 + jexec two ifconfig $gtwo inet6 no_dad 2001:db8:1::2/64 up + + # Sanity check + atf_check -s exit:0 -o ignore \ + jexec one ping -c 1 192.0.2.2 + + # Tunnel test + atf_check -s exit:0 -o ignore \ + jexec one ping -6 -c 1 2001:db8:1::2 + atf_check -s exit:0 -o ignore \ + jexec two ping -6 -c 1 2001:db8:1::1 +} + +6in4_cleanup() +{ + vnet_cleanup +} + +atf_test_case "4in6" "cleanup" +4in6_head() +{ + atf_set descr 'IPv4 in IPv6 tunnel' + atf_set require.user root +} + +4in6_body() +{ + vnet_init + if ! kldstat -q -m if_gif; then + atf_skip "This test requires if_gif" + fi + + epair=$(vnet_mkepair) + + vnet_mkjail one ${epair}a + jexec one ifconfig ${epair}a inet6 no_dad 2001:db8::1/64 up + gone=$(jexec one ifconfig gif create) + jexec one ifconfig $gone inet6 tunnel 2001:db8::1 2001:db8::2 + jexec one ifconfig $gone inet 198.51.100.1/24 198.51.100.2 up + + vnet_mkjail two ${epair}b + jexec two ifconfig ${epair}b inet6 no_dad 2001:db8::2/64 up + gtwo=$(jexec two ifconfig gif create) + jexec two ifconfig $gtwo inet6 tunnel 2001:db8::2 2001:db8::1 + jexec two ifconfig $gtwo inet 198.51.100.2/24 198.51.100.1 up + + # Sanity check + atf_check -s exit:0 -o ignore \ + jexec one ping -6 -c 1 2001:db8::2 + + # Tunnel test + atf_check -s exit:0 -o ignore \ + jexec one ping -c 1 198.51.100.2 + atf_check -s exit:0 -o ignore \ + jexec two ping -c 1 198.51.100.1 +} + +4in6_cleanup() +{ + vnet_cleanup +} + +atf_test_case "6in6" "cleanup" +6in6_head() +{ + atf_set descr 'IPv6 in IPv6 tunnel' + atf_set require.user root +} + +6in6_body() +{ + vnet_init + if ! kldstat -q -m if_gif; then + atf_skip "This test requires if_gif" + fi + + epair=$(vnet_mkepair) + + vnet_mkjail one ${epair}a + jexec one ifconfig ${epair}a inet6 no_dad 2001:db8::1/64 up + gone=$(jexec one ifconfig gif create) + jexec one ifconfig $gone inet6 tunnel 2001:db8::1 2001:db8::2 + jexec one ifconfig $gone inet6 no_dad 2001:db8:1::1/64 up + + vnet_mkjail two ${epair}b + jexec two ifconfig ${epair}b inet6 no_dad 2001:db8::2/64 up + gtwo=$(jexec two ifconfig gif create) + jexec two ifconfig $gtwo inet6 tunnel 2001:db8::2 2001:db8::1 + jexec two ifconfig $gtwo inet6 no_dad 2001:db8:1::2/64 up + + # Sanity check + atf_check -s exit:0 -o ignore \ + jexec one ping -6 -c 1 2001:db8::2 + + # Tunnel test + atf_check -s exit:0 -o ignore \ + jexec one ping -6 -c 1 2001:db8:1::2 + atf_check -s exit:0 -o ignore \ + jexec two ping -6 -c 1 2001:db8:1::1 +} + +6in6_cleanup() +{ + vnet_cleanup +} + +atf_test_case "etherip" "cleanup" +etherip_head() +{ + atf_set descr 'EtherIP regression' + atf_set require.user root +} + +etherip_body() +{ + vnet_init + vnet_init_bridge + + if ! kldstat -q -m if_gif; then + atf_skip "This test requires if_gif" + fi + + epair=$(vnet_mkepair) + + vnet_mkjail one ${epair}a + gone=$(jexec one ifconfig gif create) + jexec one ifconfig ${epair}a 192.0.2.1/24 up + jexec one ifconfig $gone tunnel 192.0.2.1 192.0.2.2 + jexec one ifconfig $gone 198.51.100.1/24 198.51.100.2 up + jexec one ifconfig $gone inet6 no_dad 2001:db8:1::1/64 + + bone=$(jexec one ifconfig bridge create) + jexec one ifconfig $bone addm $gone + jexec one ifconfig $bone 192.168.169.253/24 up + jexec one ifconfig $bone inet6 no_dad 2001:db8:2::1/64 + + vnet_mkjail two ${epair}b + gtwo=$(jexec two ifconfig gif create) + jexec two ifconfig ${epair}b 192.0.2.2/24 up + jexec two ifconfig $gtwo tunnel 192.0.2.2 192.0.2.1 + jexec two ifconfig $gtwo 198.51.100.2/24 198.51.100.1 up + jexec two ifconfig $gtwo inet6 no_dad 2001:db8:1::2/64 + + btwo=$(jexec two ifconfig bridge create) + jexec two ifconfig $btwo addm $gtwo + jexec two ifconfig $btwo 192.168.169.254/24 up + jexec two ifconfig $btwo inet6 no_dad 2001:db8:2::2/64 + + # Sanity check + atf_check -s exit:0 -o ignore \ + jexec one ping -c 1 192.0.2.2 + + # EtherIP tunnel test + atf_check -s exit:0 -o ignore \ + jexec one ping -c 1 192.168.169.254 + atf_check -s exit:0 -o ignore \ + jexec one ping -6 -c 1 2001:db8:2::2 + atf_check -s exit:0 -o ignore \ + jexec two ping -c 1 192.168.169.253 + atf_check -s exit:0 -o ignore \ + jexec two ping -6 -c 1 2001:db8:2::1 + + # EtherIP should not affect normal IPv[46] over IPv4 tunnel + # See bugzilla PR 227450 + # IPv4 in IPv4 Tunnel test + atf_check -s exit:0 -o ignore \ + jexec one ping -c 1 198.51.100.2 + atf_check -s exit:0 -o ignore \ + jexec two ping -c 1 198.51.100.1 + + # IPv6 in IPv4 tunnel test + atf_check -s exit:0 -o ignore \ + jexec one ping -6 -c 1 2001:db8:1::2 + atf_check -s exit:0 -o ignore \ + jexec two ping -6 -c 1 2001:db8:1::1 +} + +etherip_cleanup() +{ + vnet_cleanup +} + +atf_test_case "etherip6" "cleanup" +etherip6_head() +{ + atf_set descr 'EtherIP over IPv6 regression' + atf_set require.user root +} + +etherip6_body() +{ + vnet_init + vnet_init_bridge + + if ! kldstat -q -m if_gif; then + atf_skip "This test requires if_gif" + fi + + epair=$(vnet_mkepair) + + vnet_mkjail one ${epair}a + gone=$(jexec one ifconfig gif create) + jexec one ifconfig ${epair}a inet6 no_dad 2001:db8::1/64 up + jexec one ifconfig $gone inet6 tunnel 2001:db8::1 2001:db8::2 + jexec one ifconfig $gone 198.51.100.1/24 198.51.100.2 up + jexec one ifconfig $gone inet6 no_dad 2001:db8:1::1/64 + + bone=$(jexec one ifconfig bridge create) + jexec one ifconfig $bone addm $gone + jexec one ifconfig $bone 192.168.169.253/24 up + jexec one ifconfig $bone inet6 no_dad 2001:db8:2::1/64 + + vnet_mkjail two ${epair}b + gtwo=$(jexec two ifconfig gif create) + jexec two ifconfig ${epair}b inet6 no_dad 2001:db8::2/64 up + jexec two ifconfig $gtwo inet6 tunnel 2001:db8::2 2001:db8::1 + jexec two ifconfig $gtwo 198.51.100.2/24 198.51.100.1 up + jexec two ifconfig $gtwo inet6 no_dad 2001:db8:1::2/64 + + btwo=$(jexec two ifconfig bridge create) + jexec two ifconfig $btwo addm $gtwo + jexec two ifconfig $btwo 192.168.169.254/24 up + jexec two ifconfig $btwo inet6 no_dad 2001:db8:2::2/64 + + # Sanity check + atf_check -s exit:0 -o ignore \ + jexec one ping -6 -c 1 2001:db8::2 + + # EtherIP tunnel test + atf_check -s exit:0 -o ignore \ + jexec one ping -c 1 192.168.169.254 + atf_check -s exit:0 -o ignore \ + jexec one ping -6 -c 1 2001:db8:2::2 + atf_check -s exit:0 -o ignore \ + jexec two ping -c 1 192.168.169.253 + atf_check -s exit:0 -o ignore \ + jexec two ping -6 -c 1 2001:db8:2::1 + + # EtherIP should not affect normal IPv[46] over IPv6 tunnel + # See bugzilla PR 227450 + # IPv4 in IPv6 Tunnel test + atf_check -s exit:0 -o ignore \ + jexec one ping -c 1 198.51.100.2 + atf_check -s exit:0 -o ignore \ + jexec two ping -c 1 198.51.100.1 + + # IPv6 in IPv6 tunnel test + atf_check -s exit:0 -o ignore \ + jexec one ping -6 -c 1 2001:db8:1::2 + atf_check -s exit:0 -o ignore \ + jexec two ping -6 -c 1 2001:db8:1::1 +} + +etherip6_cleanup() +{ + vnet_cleanup +} + +atf_init_test_cases() +{ + atf_add_test_case "4in4" + atf_add_test_case "6in4" + atf_add_test_case "4in6" + atf_add_test_case "6in6" + atf_add_test_case "etherip" + atf_add_test_case "etherip6" +} diff --git a/tests/sys/net/if_lagg_test.sh b/tests/sys/net/if_lagg_test.sh new file mode 100755 index 000000000000..e2b998599991 --- /dev/null +++ b/tests/sys/net/if_lagg_test.sh @@ -0,0 +1,463 @@ +# +# Copyright (c) 2014 Spectra Logic Corporation +# All rights reserved. +# +# 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, +# without modification. +# 2. Redistributions in binary form must reproduce at minimum a disclaimer +# substantially similar to the "NO WARRANTY" disclaimer below +# ("Disclaimer") and any redistribution must be conditioned upon +# including a substantially similar Disclaimer requirement for further +# binary redistribution. +# +# NO WARRANTY +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. +# +# Authors: Alan Somers (Spectra Logic Corporation) +# + +atf_test_case create cleanup +create_head() +{ + atf_set "descr" "Create a lagg and assign an address" + atf_set "require.user" "root" +} +create_body() +{ + local TAP0 TAP1 LAGG MAC + + # Configure the lagg interface to use an RFC5737 nonrouteable addresses + ADDR="192.0.2.2" + MASK="24" + + TAP0=`get_tap` + TAP1=`get_tap` + LAGG=`get_lagg` + + # Create the lagg + ifconfig $TAP0 up + ifconfig $TAP1 up + atf_check ifconfig $LAGG up laggport $TAP0 laggport $TAP1 \ + ${ADDR}/${MASK} + atf_check -o match:"inet ${ADDR}" ifconfig $LAGG + atf_check -o match:"laggport: ${TAP0}" ifconfig $LAGG + atf_check -o match:"laggport: ${TAP1}" ifconfig $LAGG + + # Check that all members have the same MAC + MAC=`ifconfig $LAGG | awk '/ether/ {print $2}'` + atf_check -o match:"ether ${MAC}" ifconfig $TAP0 + atf_check -o match:"ether ${MAC}" ifconfig $TAP1 + + # Check that no members have an IPv6 link-local address. IPv6 + # link-local addresses should never be merged in any way to prevent + # scope violation. + atf_check -o not-match:"inet6 fe80:" ifconfig $TAP0 + atf_check -o not-match:"inet6 fe80:" ifconfig $TAP1 +} +create_cleanup() +{ + cleanup_tap_and_lagg +} + +atf_test_case status_stress cleanup +status_stress_head() +{ + atf_set "descr" "Simultaneously query a lagg while also creating or destroying it." + atf_set "require.user" "root" +} +status_stress_body() +{ + local TAP0 TAP1 LAGG MAC + + # Configure the lagg interface to use an RFC5737 nonrouteable addresses + ADDR="192.0.2.2" + MASK="24" + + TAP0=`get_tap` + TAP1=`get_tap` + TAP2=`get_tap` + TAP3=`get_tap` + LAGG=`get_lagg` + + # Up the lagg's children + ifconfig $TAP0 inet6 ifdisabled up + ifconfig $TAP1 inet6 ifdisabled up + ifconfig $TAP2 inet6 ifdisabled up + ifconfig $TAP3 inet6 ifdisabled up + + # First thread: create and destroy the lagg + while true; do + ifconfig $LAGG destroy 2>&1 + ifconfig $LAGG create 2>/dev/null + ifconfig $LAGG inet6 ifdisabled + ifconfig $LAGG up laggport $TAP0 laggport $TAP1 laggport $TAP2\ + laggport $TAP3 ${ADDR}/${MASK} 2>/dev/null + echo -n . >> creator_count.txt + done & + CREATOR_PID=$! + + # Second thread: Query the lagg's status + while true; do + ifconfig -am 2> /dev/null > /dev/null + echo -n . >> querier_count.txt + done & + QUERIER_PID=$! + + sleep 60 + kill $CREATOR_PID + kill $QUERIER_PID + echo "Created the lagg `stat -f %z creator_count.txt` times." + echo "Queried its status `stat -f %z querier_count.txt` times" +} +status_stress_cleanup() +{ + cleanup_tap_and_lagg +} + +atf_test_case create_destroy_stress cleanup +create_destroy_stress_head() +{ + atf_set "descr" "Simultaneously create and destroy a lagg" + atf_set "require.user" "root" +} +create_destroy_stress_body() +{ + local TAP0 TAP1 LAGG MAC + + TAP0=`get_tap` + TAP1=`get_tap` + TAP2=`get_tap` + TAP3=`get_tap` + LAGG=`get_lagg` + + # Up the lagg's children + ifconfig $TAP0 inet6 ifdisabled up + ifconfig $TAP1 inet6 ifdisabled up + ifconfig $TAP2 inet6 ifdisabled up + ifconfig $TAP3 inet6 ifdisabled up + + # First thread: create the lagg + while true; do + ifconfig $LAGG create 2>/dev/null && \ + echo -n . >> creator_count.txt + done & + CREATOR_PID=$! + + # Second thread: destroy the lagg + while true; do + ifconfig $LAGG destroy 2>/dev/null && \ + echo -n . >> destroyer_count.txt + done & + DESTROYER_PID=$! + + sleep 60 + kill $CREATOR_PID + kill $DESTROYER_PID + echo "Created the lagg `stat -f %z creator_count.txt` times." + echo "Destroyed it `stat -f %z destroyer_count.txt` times." +} +create_destroy_stress_cleanup() +{ + cleanup_tap_and_lagg +} + +# This test regresses a panic that is particular to LACP. If the child's link +# state changes while the lagg is being destroyed, lacp_linkstate can +# use-after-free. The problem is compounded by two factors: +# 1) In SpectraBSD, downing the parent will also down the child +# 2) The cxgbe driver will show the link state as "no carrier" as soon as you +# down the interface. +# TeamTrack: P2_30328 +atf_test_case lacp_linkstate_destroy_stress cleanup +lacp_linkstate_destroy_stress_head() +{ + atf_set "descr" "Simultaneously destroy an LACP lagg and change its childrens link states" + atf_set "require.user" "root" +} +lacp_linkstate_destroy_stress_body() +{ + local TAP0 TAP1 LAGG MAC SRCDIR + + # Configure the lagg interface to use an RFC5737 nonrouteable addresses + ADDR="192.0.2.2" + MASK="24" + # ifconfig takes about 10ms to run. To increase race coverage, + # randomly delay the two commands relative to each other by 5ms either + # way. + MEAN_SLEEP_SECONDS=.005 + MAX_SLEEP_USECS=10000 + + TAP0=`get_tap` + TAP1=`get_tap` + LAGG=`get_lagg` + + # Up the lagg's children + ifconfig $TAP0 inet6 ifdisabled up + ifconfig $TAP1 inet6 ifdisabled up + + SRCDIR=$( atf_get_srcdir ) + while true; do + ifconfig $LAGG inet6 ifdisabled + # We must open the tap devices to change their link states + cat /dev/$TAP0 > /dev/null & + CAT0_PID=$! + cat /dev/$TAP1 > /dev/null & + CAT1_PID=$! + ifconfig $LAGG up laggport $TAP0 laggport $TAP1 \ + ${ADDR}/${MASK} 2> /dev/null && + { sleep ${MEAN_SLEEP_SECONDS} && \ + kill $CAT0_PID && + kill $CAT1_PID && + echo -n . >> linkstate_count.txt ; } & + { ${SRCDIR}/randsleep ${MAX_SLEEP_USECS} && \ + ifconfig $LAGG destroy && + echo -n . >> destroy_count.txt ; } & + wait + ifconfig $LAGG create + done & + LOOP_PID=$! + + sleep 60 + kill $LOOP_PID + echo "Disconnected the children `stat -f %z linkstate_count.txt` times." + echo "Destroyed the lagg `stat -f %z destroy_count.txt` times." +} +lacp_linkstate_destroy_stress_cleanup() +{ + cleanup_tap_and_lagg +} + +atf_test_case up_destroy_stress cleanup +up_destroy_stress_head() +{ + atf_set "descr" "Simultaneously up and destroy a lagg" + atf_set "require.user" "root" +} +up_destroy_stress_body() +{ + local TAP0 TAP1 LAGG MAC SRCDIR + + # Configure the lagg interface to use an RFC5737 nonrouteable addresses + ADDR="192.0.2.2" + MASK="24" + # ifconfig takes about 10ms to run. To increase race coverage, + # randomly delay the two commands relative to each other by 5ms either + # way. + MEAN_SLEEP_SECONDS=.005 + MAX_SLEEP_USECS=10000 + + TAP0=`get_tap` + TAP1=`get_tap` + TAP2=`get_tap` + TAP3=`get_tap` + LAGG=`get_lagg` + + # Up the lagg's children + ifconfig $TAP0 inet6 ifdisabled up + ifconfig $TAP1 inet6 ifdisabled up + ifconfig $TAP2 inet6 ifdisabled up + ifconfig $TAP3 inet6 ifdisabled up + + SRCDIR=$( atf_get_srcdir ) + while true; do + ifconfig $LAGG inet6 ifdisabled + { sleep ${MEAN_SLEEP_SECONDS} && \ + ifconfig $LAGG up laggport $TAP0 laggport $TAP1 \ + laggport $TAP2 laggport $TAP3 \ + ${ADDR}/${MASK} 2> /dev/null && + echo -n . >> up_count.txt ; } & + { ${SRCDIR}/randsleep ${MAX_SLEEP_USECS} && \ + ifconfig $LAGG destroy && + echo -n . >> destroy_count.txt ; } & + wait + ifconfig $LAGG create + done & + LOOP_PID=$! + + sleep 60 + kill $LOOP_PID + echo "Upped the lagg `stat -f %z up_count.txt` times." + echo "Destroyed it `stat -f %z destroy_count.txt` times." +} +up_destroy_stress_cleanup() +{ + cleanup_tap_and_lagg +} + +atf_test_case set_ether cleanup +set_ether_head() +{ + atf_set "descr" "Set a lagg's ethernet address" + atf_set "require.user" "root" +} +set_ether_body() +{ + local TAP0 TAP1 LAGG MAC + + # Configure the lagg interface to use an RFC5737 nonrouteable addresses + ADDR="192.0.2.2" + MASK="24" + MAC="00:11:22:33:44:55" + + TAP0=`get_tap` + TAP1=`get_tap` + LAGG=`get_lagg` + + # Create the lagg + ifconfig $TAP0 up + ifconfig $TAP1 up + atf_check ifconfig $LAGG up laggport $TAP0 laggport $TAP1 \ + ${ADDR}/${MASK} + + # Change the lagg's ethernet address + atf_check ifconfig $LAGG ether ${MAC} + + # Check that all members have the same MAC + atf_check -o match:"ether ${MAC}" ifconfig $LAGG + atf_check -o match:"ether ${MAC}" ifconfig $TAP0 + atf_check -o match:"ether ${MAC}" ifconfig $TAP1 +} +set_ether_cleanup() +{ + cleanup_tap_and_lagg +} + +atf_test_case updown cleanup +updown_head() +{ + atf_set "descr" "upping or downing a lagg ups or downs its children" + atf_set "require.user" "root" +} +updown_body() +{ + local TAP0 TAP1 LAGG MAC + + # Configure the lagg interface to use an RFC5737 nonrouteable addresses + ADDR="192.0.2.2" + MASK="24" + MAC="00:11:22:33:44:55" + + TAP0=`get_tap` + TAP1=`get_tap` + LAGG=`get_lagg` + + # Create the lagg + ifconfig $TAP0 up + ifconfig $TAP1 up + atf_check ifconfig $LAGG up laggport $TAP0 laggport $TAP1 \ + ${ADDR}/${MASK} + + # Down the lagg + ifconfig $LAGG down + atf_check -o not-match:"flags=.*\<UP\>" ifconfig $LAGG + atf_check -o not-match:"flags=.*\<UP\>" ifconfig $TAP0 + atf_check -o not-match:"flags=.*\<UP\>" ifconfig $TAP1 + # Up the lagg again + ifconfig $LAGG up + atf_check -o match:"flags=.*\<UP\>" ifconfig $LAGG + atf_check -o match:"flags=.*\<UP\>" ifconfig $TAP0 + atf_check -o match:"flags=.*\<UP\>" ifconfig $TAP1 + + # Check that no members have acquired an IPv6 link-local address by + # virtue of being upped. IPv6 link-local addresses should never be + # merged in any way to prevent scope violation. + atf_check -o not-match:"inet6 fe80:" ifconfig $TAP0 + atf_check -o not-match:"inet6 fe80:" ifconfig $TAP1 +} +updown_cleanup() +{ + cleanup_tap_and_lagg +} + +# Check for lock-order reversals. For best results, this test should be run +# last. +atf_test_case witness +witness_head() +{ + atf_set "descr" "Check witness(4) for lock-order reversals in if_lagg" +} +witness_body() +{ + if [ "$(atf_config_get ci false)" = "true" ]; then + atf_skip "https://bugs.freebsd.org/244163 and https://bugs.freebsd.org/251726" + fi + if [ `sysctl -n debug.witness.watch` -ne 1 ]; then + atf_skip "witness(4) is not enabled" + fi + if `sysctl -n debug.witness.badstacks | grep -q 'at lagg_'`; then + sysctl debug.witness.badstacks + atf_fail "Lock-order reversals involving if_lagg.c detected" + fi +} + +atf_init_test_cases() +{ + atf_add_test_case create + atf_add_test_case create_destroy_stress + atf_add_test_case lacp_linkstate_destroy_stress + atf_add_test_case set_ether + atf_add_test_case status_stress + atf_add_test_case up_destroy_stress + atf_add_test_case updown + # For best results, keep the witness test last + atf_add_test_case witness +} + + +# Creates a new tap(4) interface, registers it for cleanup, and echoes it +get_tap() +{ + local TAPN=0 + while ! ifconfig tap${TAPN} create > /dev/null 2>&1; do + if [ "$TAPN" -ge 8 ]; then + atf_skip "Could not create a tap(4) interface" + else + TAPN=$(($TAPN + 1)) + fi + done + local TAPD=tap${TAPN} + # Record the TAP device so we can clean it up later + echo ${TAPD} >> "devices_to_cleanup" + echo ${TAPD} +} + +# Creates a new lagg(4) interface, registers it for cleanup, and echoes it +get_lagg() +{ + local LAGGN=0 + while ! ifconfig lagg${LAGGN} create > /dev/null 2>&1; do + if [ "$LAGGN" -ge 8 ]; then + atf_skip "Could not create a lagg(4) interface" + else + LAGGN=$(($LAGGN + 1)) + fi + done + local LAGGD=lagg${LAGGN} + # Record the lagg device so we can clean it up later + echo ${LAGGD} >> "devices_to_cleanup" + echo ${LAGGD} +} + +cleanup_tap_and_lagg() +{ + local DEV + + for DEV in `cat "devices_to_cleanup"`; do + ifconfig ${DEV} destroy + done + true +} diff --git a/tests/sys/net/if_ovpn/Makefile b/tests/sys/net/if_ovpn/Makefile new file mode 100644 index 000000000000..85746226e122 --- /dev/null +++ b/tests/sys/net/if_ovpn/Makefile @@ -0,0 +1,30 @@ +.include <src.opts.mk> + +PACKAGE= tests + +TESTSDIR= ${TESTSBASE}/sys/net/if_ovpn + +.if ${MK_PF} != "no" +ATF_TESTS_SH+= if_ovpn +TEST_METADATA.if_ovpn+= execenv="jail" +TEST_METADATA.if_ovpn+= execenv_jail_params="vnet allow.raw_sockets" +.endif +ATF_TESTS_C+= if_ovpn_c + +LIBADD+= nv + +TESTS_SUBDIRS+= ccd + +${PACKAGE}FILES+= \ + ca.crt \ + client.crt \ + client.key \ + client2.crt \ + client2.key \ + dh.pem \ + server.crt \ + server.key \ + user.pass \ + utils.subr + +.include <bsd.test.mk> diff --git a/tests/sys/net/if_ovpn/ca.crt b/tests/sys/net/if_ovpn/ca.crt new file mode 100644 index 000000000000..4bdde726d12b --- /dev/null +++ b/tests/sys/net/if_ovpn/ca.crt @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +MIIFrzCCA5egAwIBAgIUFByUexsc+WCQtaEQZCg+nrJaFDowDQYJKoZIhvcNAQEL +BQAwZjELMAkGA1UEBhMCS0cxCzAJBgNVBAgMAk5BMRAwDgYDVQQHDAdCSVNIS0VL +MRUwEwYDVQQKDAxPcGVuVlBOLVRFU1QxITAfBgkqhkiG9w0BCQEWEm1lQG15aG9z +dC5teWRvbWFpbjAgFw0yMjA0MjcxNDM1NTZaGA8yMTIyMDQwMzE0MzU1NlowZjEL +MAkGA1UEBhMCS0cxCzAJBgNVBAgMAk5BMRAwDgYDVQQHDAdCSVNIS0VLMRUwEwYD +VQQKDAxPcGVuVlBOLVRFU1QxITAfBgkqhkiG9w0BCQEWEm1lQG15aG9zdC5teWRv +bWFpbjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJnak7swgeJta01H +4pNQte3S0bdEGRvyhkzY8HIxe/pdz371fSL40iBQ29PVZseMVOsQIrIolhma4qTy +tMyU9VEfJRlBj8hdlbIpb8FDsvMOFyC+7+GxHdkAQlFV5fUj7JydnxiY+L87W7WE +VaTy9ZRtuIXhvkVZYTHzvVwXwL5e5EdcJKHsdam49ty4rs6CE7J06GYnwmLk/nDw +vZZhHTB5V+vNeurRoxKyYvUXePTCqh6cGh2as061JxU1j1vj3cTNVlPymO85Etl1 +t4uEHjXyLVK2+OCcauQGn6QT02xqFLRsbk42gWOksFj50NDQVtp71JIMAmwzz2yi ++cqz1qqdyOlfLeP2oow1jYa3Hhht5Es90m6YkeF/073eLW/nuVsoD57PZ1rnClX+ +9Zs24LAN/vnXR1gK/nAZsv9CNtRdWt9yTSmq/oGZj4kWU154XxRLfcDa9gj5X+g9 +Z4w6YaJtsa8XaOelPWnBg/JXLrdE731DwyDRqlSPYgwzU6g3CgjRV3/cJs/RaxWB +Au1Tdpd2T5KbeLivYJnhNApE0J4CxXrudfglAaZV8tG0SK8F07Sd0uS4Sa83I1Bh +IjAFcjBcMxv1gWNZN903V0etmCkIwONlqTSWKNYGV+9EQ1moHDDXRUK14He95AQ7 +ZNadjsqZuDGl925HW4wjk2HLa42FAgMBAAGjUzBRMB0GA1UdDgQWBBSIBxjB/i4J +Ln3guRuDqVTeCbEWfjAfBgNVHSMEGDAWgBSIBxjB/i4JLn3guRuDqVTeCbEWfjAP +BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQAMrtwU2YfLGp2QWH33 +qH4dZh92iaSApEDwIggBj+wrc/6LbbS+Mm6UTOj9SYNcDZpj/ia4m1OXV7dC1adZ +wGk94qBknwgxPkxS0f3HLlvYDnSWCOTP5CJPfdQWPRX0WQO8l5s+tIigLaioS76N +C/g+I7ytPYbCN7sZRgQJr+vGxOxaBD8GrSq6/brTSUMCHUjE/ylFZ9ykBtmXTVIb +u9WsnWTyA7h5Nzhkh0VvN0o/EhlgRpUdT1661QlIvWsyfd6sxrtLum4h88DUkjw9 +qlMDTnkhWUfyPg8kS99dLodnxp1QeW0ISeWpAucJuOvu3ode/N3lOrxq88OrZQNJ +upQsdUxLEU6DzQlvBd2s4d8Ghvk3l65u688cE6dXIcNPEp78wy/IXkVvTRTstpuA +Ep9ZNwrEvaPQDxBJ6a2sPKwXst1NZZgmPQG2ZbpQfCQtJ0zYZWpI/LytiC/05joi +/aGh01GN7nODt9U7rtZtCQjjmIlK7fuBJLL9yQXcpzT5sItdQSkn9QuCBlnlxMqx +felbaNPxTLJVqCilqlx/xaybDljduKLvJouR+l/UjrXz+n02lzxPQ3FIr8/vJlVf +EFbSmkzS3C/O5gXUxHTq44z6LbnosjyiPEB2J7n5kvsA8HTynZU4GCrHa5LLG1eg +1odsgCIYiAwNBCbPtWWykUHXhA== +-----END CERTIFICATE----- diff --git a/tests/sys/net/if_ovpn/ca.key b/tests/sys/net/if_ovpn/ca.key new file mode 100644 index 000000000000..79537a84c6a0 --- /dev/null +++ b/tests/sys/net/if_ovpn/ca.key @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/tests/sys/net/if_ovpn/ccd/Makefile b/tests/sys/net/if_ovpn/ccd/Makefile new file mode 100644 index 000000000000..2d3fefa1f321 --- /dev/null +++ b/tests/sys/net/if_ovpn/ccd/Makefile @@ -0,0 +1,8 @@ +PACKAGE= tests + +TESTSDIR= ${TESTSBASE}/sys/net/if_ovpn/ccd + +${PACKAGE}FILES+= \ + Test-Client2 + +.include <bsd.test.mk> diff --git a/tests/sys/net/if_ovpn/ccd/Test-Client2 b/tests/sys/net/if_ovpn/ccd/Test-Client2 new file mode 100644 index 000000000000..b378ad0d4394 --- /dev/null +++ b/tests/sys/net/if_ovpn/ccd/Test-Client2 @@ -0,0 +1,2 @@ +iroute 203.0.113.0 255.255.255.0 +ifconfig-push 198.51.100.3 255.255.255.0 diff --git a/tests/sys/net/if_ovpn/client.crt b/tests/sys/net/if_ovpn/client.crt new file mode 100644 index 000000000000..92ba8ad1ba4b --- /dev/null +++ b/tests/sys/net/if_ovpn/client.crt @@ -0,0 +1,123 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1048686 (0x10006e) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=KG, ST=NA, L=BISHKEK, O=OpenVPN-TEST/emailAddress=me@myhost.mydomain + Validity + Not Before: Apr 27 15:00:37 2022 GMT + Not After : Apr 3 15:00:37 2122 GMT + Subject: C=KG, ST=NA, O=OpenVPN-TEST, CN=Test-Client/emailAddress=me@myhost.mydomain + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public-Key: (4096 bit) + Modulus: + 00:f0:86:38:95:a6:e0:76:52:eb:25:57:8b:47:2a: + e1:17:db:9b:84:64:33:0c:70:91:74:ae:f5:39:cc: + a7:9e:37:3c:d2:32:df:cd:57:8e:21:00:c9:43:b6: + b3:d3:a8:fc:15:84:c4:15:86:d8:d6:69:5b:d4:9b: + 19:c9:8e:71:9f:1d:12:72:89:e3:7b:db:80:6e:dc: + 69:fd:98:eb:22:33:ac:59:4a:57:bd:6d:48:60:fd: + 89:8c:94:fd:64:24:cf:98:ea:31:c0:20:50:38:2e: + c6:f3:67:54:c1:ea:70:13:a4:34:fd:38:59:9c:64: + bf:11:f1:ed:01:46:08:31:c8:de:32:13:47:38:81: + 84:4d:f6:00:d3:8c:ee:6f:71:a1:5a:b1:34:60:95: + 25:67:7f:4c:d5:86:09:0b:dc:75:a1:e5:aa:05:74: + 0f:e8:f1:b1:2a:63:be:53:cb:d8:a3:9f:f1:1a:c6: + fb:c8:c5:ec:6c:34:86:13:c7:e4:83:d2:11:66:2f: + ee:8e:19:8e:e5:da:0c:59:09:b3:c6:35:aa:7e:88: + 15:eb:53:29:cd:f6:a5:c4:d2:af:72:28:b0:a8:f5: + a4:38:5b:ab:9f:e0:db:f1:b9:e4:ca:d0:e8:c7:dd: + 95:81:c9:75:e2:23:74:30:59:b0:ca:74:b1:fe:86: + 0d:7c:5a:f3:5d:bb:42:75:7d:48:51:d7:6a:ee:93: + d2:e4:30:2a:5c:65:56:f4:5e:74:97:e1:7e:ae:2c: + f7:da:95:12:e0:1a:dd:f5:07:c0:4b:85:90:45:d1: + b0:61:ec:90:ab:20:c3:55:78:6c:da:bb:48:4f:33: + 61:04:4f:8d:1a:e4:57:8a:cb:e1:ea:db:8f:f3:9f: + d4:98:5f:27:dd:20:9e:76:35:54:75:ab:ef:74:6b: + 77:93:02:e9:79:a4:0b:83:a4:ff:fd:3d:bd:a5:e3: + 96:b8:78:13:5a:91:7d:bd:a2:90:54:9d:07:87:fd: + 62:e2:d9:01:9c:50:8b:d4:7c:a4:28:f6:31:2b:9a: + f1:6f:6f:85:71:7e:71:b2:bc:6d:97:e7:fc:8c:5e: + 97:85:c1:6a:61:10:c1:e5:b4:db:52:db:20:e3:42: + 8f:fa:48:4c:27:87:0f:05:0d:6d:93:4e:2f:a1:36: + 58:16:73:9f:61:68:d5:cb:67:1b:5d:41:c2:e6:6f: + e6:ca:e1:f3:b6:92:c1:48:72:3f:a9:84:3b:1f:9b: + 3d:73:85:46:f2:f7:dc:5e:de:e9:18:47:24:f4:7d: + 46:e1:0e:2e:5a:4a:9a:4e:f1:e5:7c:71:d0:7b:9e: + 62:3d:43:a3:62:9e:55:0a:77:a6:98:31:b7:a1:11: + b3:58:35 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + Netscape Comment: + OpenSSL Generated Certificate + X509v3 Subject Key Identifier: + FC:B5:C3:D3:A0:B5:6C:8F:3D:52:4F:07:03:08:0A:BD:9D:A8:94:3A + X509v3 Authority Key Identifier: + keyid:88:07:18:C1:FE:2E:09:2E:7D:E0:B9:1B:83:A9:54:DE:09:B1:16:7E + + Signature Algorithm: sha256WithRSAEncryption + 5d:26:45:db:9f:d6:c7:86:a0:22:85:f2:fd:4a:41:3b:3a:2f: + 81:1b:93:38:e7:0e:81:bf:bc:5f:47:95:94:92:a6:12:47:78: + 74:68:65:fd:e0:99:b2:08:d5:2b:fa:aa:05:13:88:7f:00:e8: + cb:17:b0:04:4d:d1:6e:80:4b:11:1b:71:45:b9:61:c2:66:14: + e6:86:d9:13:a0:7d:63:14:fe:41:7c:86:42:c8:53:0e:04:da: + 1b:28:cc:a4:e8:ff:f4:b0:73:4b:c0:a3:d7:be:7c:2c:2a:e5: + aa:3e:8b:ce:07:8b:b2:62:a4:7d:f0:b3:75:39:02:10:f0:a8: + b9:d8:1f:70:4d:d1:b0:68:46:43:02:bc:8b:15:e6:df:5d:c3: + ae:e3:89:80:48:64:35:9b:0b:2b:d6:75:38:96:0d:6c:f1:cb: + 03:91:ec:75:58:3b:fb:f7:78:cf:38:58:9b:a6:04:48:fc:aa: + c0:fa:a3:9c:da:c3:26:e0:82:9a:0e:0e:2b:2f:50:00:56:7f: + d5:ab:87:61:dd:bb:34:23:af:38:5f:ea:40:72:cf:46:38:31: + 8c:a3:68:1c:a1:84:62:03:05:7e:92:46:1b:0f:e2:a3:47:d3: + a2:c5:f9:e8:7b:d1:0a:20:63:d6:ca:01:05:7f:3f:4c:4f:d5: + 6c:51:e8:ee:82:35:37:9b:1e:e8:76:6d:05:50:88:43:cc:8c: + 20:81:09:a9:76:57:97:7b:bc:38:14:d5:3e:38:b1:a5:7e:51: + b2:67:9b:50:05:00:1b:24:90:cc:57:e1:b1:27:3e:50:09:0b: + bc:9c:0e:b3:d1:08:80:30:d6:28:85:6c:4d:9f:d2:ea:96:de: + 6f:0d:25:0c:03:94:65:4e:88:aa:d8:81:78:49:44:09:4d:85: + c8:db:8c:57:be:6d:49:97:2b:a5:28:97:e3:99:ea:f1:b7:46: + 2e:a6:dc:85:1c:d6:66:6e:dd:a9:db:d6:d3:34:71:95:0a:6d: + bb:47:b5:18:b5:7e:95:92:9b:53:f9:9b:a3:6c:09:2c:e2:d0: + d3:9a:4e:31:21:0b:18:b6:b4:fc:65:8a:a2:e5:b9:c8:f5:4a: + 92:3f:4e:de:db:e5:3a:bf:22:4e:39:b6:ae:09:d1:1b:84:f5: + e6:53:6d:c2:8e:24:26:58:58:80:aa:8d:dd:54:21:9e:7b:c1: + 01:f8:94:cc:a6:c0:32:9a:4a:0c:b4:f5:b8:e7:b0:5c:c2:18: + 57:1e:49:93:72:c9:01:91:b7:ea:a1:0b:fa:f0:33:0d:ff:55: + b6:fb:07:30:85:47:ab:cd:05:4e:cc:a2:49:91:0b:7d:b7:a4: + bb:43:ea:bb:f9:95:bb:e9 +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- diff --git a/tests/sys/net/if_ovpn/client.key b/tests/sys/net/if_ovpn/client.key new file mode 100644 index 000000000000..7ad255b52556 --- /dev/null +++ b/tests/sys/net/if_ovpn/client.key @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/tests/sys/net/if_ovpn/client2.crt b/tests/sys/net/if_ovpn/client2.crt new file mode 100644 index 000000000000..83aec7eedaa0 --- /dev/null +++ b/tests/sys/net/if_ovpn/client2.crt @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFbTCCA1UCFC6I/36G1ZhmNxvabxL+BppMd38jMA0GCSqGSIb3DQEBCwUAMGYx +CzAJBgNVBAYTAktHMQswCQYDVQQIDAJOQTEQMA4GA1UEBwwHQklTSEtFSzEVMBMG +A1UECgwMT3BlblZQTi1URVNUMSEwHwYJKoZIhvcNAQkBFhJtZUBteWhvc3QubXlk +b21haW4wIBcNMjIwNjE1MTIwNzQzWhgPMjEyMjA1MjIxMjA3NDNaMH4xCzAJBgNV +BAYTAktHMQswCQYDVQQIDAJOQTEQMA4GA1UEBwwHQklTSEtFSzEVMBMGA1UECgwM +T3BlblZQTi1URVNUMRUwEwYDVQQDDAxUZXN0LUNsaWVudDIxIjAgBgkqhkiG9w0B +CQEWE21lMkBteWhvc3QubXlkb21haW4wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw +ggIKAoICAQDteW+ZsfahA+NJHgTycmGejCIw/jwbVpaFlwYLBe39OsDK44XUjVn1 +i8k4Vce9F1UcGeY9scyLZ797Ify5Sm59ejVkm2EriuA/jQeNpr8A0HxjcmEcn/G5 +5cM/zZYj7f9Bfj+XVgHG0zHVfD9PItwEUHKNp3hVr/86FwbnHKpcQK/QjYlDOFZB +wiIxDUSpaMLT7eFUqLOem1ZmnBd0qT3GPjBJsbpzzK+LZd9V0brvIc8XCnoUGs2V +wzsg8oRCpVpQsKUNrW3mid9lCJQvRAm6j0/14nZHm3sP5BroOTOzcLKiWuYMwizs +QkkEYP0G9ZtipbIhAdnDB4FgjF+9arH3IXw3UZxXNPguA2UasuqcCwiwyp2aPNAf +G0sIv3rvOGyTp0QfhrsQW0/xcJxfYlMONHft9kvuhC9ITKaH1ei8iQuFhm2QZCrO +f/jEf8d6nckpM3GAp/WIze49HZgdVfAIGV3+DcF2u/gwBjKsRe9W4KN5GxLQEx0x +gWLJN34O340N/Sy+NX82KP/kO/Zb3N1rKVmDIZx49ZJy1eN/Kt7pl0+AqifZzneu +pLl9nziwe0csUtCQbIJHZQQon6vwDQVR3VuGwMra/sayxZDY5IOwueEm62/cJhoQ +rxGknCM99WPhJau3S0gBV1nsH7M37AQxyHhC7q3ambdpEqzUDzf3XwIDAQABMA0G +CSqGSIb3DQEBCwUAA4ICAQBtV12w72Yflc0bIJ3IsnQ1om820Fx8/0Ndr9GD8vov +XXupazyuQmfRBpB0qcVR0tStxJrf8S19WRiLFM2UJexT4H8A3Rp788IESYo5JytV +kAvTtJ+LE74EIRXt9M3II5vFaGiFRyozN7Vdr8mUJO5sXNJaZPQkOsAta652J2JV +Qy5rOgAUEylUWZMVKkmSAdU4LGVgJC86XA9eQGtqtbXj09v3YW/EPsobCi0YbFYS +5WgGCunqw7zT4Ko8KP+horaV/bQWZKnKIb3e5xDh9Zkm48RBRU4pYZ0VoOSp1xAy +qzn/818NVPfhKWSXxLFBVWgsIzLO825vH5WEaQNgg+vfq2/AZcfl6UNGn5dufkAk +73t5dNq46H2Z6t02dfOQ7U4tduCUPbWmPXD/kjFqryQ4GXNR8TMKLf6GZRKD5nOt +KRfrkPL4tbsWL8WY9c5KQRC/vaLXETuuavDMVp0AFwTz846tB2njjyTc5jFcTgfY +X8PgUw/miJszbQd6Z9HTDTTH0osv+VNXE5MCYPWe3QaobBJGRjaPJyO5OA/SXZa+ ++9XCXyEBdVvckHpc4yHK9ATlCeiouDi45lzlnXpvuQz6VXwB8v4JKB/qqFlrzO2E +09yAyw3qPH43TBbgvJwtpD+g6k9VvE7ojHS4fl2epyQAm/orT6RLLHMHEkaYqRCU +2A== +-----END CERTIFICATE----- diff --git a/tests/sys/net/if_ovpn/client2.key b/tests/sys/net/if_ovpn/client2.key new file mode 100644 index 000000000000..7e5c6857de1c --- /dev/null +++ b/tests/sys/net/if_ovpn/client2.key @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/tests/sys/net/if_ovpn/dh.pem b/tests/sys/net/if_ovpn/dh.pem new file mode 100644 index 000000000000..8eda59aa139e --- /dev/null +++ b/tests/sys/net/if_ovpn/dh.pem @@ -0,0 +1,8 @@ +-----BEGIN DH PARAMETERS----- +MIIBCAKCAQEArdnA32xujHPlPI+jPffHSoMUZ+b5gRz1H1Lw9//Gugm5TAsRiYrB +t2BDSsMKvAjyqN+i5SJv4TOk98kRRKB27iPvyXmiL945VaDQl/UehCySjYlGFUjW +9nuo+JwQxeSbw0TLiSYoYJZQ8X1CxPl9mgJl277O4cW1Gc8I/bWa+ipU/4K5wv3h +GI8nt+6A0jN3M/KebotMP101G4k0l0qsY4oRMTmP+z3oAP0qU9NZ1jiuMFVzRlNp +5FdYF7ctrH+tBF+QmyT4SRKSED4wE4oX6gp420NaBhIEQifIj75wlMDtxQlpkN+x +QkjsEbPlaPKHGQ4uupssChVUi8IM2yq5EwIBAg== +-----END DH PARAMETERS----- diff --git a/tests/sys/net/if_ovpn/if_ovpn.sh b/tests/sys/net/if_ovpn/if_ovpn.sh new file mode 100644 index 000000000000..9dafce2242d8 --- /dev/null +++ b/tests/sys/net/if_ovpn/if_ovpn.sh @@ -0,0 +1,1504 @@ +## +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2022 Rubicon Communications, LLC ("Netgate") +# +# 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. + +. $(atf_get_srcdir)/utils.subr +. $(atf_get_srcdir)/../../netpfil/pf/utils.subr + +atf_test_case "4in4" "cleanup" +4in4_head() +{ + atf_set descr 'IPv4 in IPv4 tunnel' + atf_set require.user root + atf_set require.progs openvpn +} + +4in4_body() +{ + ovpn_init + + l=$(vnet_mkepair) + + vnet_mkjail a ${l}a + jexec a ifconfig ${l}a 192.0.2.1/24 up + vnet_mkjail b ${l}b + jexec b ifconfig ${l}b 192.0.2.2/24 up + + # Sanity check + atf_check -s exit:0 -o ignore jexec a ping -c 1 192.0.2.2 + + ovpn_start a " + dev ovpn0 + dev-type tun + proto udp4 + + cipher AES-256-GCM + auth SHA256 + + local 192.0.2.1 + server 198.51.100.0 255.255.255.0 + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/server.crt + key $(atf_get_srcdir)/server.key + dh $(atf_get_srcdir)/dh.pem + + mode server + script-security 2 + auth-user-pass-verify /usr/bin/true via-env + topology subnet + + keepalive 100 600 + " + ovpn_start b " + dev tun0 + dev-type tun + + client + + remote 192.0.2.1 + auth-user-pass $(atf_get_srcdir)/user.pass + + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/client.crt + key $(atf_get_srcdir)/client.key + dh $(atf_get_srcdir)/dh.pem + + keepalive 100 600 + " + + # Give the tunnel time to come up + sleep 10 + + atf_check -s exit:0 -o ignore jexec b ping -c 1 198.51.100.1 + + echo 'foo' | jexec b nc -u -w 2 192.0.2.1 1194 + atf_check -s exit:0 -o ignore jexec b ping -c 3 198.51.100.1 + + # Test routing loop protection + jexec b route add 192.0.2.1 198.51.100.1 + atf_check -s exit:2 -o ignore jexec b ping -t 1 -c 1 198.51.100.1 +} + +4in4_cleanup() +{ + ovpn_cleanup +} + +atf_test_case "bz283426" "cleanup" +bz283426_head() +{ + atf_set descr 'FreeBSD Bugzilla 283426' + atf_set require.user root + atf_set require.progs openvpn python3 +} + +bz283426_body() +{ + ovpn_init + + l=$(vnet_mkepair) + + vnet_mkjail a ${l}a + jexec a ifconfig ${l}a 192.0.2.1/24 up + vnet_mkjail b ${l}b + jexec b ifconfig ${l}b 192.0.2.2/24 up + + # Sanity check + atf_check -s exit:0 -o ignore jexec a ping -c 1 192.0.2.2 + + ovpn_start a " + dev ovpn0 + dev-type tun + proto udp4 + + cipher AES-256-GCM + auth SHA256 + + bind 0.0.0.0:1194 + server 198.51.100.0 255.255.255.0 + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/server.crt + key $(atf_get_srcdir)/server.key + dh $(atf_get_srcdir)/dh.pem + + mode server + script-security 2 + auth-user-pass-verify /usr/bin/true via-env + topology subnet + + keepalive 100 600 + " + ovpn_start b " + dev tun0 + dev-type tun + + client + + remote 192.0.2.1 + auth-user-pass $(atf_get_srcdir)/user.pass + + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/client.crt + key $(atf_get_srcdir)/client.key + dh $(atf_get_srcdir)/dh.pem + + keepalive 100 600 + " + + # Give the tunnel time to come up + sleep 10 + + atf_check -s exit:0 -o ignore jexec b ping -c 1 198.51.100.1 + + # Send a broadcast packet in the outer link. + echo "import socket as sk +s = sk.socket(sk.AF_INET, sk.SOCK_DGRAM) +s.setsockopt(sk.SOL_SOCKET, sk.SO_BROADCAST, 1) +s.sendto(b'x' * 1000, ('192.0.2.255', 1194))" | jexec b python3 + + atf_check -s exit:0 -o ignore jexec b ping -c 3 198.51.100.1 +} + +bz283426_cleanup() +{ + ovpn_cleanup +} + +atf_test_case "4mapped" "cleanup" +4mapped_head() +{ + atf_set descr 'IPv4 mapped addresses' + atf_set require.user root + atf_set require.progs openvpn +} + +4mapped_body() +{ + ovpn_init + + l=$(vnet_mkepair) + + vnet_mkjail a ${l}a + jexec a ifconfig ${l}a 192.0.2.1/24 up + vnet_mkjail b ${l}b + jexec b ifconfig ${l}b 192.0.2.2/24 up + + # Sanity check + atf_check -s exit:0 -o ignore jexec a ping -c 1 192.0.2.2 + + #jexec a ifconfig ${l}a + + ovpn_start a " + dev ovpn0 + dev-type tun + + cipher AES-256-GCM + auth SHA256 + + server 198.51.100.0 255.255.255.0 + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/server.crt + key $(atf_get_srcdir)/server.key + dh $(atf_get_srcdir)/dh.pem + + mode server + script-security 2 + auth-user-pass-verify /usr/bin/true via-env + topology subnet + + keepalive 100 600 + " + ovpn_start b " + dev tun0 + dev-type tun + + client + + remote 192.0.2.1 + auth-user-pass $(atf_get_srcdir)/user.pass + + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/client.crt + key $(atf_get_srcdir)/client.key + dh $(atf_get_srcdir)/dh.pem + + keepalive 100 600 + " + + # Give the tunnel time to come up + sleep 10 + + atf_check -s exit:0 -o ignore jexec b ping -c 3 198.51.100.1 +} + +4mapped_cleanup() +{ + ovpn_cleanup +} + +atf_test_case "6in4" "cleanup" +6in4_head() +{ + atf_set descr 'IPv6 in IPv4 tunnel' + atf_set require.user root + atf_set require.progs openvpn +} + +6in4_body() +{ + ovpn_init + + l=$(vnet_mkepair) + + vnet_mkjail a ${l}a + jexec a ifconfig ${l}a 192.0.2.1/24 up + vnet_mkjail b ${l}b + jexec b ifconfig ${l}b 192.0.2.2/24 up + + # Sanity check + atf_check -s exit:0 -o ignore jexec a ping -c 1 192.0.2.2 + + ovpn_start a " + dev ovpn0 + dev-type tun + proto udp + + cipher AES-256-GCM + auth SHA256 + + local 192.0.2.1 + server-ipv6 2001:db8:1::/64 + + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/server.crt + key $(atf_get_srcdir)/server.key + dh $(atf_get_srcdir)/dh.pem + + mode server + script-security 2 + auth-user-pass-verify /usr/bin/true via-env + topology subnet + + keepalive 100 600 + " + ovpn_start b " + dev tun0 + dev-type tun + + client + + remote 192.0.2.1 + auth-user-pass $(atf_get_srcdir)/user.pass + + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/client.crt + key $(atf_get_srcdir)/client.key + dh $(atf_get_srcdir)/dh.pem + + keepalive 100 600 + " + + # Give the tunnel time to come up + sleep 10 + + atf_check -s exit:0 -o ignore jexec b ping6 -c 3 2001:db8:1::1 +} + +6in4_cleanup() +{ + ovpn_cleanup +} + +atf_test_case "4in6" "cleanup" +4in6_head() +{ + atf_set descr 'IPv4 in IPv6 tunnel' + atf_set require.user root + atf_set require.progs openvpn +} + +4in6_body() +{ + ovpn_init + + l=$(vnet_mkepair) + + vnet_mkjail a ${l}a + jexec a ifconfig ${l}a inet6 2001:db8::1/64 up no_dad + vnet_mkjail b ${l}b + jexec b ifconfig ${l}b inet6 2001:db8::2/64 up no_dad + + # Sanity check + atf_check -s exit:0 -o ignore jexec a ping6 -c 1 2001:db8::2 + + ovpn_start a " + dev ovpn0 + dev-type tun + proto udp6 + + cipher AES-256-GCM + auth SHA256 + + local 2001:db8::1 + server 198.51.100.0 255.255.255.0 + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/server.crt + key $(atf_get_srcdir)/server.key + dh $(atf_get_srcdir)/dh.pem + + mode server + script-security 2 + auth-user-pass-verify /usr/bin/true via-env + topology subnet + + keepalive 100 600 + " + ovpn_start b " + dev tun0 + dev-type tun + + client + + remote 2001:db8::1 + auth-user-pass $(atf_get_srcdir)/user.pass + + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/client.crt + key $(atf_get_srcdir)/client.key + dh $(atf_get_srcdir)/dh.pem + + keepalive 100 600 + " + + dd if=/dev/random of=test.img bs=1024 count=1024 + cat test.img | jexec a nc -N -l 1234 & + + # Give the tunnel time to come up + sleep 10 + + atf_check -s exit:0 -o ignore jexec b ping -c 3 198.51.100.1 + + # MTU sweep + for i in `seq 1000 1500` + do + atf_check -s exit:0 -o ignore jexec b \ + ping -c 1 -s $i 198.51.100.1 + done + + rcvmd5=$(jexec b nc -N -w 3 198.51.100.1 1234 | md5) + md5=$(md5 test.img) + + if [ $md5 != $rcvmd5 ]; + then + atf_fail "Transmit corruption!" + fi +} + +4in6_cleanup() +{ + ovpn_cleanup +} + +atf_test_case "6in6" "cleanup" +6in6_head() +{ + atf_set descr 'IPv6 in IPv6 tunnel' + atf_set require.user root + atf_set require.progs openvpn +} + +6in6_body() +{ + ovpn_init + + l=$(vnet_mkepair) + + vnet_mkjail a ${l}a + jexec a ifconfig ${l}a inet6 2001:db8::1/64 up no_dad + vnet_mkjail b ${l}b + jexec b ifconfig ${l}b inet6 2001:db8::2/64 up no_dad + + # Sanity check + atf_check -s exit:0 -o ignore jexec a ping6 -c 1 2001:db8::2 + + ovpn_start a " + dev ovpn0 + dev-type tun + proto udp6 + + cipher AES-256-GCM + auth SHA256 + + local 2001:db8::1 + server-ipv6 2001:db8:1::/64 + + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/server.crt + key $(atf_get_srcdir)/server.key + dh $(atf_get_srcdir)/dh.pem + + mode server + script-security 2 + auth-user-pass-verify /usr/bin/true via-env + topology subnet + + keepalive 100 600 + " + ovpn_start b " + dev tun0 + dev-type tun + + client + + remote 2001:db8::1 + auth-user-pass $(atf_get_srcdir)/user.pass + + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/client.crt + key $(atf_get_srcdir)/client.key + dh $(atf_get_srcdir)/dh.pem + + keepalive 100 600 + " + + # Give the tunnel time to come up + sleep 10 + + atf_check -s exit:0 -o ignore jexec b ping6 -c 3 2001:db8:1::1 + atf_check -s exit:0 -o ignore jexec b ping6 -c 3 -z 16 2001:db8:1::1 + + # Test routing loop protection + jexec b route add -6 2001:db8::1 2001:db8:1::1 + atf_check -s exit:2 -o ignore jexec b ping6 -t 1 -c 3 2001:db8:1::1 +} + +6in6_cleanup() +{ + ovpn_cleanup +} + +atf_test_case "linklocal" "cleanup" +linklocal_head() +{ + atf_set descr 'Use IPv6 link-local addresses' + atf_set require.user root + atf_set require.progs openvpn +} + +linklocal_body() +{ + ovpn_init + ovpn_check_version 2.7.0 + + l=$(vnet_mkepair) + + vnet_mkjail a ${l}a + jexec a ifconfig ${l}a inet6 fe80::a/64 up no_dad + vnet_mkjail b ${l}b + jexec b ifconfig ${l}b inet6 fe80::b/64 up no_dad + + # Sanity check + atf_check -s exit:0 -o ignore jexec a ping6 -c 1 fe80::b%${l}a + + ovpn_start a " + dev ovpn0 + dev-type tun + proto udp6 + + cipher AES-256-GCM + auth SHA256 + + local fe80::a%${l}a + server-ipv6 2001:db8:1::/64 + + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/server.crt + key $(atf_get_srcdir)/server.key + dh $(atf_get_srcdir)/dh.pem + + mode server + script-security 2 + auth-user-pass-verify /usr/bin/true via-env + topology subnet + + keepalive 100 600 + " + ovpn_start b " + dev tun0 + dev-type tun + + client + + remote fe80::a%${l}b + auth-user-pass $(atf_get_srcdir)/user.pass + + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/client.crt + key $(atf_get_srcdir)/client.key + dh $(atf_get_srcdir)/dh.pem + + keepalive 100 600 + " + + # Give the tunnel time to come up + sleep 10 + jexec a ifconfig + + atf_check -s exit:0 -o ignore jexec b ping6 -c 3 2001:db8:1::1 + atf_check -s exit:0 -o ignore jexec b ping6 -c 3 -z 16 2001:db8:1::1 +} + +linklocal_cleanup() +{ + ovpn_cleanup +} + +atf_test_case "timeout_client" "cleanup" +timeout_client_head() +{ + atf_set descr 'IPv4 in IPv4 tunnel' + atf_set require.user root + atf_set require.progs openvpn +} + +timeout_client_body() +{ + ovpn_init + + l=$(vnet_mkepair) + + vnet_mkjail a ${l}a + jexec a ifconfig ${l}a 192.0.2.1/24 up + jexec a ifconfig lo0 127.0.0.1/8 up + vnet_mkjail b ${l}b + jexec b ifconfig ${l}b 192.0.2.2/24 up + + # Sanity check + atf_check -s exit:0 -o ignore jexec a ping -c 1 192.0.2.2 + + ovpn_start a " + dev ovpn0 + dev-type tun + proto udp4 + + cipher AES-256-GCM + auth SHA256 + + local 192.0.2.1 + server 198.51.100.0 255.255.255.0 + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/server.crt + key $(atf_get_srcdir)/server.key + dh $(atf_get_srcdir)/dh.pem + + mode server + script-security 2 + auth-user-pass-verify /usr/bin/true via-env + topology subnet + + keepalive 2 10 + + management 192.0.2.1 1234 + " + ovpn_start b " + dev tun0 + dev-type tun + + client + + remote 192.0.2.1 + auth-user-pass $(atf_get_srcdir)/user.pass + + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/client.crt + key $(atf_get_srcdir)/client.key + dh $(atf_get_srcdir)/dh.pem + + keepalive 2 10 + " + + # Give the tunnel time to come up + sleep 10 + + atf_check -s exit:0 -o ignore jexec b ping -c 3 198.51.100.1 + + # Kill the client + jexec b killall openvpn + + # Now wait for the server to notice + sleep 15 + + while echo "status" | jexec a nc -N 192.0.2.1 1234 | grep 192.0.2.2; do + echo "Client disconnect not discovered" + sleep 1 + done +} + +timeout_client_cleanup() +{ + ovpn_cleanup +} + +atf_test_case "explicit_exit" "cleanup" +explicit_exit_head() +{ + atf_set descr 'Test explicit exit notification' + atf_set require.user root + atf_set require.progs openvpn +} + +explicit_exit_body() +{ + ovpn_init + + l=$(vnet_mkepair) + + vnet_mkjail a ${l}a + jexec a ifconfig ${l}a 192.0.2.1/24 up + jexec a ifconfig lo0 127.0.0.1/8 up + vnet_mkjail b ${l}b + jexec b ifconfig ${l}b 192.0.2.2/24 up + + # Sanity check + atf_check -s exit:0 -o ignore jexec a ping -c 1 192.0.2.2 + + ovpn_start a " + dev ovpn0 + dev-type tun + proto udp4 + + cipher AES-256-GCM + auth SHA256 + + local 192.0.2.1 + server 198.51.100.0 255.255.255.0 + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/server.crt + key $(atf_get_srcdir)/server.key + dh $(atf_get_srcdir)/dh.pem + + mode server + script-security 2 + auth-user-pass-verify /usr/bin/true via-env + topology subnet + + management 192.0.2.1 1234 + " + ovpn_start b " + dev tun0 + dev-type tun + + client + + remote 192.0.2.1 + auth-user-pass $(atf_get_srcdir)/user.pass + + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/client.crt + key $(atf_get_srcdir)/client.key + dh $(atf_get_srcdir)/dh.pem + + explicit-exit-notify + " + + # Give the tunnel time to come up + sleep 10 + + atf_check -s exit:0 -o ignore jexec b ping -c 3 198.51.100.1 + + if ! echo "status" | jexec a nc -N 192.0.2.1 1234 | grep 192.0.2.2; then + atf_fail "Client not found in status list!" + fi + + # Kill the client + jexec b killall openvpn + + while echo "status" | jexec a nc -N 192.0.2.1 1234 | grep 192.0.2.2; do + jexec a ps auxf + echo "Client disconnect not discovered" + sleep 1 + done +} + +explicit_exit_cleanup() +{ + ovpn_cleanup +} + +atf_test_case "multi_client" "cleanup" +multi_client_head() +{ + atf_set descr 'Multiple simultaneous clients' + atf_set require.user root + atf_set require.progs openvpn +} + +multi_client_body() +{ + ovpn_init + vnet_init_bridge + + bridge=$(vnet_mkbridge) + srv=$(vnet_mkepair) + one=$(vnet_mkepair) + two=$(vnet_mkepair) + + ifconfig ${bridge} up + + ifconfig ${srv}a up + ifconfig ${bridge} addm ${srv}a + ifconfig ${one}a up + ifconfig ${bridge} addm ${one}a + ifconfig ${two}a up + ifconfig ${bridge} addm ${two}a + + vnet_mkjail srv ${srv}b + jexec srv ifconfig ${srv}b 192.0.2.1/24 up + vnet_mkjail one ${one}b + jexec one ifconfig ${one}b 192.0.2.2/24 up + vnet_mkjail two ${two}b + jexec two ifconfig ${two}b 192.0.2.3/24 up + jexec two ifconfig lo0 127.0.0.1/8 up + jexec two ifconfig lo0 inet alias 203.0.113.1/24 + + # Sanity checks + atf_check -s exit:0 -o ignore jexec one ping -c 1 192.0.2.1 + atf_check -s exit:0 -o ignore jexec two ping -c 1 192.0.2.1 + + jexec srv sysctl net.inet.ip.forwarding=1 + + ovpn_start srv " + dev ovpn0 + dev-type tun + proto udp4 + + cipher AES-256-GCM + auth SHA256 + + local 192.0.2.1 + server 198.51.100.0 255.255.255.0 + + push \"route 203.0.113.0 255.255.255.0 198.51.100.1\" + + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/server.crt + key $(atf_get_srcdir)/server.key + dh $(atf_get_srcdir)/dh.pem + + mode server + duplicate-cn + script-security 2 + auth-user-pass-verify /usr/bin/true via-env + topology subnet + + keepalive 100 600 + + client-config-dir $(atf_get_srcdir)/ccd + " + ovpn_start one " + dev tun0 + dev-type tun + + client + + remote 192.0.2.1 + auth-user-pass $(atf_get_srcdir)/user.pass + + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/client.crt + key $(atf_get_srcdir)/client.key + dh $(atf_get_srcdir)/dh.pem + + keepalive 100 600 + " + ovpn_start two " + dev tun0 + dev-type tun + + client + + remote 192.0.2.1 + auth-user-pass $(atf_get_srcdir)/user.pass + + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/client2.crt + key $(atf_get_srcdir)/client2.key + dh $(atf_get_srcdir)/dh.pem + + keepalive 100 600 + " + + # Give the tunnel time to come up + sleep 10 + + atf_check -s exit:0 -o ignore jexec one ping -c 3 198.51.100.1 + atf_check -s exit:0 -o ignore jexec two ping -c 3 198.51.100.1 + + # Client-to-client communication + atf_check -s exit:0 -o ignore jexec one ping -c 3 198.51.100.3 + atf_check -s exit:0 -o ignore jexec two ping -c 3 198.51.100.2 + + # iroute test + atf_check -s exit:0 -o ignore jexec one ping -c 3 203.0.113.1 +} + +multi_client_cleanup() +{ + ovpn_cleanup +} + +atf_test_case "route_to" "cleanup" +route_to_head() +{ + atf_set descr "Test pf's route-to with OpenVPN tunnels" + atf_set require.user root + atf_set require.progs openvpn +} + +route_to_body() +{ + pft_init + ovpn_init + + l=$(vnet_mkepair) + n=$(vnet_mkepair) + + vnet_mkjail a ${l}a + jexec a ifconfig ${l}a 192.0.2.1/24 up + vnet_mkjail b ${l}b ${n}a + jexec b ifconfig ${l}b 192.0.2.2/24 up + jexec b ifconfig ${n}a up + + # Sanity check + atf_check -s exit:0 -o ignore jexec a ping -c 1 192.0.2.2 + + ovpn_start a " + dev ovpn0 + dev-type tun + proto udp4 + + cipher AES-256-GCM + auth SHA256 + + local 192.0.2.1 + server 198.51.100.0 255.255.255.0 + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/server.crt + key $(atf_get_srcdir)/server.key + dh $(atf_get_srcdir)/dh.pem + + mode server + script-security 2 + auth-user-pass-verify /usr/bin/true via-env + topology subnet + + keepalive 100 600 + " + ovpn_start b " + dev tun0 + dev-type tun + + client + + remote 192.0.2.1 + auth-user-pass $(atf_get_srcdir)/user.pass + + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/client.crt + key $(atf_get_srcdir)/client.key + dh $(atf_get_srcdir)/dh.pem + + keepalive 100 600 + " + + # Give the tunnel time to come up + sleep 10 + jexec a ifconfig ovpn0 inet alias 198.51.100.254/24 + + # Check the tunnel + atf_check -s exit:0 -o ignore jexec b ping -c 1 -S 198.51.100.2 198.51.100.1 + atf_check -s exit:0 -o ignore jexec b ping -c 1 -S 198.51.100.2 198.51.100.254 + + # Break our route to .254 so that we need a route-to to make things work. + jexec b ifconfig ${n}a 203.0.113.1/24 up + jexec b route add 198.51.100.254 -interface ${n}a + + # Make sure it's broken. + atf_check -s exit:2 -o ignore jexec b ping -c 1 -S 198.51.100.2 198.51.100.254 + + jexec b pfctl -e + pft_set_rules b \ + "pass out route-to (tun0 198.51.100.1) proto icmp from 198.51.100.2 " + atf_check -s exit:0 -o ignore jexec b ping -c 3 -S 198.51.100.2 198.51.100.254 +} + +route_to_cleanup() +{ + ovpn_cleanup + pft_cleanup +} + +atf_test_case "ra" "cleanup" +ra_head() +{ + atf_set descr 'Remote access with multiple clients' + atf_set require.user root + atf_set require.progs openvpn +} + +ra_body() +{ + ovpn_init + vnet_init_bridge + + bridge=$(vnet_mkbridge) + srv=$(vnet_mkepair) + lan=$(vnet_mkepair) + one=$(vnet_mkepair) + two=$(vnet_mkepair) + + ifconfig ${bridge} up + + ifconfig ${srv}a up + ifconfig ${bridge} addm ${srv}a + ifconfig ${one}a up + ifconfig ${bridge} addm ${one}a + ifconfig ${two}a up + ifconfig ${bridge} addm ${two}a + + vnet_mkjail srv ${srv}b ${lan}a + jexec srv ifconfig lo0 inet 127.0.0.1/8 up + jexec srv ifconfig ${srv}b 192.0.2.1/24 up + jexec srv ifconfig ${lan}a 203.0.113.1/24 up + vnet_mkjail lan ${lan}b + jexec lan ifconfig lo0 inet 127.0.0.1/8 up + jexec lan ifconfig ${lan}b 203.0.113.2/24 up + jexec lan route add default 203.0.113.1 + vnet_mkjail one ${one}b + jexec one ifconfig lo0 inet 127.0.0.1/8 up + jexec one ifconfig ${one}b 192.0.2.2/24 up + vnet_mkjail two ${two}b + jexec two ifconfig lo0 inet 127.0.0.1/8 up + jexec two ifconfig ${two}b 192.0.2.3/24 up + + # Sanity checks + atf_check -s exit:0 -o ignore jexec one ping -c 1 192.0.2.1 + atf_check -s exit:0 -o ignore jexec two ping -c 1 192.0.2.1 + atf_check -s exit:0 -o ignore jexec srv ping -c 1 203.0.113.2 + + jexec srv sysctl net.inet.ip.forwarding=1 + + ovpn_start srv " + dev ovpn0 + dev-type tun + proto udp4 + + cipher AES-256-GCM + auth SHA256 + + local 192.0.2.1 + server 198.51.100.0 255.255.255.0 + + push \"route 203.0.113.0 255.255.255.0\" + + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/server.crt + key $(atf_get_srcdir)/server.key + dh $(atf_get_srcdir)/dh.pem + + mode server + duplicate-cn + script-security 2 + auth-user-pass-verify /usr/bin/true via-env + topology subnet + + keepalive 100 600 + " + ovpn_start one " + dev tun0 + dev-type tun + + client + + remote 192.0.2.1 + auth-user-pass $(atf_get_srcdir)/user.pass + + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/client.crt + key $(atf_get_srcdir)/client.key + dh $(atf_get_srcdir)/dh.pem + + keepalive 100 600 + " + sleep 2 + ovpn_start two " + dev tun0 + dev-type tun + + client + + remote 192.0.2.1 + auth-user-pass $(atf_get_srcdir)/user.pass + + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/client2.crt + key $(atf_get_srcdir)/client2.key + dh $(atf_get_srcdir)/dh.pem + + keepalive 100 600 + " + + # Give the tunnel time to come up + sleep 10 + + atf_check -s exit:0 -o ignore jexec one ping -c 1 198.51.100.1 + atf_check -s exit:0 -o ignore jexec two ping -c 1 198.51.100.1 + + # Client-to-client communication + atf_check -s exit:0 -o ignore jexec one ping -c 1 198.51.100.3 + atf_check -s exit:0 -o ignore jexec one ping -c 1 198.51.100.2 + atf_check -s exit:0 -o ignore jexec two ping -c 1 198.51.100.2 + atf_check -s exit:0 -o ignore jexec two ping -c 1 198.51.100.3 + + # RA test + atf_check -s exit:0 -o ignore jexec one ping -c 1 203.0.113.1 + atf_check -s exit:0 -o ignore jexec two ping -c 1 203.0.113.1 + + atf_check -s exit:0 -o ignore jexec srv ping -c 1 -S 203.0.113.1 198.51.100.2 + atf_check -s exit:0 -o ignore jexec srv ping -c 1 -S 203.0.113.1 198.51.100.3 + + atf_check -s exit:0 -o ignore jexec one ping -c 1 203.0.113.2 + atf_check -s exit:0 -o ignore jexec two ping -c 1 203.0.113.2 + + atf_check -s exit:0 -o ignore jexec lan ping -c 1 198.51.100.1 + atf_check -s exit:0 -o ignore jexec lan ping -c 1 198.51.100.2 + atf_check -s exit:0 -o ignore jexec lan ping -c 1 198.51.100.3 + atf_check -s exit:2 -o ignore jexec lan ping -c 1 198.51.100.4 +} + +ra_cleanup() +{ + ovpn_cleanup +} + +ovpn_algo_body() +{ + algo=$1 + + ovpn_init + + l=$(vnet_mkepair) + + vnet_mkjail a ${l}a + jexec a ifconfig ${l}a 192.0.2.1/24 up + vnet_mkjail b ${l}b + jexec b ifconfig ${l}b 192.0.2.2/24 up + + # Sanity check + atf_check -s exit:0 -o ignore jexec a ping -c 1 192.0.2.2 + + ovpn_start a " + dev ovpn0 + dev-type tun + proto udp4 + + cipher ${algo} + data-ciphers ${algo} + auth SHA256 + + local 192.0.2.1 + server 198.51.100.0 255.255.255.0 + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/server.crt + key $(atf_get_srcdir)/server.key + dh $(atf_get_srcdir)/dh.pem + + mode server + script-security 2 + auth-user-pass-verify /usr/bin/true via-env + topology subnet + + keepalive 100 600 + " + ovpn_start b " + dev tun0 + dev-type tun + + client + + cipher ${algo} + data-ciphers ${algo} + + remote 192.0.2.1 + auth-user-pass $(atf_get_srcdir)/user.pass + + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/client.crt + key $(atf_get_srcdir)/client.key + dh $(atf_get_srcdir)/dh.pem + + keepalive 100 600 + " + + # Give the tunnel time to come up + sleep 10 + + atf_check -s exit:0 -o ignore jexec b ping -c 3 198.51.100.1 +} + +atf_test_case "chacha" "cleanup" +chacha_head() +{ + atf_set descr 'Test DCO with the chacha algorithm' + atf_set require.user root + atf_set require.progs openvpn +} + +chacha_body() +{ + ovpn_algo_body CHACHA20-POLY1305 +} + +chacha_cleanup() +{ + ovpn_cleanup +} + +atf_test_case "gcm_128" "cleanup" +gcm_128_head() +{ + atf_set descr 'Test DCO with AES-128-GCM' + atf_set require.user root + atf_set require.progs openvpn +} + +gcm_128_body() +{ + ovpn_algo_body AES-128-GCM +} + +gcm_128_cleanup() +{ + ovpn_cleanup +} + +atf_test_case "destroy_unused" "cleanup" +destroy_unused_head() +{ + atf_set descr 'Destroy an if_ovpn interface before it is used' + atf_set require.user root +} + +destroy_unused_body() +{ + ovpn_init + + intf=$(ifconfig ovpn create) + atf_check -s exit:0 \ + ifconfig ${intf} destroy +} + +destroy_unused_cleanup() +{ + ovpn_cleanup +} + +atf_test_case "multihome4" "cleanup" +multihome4_head() +{ + atf_set descr 'Test multihome IPv4 with OpenVPN' + atf_set require.user root + atf_set require.progs openvpn +} + +multihome4_body() +{ + pft_init + ovpn_init + + l=$(vnet_mkepair) + + vnet_mkjail a ${l}a + atf_check jexec a ifconfig ${l}a inet 192.0.2.1/24 + atf_check jexec a ifconfig ${l}a alias 192.0.2.2/24 + vnet_mkjail b ${l}b + atf_check jexec b ifconfig ${l}b inet 192.0.2.3/24 + + # Sanity check + atf_check -s exit:0 -o ignore jexec b ping -c 1 192.0.2.1 + atf_check -s exit:0 -o ignore jexec b ping -c 1 192.0.2.2 + + ovpn_start a " + dev ovpn0 + dev-type tun + proto udp4 + + cipher AES-256-GCM + auth SHA256 + + multihome + server 198.51.100.0 255.255.255.0 + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/server.crt + key $(atf_get_srcdir)/server.key + dh $(atf_get_srcdir)/dh.pem + + mode server + script-security 2 + auth-user-pass-verify /usr/bin/true via-env + topology subnet + + keepalive 100 600 + " + ovpn_start b " + dev tun0 + dev-type tun + + client + + remote 192.0.2.2 + auth-user-pass $(atf_get_srcdir)/user.pass + + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/client.crt + key $(atf_get_srcdir)/client.key + dh $(atf_get_srcdir)/dh.pem + + keepalive 100 600 + " + + # Block packets from the primary address, openvpn should only use the + # configured remote address. + jexec b pfctl -e + pft_set_rules b \ + "block in quick from 192.0.2.1 to any" \ + "pass all" + + # Give the tunnel time to come up + sleep 10 + + atf_check -s exit:0 -o ignore jexec b ping -c 3 198.51.100.1 +} + +multihome4_cleanup() +{ + ovpn_cleanup + pft_cleanup +} + +multihome6_head() +{ + atf_set descr 'Test multihome IPv6 with OpenVPN' + atf_set require.user root + atf_set require.progs openvpn +} + +multihome6_body() +{ + ovpn_init + + l=$(vnet_mkepair) + + vnet_mkjail a ${l}a + atf_check jexec a ifconfig ${l}a inet6 2001:db8::1/64 no_dad + atf_check jexec a ifconfig ${l}a inet6 alias 2001:db8::2/64 no_dad + vnet_mkjail b ${l}b + atf_check jexec b ifconfig ${l}b inet6 2001:db8::3/64 no_dad + + # Sanity check + atf_check -s exit:0 -o ignore jexec b ping6 -c 1 2001:db8::1 + atf_check -s exit:0 -o ignore jexec b ping6 -c 1 2001:db8::2 + + ovpn_start a " + dev ovpn0 + dev-type tun + proto udp6 + + cipher AES-256-GCM + auth SHA256 + + multihome + server-ipv6 2001:db8:1::/64 + + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/server.crt + key $(atf_get_srcdir)/server.key + dh $(atf_get_srcdir)/dh.pem + + mode server + script-security 2 + auth-user-pass-verify /usr/bin/true via-env + topology subnet + + keepalive 100 600 + " + ovpn_start b " + dev tun0 + dev-type tun + + client + + remote 2001:db8::2 + auth-user-pass $(atf_get_srcdir)/user.pass + + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/client.crt + key $(atf_get_srcdir)/client.key + dh $(atf_get_srcdir)/dh.pem + + keepalive 100 600 + " + + # Block packets from the primary address, openvpn should only use the + # configured remote address. + jexec b pfctl -e + pft_set_rules b \ + "block in quick from 2001:db8::1 to any" \ + "pass all" + + # Give the tunnel time to come up + sleep 10 + + atf_check -s exit:0 -o ignore jexec b ping6 -c 3 2001:db8:1::1 + atf_check -s exit:0 -o ignore jexec b ping6 -c 3 -z 16 2001:db8:1::1 +} + +multihome6_cleanup() +{ + ovpn_cleanup +} + +atf_test_case "float" "cleanup" +float_head() +{ + atf_set descr 'Test peer float notification' + atf_set require.user root +} + +float_body() +{ + ovpn_init + ovpn_check_version 2.7.0 + + l=$(vnet_mkepair) + + vnet_mkjail a ${l}a + jexec a ifconfig ${l}a 192.0.2.1/24 up + jexec a ifconfig lo0 127.0.0.1/8 up + vnet_mkjail b ${l}b + jexec b ifconfig ${l}b 192.0.2.2/24 up + + # Sanity check + atf_check -s exit:0 -o ignore jexec a ping -c 1 192.0.2.2 + + ovpn_start a " + dev ovpn0 + dev-type tun + proto udp4 + + cipher AES-256-GCM + auth SHA256 + + local 192.0.2.1 + server 198.51.100.0 255.255.255.0 + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/server.crt + key $(atf_get_srcdir)/server.key + dh $(atf_get_srcdir)/dh.pem + + mode server + script-security 2 + auth-user-pass-verify /usr/bin/true via-env + topology subnet + + keepalive 2 10 + + management 192.0.2.1 1234 + " + ovpn_start b " + dev tun0 + dev-type tun + + client + + remote 192.0.2.1 + auth-user-pass $(atf_get_srcdir)/user.pass + + ca $(atf_get_srcdir)/ca.crt + cert $(atf_get_srcdir)/client.crt + key $(atf_get_srcdir)/client.key + dh $(atf_get_srcdir)/dh.pem + + keepalive 2 10 + " + + # Give the tunnel time to come up + sleep 10 + + atf_check -s exit:0 -o ignore jexec b ping -c 3 198.51.100.1 + + # We expect the client on 192.0.2.2 + if ! echo "status" | jexec a nc -N 192.0.2.1 1234 | grep 192.0.2.2; then + atf_fail "Client not found in status list!" + fi + + # Now change the client IP + jexec b ifconfig ${l}b 192.0.2.3/24 up + + # And wait for keepalives to trigger the float notification + sleep 5 + + # So the client now has the new address in userspace + if ! echo "status" | jexec a nc -N 192.0.2.1 1234 | grep 192.0.2.3; then + atf_fail "Client not found in status list!" + fi +} + +float_cleanup() +{ + ovpn_cleanup +} + +atf_init_test_cases() +{ + atf_add_test_case "4in4" + atf_add_test_case "bz283426" + atf_add_test_case "4mapped" + atf_add_test_case "6in4" + atf_add_test_case "6in6" + atf_add_test_case "4in6" + atf_add_test_case "linklocal" + atf_add_test_case "timeout_client" + atf_add_test_case "explicit_exit" + atf_add_test_case "multi_client" + atf_add_test_case "route_to" + atf_add_test_case "ra" + atf_add_test_case "chacha" + atf_add_test_case "gcm_128" + atf_add_test_case "destroy_unused" + atf_add_test_case "multihome4" + atf_add_test_case "multihome6" + atf_add_test_case "float" +} diff --git a/tests/sys/net/if_ovpn/if_ovpn_c.c b/tests/sys/net/if_ovpn/if_ovpn_c.c new file mode 100644 index 000000000000..7b558f1975dd --- /dev/null +++ b/tests/sys/net/if_ovpn/if_ovpn_c.c @@ -0,0 +1,132 @@ +//#include <sys/param.h> +#include <stdio.h> + +#include <net/if.h> +#include <netinet/in.h> +#include <sys/param.h> +#include <sys/errno.h> +#include <sys/linker.h> +#include <sys/ioctl.h> +#include <sys/nv.h> +#include <sys/socket.h> +#include <sys/sockio.h> + +#include <atf-c.h> + +#define OVPN_NEW_PEER _IO ('D', 1) + +static nvlist_t * +fake_sockaddr(void) +{ + uint32_t addr = htonl(INADDR_LOOPBACK); + nvlist_t *nvl; + + nvl = nvlist_create(0); + + nvlist_add_number(nvl, "af", AF_INET); + nvlist_add_binary(nvl, "address", &addr, 4); + nvlist_add_number(nvl, "port", 1024); + + return (nvl); +} + +static char ovpn_ifname[IFNAMSIZ]; +static int ovpn_fd; + +static int +create_interface(int fd) +{ + int ret; + struct ifreq ifr; + + bzero(&ifr, sizeof(ifr)); + + /* Create ovpnx first, then rename it. */ + snprintf(ifr.ifr_name, IFNAMSIZ, "ovpn"); + ret = ioctl(fd, SIOCIFCREATE2, &ifr); + if (ret) + return (ret); + + snprintf(ovpn_ifname, IFNAMSIZ, "%s", ifr.ifr_name); + printf("Created %s\n", ovpn_ifname); + + return (0); +} + +static void +destroy_interface(int fd) +{ + int ret; + struct ifreq ifr; + + if (ovpn_ifname[0] == 0) + return; + + printf("Destroy %s\n", ovpn_ifname); + + bzero(&ifr, sizeof(ifr)); + snprintf(ifr.ifr_name, IFNAMSIZ, "%s", ovpn_ifname); + + ret = ioctl(fd, SIOCIFDESTROY, &ifr); + if (ret) + atf_tc_fail("Failed to destroy interface"); + + ovpn_ifname[0] = 0; +} + +ATF_TC_WITH_CLEANUP(tcp); +ATF_TC_HEAD(tcp, tc) +{ + atf_tc_set_md_var(tc, "require.user", "root"); + atf_tc_set_md_var(tc, "require.kmods", "if_ovpn"); +} + +ATF_TC_BODY(tcp, tc) +{ + struct ifdrv drv; + struct sockaddr_in sock_in; + int ret; + nvlist_t *nvl; + + ovpn_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); + + /* Kick off a connect so there's a local address set, which we need for + * ovpn_new_peer() to get to the critical point. */ + bzero(&sock_in, sizeof(sock_in)); + sock_in.sin_family = AF_INET; + sock_in.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + sock_in.sin_port = htons(1024); + connect(ovpn_fd, (struct sockaddr *)&sock_in, sizeof(sock_in)); + + ret = create_interface(ovpn_fd); + if (ret) + atf_tc_fail("Failed to create interface"); + + nvl = nvlist_create(0); + + nvlist_add_number(nvl, "peerid", 0); + nvlist_add_number(nvl, "fd", ovpn_fd); + nvlist_add_nvlist(nvl, "remote", fake_sockaddr()); + + bzero(&drv, sizeof(drv)); + snprintf(drv.ifd_name, IFNAMSIZ, "%s", ovpn_ifname); + drv.ifd_cmd = OVPN_NEW_PEER; + drv.ifd_data = nvlist_pack(nvl, &drv.ifd_len); + + ret = ioctl(ovpn_fd, SIOCSDRVSPEC, &drv); + ATF_CHECK_EQ(ret, -1); + ATF_CHECK_EQ(errno, EPROTOTYPE); +} + +ATF_TC_CLEANUP(tcp, tc) +{ + destroy_interface(ovpn_fd); + close(ovpn_fd); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, tcp); + + return (atf_no_error()); +} diff --git a/tests/sys/net/if_ovpn/server.crt b/tests/sys/net/if_ovpn/server.crt new file mode 100644 index 000000000000..e4166fa2e0ae --- /dev/null +++ b/tests/sys/net/if_ovpn/server.crt @@ -0,0 +1,123 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1048687 (0x10006f) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=KG, ST=NA, L=BISHKEK, O=OpenVPN-TEST/emailAddress=me@myhost.mydomain + Validity + Not Before: Apr 27 15:01:41 2022 GMT + Not After : Apr 3 15:01:41 2122 GMT + Subject: C=KG, ST=NA, O=OpenVPN-TEST, CN=Test-Server/emailAddress=me@myhost.mydomain + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public-Key: (4096 bit) + Modulus: + 00:b1:4c:f3:d9:6e:39:ac:d7:8f:78:e9:37:dd:ae: + 73:f4:d6:77:84:42:3e:2a:76:76:6f:71:6a:b4:45: + a9:e3:84:0e:ee:3d:18:20:47:9d:fb:d1:ca:bb:d7: + cd:d5:e3:b8:3d:1b:9e:c1:f5:26:72:4a:bb:fe:4e: + ec:49:06:c0:ff:21:f4:b5:5c:72:fc:c7:3e:27:86: + 03:65:e2:d8:f7:5c:c7:23:16:82:ab:ee:81:7d:44: + 41:a0:34:06:14:19:08:7a:47:69:5e:b6:aa:6f:74: + 08:4f:13:ca:1d:b1:d8:2e:3a:a7:41:ec:e0:3e:b4: + 54:b2:7c:2e:dd:ee:f5:07:92:ed:f2:64:62:2f:7a: + c2:8e:f0:50:2d:f6:2b:1c:9d:1d:db:25:04:1e:b5: + 0d:18:c8:a4:b6:1e:cc:05:a1:10:74:e2:4c:98:32: + 44:6c:95:94:18:a0:64:0d:32:6b:84:f9:25:4d:04: + 0d:39:73:23:cf:b0:5a:ab:c0:ff:ec:c1:6b:ed:fa: + 9b:26:d0:45:d5:0e:75:72:d6:2f:36:26:fe:2d:bc: + 50:e0:a0:14:d4:34:e0:10:cf:aa:6a:46:79:7d:dc: + 30:e7:6c:c0:44:3e:fc:20:dd:e1:05:b2:a2:2f:aa: + 06:76:dd:33:44:19:8e:5c:54:50:d0:2b:a8:03:06: + ec:31:1c:48:1b:39:51:52:0f:44:b0:90:d5:29:c0: + b9:ae:e2:74:af:e2:08:c7:b2:e5:4e:71:f0:88:33: + 97:16:92:69:0b:48:8c:25:7c:8e:20:7c:8a:0f:32: + e6:15:90:02:33:d6:00:4f:1d:c9:7e:ef:5c:af:5f: + b4:f9:c5:8b:7b:c8:47:34:4d:85:80:f2:a9:3c:e0: + 53:d0:b7:15:59:67:0e:1b:17:6d:9b:ed:a8:14:e3: + 90:9d:6e:3a:83:ae:6f:0c:c6:58:2f:e6:41:f2:67: + b5:7c:86:97:98:55:59:14:a0:0a:f4:5f:2e:8d:ae: + e2:d9:68:a9:34:b1:c3:0f:44:04:9e:81:a5:45:8b: + 2c:82:a9:6c:ea:e7:a8:dd:27:8b:0e:0c:8c:14:35: + c8:86:01:1e:69:43:93:cb:57:c8:9d:43:d9:8e:22: + de:f8:27:e4:44:1c:3b:58:47:10:13:5f:ca:85:8b: + a6:ee:5a:27:17:13:9e:72:c5:a6:d1:60:e3:ec:69: + f0:2f:d0:d8:25:a1:80:c3:03:6b:5d:32:21:26:31: + 48:b7:ce:d2:32:2c:e9:1e:34:eb:21:e7:8e:a9:26: + 86:e6:a1:ba:0f:4a:8a:05:eb:4b:02:7a:65:e4:d3: + 39:da:10:3a:e6:0e:6f:22:ca:a0:2f:b7:7b:1e:65: + 05:73:d7 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + Netscape Comment: + OpenSSL Generated Certificate + X509v3 Subject Key Identifier: + 4E:69:D9:D4:F1:8C:C7:F0:7D:02:79:DD:00:AB:5C:0B:61:42:F5:E2 + X509v3 Authority Key Identifier: + keyid:88:07:18:C1:FE:2E:09:2E:7D:E0:B9:1B:83:A9:54:DE:09:B1:16:7E + + Signature Algorithm: sha256WithRSAEncryption + 10:40:c5:a6:b4:4f:10:e1:ff:d4:fd:68:6d:b1:f4:02:7e:18: + 03:99:d2:fa:ce:24:b9:10:c3:d7:18:48:e6:9c:4c:b2:45:39: + a8:bc:7b:1f:66:bf:1a:bc:d5:22:f6:bc:61:e3:87:4d:d4:c8: + dc:ea:ee:5f:ad:95:94:e0:17:ff:7f:d5:b6:bd:a7:5f:2b:9d: + a4:5b:65:58:9f:83:c6:91:6f:d9:d9:1b:2e:e8:19:d8:d7:35: + ef:07:5d:1b:cd:89:2b:b3:d1:0c:bd:41:99:fc:54:fe:44:03: + a6:25:06:3a:e3:f5:3e:a1:9e:de:6b:7c:8e:dc:71:32:a9:2b: + 48:06:b7:72:f3:e4:38:fd:88:c0:62:48:d3:48:81:30:9e:ac: + 3f:d9:c6:40:92:98:39:7f:ec:bb:b8:8d:25:a0:c0:ed:c3:be: + 3c:df:54:42:3c:5d:2d:48:f5:35:b1:e9:b5:2f:0b:53:f1:fa: + 30:56:da:7d:2e:46:7d:7e:27:59:e5:ab:19:7b:be:9c:77:df: + 5d:6d:94:d7:3d:3b:45:09:0d:4e:a6:3e:2d:6d:95:76:78:af: + 03:d9:62:5d:64:14:f6:9b:36:a1:e4:6c:07:07:7f:ae:31:e6: + 69:6b:56:e6:42:6b:f6:de:24:ae:12:6a:58:13:31:9c:1a:87: + 01:a5:57:57:1f:0a:9e:16:85:30:c9:95:46:d1:05:70:df:39: + 80:fb:75:b1:44:43:e3:ba:8e:ef:c0:4d:db:9d:53:6c:32:e0: + 69:c8:74:b3:24:51:db:f9:7b:fb:0e:bc:61:0e:f3:56:31:2e: + 29:51:ed:dc:93:14:13:d0:6b:ab:88:d4:ae:e7:41:c2:da:7b: + 73:ec:d1:b9:49:07:85:73:e3:75:ed:ea:e8:48:09:01:45:52: + 58:05:37:bf:f8:5e:74:61:5f:1e:b2:db:1f:49:62:d1:ab:8f: + e6:d4:3e:69:1d:da:0b:93:88:3e:1c:2e:f4:03:32:9e:75:df: + af:65:0d:1c:cf:fd:35:36:f7:a5:93:01:11:69:2a:7b:76:8a: + 52:bb:e3:e9:b0:dc:e8:5b:78:65:3d:e9:36:84:8f:03:b3:ed: + 43:88:1f:97:71:ce:c6:c1:9e:e2:5d:51:b5:1a:bd:c6:fe:f1: + e2:c3:6c:95:6d:08:06:5b:b1:13:4b:57:36:07:c4:81:ea:d8: + f7:e4:2a:27:ce:75:1d:fd:ca:e4:82:a3:d1:0b:75:a3:77:8e: + c0:2b:34:87:2b:84:17:8c:23:e0:6d:fb:18:01:06:56:4b:f5: + 39:bc:3e:59:3d:ed:68:17:c0:12:66:56:0e:34:64:95:b2:84: + 99:de:71:94:2a:ca:a6:70 +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- diff --git a/tests/sys/net/if_ovpn/server.key b/tests/sys/net/if_ovpn/server.key new file mode 100644 index 000000000000..f35d6fcd4563 --- /dev/null +++ b/tests/sys/net/if_ovpn/server.key @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/tests/sys/net/if_ovpn/user.pass b/tests/sys/net/if_ovpn/user.pass new file mode 100644 index 000000000000..59d468ee39af --- /dev/null +++ b/tests/sys/net/if_ovpn/user.pass @@ -0,0 +1,2 @@ +username +password diff --git a/tests/sys/net/if_ovpn/utils.subr b/tests/sys/net/if_ovpn/utils.subr new file mode 100644 index 000000000000..fbe7dc98630a --- /dev/null +++ b/tests/sys/net/if_ovpn/utils.subr @@ -0,0 +1,92 @@ +## +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2022 Rubicon Communications, LLC ("Netgate") +# +# 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. + +. $(atf_get_srcdir)/../../common/vnet.subr + +ovpn_init() +{ + vnet_init + + if ! kldstat -q -m if_ovpn; then + atf_skip "This test requires if_ovpn" + fi + + has_dco=$(openvpn --version 2>&1 | grep '\[DCO\]') + if [ -z "$has_dco" ]; then + atf_skip "openvpn binary does not support DCO" + fi +} + +ovpn_check_version() +{ + expected=$1 + + expected_minor=$(echo $expected | + awk '{ split($1, ver, "\."); print(ver[2]); }') + actual_minor=$(openvpn --version 2>&1 | + awk 'NR == 1 \ + { \ + split($2, ver, "\."); \ + split(ver[2], minor, "_"); \ + print(minor[1]); \ + }') + + if [ ${actual_minor} -lt ${expected_minor} ]; then + atf_skip "OpenVPN version < ${expected}" + fi +} + +ovpn_cleanup() +{ + for jail in `cat ovpn_jails.lst | sort -u` + do + cat ovpn_${jail}.log| sed s/^/\[${jail}\]\ / + done + + vnet_cleanup +} + +ovpn_start() +{ + jail=$1 + cfg=$2 + + echo ${jail} >> ovpn_jails.lst + + dir=$(pwd) + + echo "Start" >> ovpn_${jail}.log + echo "=====" >> ovpn_${jail}.log + + echo "$cfg" > ovpn_${jail}.ovpn + + echo "Jail $jail:" + echo "===========" + cat ovpn_${jail}.ovpn + + jexec $jail sh -c "cd ${dir} && + openvpn --config ovpn_${jail}.ovpn >> ovpn_${jail}.log &" +} diff --git a/tests/sys/net/if_stf.sh b/tests/sys/net/if_stf.sh new file mode 100644 index 000000000000..71b00f308014 --- /dev/null +++ b/tests/sys/net/if_stf.sh @@ -0,0 +1,203 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2021 Rubicon Communications, LLC (Netgate) +# +# 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. + +. $(atf_get_srcdir)/../common/vnet.subr + +atf_test_case "6to4" "cleanup" +6to4_head() +{ + atf_set descr 'Test 6to4' + atf_set require.user root +} + +6to4_body() +{ + vnet_init + if ! kldstat -q -m if_stf; then + atf_skip "This test requires if_stf" + fi + if ! kldstat -q -m if_gif; then + atf_skip "This test requires if_gif" + fi + + epair=$(vnet_mkepair) + + vnet_mkjail relay ${epair}a + jexec relay ifconfig lo0 inet6 2001:db8::1/64 up + jexec relay ifconfig ${epair}a 192.0.2.1/24 up + # Simple gif to terminate 6to4 + gif=$(jexec relay ifconfig gif create) + jexec relay ifconfig $gif inet6 2002:c000:0201::1/64 up + jexec relay ifconfig $gif tunnel 192.0.2.1 192.0.2.2 + jexec relay route -6 add default -interface $gif + + vnet_mkjail client ${epair}b + jexec client ifconfig lo0 up + jexec client ifconfig ${epair}b 192.0.2.2/24 up + stf=$(jexec client ifconfig stf create) + jexec client ifconfig $stf inet6 2002:c000:0202::1/32 up + jexec client route -6 add default 2002:c000:0201::1 + + # Sanity check + atf_check -s exit:0 -o ignore \ + jexec client ping -c 1 192.0.2.1 + # 6to4 direct + atf_check -s exit:0 -o ignore \ + jexec client ping6 -c 1 2002:c000:0201::1 + + # "Wider internet" + atf_check -s exit:0 -o ignore \ + jexec client ping6 -c 1 2001:db8::1 +} + +6to4_cleanup() +{ + vnet_cleanup +} + +atf_test_case "6rd" "cleanup" +6rd_head() +{ + atf_set descr '6RD test' + atf_set require.user root +} + +6rd_body() +{ + vnet_init + + if ! kldstat -q -m if_stf; then + atf_skip "This test requires if_stf" + fi + if ! kldstat -q -m if_gif; then + atf_skip "This test requires if_gif" + fi + + epair=$(vnet_mkepair) + vnet_mkjail br ${epair}a + jexec br ifconfig ${epair}a 192.0.2.1/24 up + + # Simple gif to terminate the 6RD tunnel + gif=$(jexec br ifconfig gif create) + jexec br ifconfig lo0 inet6 2001:db9::1/64 up + jexec br ifconfig $gif inet6 2001:db8::/64 up + jexec br ifconfig $gif tunnel 192.0.2.1 192.0.2.2 + jexec br route -6 add default -interface $gif + + vnet_mkjail client ${epair}b + jexec client ifconfig lo0 up + jexec client ifconfig ${epair}b 192.0.2.2/24 up + + # Sanity check + atf_check -s exit:0 -o ignore \ + jexec client ping -c 1 192.0.2.1 + + stf=$(jexec client ifconfig stf create) + + jexec client ifconfig $stf stfv4br 192.0.2.1 + jexec client ifconfig $stf stfv4net 192.0.2.2/32 + jexec client ifconfig $stf inet6 2001:db8:c000:0202::1/32 up + jexec client route -6 add default -interface $stf + + atf_check -s exit:0 -o ignore \ + jexec client ping6 -c 1 2001:db9::1 +} + +6rd_cleanup() +{ + vnet_cleanup +} + +atf_test_case "6rd_peer" "cleanup" +6rd_peer_head() +{ + atf_set descr '6RD peer test' + atf_set require.user root +} + +6rd_peer_body() +{ + vnet_init + + if ! kldstat -q -m if_stf; then + atf_skip "This test requires if_stf" + fi + + epair=$(vnet_mkepair) + + vnet_mkjail one ${epair}a + jexec one ifconfig lo0 up + jexec one ifconfig ${epair}a 192.0.2.1/24 up + stf_one=$(jexec one ifconfig stf create) + jexec one ifconfig $stf_one stfv4br 192.0.2.3 + jexec one ifconfig $stf_one stfv4net 192.0.2.1/32 + jexec one ifconfig $stf_one inet6 2001:db8:c000:0201::1/32 up + jexec one route -6 add default -interface $stf_one + + vnet_mkjail two ${epair}b + jexec two ifconfig lo0 up + jexec two ifconfig ${epair}b 192.0.2.2/24 up + stf_two=$(jexec two ifconfig stf create) + jexec two ifconfig $stf_two stfv4br 192.0.2.3 + jexec two ifconfig $stf_two stfv4net 192.0.2.2/32 + jexec two ifconfig $stf_two inet6 2001:db8:c000:0202::1/32 up + jexec two route -6 add default -interface $stf_two + + # Sanity check + atf_check -s exit:0 -o ignore \ + jexec one ping -c 1 192.0.2.2 + + # Test 6rd + atf_check -s exit:0 -o ignore \ + jexec one ping6 -c 1 2001:db8:c000:0202::1 + atf_check -s exit:0 -o ignore \ + jexec two ping6 -c 1 2001:db8:c000:0201::1 + + # Shorter prefixes, for both v4 and v6 + jexec one ifconfig $stf_one inet6 2001:db8:c000:0201::1 delete + jexec one ifconfig $stf_one inet6 2001:0201::1/16 + jexec one ifconfig $stf_one stfv4net 192.0.2.1/16 + jexec two ifconfig $stf_two inet6 2001:db8:c000:0202::1 delete + jexec two ifconfig $stf_two inet6 2001:0202::1/16 + jexec two ifconfig $stf_two stfv4net 192.0.2.2/16 + + atf_check -s exit:0 -o ignore \ + jexec one ping6 -c 1 2001:0202::1 + atf_check -s exit:0 -o ignore \ + jexec two ping6 -c 1 2001:0201::1 +} + +6rd_peer_cleanup() +{ + vnet_cleanup +} + +atf_init_test_cases() +{ + atf_add_test_case "6to4" + atf_add_test_case "6rd" + atf_add_test_case "6rd_peer" +} diff --git a/tests/sys/net/if_tun_test.sh b/tests/sys/net/if_tun_test.sh new file mode 100755 index 000000000000..f4ce7800272e --- /dev/null +++ b/tests/sys/net/if_tun_test.sh @@ -0,0 +1,85 @@ + +. $(atf_get_srcdir)/../common/vnet.subr + +atf_test_case "235704" "cleanup" +235704_head() +{ + atf_set descr "Test PR #235704" + atf_set require.user root +} +235704_body() +{ + vnet_init + vnet_mkjail one + + tun=$(jexec one ifconfig tun create) + jexec one ifconfig ${tun} name foo + atf_check -s exit:0 jexec one ifconfig foo destroy +} +235704_cleanup() +{ + vnet_cleanup +} + +atf_test_case "basic" "cleanup" +basic_head() +{ + atf_set descr "Test if_tun using nc" + atf_set require.user root +} +basic_body() +{ + vnet_init + + epair=$(vnet_mkepair) + + tun_duke=$(ifconfig tun create) + tun_bass=$(ifconfig tun create) + + vnet_mkjail duke ${epair}a ${tun_duke} + vnet_mkjail bass ${epair}b ${tun_bass} + + jexec duke ifconfig ${epair}a inet 10.0.0.1/24 up + jexec bass ifconfig ${epair}b inet 10.0.0.2/24 up + + jexec duke nc -u -l --tun /dev/${tun_duke} 10.0.0.1 2600 & + jexec bass nc -u --tun /dev/${tun_bass} 10.0.0.1 2600 & + + jexec duke ifconfig ${tun_duke} inet 10.100.0.1/24 10.100.0.2 up + jexec bass ifconfig ${tun_bass} inet 10.100.0.2/24 10.100.0.1 up + + atf_check -s exit:0 -o ignore \ + jexec bass ping -c 1 10.100.0.1 +} +basic_cleanup() +{ + vnet_cleanup +} + +atf_test_case "transient" "cleanup" +transient_head() +{ + atf_set descr "Test transient tunnel support" + atf_set require.user root +} +transient_body() +{ + vnet_init + vnet_mkjail one + + tun=$(jexec one ifconfig tun create) + atf_check -s exit:0 -o not-empty jexec one ifconfig ${tun} + jexec one $(atf_get_srcdir)/transient_tuntap /dev/${tun} + atf_check -s not-exit:0 -e not-empty jexec one ifconfig ${tun} +} +transient_cleanup() +{ + vnet_cleanup +} + +atf_init_test_cases() +{ + atf_add_test_case "235704" + atf_add_test_case "basic" + atf_add_test_case "transient" +} diff --git a/tests/sys/net/if_vlan.sh b/tests/sys/net/if_vlan.sh new file mode 100755 index 000000000000..8122203337e2 --- /dev/null +++ b/tests/sys/net/if_vlan.sh @@ -0,0 +1,373 @@ + +. $(atf_get_srcdir)/../common/vnet.subr + +atf_test_case "basic" "cleanup" +basic_head() +{ + atf_set descr 'Basic VLAN test' + atf_set require.user root +} + +basic_body() +{ + vnet_init + + epair_vlan=$(vnet_mkepair) + + vnet_mkjail alcatraz ${epair_vlan}a + vnet_mkjail singsing ${epair_vlan}b + + vlan0=$(jexec alcatraz ifconfig vlan create vlandev ${epair_vlan}a \ + vlan 42) + jexec alcatraz ifconfig ${epair_vlan}a up + jexec alcatraz ifconfig ${vlan0} 10.0.0.1/24 up + + vlan1=$(jexec singsing ifconfig vlan create) + + # Test associating the physical interface + atf_check -s exit:0 \ + jexec singsing ifconfig ${vlan1} vlandev ${epair_vlan}b vlan 42 + + jexec singsing ifconfig ${epair_vlan}b up + jexec singsing ifconfig ${vlan1} 10.0.0.2/24 up + + atf_check -s exit:0 -o ignore jexec singsing ping -c 1 10.0.0.1 + + # Test changing the vlan ID + atf_check -s exit:0 \ + jexec singsing ifconfig ${vlan1} vlandev ${epair_vlan}b vlan 43 + atf_check -s exit:2 -o ignore jexec singsing ping -c 1 10.0.0.1 + + # And change back + # Test changing the vlan ID + atf_check -s exit:0 \ + jexec singsing ifconfig ${vlan1} vlan 42 vlandev ${epair_vlan}b + atf_check -s exit:0 -o ignore jexec singsing ping -c 1 10.0.0.1 +} + +basic_cleanup() +{ + vnet_cleanup +} + +# Simple Q-in-Q (802.1Q over 802.1ad) + +atf_test_case "qinq_simple" "cleanup" +qinq_simple_head() +{ + atf_set descr 'Simple Q-in-Q test (802.1Q over 802.1ad)' + atf_set require.user root +} + +qinq_simple_body() +{ + vnet_init + + epair_qinq=$(vnet_mkepair) + + vnet_mkjail jqinq0 ${epair_qinq}a + vnet_mkjail jqinq1 ${epair_qinq}b + + vlan5a=$(jexec jqinq0 ifconfig vlan create \ + vlandev ${epair_qinq}a vlan 5 vlanproto 802.1ad) + vlan42a=$(jexec jqinq0 ifconfig vlan create \ + vlandev ${vlan5a} vlan 42 vlanproto 802.1q) + jexec jqinq0 ifconfig ${epair_qinq}a up + jexec jqinq0 ifconfig ${vlan5a} up + jexec jqinq0 ifconfig ${vlan42a} 10.5.42.1/24 up + + vlan5b=$(jexec jqinq1 ifconfig vlan create \ + vlandev ${epair_qinq}b vlan 5 vlanproto 802.1ad) + vlan42b=$(jexec jqinq1 ifconfig vlan create \ + vlandev ${vlan5b} vlan 42 vlanproto 802.1q) + jexec jqinq1 ifconfig ${epair_qinq}b up + jexec jqinq1 ifconfig ${vlan5b} up + jexec jqinq1 ifconfig ${vlan42b} 10.5.42.2/24 up + + atf_check -s exit:0 -o ignore jexec jqinq1 ping -c 1 10.5.42.1 +} + +qinq_simple_cleanup() +{ + vnet_cleanup +} + +# Deep Q-in-Q (802.1Q over 802.1ad over 802.1ad) + +atf_test_case "qinq_deep" "cleanup" +qinq_deep_head() +{ + atf_set descr 'Deep Q-in-Q test (802.1Q over 802.1ad over 802.1ad)' + atf_set require.user root +} + +qinq_deep_body() +{ + vnet_init + + epair_qinq=$(vnet_mkepair) + + vnet_mkjail jqinq2 ${epair_qinq}a + vnet_mkjail jqinq3 ${epair_qinq}b + + vlan5a=$(jexec jqinq2 ifconfig vlan create \ + vlandev ${epair_qinq}a vlan 5 vlanproto 802.1ad) + vlan6a=$(jexec jqinq2 ifconfig vlan create \ + vlandev ${vlan5a} vlan 6 vlanproto 802.1ad) + vlan42a=$(jexec jqinq2 ifconfig vlan create \ + vlandev ${vlan6a} vlan 42 vlanproto 802.1q) + jexec jqinq2 ifconfig ${epair_qinq}a up + jexec jqinq2 ifconfig ${vlan5a} up + jexec jqinq2 ifconfig ${vlan6a} up + jexec jqinq2 ifconfig ${vlan42a} 10.6.42.1/24 up + + vlan5b=$(jexec jqinq3 ifconfig vlan create \ + vlandev ${epair_qinq}b vlan 5 vlanproto 802.1ad) + vlan6b=$(jexec jqinq3 ifconfig vlan create \ + vlandev ${vlan5b} vlan 6 vlanproto 802.1ad) + vlan42b=$(jexec jqinq3 ifconfig vlan create \ + vlandev ${vlan6b} vlan 42 vlanproto 802.1q) + jexec jqinq3 ifconfig ${epair_qinq}b up + jexec jqinq3 ifconfig ${vlan5b} up + jexec jqinq3 ifconfig ${vlan6b} up + jexec jqinq3 ifconfig ${vlan42b} 10.6.42.2/24 up + + atf_check -s exit:0 -o ignore jexec jqinq3 ping -c 1 10.6.42.1 +} + +qinq_deep_cleanup() +{ + vnet_cleanup +} + +# Legacy Q-in-Q (802.1Q over 802.1Q) + +atf_test_case "qinq_legacy" "cleanup" +qinq_legacy_head() +{ + atf_set descr 'Legacy Q-in-Q test (802.1Q over 802.1Q)' + atf_set require.user root +} + +qinq_legacy_body() +{ + vnet_init + + epair_qinq=$(vnet_mkepair) + + vnet_mkjail jqinq4 ${epair_qinq}a + vnet_mkjail jqinq5 ${epair_qinq}b + + vlan5a=$(jexec jqinq4 ifconfig vlan create \ + vlandev ${epair_qinq}a vlan 5) + vlan42a=$(jexec jqinq4 ifconfig vlan create \ + vlandev ${vlan5a} vlan 42) + jexec jqinq4 ifconfig ${epair_qinq}a up + jexec jqinq4 ifconfig ${vlan5a} up + jexec jqinq4 ifconfig ${vlan42a} 10.5.42.1/24 up + + vlan5b=$(jexec jqinq5 ifconfig vlan create \ + vlandev ${epair_qinq}b vlan 5) + vlan42b=$(jexec jqinq5 ifconfig vlan create \ + vlandev ${vlan5b} vlan 42) + jexec jqinq5 ifconfig ${epair_qinq}b up + jexec jqinq5 ifconfig ${vlan5b} up + jexec jqinq5 ifconfig ${vlan42b} 10.5.42.2/24 up + + atf_check -s exit:0 -o ignore jexec jqinq5 ping -c 1 10.5.42.1 +} + +qinq_legacy_cleanup() +{ + vnet_cleanup +} + +# Simple Q-in-Q with dot notation + +atf_test_case "qinq_dot" "cleanup" +qinq_dot_head() +{ + atf_set descr 'Simple Q-in-Q test with dot notation' + atf_set require.user root +} + +qinq_dot_body() +{ + vnet_init + + epair_qinq=$(vnet_mkepair) + + vnet_mkjail jqinq6 ${epair_qinq}a + vnet_mkjail jqinq7 ${epair_qinq}b + + jexec jqinq6 ifconfig vlan5 create \ + vlandev ${epair_qinq}a vlan 5 vlanproto 802.1ad + jexec jqinq6 ifconfig vlan5.42 create \ + vlanproto 802.1q + jexec jqinq6 ifconfig ${epair_qinq}a up + jexec jqinq6 ifconfig vlan5 up + jexec jqinq6 ifconfig vlan5.42 10.5.42.1/24 up + + vlan5b=$(jexec jqinq7 ifconfig vlan create \ + vlandev ${epair_qinq}b vlan 5 vlanproto 802.1ad) + vlan42b=$(jexec jqinq7 ifconfig vlan create \ + vlandev ${vlan5b} vlan 42 vlanproto 802.1q) + jexec jqinq7 ifconfig ${epair_qinq}b up + jexec jqinq7 ifconfig ${vlan5b} up + jexec jqinq7 ifconfig ${vlan42b} 10.5.42.2/24 up + + atf_check -s exit:0 -o ignore jexec jqinq7 ping -c 1 10.5.42.1 +} + +qinq_dot_cleanup() +{ + vnet_cleanup +} + +atf_test_case "qinq_setflags" "cleanup" +qinq_setflags_head() +{ + atf_set descr 'Test setting flags on a QinQ device' + atf_set require.user root +} + +qinq_setflags_body() +{ + vnet_init + + epair=$(vnet_mkepair) + + ifconfig ${epair}a up + vlan1=$(ifconfig vlan create) + ifconfig $vlan1 vlan 1 vlandev ${epair}a + vlan2=$(ifconfig vlan create) + ifconfig $vlan2 vlan 2 vlandev $vlan1 + + # This panics, incorrect locking + ifconfig $vlan2 promisc +} + +qinq_setflags_cleanup() +{ + vnet_cleanup +} + +atf_test_case "bpf_pcp" "cleanup" +bpf_pcp_head() +{ + atf_set descr 'Set VLAN PCP through BPF' + atf_set require.user root + atf_set require.progs python3 scapy +} + +bpf_pcp_body() +{ + vnet_init + + epair=$(vnet_mkepair) + + ifconfig ${epair}a up + + vnet_mkjail alcatraz ${epair}b + vlan=$(jexec alcatraz ifconfig vlan create) + jexec alcatraz ifconfig ${vlan} vlan 42 vlandev ${epair}b + jexec alcatraz ifconfig ${vlan} up + jexec alcatraz ifconfig ${epair}b up + + jexec alcatraz sysctl net.link.vlan.mtag_pcp=1 + + jexec alcatraz dhclient ${vlan} & + atf_check -s exit:1 -o ignore -e ignore $(atf_get_srcdir)/pcp.py \ + --expect-pcp 6 \ + --recvif ${epair}a + + jexec alcatraz killall dhclient + sleep 1 + + jexec alcatraz dhclient -c $(atf_get_srcdir)/dhclient_pcp.conf ${vlan} & + atf_check -s exit:0 -o ignore -e ignore $(atf_get_srcdir)/pcp.py \ + --expect-pcp 6 \ + --recvif ${epair}a +} + +bpf_pcp_cleanup() +{ + sysctl net.link.vlan.mtag_pcp=0 + jexec alcatraz killall dhclient + vnet_cleanup +} + +atf_test_case "conflict_id" "cleanup" +conflict_id_head() +{ + atf_set descr 'Test conflicting VLAN IDs, PR #279195' + atf_set require.user root +} + +conflict_id_body() +{ + vnet_init + + epair=$(vnet_mkepair) + + vnet_mkjail alcatraz ${epair}b + vlan_a=$(jexec alcatraz ifconfig vlan create) + vlan_b=$(jexec alcatraz ifconfig vlan create) + + jexec alcatraz ifconfig ${vlan_a} vlan 100 vlandev ${epair}b + jexec alcatraz ifconfig ${vlan_b} vlan 101 vlandev ${epair}b + + atf_check -s exit:1 -o ignore -e ignore \ + jexec alcatraz ifconfig ${vlan_a} vlan 101 + + atf_check -s exit:0 -o match:"vlan: 100" \ + jexec alcatraz ifconfig ${vlan_a} + + atf_check -s exit:0 -o ignore -e ignore \ + jexec alcatraz ifconfig ${vlan_a} vlan 100 +} + +conflict_id_cleanup() +{ + vnet_cleanup + +} + +# If a vlan interface is in a bridge, changing the vlandev to refer to +# a bridge should not be allowed. +atf_test_case "bridge_vlandev" "cleanup" +bridge_vlandev_head() +{ + atf_set descr 'transforming a bridge member vlan into an SVI is not allowed' + atf_set require.user root +} + +bridge_vlandev_body() +{ + vnet_init + vnet_init_bridge + + bridge=$(vnet_mkbridge) + vlan=$(vnet_mkvlan) + + atf_check -s exit:0 ifconfig ${bridge} addm ${vlan} + atf_check -s exit:1 -e ignore ifconfig ${vlan} vlan 1 vlandev ${bridge} +} + +bridge_vlandev_cleanup() +{ + vnet_cleanup +} + +atf_init_test_cases() +{ + atf_add_test_case "basic" + atf_add_test_case "qinq_simple" + atf_add_test_case "qinq_deep" + atf_add_test_case "qinq_legacy" + atf_add_test_case "qinq_dot" + atf_add_test_case "qinq_setflags" + atf_add_test_case "bpf_pcp" + atf_add_test_case "conflict_id" + atf_add_test_case "bridge_vlandev" +} diff --git a/tests/sys/net/if_wg.sh b/tests/sys/net/if_wg.sh new file mode 100644 index 000000000000..1f51d86c8efa --- /dev/null +++ b/tests/sys/net/if_wg.sh @@ -0,0 +1,633 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2021 The FreeBSD Foundation +# +# This software was developed by Mark Johnston under sponsorship +# from the FreeBSD Foundation. +# +# 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. + +. $(atf_get_srcdir)/../common/vnet.subr + +atf_test_case "wg_basic" "cleanup" +wg_basic_head() +{ + atf_set descr 'Create a wg(4) tunnel over an epair and pass traffic between jails' + atf_set require.user root + atf_set require.kmods if_wg +} + +wg_basic_body() +{ + local epair pri1 pri2 pub1 pub2 wg1 wg2 + local endpoint1 endpoint2 tunnel1 tunnel2 + + pri1=$(wg genkey) + pri2=$(wg genkey) + + endpoint1=192.168.2.1 + endpoint2=192.168.2.2 + tunnel1=169.254.0.1 + tunnel2=169.254.0.2 + + epair=$(vnet_mkepair) + + vnet_init + + vnet_mkjail wgtest1 ${epair}a + vnet_mkjail wgtest2 ${epair}b + + jexec wgtest1 ifconfig ${epair}a ${endpoint1}/24 up + jexec wgtest2 ifconfig ${epair}b ${endpoint2}/24 up + + wg1=$(jexec wgtest1 ifconfig wg create) + echo "$pri1" | jexec wgtest1 wg set $wg1 listen-port 12345 \ + private-key /dev/stdin + pub1=$(jexec wgtest1 wg show $wg1 public-key) + wg2=$(jexec wgtest2 ifconfig wg create) + echo "$pri2" | jexec wgtest2 wg set $wg2 listen-port 12345 \ + private-key /dev/stdin + pub2=$(jexec wgtest2 wg show $wg2 public-key) + + atf_check -s exit:0 -o ignore \ + jexec wgtest1 wg set $wg1 peer "$pub2" \ + endpoint ${endpoint2}:12345 allowed-ips ${tunnel2}/32 + atf_check -s exit:0 \ + jexec wgtest1 ifconfig $wg1 inet ${tunnel1}/24 up + + atf_check -s exit:0 -o ignore \ + jexec wgtest2 wg set $wg2 peer "$pub1" \ + endpoint ${endpoint1}:12345 allowed-ips ${tunnel1}/32 + atf_check -s exit:0 \ + jexec wgtest2 ifconfig $wg2 inet ${tunnel2}/24 up + + # Generous timeout since the handshake takes some time. + atf_check -s exit:0 -o ignore jexec wgtest1 ping -c 1 -t 5 $tunnel2 + atf_check -s exit:0 -o ignore jexec wgtest2 ping -c 1 $tunnel1 +} + +wg_basic_cleanup() +{ + vnet_cleanup +} + +atf_test_case "wg_basic_crossaf" "cleanup" +wg_basic_crossaf_head() +{ + atf_set descr 'Create a wg(4) tunnel and pass IPv4 traffic over an IPv6 nexthop' + atf_set require.user root +} + +wg_basic_crossaf_body() +{ + local epair pri1 pri2 pub1 pub2 wg1 wg2 + local endpoint1 endpoint2 tunnel1 tunnel2 + local testnet testlocal testremote + + kldload -n if_wg || atf_skip "This test requires if_wg and could not load it" + + pri1=$(wg genkey) + pri2=$(wg genkey) + + endpoint1=192.168.2.1 + endpoint2=192.168.2.2 + tunnel1=2001:db8:1::1 + tunnel2=2001:db8:1::2 + + testnet=192.168.3.0/24 + testlocal=192.168.3.1 + testremote=192.168.3.2 + + epair=$(vnet_mkepair) + + vnet_init + + vnet_mkjail wgtest1 ${epair}a + vnet_mkjail wgtest2 ${epair}b + + jexec wgtest1 ifconfig ${epair}a ${endpoint1}/24 up + jexec wgtest2 ifconfig ${epair}b ${endpoint2}/24 up + + wg1=$(jexec wgtest1 ifconfig wg create) + echo "$pri1" | jexec wgtest1 wg set $wg1 listen-port 12345 \ + private-key /dev/stdin + pub1=$(jexec wgtest1 wg show $wg1 public-key) + wg2=$(jexec wgtest2 ifconfig wg create) + echo "$pri2" | jexec wgtest2 wg set $wg2 listen-port 12345 \ + private-key /dev/stdin + pub2=$(jexec wgtest2 wg show $wg2 public-key) + + atf_check -s exit:0 -o ignore \ + jexec wgtest1 wg set $wg1 peer "$pub2" \ + endpoint ${endpoint2}:12345 allowed-ips ${tunnel2}/128,${testnet} + atf_check -s exit:0 \ + jexec wgtest1 ifconfig $wg1 inet6 ${tunnel1}/64 up + + atf_check -s exit:0 -o ignore \ + jexec wgtest2 wg set $wg2 peer "$pub1" \ + endpoint ${endpoint1}:12345 allowed-ips ${tunnel1}/128,${testnet} + atf_check -s exit:0 \ + jexec wgtest2 ifconfig $wg2 inet6 ${tunnel2}/64 up + + atf_check -s exit:0 jexec wgtest1 ifconfig $wg1 inet ${testlocal}/32 + atf_check -s exit:0 jexec wgtest2 ifconfig $wg2 inet ${testremote}/32 + + # Generous timeout since the handshake takes some time. + atf_check -s exit:0 -o ignore jexec wgtest1 ping -c 1 -t 5 "$tunnel2" + + # Setup our IPv6 endpoint and routing + atf_check -s exit:0 -o ignore \ + jexec wgtest1 route add -inet ${testnet} -inet6 "$tunnel2" + atf_check -s exit:0 -o ignore \ + jexec wgtest2 route add -inet ${testnet} -inet6 "$tunnel1" + # Now ping an address on the other side + atf_check -s exit:0 -o ignore jexec wgtest1 ping -c 1 -t 3 ${testremote} +} + +wg_basic_crossaf_cleanup() +{ + vnet_cleanup +} + +atf_test_case "wg_basic_netmap" "cleanup" +wg_basic_netmap_head() +{ + atf_set descr 'Create a wg(4) tunnel over an epair and pass traffic between jails with netmap' + atf_set require.user root + atf_set require.kmods if_wg netmap +} + +wg_basic_netmap_body() +{ + local epair pri1 pri2 pub1 pub2 wg1 wg2 + local endpoint1 endpoint2 tunnel1 tunnel2 tunnel3 tunnel4 + local pid status + + pri1=$(wg genkey) + pri2=$(wg genkey) + + endpoint1=192.168.2.1 + endpoint2=192.168.2.2 + tunnel1=192.168.3.1 + tunnel2=192.168.3.2 + tunnel3=192.168.3.3 + tunnel4=192.168.3.4 + + epair=$(vnet_mkepair) + + vnet_init + + vnet_mkjail wgtest1 ${epair}a + vnet_mkjail wgtest2 ${epair}b + + jexec wgtest1 ifconfig ${epair}a ${endpoint1}/24 up + jexec wgtest2 ifconfig ${epair}b ${endpoint2}/24 up + + wg1=$(jexec wgtest1 ifconfig wg create) + echo "$pri1" | jexec wgtest1 wg set $wg1 listen-port 12345 \ + private-key /dev/stdin + pub1=$(jexec wgtest1 wg show $wg1 public-key) + wg2=$(jexec wgtest2 ifconfig wg create) + echo "$pri2" | jexec wgtest2 wg set $wg2 listen-port 12345 \ + private-key /dev/stdin + pub2=$(jexec wgtest2 wg show $wg2 public-key) + + atf_check -s exit:0 -o ignore \ + jexec wgtest1 wg set $wg1 peer "$pub2" \ + endpoint ${endpoint2}:12345 allowed-ips ${tunnel2}/32,${tunnel4}/32 + atf_check -s exit:0 \ + jexec wgtest1 ifconfig $wg1 inet ${tunnel1}/24 up + + atf_check -s exit:0 -o ignore \ + jexec wgtest2 wg set $wg2 peer "$pub1" \ + endpoint ${endpoint1}:12345 allowed-ips ${tunnel1}/32,${tunnel3}/32 + atf_check -s exit:0 \ + jexec wgtest2 ifconfig $wg2 inet ${tunnel2}/24 up + + atf_check -s exit:0 -o ignore \ + jexec wgtest1 sysctl net.inet.ip.forwarding=1 + atf_check -s exit:0 -o ignore \ + jexec wgtest2 sysctl net.inet.ip.forwarding=1 + + jexec wgtest1 $(atf_get_srcdir)/bridge -w 0 -i netmap:wg0 -i netmap:wg0^ & + pid=$! + + # Generous timeout since the handshake takes some time. + atf_check -s exit:0 -o ignore jexec wgtest1 ping -c 1 -t 5 $tunnel2 + atf_check -s exit:0 -o ignore jexec wgtest2 ping -c 1 $tunnel1 + + # Verify that we cannot ping non-existent tunnel addresses. In general + # the remote side should respond with an ICMP message. + atf_check -s exit:2 -o ignore jexec wgtest1 ping -c 1 -t 2 $tunnel4 + atf_check -s exit:2 -o ignore jexec wgtest2 ping -c 1 -t 2 $tunnel3 + + # Make sure that the bridge is still functional. + atf_check -s exit:0 -o ignore jexec wgtest1 ping -c 1 $tunnel2 + atf_check -s exit:0 -o ignore jexec wgtest2 ping -c 1 $tunnel1 + + atf_check -s exit:0 kill -TERM $pid + wait $pid + status=$? + + # Make sure that SIGTERM was received and handled. + atf_check_equal $status 143 +} + +wg_basic_netmap_cleanup() +{ + vnet_cleanup +} + +# The kernel is expected to silently ignore any attempt to add a peer with a +# public key identical to the host's. +atf_test_case "wg_key_peerdev_shared" "cleanup" +wg_key_peerdev_shared_head() +{ + atf_set descr 'Create a wg(4) interface with a shared pubkey between device and a peer' + atf_set require.user root + atf_set require.kmods if_wg +} + +wg_key_peerdev_shared_body() +{ + local epair pri1 pub1 wg1 + local endpoint1 tunnel1 + + pri1=$(wg genkey) + + endpoint1=192.168.2.1 + tunnel1=169.254.0.1 + + vnet_mkjail wgtest1 + + wg1=$(jexec wgtest1 ifconfig wg create) + echo "$pri1" | jexec wgtest1 wg set $wg1 listen-port 12345 \ + private-key /dev/stdin + pub1=$(jexec wgtest1 wg show $wg1 public-key) + + atf_check -s exit:0 \ + jexec wgtest1 wg set ${wg1} peer "${pub1}" \ + allowed-ips "${tunnel1}/32" + + atf_check -o empty jexec wgtest1 wg show ${wg1} peers +} + +wg_key_peerdev_shared_cleanup() +{ + vnet_cleanup +} + +# When a wg(8) interface has a private key reassigned that corresponds to the +# public key already on a peer, the kernel is expected to deconfigure the peer +# to resolve the conflict. +atf_test_case "wg_key_peerdev_makeshared" "cleanup" +wg_key_peerdev_makeshared_head() +{ + atf_set descr 'Create a wg(4) interface and assign peer key to device' + atf_set require.progs wg +} + +wg_key_peerdev_makeshared_body() +{ + local epair pri1 pub1 pri2 wg1 wg2 + local endpoint1 tunnel1 + + pri1=$(wg genkey) + pri2=$(wg genkey) + + endpoint1=192.168.2.1 + tunnel1=169.254.0.1 + + vnet_mkjail wgtest1 + + wg1=$(jexec wgtest1 ifconfig wg create) + echo "$pri1" | jexec wgtest1 wg set $wg1 listen-port 12345 \ + private-key /dev/stdin + pub1=$(jexec wgtest1 wg show $wg1 public-key) + wg2=$(jexec wgtest1 ifconfig wg create) + echo "$pri2" | jexec wgtest1 wg set $wg2 listen-port 12345 \ + private-key /dev/stdin + + atf_check -s exit:0 -o ignore \ + jexec wgtest1 wg set ${wg2} peer "${pub1}" \ + allowed-ips "${tunnel1}/32" + + atf_check -o not-empty jexec wgtest1 wg show ${wg2} peers + + jexec wgtest1 sh -c "echo '${pri1}' > pri1" + + atf_check -s exit:0 \ + jexec wgtest1 wg set ${wg2} private-key pri1 + + atf_check -o empty jexec wgtest1 wg show ${wg2} peers +} + +wg_key_peerdev_makeshared_cleanup() +{ + vnet_cleanup +} + +# The kernel is expected to create the wg socket in the jail context that the +# wg interface was created in, even if the interface is moved to a different +# vnet. +atf_test_case "wg_vnet_parent_routing" "cleanup" +wg_vnet_parent_routing_head() +{ + atf_set descr 'Create a wg(4) tunnel without epairs and pass traffic between jails' + atf_set require.user root + atf_set require.kmods if_wg +} + +wg_vnet_parent_routing_body() +{ + local pri1 pri2 pub1 pub2 wg1 wg2 + local tunnel1 tunnel2 + + pri1=$(wg genkey) + pri2=$(wg genkey) + + tunnel1=169.254.0.1 + tunnel2=169.254.0.2 + + vnet_init + + wg1=$(ifconfig wg create) + wg2=$(ifconfig wg create) + + vnet_mkjail wgtest1 ${wg1} + vnet_mkjail wgtest2 ${wg2} + + echo "$pri1" | jexec wgtest1 wg set $wg1 listen-port 12345 \ + private-key /dev/stdin + pub1=$(jexec wgtest1 wg show $wg1 public-key) + echo "$pri2" | jexec wgtest2 wg set $wg2 listen-port 12346 \ + private-key /dev/stdin + pub2=$(jexec wgtest2 wg show $wg2 public-key) + + atf_check -s exit:0 -o ignore \ + jexec wgtest1 wg set $wg1 peer "$pub2" \ + endpoint 127.0.0.1:12346 allowed-ips ${tunnel2}/32 + atf_check -s exit:0 \ + jexec wgtest1 ifconfig $wg1 inet ${tunnel1}/24 up + + atf_check -s exit:0 -o ignore \ + jexec wgtest2 wg set $wg2 peer "$pub1" \ + endpoint 127.0.0.1:12345 allowed-ips ${tunnel1}/32 + atf_check -s exit:0 \ + jexec wgtest2 ifconfig $wg2 inet ${tunnel2}/24 up + + # Sanity check ICMP counters; should clearly be nothing on these new + # jails. We'll check them as we go to ensure that the ICMP packets + # generated really are being handled by the jails' vnets. + atf_check -o not-match:"histogram" jexec wgtest1 netstat -s -p icmp + atf_check -o not-match:"histogram" jexec wgtest2 netstat -s -p icmp + + # Generous timeout since the handshake takes some time. + atf_check -s exit:0 -o ignore jexec wgtest1 ping -c 1 -t 5 $tunnel2 + atf_check -o match:"echo reply: 1" jexec wgtest1 netstat -s -p icmp + atf_check -o match:"echo: 1" jexec wgtest2 netstat -s -p icmp + + atf_check -s exit:0 -o ignore jexec wgtest2 ping -c 1 $tunnel1 + atf_check -o match:"echo reply: 1" jexec wgtest2 netstat -s -p icmp + atf_check -o match:"echo: 1" jexec wgtest1 netstat -s -p icmp +} + +wg_vnet_parent_routing_cleanup() +{ + vnet_cleanup +} + +# The kernel should now allow removing a single allowed-ip without having to +# replace the whole list. We can't really test the atomicity of it all that +# easily, but we'll trust that it worked right if just that addr/mask is gone. +atf_test_case "wg_allowedip_incremental" "cleanup" +wg_allowedip_incremental_head() +{ + atf_set descr "Add/remove allowed-ips from a peer with the +/- incremental syntax" + atf_set require.user root +} + +wg_allowedip_incremental_body() +{ + local pri1 pri2 pub1 pub2 wg1 + local tunnel1 tunnel2 tunnel3 + + kldload -n if_wg || atf_skip "This test requires if_wg and could not load it" + + pri1=$(wg genkey) + pri2=$(wg genkey) + pub2=$(echo "$pri2" | wg pubkey) + + tunnel1=169.254.0.1 + tunnel2=169.254.0.2 + tunnel3=169.254.0.3 + + vnet_mkjail wgtest1 + + wg1=$(jexec wgtest1 ifconfig wg create) + echo "$pri1" | jexec wgtest1 wg set $wg1 private-key /dev/stdin + pub1=$(jexec wgtest1 wg show $wg1 public-key) + + atf_check -s exit:0 \ + jexec wgtest1 wg set $wg1 peer $pub2 \ + allowed-ips "${tunnel1}/32,${tunnel2}/32" + + atf_check -o save:wg.allowed jexec wgtest1 wg show $wg1 allowed-ips + atf_check grep -q "${tunnel1}/32" wg.allowed + atf_check grep -q "${tunnel2}/32" wg.allowed + + atf_check -s exit:0 \ + jexec wgtest1 wg set $wg1 peer $pub2 \ + allowed-ips "-${tunnel2}/32" + + atf_check -o save:wg-2.allowed jexec wgtest1 wg show $wg1 allowed-ips + atf_check grep -q "${tunnel1}/32" wg-2.allowed + atf_check -s not-exit:0 grep -q "${tunnel2}/32" wg-2.allowed + + atf_check -s exit:0 \ + jexec wgtest1 wg set $wg1 peer $pub2 \ + allowed-ips "+${tunnel2}/32" + + atf_check -o save:wg-3.allowed jexec wgtest1 wg show $wg1 allowed-ips + atf_check grep -q "${tunnel1}/32" wg-3.allowed + atf_check grep -q "${tunnel2}/32" wg-3.allowed + + # Now attempt to add the address yet again to confirm that it's not + # harmful. + atf_check -s exit:0 \ + jexec wgtest1 wg set $wg1 peer $pub2 \ + allowed-ips "+${tunnel2}/32" + + atf_check -o save:wg-4.allowed -x \ + "jexec wgtest1 wg show $wg1 allowed-ips | cut -f2 | tr ' ' '\n'" + atf_check -o match:"2 wg-4.allowed$" wc -l wg-4.allowed + + # Finally, let's try removing an address that we never had at all and + # confirm that we still have our two addresses. + atf_check -s exit:0 \ + jexec wgtest1 wg set $wg1 peer $pub2 \ + allowed-ips "-${tunnel3}/32" + + atf_check -o save:wg-5.allowed -x \ + "jexec wgtest1 wg show $wg1 allowed-ips | cut -f2 | tr ' ' '\n'" + atf_check cmp -s wg-4.allowed wg-5.allowed +} + +wg_allowedip_incremental_cleanup() +{ + vnet_cleanup +} + +atf_test_case "wg_allowedip_incremental_inet6" "cleanup" +wg_allowedip_incremental_inet6_head() +{ + atf_set descr "Add/remove IPv6 allowed-ips from a peer with the +/- incremental syntax" + atf_set require.user root +} + +wg_allowedip_incremental_inet6_body() +{ + local pri1 pri2 pub1 pub2 wg1 + local tunnel1 tunnel2 + + kldload -n if_wg || atf_skip "This test requires if_wg and could not load it" + + pri1=$(wg genkey) + pri2=$(wg genkey) + pub2=$(echo "$pri2" | wg pubkey) + + tunnel1=2001:db8:1::1 + tunnel2=2001:db8:1::2 + + vnet_mkjail wgtest1 + + wg1=$(jexec wgtest1 ifconfig wg create) + echo "$pri1" | jexec wgtest1 wg set $wg1 private-key /dev/stdin + pub1=$(jexec wgtest1 wg show $wg1 public-key) + + atf_check -s exit:0 \ + jexec wgtest1 wg set $wg1 peer $pub2 \ + allowed-ips "${tunnel1}/128" + atf_check -o save:wg.allowed jexec wgtest1 wg show $wg1 allowed-ips + atf_check grep -q "${tunnel1}/128" wg.allowed + + atf_check -s exit:0 \ + jexec wgtest1 wg set $wg1 peer $pub2 \ + allowed-ips "+${tunnel2}/128" + atf_check -o save:wg-2.allowed jexec wgtest1 wg show $wg1 allowed-ips + atf_check grep -q "${tunnel1}/128" wg-2.allowed + atf_check grep -q "${tunnel2}/128" wg-2.allowed + + atf_check -s exit:0 \ + jexec wgtest1 wg set $wg1 peer $pub2 \ + allowed-ips "-${tunnel1}/128" + atf_check -o save:wg-3.allowed jexec wgtest1 wg show $wg1 allowed-ips + atf_check -s not-exit:0 grep -q "${tunnel1}/128" wg-3.allowed + atf_check grep -q "${tunnel2}/128" wg-3.allowed +} + +wg_allowedip_incremental_inet6_cleanup() +{ + vnet_cleanup +} + + +atf_test_case "wg_allowedip_incremental_stealing" "cleanup" +wg_allowedip_incremental_stealing_head() +{ + atf_set descr "Add/remove allowed-ips from a peer with the +/- incremental syntax to steal" + atf_set require.user root +} + +wg_allowedip_incremental_stealing_body() +{ + local pri1 pri2 pri3 pub1 pub2 pub3 wg1 + local regex2 regex3 + local tunnel1 tunnel2 + + kldload -n if_wg || atf_skip "This test requires if_wg and could not load it" + + pri1=$(wg genkey) + pri2=$(wg genkey) + pri3=$(wg genkey) + pub2=$(echo "$pri2" | wg pubkey) + pub3=$(echo "$pri3" | wg pubkey) + + regex2=$(echo "$pub2" | sed -e 's/[+]/[+]/g') + regex3=$(echo "$pub3" | sed -e 's/[+]/[+]/g') + + tunnel1=169.254.0.1 + tunnel2=169.254.0.2 + tunnel3=169.254.0.3 + + vnet_mkjail wgtest1 + + wg1=$(jexec wgtest1 ifconfig wg create) + echo "$pri1" | jexec wgtest1 wg set $wg1 private-key /dev/stdin + pub1=$(jexec wgtest1 wg show $wg1 public-key) + + atf_check -s exit:0 \ + jexec wgtest1 wg set $wg1 peer $pub2 \ + allowed-ips "${tunnel1}/32,${tunnel2}/32" + + atf_check -s exit:0 \ + jexec wgtest1 wg set $wg1 peer $pub3 \ + allowed-ips "${tunnel3}/32" + + # First, confirm that the negative syntax doesn't do anything because + # we have the wrong peer. + atf_check -s exit:0 \ + jexec wgtest1 wg set $wg1 peer $pub2 \ + allowed-ips "-${tunnel3}/32" + + atf_check -o save:wg.allowed jexec wgtest1 wg show $wg1 allowed-ips + atf_check grep -Eq "^${regex3}.+${tunnel3}/32" wg.allowed + + # Next, steal it with an incremental move and check that it moved. + atf_check -s exit:0 \ + jexec wgtest1 wg set $wg1 peer $pub2 \ + allowed-ips "+${tunnel3}/32" + + atf_check -o save:wg-2.allowed jexec wgtest1 wg show $wg1 allowed-ips + + atf_check grep -Eq "^${regex2}.+${tunnel3}/32" wg-2.allowed + atf_check grep -Evq "^${regex3}.+${tunnel3}/32" wg-2.allowed +} + +wg_allowedip_incremental_stealing_cleanup() +{ + vnet_cleanup +} + +atf_init_test_cases() +{ + atf_add_test_case "wg_basic" + atf_add_test_case "wg_basic_crossaf" + atf_add_test_case "wg_basic_netmap" + atf_add_test_case "wg_key_peerdev_shared" + atf_add_test_case "wg_key_peerdev_makeshared" + atf_add_test_case "wg_vnet_parent_routing" + atf_add_test_case "wg_allowedip_incremental" + atf_add_test_case "wg_allowedip_incremental_inet6" + atf_add_test_case "wg_allowedip_incremental_stealing" +} diff --git a/tests/sys/net/pcp.py b/tests/sys/net/pcp.py new file mode 100644 index 000000000000..c0b6d4efc3b0 --- /dev/null +++ b/tests/sys/net/pcp.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2021 Rubicon Communications, LLC (Netgate). +# +# 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. + +import argparse +import logging +logging.getLogger("scapy").setLevel(logging.CRITICAL) +import scapy.all as sp +import sys +import os +curdir = os.path.dirname(os.path.realpath(__file__)) +netpfil_common = curdir + "/../netpfil/common" +sys.path.append(netpfil_common) +from sniffer import Sniffer + +def check_pcp(args, packet): + vlan = packet.getlayer(sp.Dot1Q) + + if vlan is None: + return False + + if not packet.getlayer(sp.BOOTP): + return False + + if vlan.prio == int(args.expect_pcp[0]): + return True + + return False + +def main(): + parser = argparse.ArgumentParser("pcp.py", + description="PCP test tool") + parser.add_argument('--recvif', nargs=1, + required=True, + help='The interface where to look for packets to check') + parser.add_argument('--expect-pcp', nargs=1, + help='The expected PCP value on VLAN packets') + + args = parser.parse_args() + + sniffer = Sniffer(args, check_pcp, args.recvif[0], timeout=20) + + sniffer.join() + + if sniffer.correctPackets: + sys.exit(0) + + sys.exit(1) + +if __name__ == '__main__': + main() diff --git a/tests/sys/net/randsleep.c b/tests/sys/net/randsleep.c new file mode 100644 index 000000000000..55f1fff45617 --- /dev/null +++ b/tests/sys/net/randsleep.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2014 Spectra Logic Corporation + * All rights reserved. + * + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + * + * Authors: Alan Somers (Spectra Logic Corporation) + */ + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#define RANDOM_MAX ((1U<<31) - 1) + +int main(int argc, char** argv){ + useconds_t max_usecs, usecs; + double frac; + + if (argc != 2) { + printf("Usage: randsleep <max_microseconds>\n"); + exit(2); + } + + errno = 0; + max_usecs = (useconds_t)strtol(argv[1], NULL, 0); + if (errno != 0) { + perror("strtol"); + exit(1); + } + srandomdev(); + frac = (double)random() / (double)RANDOM_MAX; + usecs = (useconds_t)((double)max_usecs * frac); + usleep(usecs); + + return (0); +} diff --git a/tests/sys/net/routing/Makefile b/tests/sys/net/routing/Makefile new file mode 100644 index 000000000000..c725d23f15d1 --- /dev/null +++ b/tests/sys/net/routing/Makefile @@ -0,0 +1,21 @@ +PACKAGE= tests +WARNS?= 1 + +TESTSDIR= ${TESTSBASE}/sys/net/routing + +ATF_TESTS_C += test_rtsock_l3 +ATF_TESTS_C += test_rtsock_lladdr +ATF_TESTS_C += test_rtsock_ops +ATF_TESTS_PYTEST += test_routing_l3.py +ATF_TESTS_PYTEST += test_rtsock_multipath.py + +${PACKAGE}FILES+= generic_cleanup.sh +${PACKAGE}FILESMODE_generic_cleanup.sh=0555 + +# Most of the tests operates on a common IPv4/IPv6 prefix, +# so running them in parallel will lead to weird results. +TEST_METADATA+= is_exclusive=true + +CFLAGS+= -I${.CURDIR:H:H:H} + +.include <bsd.test.mk> diff --git a/tests/sys/net/routing/generic_cleanup.sh b/tests/sys/net/routing/generic_cleanup.sh new file mode 100755 index 000000000000..4c9ded969d1f --- /dev/null +++ b/tests/sys/net/routing/generic_cleanup.sh @@ -0,0 +1,35 @@ +#!/bin/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. +# +# + + +srcdir=`dirname $0` +. ${srcdir}/../../common/vnet.subr + +vnet_cleanup + diff --git a/tests/sys/net/routing/params.h b/tests/sys/net/routing/params.h new file mode 100644 index 000000000000..f66678bceca9 --- /dev/null +++ b/tests/sys/net/routing/params.h @@ -0,0 +1,36 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 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. + */ + +#ifndef _NET_ROUTING_PARAMS_H_ +#define _NET_ROUTING_PARAMS_H_ + +/* files to store state */ +#define JAILS_FNAME "created_jails.lst" +#define IFACES_FNAME "created_interfaces.lst" + +#endif + diff --git a/tests/sys/net/routing/rtsock_common.h b/tests/sys/net/routing/rtsock_common.h new file mode 100644 index 000000000000..eed4a348ad74 --- /dev/null +++ b/tests/sys/net/routing/rtsock_common.h @@ -0,0 +1,882 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 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. + */ + +#ifndef _NET_ROUTING_RTSOCK_COMMON_H_ +#define _NET_ROUTING_RTSOCK_COMMON_H_ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdbool.h> +#include <ctype.h> +#include <poll.h> + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/jail.h> +#include <sys/linker.h> +#include <net/if.h> +#include <net/if_dl.h> +#include <net/route.h> + +#include <arpa/inet.h> +#include <net/ethernet.h> + +#include <netinet/in.h> +#include <netinet6/in6_var.h> +#include <netinet6/nd6.h> + +#include <ifaddrs.h> + +#include <errno.h> +#include <err.h> +#include <sysexits.h> + +#include <atf-c.h> +#include "freebsd_test_suite/macros.h" + +#include "rtsock_print.h" +#include "params.h" + +void rtsock_update_rtm_len(struct rt_msghdr *rtm); +void rtsock_validate_message(char *buffer, ssize_t len); +void rtsock_add_rtm_sa(struct rt_msghdr *rtm, int addr_type, struct sockaddr *sa); + +void file_append_line(char *fname, char *text); + +static int _rtm_seq = 42; + + +/* + * Checks if the interface cloner module is present for @name. + */ +static int +_check_cloner(char *name) +{ + struct if_clonereq ifcr; + char *cp, *buf; + int idx; + int s; + int found = 0; + + s = socket(AF_LOCAL, SOCK_DGRAM, 0); + if (s == -1) + err(1, "socket(AF_LOCAL,SOCK_DGRAM)"); + + memset(&ifcr, 0, sizeof(ifcr)); + + if (ioctl(s, SIOCIFGCLONERS, &ifcr) < 0) + err(1, "SIOCIFGCLONERS for count"); + + buf = malloc(ifcr.ifcr_total * IFNAMSIZ); + if (buf == NULL) + err(1, "unable to allocate cloner name buffer"); + + ifcr.ifcr_count = ifcr.ifcr_total; + ifcr.ifcr_buffer = buf; + + if (ioctl(s, SIOCIFGCLONERS, &ifcr) < 0) + err(1, "SIOCIFGCLONERS for names"); + + /* + * In case some disappeared in the mean time, clamp it down. + */ + if (ifcr.ifcr_count > ifcr.ifcr_total) + ifcr.ifcr_count = ifcr.ifcr_total; + + for (cp = buf, idx = 0; idx < ifcr.ifcr_count; idx++, cp += IFNAMSIZ) { + if (!strcmp(cp, name)) { + found = 1; + break; + } + } + + free(buf); + close(s); + + return (found); +} + +static char * +iface_create(char *ifname_orig) +{ + struct ifreq ifr; + int s; + char prefix[IFNAMSIZ], ifname[IFNAMSIZ], *result; + + char *src, *dst; + for (src = ifname_orig, dst = prefix; *src && isalpha(*src); src++) + *dst++ = *src; + *dst = '\0'; + + memset(&ifr, 0, sizeof(struct ifreq)); + + s = socket(AF_LOCAL, SOCK_DGRAM, 0); + strlcpy(ifr.ifr_name, ifname_orig, sizeof(ifr.ifr_name)); + + RLOG("creating iface %s %s", prefix, ifr.ifr_name); + if (ioctl(s, SIOCIFCREATE2, &ifr) < 0) + err(1, "SIOCIFCREATE2"); + + strlcpy(ifname, ifr.ifr_name, IFNAMSIZ); + RLOG("created interface %s", ifname); + + result = strdup(ifname); + + file_append_line(IFACES_FNAME, ifname); + if (strstr(ifname, "epair") == ifname) { + /* call returned epairXXXa, need to add epairXXXb */ + ifname[strlen(ifname) - 1] = 'b'; + file_append_line(IFACES_FNAME, ifname); + } + + return (result); +} + +static int +iface_destroy(char *ifname) +{ + struct ifreq ifr; + int s; + + s = socket(AF_LOCAL, SOCK_DGRAM, 0); + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + + RLOG("destroying interface %s", ifname); + if (ioctl(s, SIOCIFDESTROY, &ifr) < 0) + return (0); + + return (1); +} + +/* + * Open tunneling device such as tuntap and returns fd. + */ +int +iface_open(char *ifname) +{ + char path[256]; + + snprintf(path, sizeof(path), "/dev/%s", ifname); + + RLOG("opening interface %s", ifname); + int fd = open(path, O_RDWR|O_EXCL); + if (fd == -1) { + RLOG_ERRNO("unable to open interface %s", ifname); + return (-1); + } + + return (fd); +} + +/* + * Sets primary IPv4 addr. + * Returns 0 on success. + */ +static inline int +iface_setup_addr(char *ifname, char *addr, int plen) +{ + char cmd[512]; + char *af; + + if (strchr(addr, ':')) + af = "inet6"; + else + af = "inet"; + RLOG("setting af_%s %s/%d on %s", af, addr, plen, ifname); + snprintf(cmd, sizeof(cmd), "/sbin/ifconfig %s %s %s/%d", ifname, + af, addr, plen); + + return system(cmd); +} + +/* + * Removes primary IPv4 prefix. + * Returns 0 on success. + */ +static inline int +iface_delete_addr(char *ifname, char *addr) +{ + char cmd[512]; + + if (strchr(addr, ':')) { + RLOG("removing IPv6 %s from %s", addr, ifname); + snprintf(cmd, sizeof(cmd), "/sbin/ifconfig %s inet6 %s delete", ifname, addr); + } else { + RLOG("removing IPv4 %s from %s", addr, ifname); + snprintf(cmd, sizeof(cmd), "/sbin/ifconfig %s -alias %s", ifname, addr); + } + + return system(cmd); +} + +int +iface_turn_up(char *ifname) +{ + struct ifreq ifr; + int s; + + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + RLOG_ERRNO("socket"); + return (-1); + } + memset(&ifr, 0, sizeof(struct ifreq)); + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) { + RLOG_ERRNO("ioctl(SIOCGIFFLAGS)"); + return (-1); + } + /* Update flags */ + if ((ifr.ifr_flags & IFF_UP) == 0) { + ifr.ifr_flags |= IFF_UP; + if (ioctl(s, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) { + RLOG_ERRNO("ioctl(SIOSGIFFLAGS)"); + return (-1); + } + RLOG("turned interface %s up", ifname); + } + + return (0); +} + +/* + * Removes ND6_IFF_IFDISABLED from IPv6 interface flags. + * Returns 0 on success. + */ +int +iface_enable_ipv6(char *ifname) +{ + struct in6_ndireq nd; + int s; + + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + err(1, "socket"); + } + memset(&nd, 0, sizeof(nd)); + strlcpy(nd.ifname, ifname, sizeof(nd.ifname)); + if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) { + RLOG_ERRNO("ioctl(SIOCGIFINFO_IN6)"); + return (-1); + } + /* Update flags */ + if ((nd.ndi.flags & ND6_IFF_IFDISABLED) != 0) { + nd.ndi.flags &= ~ND6_IFF_IFDISABLED; + if (ioctl(s, SIOCSIFINFO_IN6, (caddr_t)&nd) < 0) { + RLOG_ERRNO("ioctl(SIOCSIFINFO_IN6)"); + return (-1); + } + RLOG("enabled IPv6 for %s", ifname); + } + + return (0); +} + +void +file_append_line(char *fname, char *text) +{ + FILE *f; + + f = fopen(fname, "a"); + fputs(text, f); + fputs("\n", f); + fclose(f); +} + +static int +vnet_wait_interface(char *vnet_name, char *ifname) +{ + char buf[512], cmd[512], *line, *token; + FILE *fp; + int i; + + snprintf(cmd, sizeof(cmd), "/usr/sbin/jexec %s /sbin/ifconfig -l", vnet_name); + for (int i = 0; i < 50; i++) { + fp = popen(cmd, "r"); + line = fgets(buf, sizeof(buf), fp); + /* cut last\n */ + if (line[0]) + line[strlen(line)-1] = '\0'; + while ((token = strsep(&line, " ")) != NULL) { + if (strcmp(token, ifname) == 0) + return (1); + } + + /* sleep 100ms */ + usleep(1000 * 100); + } + + return (0); +} + +void +vnet_switch(char *vnet_name, char **ifnames, int count) +{ + char buf[512], cmd[512], *line; + FILE *fp; + int jid, len, ret; + + RLOG("switching to vnet %s with interface(s) %s", vnet_name, ifnames[0]); + len = snprintf(cmd, sizeof(cmd), + "/usr/sbin/jail -i -c name=%s persist vnet", vnet_name); + for (int i = 0; i < count && len < sizeof(cmd); i++) { + len += snprintf(&cmd[len], sizeof(cmd) - len, + " vnet.interface=%s", ifnames[i]); + } + RLOG("jail cmd: \"%s\"\n", cmd); + + fp = popen(cmd, "r"); + if (fp == NULL) + atf_tc_fail("jail creation failed"); + line = fgets(buf, sizeof(buf), fp); + if (line == NULL) + atf_tc_fail("empty output from jail(8)"); + jid = strtol(line, NULL, 10); + if (jid <= 0) { + atf_tc_fail("invalid jail output: %s", line); + } + + RLOG("created jail jid=%d", jid); + file_append_line(JAILS_FNAME, vnet_name); + + /* Wait while interface appearsh inside vnet */ + for (int i = 0; i < count; i++) { + if (vnet_wait_interface(vnet_name, ifnames[i])) + continue; + atf_tc_fail("unable to move interface %s to jail %s", + ifnames[i], vnet_name); + } + + if (jail_attach(jid) == -1) { + RLOG_ERRNO("jail %s attach failed: ret=%d", vnet_name, errno); + atf_tc_fail("jail attach failed"); + } + + RLOG("attached to the jail"); +} + +void +vnet_switch_one(char *vnet_name, char *ifname) +{ + char *ifnames[1]; + + ifnames[0] = ifname; + vnet_switch(vnet_name, ifnames, 1); +} + + +#define SA_F_IGNORE_IFNAME 0x01 +#define SA_F_IGNORE_IFTYPE 0x02 +#define SA_F_IGNORE_MEMCMP 0x04 +int +sa_equal_msg_flags(const struct sockaddr *a, const struct sockaddr *b, char *msg, size_t sz, int flags) +{ + char a_s[64], b_s[64]; + const struct sockaddr_in *a4, *b4; + const struct sockaddr_in6 *a6, *b6; + const struct sockaddr_dl *al, *bl; + + if (a == NULL) { + snprintf(msg, sz, "first sa is NULL"); + return 0; + } + if (b == NULL) { + snprintf(msg, sz, "second sa is NULL"); + return 0; + } + + if (a->sa_family != b->sa_family) { + snprintf(msg, sz, "family: %d vs %d", a->sa_family, b->sa_family); + return 0; + } + if (a->sa_len != b->sa_len) { + snprintf(msg, sz, "len: %d vs %d", a->sa_len, b->sa_len); + return 0; + } + + switch (a->sa_family) { + case AF_INET: + a4 = (const struct sockaddr_in *)a; + b4 = (const struct sockaddr_in *)b; + if (a4->sin_addr.s_addr != b4->sin_addr.s_addr) { + inet_ntop(AF_INET, &a4->sin_addr, a_s, sizeof(a_s)); + inet_ntop(AF_INET, &b4->sin_addr, b_s, sizeof(b_s)); + snprintf(msg, sz, "addr diff: %s vs %s", a_s, b_s); + return 0; + } + if (a4->sin_port != b4->sin_port) { + snprintf(msg, sz, "port diff: %d vs %d", + ntohs(a4->sin_port), ntohs(b4->sin_port)); + //return 0; + } + const uint32_t *a32, *b32; + a32 = (const uint32_t *)a4->sin_zero; + b32 = (const uint32_t *)b4->sin_zero; + if ((*a32 != *b32) || (*(a32 + 1) != *(b32 + 1))) { + snprintf(msg, sz, "zero diff: 0x%08X%08X vs 0x%08X%08X", + ntohl(*a32), ntohl(*(a32 + 1)), + ntohl(*b32), ntohl(*(b32 + 1))); + return 0; + } + return 1; + case AF_INET6: + a6 = (const struct sockaddr_in6 *)a; + b6 = (const struct sockaddr_in6 *)b; + if (!IN6_ARE_ADDR_EQUAL(&a6->sin6_addr, &b6->sin6_addr)) { + inet_ntop(AF_INET6, &a6->sin6_addr, a_s, sizeof(a_s)); + inet_ntop(AF_INET6, &b6->sin6_addr, b_s, sizeof(b_s)); + snprintf(msg, sz, "addr diff: %s vs %s", a_s, b_s); + return 0; + } + if (a6->sin6_scope_id != b6->sin6_scope_id) { + snprintf(msg, sz, "scope diff: %u vs %u", a6->sin6_scope_id, b6->sin6_scope_id); + return 0; + } + break; + case AF_LINK: + al = (const struct sockaddr_dl *)a; + bl = (const struct sockaddr_dl *)b; + + if (al->sdl_index != bl->sdl_index) { + snprintf(msg, sz, "sdl_index diff: %u vs %u", al->sdl_index, bl->sdl_index); + return 0; + } + + if ((al->sdl_alen != bl->sdl_alen) || (memcmp(LLADDR(al), LLADDR(bl), al->sdl_alen) != 0)) { + char abuf[64], bbuf[64]; + sa_print_hd(abuf, sizeof(abuf), LLADDR(al), al->sdl_alen); + sa_print_hd(bbuf, sizeof(bbuf), LLADDR(bl), bl->sdl_alen); + snprintf(msg, sz, "sdl_alen diff: {%s} (%d) vs {%s} (%d)", + abuf, al->sdl_alen, bbuf, bl->sdl_alen); + return 0; + } + + if (((flags & SA_F_IGNORE_IFTYPE) == 0) && (al->sdl_type != bl->sdl_type)) { + snprintf(msg, sz, "sdl_type diff: %u vs %u", al->sdl_type, bl->sdl_type); + return 0; + } + + if (((flags & SA_F_IGNORE_IFNAME) == 0) && ((al->sdl_nlen != bl->sdl_nlen) || + (memcmp(al->sdl_data, bl->sdl_data, al->sdl_nlen) != 0))) { + char abuf[64], bbuf[64]; + memcpy(abuf, al->sdl_data, al->sdl_nlen); + abuf[al->sdl_nlen] = '\0'; + memcpy(bbuf, bl->sdl_data, bl->sdl_nlen); + abuf[bl->sdl_nlen] = '\0'; + snprintf(msg, sz, "sdl_nlen diff: {%s} (%d) vs {%s} (%d)", + abuf, al->sdl_nlen, bbuf, bl->sdl_nlen); + return 0; + } + + if (flags & SA_F_IGNORE_MEMCMP) + return 1; + break; + } + + if (memcmp(a, b, a->sa_len)) { + int i; + for (i = 0; i < a->sa_len; i++) + if (((const char *)a)[i] != ((const char *)b)[i]) + break; + + sa_print(a, 1); + sa_print(b, 1); + + snprintf(msg, sz, "overall memcmp() reports diff for af %d offset %d", + a->sa_family, i); + return 0; + } + return 1; +} + +int +sa_equal_msg(const struct sockaddr *a, const struct sockaddr *b, char *msg, size_t sz) +{ + + return sa_equal_msg_flags(a, b, msg, sz, 0); +} + +void +sa_fill_mask4(struct sockaddr_in *sin, int plen) +{ + + memset(sin, 0, sizeof(struct sockaddr_in)); + sin->sin_family = AF_INET; + sin->sin_len = sizeof(struct sockaddr_in); + sin->sin_addr.s_addr = htonl(plen ? ~((1 << (32 - plen)) - 1) : 0); +} + +void +sa_fill_mask6(struct sockaddr_in6 *sin6, uint8_t mask) +{ + uint32_t *cp; + + memset(sin6, 0, sizeof(struct sockaddr_in6)); + sin6->sin6_family = AF_INET6; + sin6->sin6_len = sizeof(struct sockaddr_in6); + + for (cp = (uint32_t *)&sin6->sin6_addr; mask >= 32; mask -= 32) + *cp++ = 0xFFFFFFFF; + if (mask > 0) + *cp = htonl(mask ? ~((1 << (32 - mask)) - 1) : 0); +} + +/* 52:54:00:14:e3:10 */ +#define ETHER_MAC_MAX_LENGTH 17 + +int +sa_convert_str_to_sa(const char *_addr, struct sockaddr *sa) +{ + int error; + + int af = AF_UNSPEC; + + char *addr = strdup(_addr); + int retcode = 0; + + /* classify AF by str */ + if (strchr(addr, ':')) { + /* inet6 or ether */ + char *k; + int delim_cnt = 0; + for (k = addr; *k; k++) + if (*k == ':') + delim_cnt++; + af = AF_INET6; + + if (delim_cnt == 5) { + k = strchr(addr, '%'); + if (k != NULL && (k - addr) <= ETHER_MAC_MAX_LENGTH) + af = AF_LINK; + } + } else if (strchr(addr, '.')) + af = AF_INET; + + /* */ + char *delimiter; + int ifindex = 0; + char *ifname = NULL; + if ((delimiter = strchr(addr, '%')) != NULL) { + *delimiter = '\0'; + ifname = delimiter + 1; + ifindex = if_nametoindex(ifname); + if (ifindex == 0) + RLOG("unable to find ifindex for '%s'", ifname); + else + RLOG("if %s mapped to %d", ifname, ifindex); + } + + if (af == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; + memset(sin6, 0, sizeof(struct sockaddr_in6)); + sin6->sin6_family = AF_INET6; + sin6->sin6_len = sizeof(struct sockaddr_in6); + sin6->sin6_scope_id = ifindex; + error = inet_pton(AF_INET6, addr, &sin6->sin6_addr); + if (error != 1) + RLOG_ERRNO("inet_ntop() failed: ret=%d", error); + else + retcode = 1; + } else if (af == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *)sa; + memset(sin, 0, sizeof(struct sockaddr_in)); + sin->sin_family = AF_INET; + sin->sin_len = sizeof(struct sockaddr_in); + error = inet_pton(AF_INET, addr, &sin->sin_addr); + if (error != 1) + RLOG("inet_ntop() failed: ret=%d", error); + else + retcode = 1; + } else if (af == AF_LINK) { + struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa; + memset(sdl, 0, sizeof(struct sockaddr_dl)); + sdl->sdl_family = AF_LINK; + sdl->sdl_len = sizeof(struct sockaddr_dl); + sdl->sdl_index = ifindex; + sdl->sdl_alen = 6; + struct ether_addr *ea = (struct ether_addr *)LLADDR(sdl); + if (ether_aton_r(addr, ea) == NULL) + RLOG("ether_aton() failed"); + else + retcode = 1; + } + + return (retcode); +} + + +int +rtsock_setup_socket() +{ + int fd; + int af = AF_UNSPEC; /* 0 to capture messages from all AFs */ + fd = socket(PF_ROUTE, SOCK_RAW, af); + + ATF_REQUIRE_MSG(fd != -1, "rtsock open failed: %s", strerror(errno)); + + /* Listen for our messages */ + int on = 1; + if (setsockopt(fd, SOL_SOCKET,SO_USELOOPBACK, &on, sizeof(on)) < 0) + RLOG_ERRNO("setsockopt failed"); + + return (fd); +} + +ssize_t +rtsock_send_rtm(int fd, struct rt_msghdr *rtm) +{ + int my_errno; + ssize_t len; + + rtsock_update_rtm_len(rtm); + + len = write(fd, rtm, rtm->rtm_msglen); + my_errno = errno; + RTSOCK_ATF_REQUIRE_MSG(rtm, len == rtm->rtm_msglen, + "rtsock write failed: want %d got %zd (%s)", + rtm->rtm_msglen, len, strerror(my_errno)); + + return (len); +} + +struct rt_msghdr * +rtsock_read_rtm(int fd, char *buffer, size_t buflen) +{ + ssize_t len; + struct pollfd pfd; + int poll_delay = 5 * 1000; /* 5 seconds */ + + /* Check for the data available to read first */ + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = fd; + pfd.events = POLLIN; + + if (poll(&pfd, 1, poll_delay) == 0) + ATF_REQUIRE_MSG(1 == 0, "rtsock read timed out (%d seconds passed)", + poll_delay / 1000); + + len = read(fd, buffer, buflen); + int my_errno = errno; + ATF_REQUIRE_MSG(len > 0, "rtsock read failed: %s", strerror(my_errno)); + + rtsock_validate_message(buffer, len); + return ((struct rt_msghdr *)buffer); +} + +struct rt_msghdr * +rtsock_read_rtm_reply(int fd, char *buffer, size_t buflen, int seq) +{ + struct rt_msghdr *rtm; + int found = 0; + + while (true) { + rtm = rtsock_read_rtm(fd, buffer, buflen); + if (rtm->rtm_pid == getpid() && rtm->rtm_seq == seq) + found = 1; + if (found) + RLOG("--- MATCHED RTSOCK MESSAGE ---"); + else + RLOG("--- SKIPPED RTSOCK MESSAGE ---"); + rtsock_print_rtm(rtm); + if (found) + return (rtm); + } + + /* NOTREACHED */ +} + +void +rtsock_prepare_route_message_base(struct rt_msghdr *rtm, int cmd) +{ + + memset(rtm, 0, sizeof(struct rt_msghdr)); + rtm->rtm_type = cmd; + rtm->rtm_version = RTM_VERSION; + rtm->rtm_seq = _rtm_seq++; +} + +void +rtsock_prepare_route_message(struct rt_msghdr *rtm, int cmd, struct sockaddr *dst, + struct sockaddr *mask, struct sockaddr *gw) +{ + + rtsock_prepare_route_message_base(rtm, cmd); + if (dst != NULL) + rtsock_add_rtm_sa(rtm, RTA_DST, dst); + + if (gw != NULL) { + rtsock_add_rtm_sa(rtm, RTA_GATEWAY, gw); + rtm->rtm_flags |= RTF_GATEWAY; + } + + if (mask != NULL) + rtsock_add_rtm_sa(rtm, RTA_NETMASK, mask); +} + +void +rtsock_add_rtm_sa(struct rt_msghdr *rtm, int addr_type, struct sockaddr *sa) +{ + char *ptr = (char *)(rtm + 1); + for (int i = 0; i < RTAX_MAX; i++) { + if (rtm->rtm_addrs & (1 << i)) { + /* add */ + ptr += ALIGN(((struct sockaddr *)ptr)->sa_len); + } + } + + rtm->rtm_addrs |= addr_type; + memcpy(ptr, sa, sa->sa_len); +} + +struct sockaddr * +rtsock_find_rtm_sa(struct rt_msghdr *rtm, int addr_type) +{ + char *ptr = (char *)(rtm + 1); + for (int i = 0; i < RTAX_MAX; i++) { + if (rtm->rtm_addrs & (1 << i)) { + if (addr_type == (1 << i)) + return ((struct sockaddr *)ptr); + /* add */ + ptr += ALIGN(((struct sockaddr *)ptr)->sa_len); + } + } + + return (NULL); +} + +size_t +rtsock_calc_rtm_len(struct rt_msghdr *rtm) +{ + size_t len = sizeof(struct rt_msghdr); + + char *ptr = (char *)(rtm + 1); + for (int i = 0; i < RTAX_MAX; i++) { + if (rtm->rtm_addrs & (1 << i)) { + /* add */ + int sa_len = ALIGN(((struct sockaddr *)ptr)->sa_len); + len += sa_len; + ptr += sa_len; + } + } + + return len; +} + +void +rtsock_update_rtm_len(struct rt_msghdr *rtm) +{ + + rtm->rtm_msglen = rtsock_calc_rtm_len(rtm); +} + +static void +_validate_message_sockaddrs(char *buffer, int rtm_len, size_t offset, int rtm_addrs) +{ + struct sockaddr *sa; + size_t parsed_len = offset; + + /* Offset denotes initial header size */ + sa = (struct sockaddr *)(buffer + offset); + + for (int i = 0; i < RTAX_MAX; i++) { + if ((rtm_addrs & (1 << i)) == 0) + continue; + parsed_len += SA_SIZE(sa); + RTSOCK_ATF_REQUIRE_MSG((struct rt_msghdr *)buffer, parsed_len <= rtm_len, + "SA %d: len %d exceeds msg size %d", i, (int)sa->sa_len, rtm_len); + if (sa->sa_family == AF_LINK) { + struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa; + int data_len = sdl->sdl_nlen + sdl->sdl_alen; + data_len += offsetof(struct sockaddr_dl, sdl_data); + + RTSOCK_ATF_REQUIRE_MSG((struct rt_msghdr *)buffer, + data_len <= rtm_len, + "AF_LINK data size exceeds total len: %u vs %u, nlen=%d alen=%d", + data_len, rtm_len, sdl->sdl_nlen, sdl->sdl_alen); + } + sa = (struct sockaddr *)((char *)sa + SA_SIZE(sa)); + } +} + +/* + * Raises error if base syntax checks fails. + */ +void +rtsock_validate_message(char *buffer, ssize_t len) +{ + struct rt_msghdr *rtm; + + ATF_REQUIRE_MSG(len > 0, "read() return %zd, error: %s", len, strerror(errno)); + + rtm = (struct rt_msghdr *)buffer; + ATF_REQUIRE_MSG(rtm->rtm_version == RTM_VERSION, "unknown RTM_VERSION: expected %d got %d", + RTM_VERSION, rtm->rtm_version); + ATF_REQUIRE_MSG(rtm->rtm_msglen <= len, "wrong message length: expected %d got %d", + (int)len, (int)rtm->rtm_msglen); + + switch (rtm->rtm_type) { + case RTM_GET: + case RTM_ADD: + case RTM_DELETE: + case RTM_CHANGE: + _validate_message_sockaddrs(buffer, rtm->rtm_msglen, + sizeof(struct rt_msghdr), rtm->rtm_addrs); + break; + case RTM_DELADDR: + case RTM_NEWADDR: + _validate_message_sockaddrs(buffer, rtm->rtm_msglen, + sizeof(struct ifa_msghdr), ((struct ifa_msghdr *)buffer)->ifam_addrs); + break; + } +} + +void +rtsock_validate_pid_ours(struct rt_msghdr *rtm) +{ + RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_pid == getpid(), "expected pid %d, got %d", + getpid(), rtm->rtm_pid); +} + +void +rtsock_validate_pid_user(struct rt_msghdr *rtm) +{ + RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_pid > 0, "expected non-zero pid, got %d", + rtm->rtm_pid); +} + +void +rtsock_validate_pid_kernel(struct rt_msghdr *rtm) +{ + RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_pid == 0, "expected zero pid, got %d", + rtm->rtm_pid); +} + +#endif diff --git a/tests/sys/net/routing/rtsock_config.h b/tests/sys/net/routing/rtsock_config.h new file mode 100644 index 000000000000..345fc13f167c --- /dev/null +++ b/tests/sys/net/routing/rtsock_config.h @@ -0,0 +1,175 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 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. + */ + +#ifndef _NET_ROUTING_RTSOCK_CONFIG_H_ +#define _NET_ROUTING_RTSOCK_CONFIG_H_ + +#include "params.h" + +struct rtsock_config_options { + int num_interfaces; /* number of interfaces to create */ +}; + +struct rtsock_test_config { + int ifindex; + char net4_str[INET_ADDRSTRLEN]; + char addr4_str[INET_ADDRSTRLEN]; + char net6_str[INET6_ADDRSTRLEN]; + char addr6_str[INET6_ADDRSTRLEN]; + struct sockaddr_in net4; + struct sockaddr_in mask4; + struct sockaddr_in addr4; + struct sockaddr_in6 net6; + struct sockaddr_in6 mask6; + struct sockaddr_in6 addr6; + int plen4; + int plen6; + char *remote_lladdr; + char *ifname; + char **ifnames; + bool autocreated_interface; + int rtsock_fd; + int num_interfaces; +}; + +struct rtsock_test_config * +config_setup(const atf_tc_t *tc, struct rtsock_config_options *co) +{ + struct rtsock_config_options default_co; + struct rtsock_test_config *c; + char buf[64], *s; + const char *key; + int mask; + + if (co == NULL) { + bzero(&default_co, sizeof(default_co)); + co = &default_co; + co->num_interfaces = 1; + } + + c = calloc(1, sizeof(struct rtsock_test_config)); + c->rtsock_fd = -1; + + key = atf_tc_get_config_var_wd(tc, "rtsock.v4prefix", "192.0.2.0/24"); + strlcpy(buf, key, sizeof(buf)); + if ((s = strchr(buf, '/')) == NULL) + return (NULL); + *s++ = '\0'; + mask = strtol(s, NULL, 10); + if (mask < 0 || mask > 32) + return (NULL); + c->plen4 = mask; + inet_pton(AF_INET, buf, &c->net4.sin_addr); + + c->net4.sin_len = sizeof(struct sockaddr_in); + c->net4.sin_family = AF_INET; + c->addr4.sin_len = sizeof(struct sockaddr_in); + c->addr4.sin_family = AF_INET; + + sa_fill_mask4(&c->mask4, c->plen4); + + /* Fill in interface IPv4 address. Assume the first address in net */ + c->addr4.sin_addr.s_addr = htonl(ntohl(c->net4.sin_addr.s_addr) + 1); + inet_ntop(AF_INET, &c->net4.sin_addr, c->net4_str, INET_ADDRSTRLEN); + inet_ntop(AF_INET, &c->addr4.sin_addr, c->addr4_str, INET_ADDRSTRLEN); + + key = atf_tc_get_config_var_wd(tc, "rtsock.v6prefix", "2001:DB8::/32"); + strlcpy(buf, key, sizeof(buf)); + if ((s = strchr(buf, '/')) == NULL) + return (NULL); + *s++ = '\0'; + mask = strtol(s, NULL, 10); + if (mask < 0 || mask > 128) + return (NULL); + c->plen6 = mask; + + inet_pton(AF_INET6, buf, &c->net6.sin6_addr); + + c->net6.sin6_len = sizeof(struct sockaddr_in6); + c->net6.sin6_family = AF_INET6; + c->addr6.sin6_len = sizeof(struct sockaddr_in6); + c->addr6.sin6_family = AF_INET6; + + sa_fill_mask6(&c->mask6, c->plen6); + + /* Fill in interface IPv6 address. Assume the first address in net */ + memcpy(&c->addr6.sin6_addr, &c->net6.sin6_addr, sizeof(struct in6_addr)); +#define _s6_addr32 __u6_addr.__u6_addr32 + c->addr6.sin6_addr._s6_addr32[3] = htonl(ntohl(c->net6.sin6_addr._s6_addr32[3]) + 1); +#undef _s6_addr32 + inet_ntop(AF_INET6, &c->net6.sin6_addr, c->net6_str, INET6_ADDRSTRLEN); + inet_ntop(AF_INET6, &c->addr6.sin6_addr, c->addr6_str, INET6_ADDRSTRLEN); + + ATF_CHECK_ERRNO(0, true); + + if (co->num_interfaces > 0) { + /* Try loading if_epair and if that fails skip the test. */ + kldload("if_epair"); + ATF_REQUIRE_KERNEL_MODULE("if_epair"); + /* Clear errno for the following tests. */ + errno = 0; + + c->ifnames = calloc(co->num_interfaces, sizeof(char *)); + for (int i = 0; i < co->num_interfaces; i++) + c->ifnames[i] = iface_create("epair"); + + c->ifname = c->ifnames[0]; + c->ifindex = if_nametoindex(c->ifname); + ATF_REQUIRE_MSG(c->ifindex != 0, "interface %s not found", + c->ifname); + } + c->num_interfaces = co->num_interfaces; + + c->remote_lladdr = strdup(atf_tc_get_config_var_wd(tc, + "rtsock.remote_lladdr", "00:00:5E:00:53:42")); + + return (c); +} + +void +config_generic_cleanup(const atf_tc_t *tc) +{ + const char *srcdir = atf_tc_get_config_var(tc, "srcdir"); + char cmd[512]; + int ret; + + snprintf(cmd, sizeof(cmd), "%s/generic_cleanup.sh", srcdir); + ret = system(cmd); + if (ret != 0) + RLOG("'%s' failed, error %d", cmd, ret); +} + +void +config_describe_root_test(atf_tc_t *tc, char *test_descr) +{ + + atf_tc_set_md_var(tc, "descr", test_descr); + // Adding/deleting prefix requires root privileges + atf_tc_set_md_var(tc, "require.user", "root"); +} + +#endif diff --git a/tests/sys/net/routing/rtsock_print.h b/tests/sys/net/routing/rtsock_print.h new file mode 100644 index 000000000000..61d70dc55670 --- /dev/null +++ b/tests/sys/net/routing/rtsock_print.h @@ -0,0 +1,412 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 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. + */ + +#ifndef _NET_ROUTING_RTSOCK_PRINT_H_ +#define _NET_ROUTING_RTSOCK_PRINT_H_ + + +#define RLOG(_fmt, ...) printf("%s: " _fmt "\n", __func__, ##__VA_ARGS__) +#define RLOG_ERRNO(_fmt, ...) do { \ + printf("%s: " _fmt, __func__, ##__VA_ARGS__); \ + printf(": %s\n", strerror(errno)); \ +} while(0) + +#define RTSOCK_ATF_REQUIRE_MSG(_rtm, _cond, _fmt, ...) do { \ + if (!(_cond)) { \ + printf("-- CONDITION FAILED, rtm dump --\n\n");\ + rtsock_print_message(_rtm); \ + rtsock_print_table(AF_INET); \ + rtsock_print_table(AF_INET6); \ + printf("===================================\n");\ + } \ + ATF_REQUIRE_MSG(_cond, _fmt, ##__VA_ARGS__); \ +} while (0); + +#define RTSOCKHD_ATF_REQUIRE_MSG(_rtm, _cond, _fmt, ...) do { \ + if (!(_cond)) { \ + printf("-- CONDITION FAILED, rtm hexdump--\n\n");\ + rtsock_print_message_hd(_rtm); \ + } \ + ATF_REQUIRE_MSG(_cond, _fmt, ##__VA_ARGS__); \ +} while (0); + + +/* from route.c */ +static const char *const msgtypes[] = { + "", + "RTM_ADD", + "RTM_DELETE", + "RTM_CHANGE", + "RTM_GET", + "RTM_LOSING", + "RTM_REDIRECT", + "RTM_MISS", + "RTM_LOCK", + "RTM_OLDADD", + "RTM_OLDDEL", + "RTM_RESOLVE", + "RTM_NEWADDR", + "RTM_DELADDR", + "RTM_IFINFO", + "RTM_NEWMADDR", + "RTM_DELMADDR", + "RTM_IFANNOUNCE", + "RTM_IEEE80211", +}; + +static const char metricnames[] = + "\011weight\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire" + "\1mtu"; +static const char routeflags[] = + "\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE" + "\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE" + "\017PROTO2\020PROTO1\021PRCLONING\022WASCLONED\023PROTO3" + "\024FIXEDMTU\025PINNED\026LOCAL\027BROADCAST\030MULTICAST\035STICKY"; +static const char ifnetflags[] = + "\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6b6\7RUNNING\010NOARP" + "\011PPROMISC\012ALLMULTI\013OACTIVE\014SIMPLEX\015LINK0\016LINK1" + "\017LINK2\020MULTICAST"; +static const char addrnames[] = + "\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD"; + +static int +_printb(char *buf, size_t bufsize, int b, const char *str) +{ + int i; + int gotsome = 0; + + char *pbuf = buf; + + if (b == 0) { + *pbuf = '\0'; + return (0); + } + while ((i = *str++) != 0) { + if (b & (1 << (i-1))) { + if (gotsome == 0) + i = '<'; + else + i = ','; + *pbuf++ = i; + gotsome = 1; + for (; (i = *str) > 32; str++) + *pbuf++ = i; + } else + while (*str > 32) + str++; + } + if (gotsome) + *pbuf++ = '>'; + *pbuf = '\0'; + + return (int)(pbuf - buf); +} + +const char * +rtsock_print_cmdtype(int cmd) +{ + + return (msgtypes[cmd]); +} + +char * +rtsock_print_rtm_flags(char *buf, int buflen, int rtm_flags) +{ + + _printb(buf, buflen, rtm_flags, routeflags); + return (buf); +} + + +#define _PRINTX(fmt, ...) do { \ + one_len = snprintf(ptr, rem_len, fmt, __VA_ARGS__); \ + ptr += one_len; \ + rem_len -= one_len; \ +} while(0) + + +void +sa_print_hd(char *buf, int buflen, const char *data, int len) +{ + char *ptr; + int one_len, rem_len; + + ptr = buf; + rem_len = buflen; + + const char *last_char = NULL; + unsigned char v; + int repeat_count = 0; + for (int i = 0; i < len; i++) { + if (last_char && *last_char == data[i] && data[i] == 0x00) { + repeat_count++; + continue; + } + + if (repeat_count > 1) { + _PRINTX("{%d}", repeat_count); + repeat_count = 0; + } + + v = ((const unsigned char *)data)[i]; + if (last_char == NULL) + _PRINTX("x%02X", v); + else + _PRINTX(", x%02X", v); + + last_char = &data[i]; + repeat_count = 1; + } + + if (repeat_count > 1) + snprintf(ptr, rem_len, "{%d}", repeat_count); +} + +#undef _PRINTX + +void +sa_print(const struct sockaddr *sa, int include_hexdump) +{ + char hdbuf[512], abuf[64]; + char ifbuf[128]; + const struct sockaddr_dl *sdl; + const struct sockaddr_in6 *sin6; + const struct sockaddr_in *sin; + int i; + + switch (sa->sa_family) { + case AF_INET: + sin = (struct sockaddr_in *)sa; + inet_ntop(AF_INET, &sin->sin_addr, abuf, sizeof(abuf)); + printf(" af=inet len=%d addr=%s", sa->sa_len, abuf); + break; + case AF_INET6: + sin6 = (struct sockaddr_in6 *)sa; + inet_ntop(AF_INET6, &sin6->sin6_addr, abuf, sizeof(abuf)); + int scope_id = sin6->sin6_scope_id; + printf(" af=inet6 len=%d addr=%s", sa->sa_len, abuf); + if (scope_id != 0) { + memset(ifbuf, 0, sizeof(ifbuf)); + if_indextoname(scope_id, ifbuf); + printf(" scope_id=%d if_name=%s", scope_id, ifbuf); + } + break; + case AF_LINK: + sdl = (const struct sockaddr_dl *)sa; + int sdl_index = sdl->sdl_index; + if (sdl_index != 0) { + memset(ifbuf, 0, sizeof(ifbuf)); + if_indextoname(sdl_index, ifbuf); + printf(" af=link len=%d sdl_index=%d if_name=%s", sdl->sdl_len, sdl_index, ifbuf); + } + if (sdl->sdl_nlen) { + char _ifname[IFNAMSIZ]; + memcpy(_ifname, sdl->sdl_data, sdl->sdl_nlen); + _ifname[sdl->sdl_nlen] = '\0'; + printf(" name=%s", _ifname); + } + if (sdl->sdl_alen) { + printf(" addr="); + const char *lladdr = LLADDR(sdl); + for (int i = 0; i < sdl->sdl_alen; i++) { + if (i + 1 < sdl->sdl_alen) + printf("%02X:", ((const unsigned char *)lladdr)[i]); + else + printf("%02X", ((const unsigned char *)lladdr)[i]); + } + } + break; + default: + printf(" af=%d len=%d", sa->sa_family, sa->sa_len); + } + + if (include_hexdump) { + sa_print_hd(hdbuf, sizeof(hdbuf), ((char *)sa), sa->sa_len); + printf(" hd={%s}", hdbuf); + } + printf("\n"); +} + +/* +got message of size 240 on Mon Dec 16 09:23:31 2019 +RTM_ADD: Add Route: len 240, pid: 25534, seq 2, errno 0, flags:<HOST,DONE,LLINFO,STATIC> +locks: inits: +sockaddrs: <DST,GATEWAY> +*/ + +void +rtsock_print_rtm(struct rt_msghdr *rtm) +{ + struct timeval tv; + struct tm tm_res; + char buf[64]; + + gettimeofday(&tv, NULL); + localtime_r(&tv.tv_sec, &tm_res); + strftime(buf, sizeof(buf), "%F %T", &tm_res); + printf("Got message of size %hu on %s\n", rtm->rtm_msglen, buf); + + char flags_buf[256]; + rtsock_print_rtm_flags(flags_buf, sizeof(flags_buf), rtm->rtm_flags); + + printf("%s: len %hu, pid: %d, seq %d, errno %d, flags: %s\n", msgtypes[rtm->rtm_type], + rtm->rtm_msglen, rtm->rtm_pid, rtm->rtm_seq, rtm->rtm_errno, flags_buf); + + if (rtm->rtm_inits > 0) { + _printb(flags_buf, sizeof(flags_buf), rtm->rtm_inits, metricnames); + printf("metrics: %s\n", flags_buf); + if (rtm->rtm_inits & RTV_MTU) + printf("mtu: %lu\n", rtm->rtm_rmx.rmx_mtu); + if (rtm->rtm_inits & RTV_EXPIRE) { + struct timeval tv; + gettimeofday(&tv, NULL); + printf("expire: %d (%lu raw)\n", + (int)(rtm->rtm_rmx.rmx_expire - tv.tv_sec), rtm->rtm_rmx.rmx_expire); + } + } + + _printb(flags_buf, sizeof(flags_buf), rtm->rtm_addrs, addrnames); + printf("sockaddrs: 0x%X %s\n", rtm->rtm_addrs, flags_buf); + + char *ptr = (char *)(rtm + 1); + for (int i = 0; i < RTAX_MAX; i++) { + if (rtm->rtm_addrs & (1 << i)) { + struct sockaddr *sa = (struct sockaddr *)ptr; + sa_print(sa, 1); + + /* add */ + ptr += ALIGN(((struct sockaddr *)ptr)->sa_len); + } + } + + printf("\n"); + +} + +void +rtsock_print_ifa(struct ifa_msghdr *ifam) +{ + struct timeval tv; + struct tm tm_res; + char buf[64]; + + gettimeofday(&tv, NULL); + localtime_r(&tv.tv_sec, &tm_res); + strftime(buf, sizeof(buf), "%F %T", &tm_res); + printf("Got message of size %hu on %s\n", ifam->ifam_msglen, buf); + + char flags_buf[256]; + _printb(flags_buf, sizeof(flags_buf), ifam->ifam_flags, routeflags); + + printf("%s: len %hu, ifindex: %d, flags: %s\n", msgtypes[ifam->ifam_type], + ifam->ifam_msglen, ifam->ifam_index, flags_buf); + + _printb(flags_buf, sizeof(flags_buf), ifam->ifam_addrs, addrnames); + printf("sockaddrs: 0x%X %s\n", ifam->ifam_addrs, flags_buf); + + char *ptr = (char *)(ifam + 1); + for (int i = 0; i < RTAX_MAX; i++) { + if (ifam->ifam_addrs & (1 << i)) { + struct sockaddr *sa = (struct sockaddr *)ptr; + sa_print(sa, 1); + + /* add */ + ptr += ALIGN(((struct sockaddr *)ptr)->sa_len); + } + } + + printf("\n"); + +} + +void +rtsock_print_message_hd(struct rt_msghdr *rtm) +{ + struct timeval tv; + struct tm tm_res; + char buf[64]; + char dumpbuf[2048]; + + gettimeofday(&tv, NULL); + localtime_r(&tv.tv_sec, &tm_res); + strftime(buf, sizeof(buf), "%F %T", &tm_res); + printf("Got message type %s of size %hu on %s\n", + rtsock_print_cmdtype(rtm->rtm_type), + rtm->rtm_msglen, buf); + + sa_print_hd(dumpbuf, sizeof(dumpbuf), (char *)rtm, rtm->rtm_msglen); + printf(" %s\n", dumpbuf); +} + +void +rtsock_print_message(struct rt_msghdr *rtm) +{ + + switch (rtm->rtm_type) { + case RTM_GET: + case RTM_ADD: + case RTM_DELETE: + case RTM_CHANGE: + rtsock_print_rtm(rtm); + break; + case RTM_DELADDR: + case RTM_NEWADDR: + rtsock_print_ifa((struct ifa_msghdr *)rtm); + break; + default: + printf("unknown rt message type %X\n", rtm->rtm_type); + } +} + +static void +print_command(char *cmd) +{ + char line[1024]; + + FILE *fp = popen(cmd, "r"); + if (fp != NULL) { + while (fgets(line, sizeof(line), fp) != NULL) + printf("%s", line); + pclose(fp); + } +} + +void +rtsock_print_table(int family) +{ + char cmdbuf[128]; + char *key = (family == AF_INET) ? "4" : "6"; + + snprintf(cmdbuf, sizeof(cmdbuf), "/usr/bin/netstat -%srnW", key); + printf("==== %s ===\n", cmdbuf); + print_command(cmdbuf); + snprintf(cmdbuf, sizeof(cmdbuf), "/usr/bin/netstat -%sonW", key); + printf("==== %s ===\n", cmdbuf); + print_command(cmdbuf); +} + +#endif diff --git a/tests/sys/net/routing/test_routing_l3.py b/tests/sys/net/routing/test_routing_l3.py new file mode 100755 index 000000000000..3a3822293424 --- /dev/null +++ b/tests/sys/net/routing/test_routing_l3.py @@ -0,0 +1,120 @@ +import ipaddress +import socket + +import pytest +from atf_python.sys.net.rtsock import RtConst +from atf_python.sys.net.rtsock import Rtsock +from atf_python.sys.net.rtsock import RtsockRtMessage +from atf_python.sys.net.rtsock import SaHelper +from atf_python.sys.net.tools import ToolsHelper +from atf_python.sys.net.vnet import SingleVnetTestTemplate +from atf_python.sys.net.vnet import VnetTestTemplate + + +class TestIfOps(VnetTestTemplate): + TOPOLOGY = { + "vnet1": {"ifaces": ["if1", "if2"]}, + "if1": {"prefixes4": [], "prefixes6": []}, + "if2": {"prefixes4": [], "prefixes6": []}, + } + + @pytest.mark.parametrize("family", ["inet", "inet6"]) + @pytest.mark.require_user("root") + def test_change_prefix_route(self, family): + """Tests that prefix route changes to the new one upon addr deletion""" + vnet = self.vnet_map["vnet1"] + first_iface = vnet.iface_alias_map["if1"] + second_iface = vnet.iface_alias_map["if2"] + if family == "inet": + first_addr = ipaddress.ip_interface("192.0.2.1/24") + second_addr = ipaddress.ip_interface("192.0.2.2/24") + else: + first_addr = ipaddress.ip_interface("2001:db8::1/64") + second_addr = ipaddress.ip_interface("2001:db8::2/64") + + first_iface.setup_addr(str(first_addr)) + second_iface.setup_addr(str(second_addr)) + + # At this time prefix should be pointing to the first interface + routes = ToolsHelper.get_routes(family) + px = [r for r in routes if r["destination"] == str(first_addr.network)][0] + assert px["interface-name"] == first_iface.name + + # Now delete address from the first interface and verify switchover + first_iface.delete_addr(first_addr.ip) + + routes = ToolsHelper.get_routes(family) + px = [r for r in routes if r["destination"] == str(first_addr.network)][0] + assert px["interface-name"] == second_iface.name + + @pytest.mark.parametrize( + "family", + [ + "inet", + pytest.param("inet6", marks=pytest.mark.xfail(reason="currently fails")), + ], + ) + @pytest.mark.require_user("root") + def test_change_prefix_route_same_iface(self, family): + """Tests that prefix route changes to the new ifa upon addr deletion""" + vnet = self.vnet_map["vnet1"] + first_iface = vnet.iface_alias_map["if1"] + + if family == "inet": + first_addr = ipaddress.ip_interface("192.0.2.1/24") + second_addr = ipaddress.ip_interface("192.0.2.2/24") + else: + first_addr = ipaddress.ip_interface("2001:db8::1/64") + second_addr = ipaddress.ip_interface("2001:db8::2/64") + + first_iface.setup_addr(str(first_addr)) + first_iface.setup_addr(str(second_addr)) + + # At this time prefix should be pointing to the first interface + routes = ToolsHelper.get_routes(family) + px = [r for r in routes if r["destination"] == str(first_addr.network)][0] + assert px["interface-name"] == first_iface.name + + # Now delete address from the first interface and verify switchover + first_iface.delete_addr(str(first_addr.ip)) + + routes = ToolsHelper.get_routes(family) + px = [r for r in routes if r["destination"] == str(first_addr.network)][0] + nhop_kidx = px["nhop"] + assert px["interface-name"] == first_iface.name + nhops = ToolsHelper.get_nhops(family) + nh = [nh for nh in nhops if nh["index"] == nhop_kidx][0] + assert nh["ifa"] == str(second_addr.ip) + + +class TestRouteCornerCase1(SingleVnetTestTemplate): + @pytest.mark.parametrize("family", ["inet", "inet6"]) + @pytest.mark.require_user("root") + def test_add_direct_route_p2p_wo_ifa(self, family): + + tun_ifname = ToolsHelper.get_output("/sbin/ifconfig tun create").rstrip() + tun_ifindex = socket.if_nametoindex(tun_ifname) + assert tun_ifindex > 0 + rtsock = Rtsock() + + if family == "inet": + prefix = "172.16.0.0/12" + else: + prefix = "2a02:6b8::/64" + IFT_ETHER = 0x06 + gw_link = SaHelper.link_sa(ifindex=tun_ifindex, iftype=IFT_ETHER) + + msg = rtsock.new_rtm_add(prefix, gw_link) + msg.add_link_attr(RtConst.RTA_IFP, tun_ifindex) + rtsock.write_message(msg) + + data = rtsock.read_data(msg.rtm_seq) + msg_in = RtsockRtMessage.from_bytes(data) + msg_in.print_in_message() + + desired_sa = { + RtConst.RTA_DST: msg.get_sa(RtConst.RTA_DST), + RtConst.RTA_NETMASK: msg.get_sa(RtConst.RTA_NETMASK), + } + + msg_in.verify(msg.rtm_type, desired_sa) diff --git a/tests/sys/net/routing/test_rtsock_l3.c b/tests/sys/net/routing/test_rtsock_l3.c new file mode 100644 index 000000000000..cdfc63d604bf --- /dev/null +++ b/tests/sys/net/routing/test_rtsock_l3.c @@ -0,0 +1,1421 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 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. + */ + +#include "rtsock_common.h" +#include "rtsock_config.h" +#include "sys/types.h" +#include <sys/time.h> +#include <sys/ioctl.h> + +#include "net/bpf.h" + +static void +jump_vnet(struct rtsock_test_config *c, const atf_tc_t *tc) +{ + char vnet_name[512]; + + snprintf(vnet_name, sizeof(vnet_name), "vt-%s", atf_tc_get_ident(tc)); + RLOG("jumping to %s", vnet_name); + + vnet_switch(vnet_name, c->ifnames, c->num_interfaces); + + /* Update ifindex cache */ + c->ifindex = if_nametoindex(c->ifname); +} + +static inline struct rtsock_test_config * +presetup_ipv6_iface(const atf_tc_t *tc) +{ + struct rtsock_test_config *c; + int ret; + + c = config_setup(tc, NULL); + + jump_vnet(c, tc); + + ret = iface_turn_up(c->ifname); + ATF_REQUIRE_MSG(ret == 0, "Unable to turn up %s", c->ifname); + + ret = iface_enable_ipv6(c->ifname); + ATF_REQUIRE_MSG(ret == 0, "Unable to enable IPv6 on %s", c->ifname); + ATF_REQUIRE_ERRNO(0, true); + + return (c); +} + +static inline struct rtsock_test_config * +presetup_ipv6(const atf_tc_t *tc) +{ + struct rtsock_test_config *c; + int ret; + + c = presetup_ipv6_iface(tc); + + ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6); + + c->rtsock_fd = rtsock_setup_socket(); + ATF_REQUIRE_ERRNO(0, true); + + return (c); +} + +static inline struct rtsock_test_config * +presetup_ipv4_iface(const atf_tc_t *tc) +{ + struct rtsock_test_config *c; + int ret; + + c = config_setup(tc, NULL); + ATF_REQUIRE(c != NULL); + + jump_vnet(c, tc); + + ret = iface_turn_up(c->ifname); + ATF_REQUIRE_MSG(ret == 0, "Unable to turn up %s", c->ifname); + ATF_REQUIRE_ERRNO(0, true); + + return (c); +} + +static inline struct rtsock_test_config * +presetup_ipv4(const atf_tc_t *tc) +{ + struct rtsock_test_config *c; + int ret; + + c = presetup_ipv4_iface(tc); + + /* assumes ifconfig doing IFF_UP */ + ret = iface_setup_addr(c->ifname, c->addr4_str, c->plen4); + ATF_REQUIRE_MSG(ret == 0, "ifconfig failed"); + + c->rtsock_fd = rtsock_setup_socket(); + ATF_REQUIRE_ERRNO(0, true); + + return (c); +} + + +static void +prepare_v4_network(struct rtsock_test_config *c, struct sockaddr_in *dst, + struct sockaddr_in *mask, struct sockaddr_in *gw) +{ + /* Create IPv4 subnetwork with smaller prefix */ + sa_fill_mask4(mask, c->plen4 + 1); + *dst = c->net4; + /* Calculate GW as last-net-address - 1 */ + *gw = c->net4; + gw->sin_addr.s_addr = htonl((ntohl(c->net4.sin_addr.s_addr) | ~ntohl(c->mask4.sin_addr.s_addr)) - 1); + sa_print((struct sockaddr *)dst, 0); + sa_print((struct sockaddr *)mask, 0); + sa_print((struct sockaddr *)gw, 0); +} + +static void +prepare_v6_network(struct rtsock_test_config *c, struct sockaddr_in6 *dst, + struct sockaddr_in6 *mask, struct sockaddr_in6 *gw) +{ + /* Create IPv6 subnetwork with smaller prefix */ + sa_fill_mask6(mask, c->plen6 + 1); + *dst = c->net6; + /* Calculate GW as last-net-address - 1 */ + *gw = c->net6; +#define _s6_addr32 __u6_addr.__u6_addr32 + gw->sin6_addr._s6_addr32[0] = htonl((ntohl(gw->sin6_addr._s6_addr32[0]) | ~ntohl(c->mask6.sin6_addr._s6_addr32[0]))); + gw->sin6_addr._s6_addr32[1] = htonl((ntohl(gw->sin6_addr._s6_addr32[1]) | ~ntohl(c->mask6.sin6_addr._s6_addr32[1]))); + gw->sin6_addr._s6_addr32[2] = htonl((ntohl(gw->sin6_addr._s6_addr32[2]) | ~ntohl(c->mask6.sin6_addr._s6_addr32[2]))); + gw->sin6_addr._s6_addr32[3] = htonl((ntohl(gw->sin6_addr._s6_addr32[3]) | ~ntohl(c->mask6.sin6_addr._s6_addr32[3])) - 1); +#undef _s6_addr32 + sa_print((struct sockaddr *)dst, 0); + sa_print((struct sockaddr *)mask, 0); + sa_print((struct sockaddr *)gw, 0); +} + +static void +prepare_route_message(struct rt_msghdr *rtm, int cmd, struct sockaddr *dst, + struct sockaddr *mask, struct sockaddr *gw) +{ + + rtsock_prepare_route_message(rtm, cmd, dst, mask, gw); + + if (cmd == RTM_ADD || cmd == RTM_CHANGE) + rtm->rtm_flags |= RTF_STATIC; +} + +static void +verify_route_message(struct rt_msghdr *rtm, int cmd, struct sockaddr *dst, + struct sockaddr *mask, struct sockaddr *gw) +{ + char msg[512]; + struct sockaddr *sa; + int ret; + + RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_type == cmd, + "expected %s message, got %d (%s)", rtsock_print_cmdtype(cmd), + rtm->rtm_type, rtsock_print_cmdtype(rtm->rtm_type)); + RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_errno == 0, + "got got errno %d as message reply", rtm->rtm_errno); + RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->_rtm_spare1 == 0, + "expected rtm_spare==0, got %d", rtm->_rtm_spare1); + + /* kernel MAY return more sockaddrs, including RTA_IFP / RTA_IFA, so verify the needed ones */ + if (dst != NULL) { + sa = rtsock_find_rtm_sa(rtm, RTA_DST); + RTSOCK_ATF_REQUIRE_MSG(rtm, sa != NULL, "DST is not set"); + ret = sa_equal_msg(sa, dst, msg, sizeof(msg)); + RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "DST sa diff: %s", msg); + } + + if (mask != NULL) { + sa = rtsock_find_rtm_sa(rtm, RTA_NETMASK); + RTSOCK_ATF_REQUIRE_MSG(rtm, sa != NULL, "NETMASK is not set"); + ret = sa_equal_msg(sa, mask, msg, sizeof(msg)); + ret = 1; + RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "NETMASK sa diff: %s", msg); + } + + if (gw != NULL) { + sa = rtsock_find_rtm_sa(rtm, RTA_GATEWAY); + RTSOCK_ATF_REQUIRE_MSG(rtm, sa != NULL, "GATEWAY is not set"); + ret = sa_equal_msg(sa, gw, msg, sizeof(msg)); + RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "GATEWAY sa diff: %s", msg); + } +} + +static void +verify_route_message_extra(struct rt_msghdr *rtm, int ifindex, int rtm_flags) +{ + RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_index == ifindex, + "expected ifindex %d, got %d", ifindex, rtm->rtm_index); + + if (rtm->rtm_flags != rtm_flags) { + char got_flags[64], expected_flags[64]; + rtsock_print_rtm_flags(got_flags, sizeof(got_flags), + rtm->rtm_flags); + rtsock_print_rtm_flags(expected_flags, sizeof(expected_flags), + rtm_flags); + + RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_flags == rtm_flags, + "expected flags: 0x%X %s, got 0x%X %s", + rtm_flags, expected_flags, + rtm->rtm_flags, got_flags); + } +} + +static void +verify_link_gateway(struct rt_msghdr *rtm, int ifindex) +{ + struct sockaddr *sa; + struct sockaddr_dl *sdl; + + sa = rtsock_find_rtm_sa(rtm, RTA_GATEWAY); + RTSOCK_ATF_REQUIRE_MSG(rtm, sa != NULL, "GATEWAY is not set"); + RTSOCK_ATF_REQUIRE_MSG(rtm, sa->sa_family == AF_LINK, "GW sa family is %d", sa->sa_family); + sdl = (struct sockaddr_dl *)sa; + RTSOCK_ATF_REQUIRE_MSG(rtm, sdl->sdl_index == ifindex, "GW ifindex is %d", sdl->sdl_index); +} + +/* TESTS */ + +#define DECLARE_TEST_VARS \ + char buffer[2048]; \ + struct rtsock_test_config *c; \ + struct rt_msghdr *rtm = (struct rt_msghdr *)buffer; \ + struct sockaddr *sa; \ + int ret; \ + \ + +#define DESCRIBE_ROOT_TEST(_msg) config_describe_root_test(tc, _msg) +#define CLEANUP_AFTER_TEST config_generic_cleanup(tc) + +#define RTM_DECLARE_ROOT_TEST(_name, _descr) \ +ATF_TC_WITH_CLEANUP(_name); \ +ATF_TC_HEAD(_name, tc) \ +{ \ + DESCRIBE_ROOT_TEST(_descr); \ +} \ +ATF_TC_CLEANUP(_name, tc) \ +{ \ + CLEANUP_AFTER_TEST; \ +} + +ATF_TC_WITH_CLEANUP(rtm_get_v4_exact_success); +ATF_TC_HEAD(rtm_get_v4_exact_success, tc) +{ + DESCRIBE_ROOT_TEST("Tests RTM_GET with exact prefix lookup on an interface prefix"); +} + +ATF_TC_BODY(rtm_get_v4_exact_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv4(tc); + + prepare_route_message(rtm, RTM_GET, (struct sockaddr *)&c->net4, + (struct sockaddr *)&c->mask4, NULL); + + rtsock_send_rtm(c->rtsock_fd, rtm); + + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + /* + * RTM_GET: Report Metrics: len 240, pid: 45072, seq 42, errno 0, flags: <UP,DONE,PINNED> + * sockaddrs: 0x7 <DST,GATEWAY,NETMASK> + * af=inet len=16 addr=192.0.2.0 hd={10, 02, 00{2}, C0, 00, 02, 00{9}} + * af=link len=54 sdl_index=3 if_name=tap4242 hd={36, 12, 03, 00, 06, 00{49}} + * af=inet len=16 addr=255.255.255.0 hd={10, 02, FF{5}, 00{9}} + */ + + verify_route_message(rtm, RTM_GET, (struct sockaddr *)&c->net4, + (struct sockaddr *)&c->mask4, NULL); + + verify_route_message_extra(rtm, c->ifindex, RTF_UP | RTF_DONE | RTF_PINNED); + + /* Explicitly verify gateway for the interface route */ + verify_link_gateway(rtm, c->ifindex); + sa = rtsock_find_rtm_sa(rtm, RTA_GATEWAY); + RTSOCK_ATF_REQUIRE_MSG(rtm, sa != NULL, "GATEWAY is not set"); + RTSOCK_ATF_REQUIRE_MSG(rtm, sa->sa_family == AF_LINK, "GW sa family is %d", sa->sa_family); + struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa; + RTSOCK_ATF_REQUIRE_MSG(rtm, sdl->sdl_index == c->ifindex, "GW ifindex is %d", sdl->sdl_index); +} + +ATF_TC_CLEANUP(rtm_get_v4_exact_success, tc) +{ + CLEANUP_AFTER_TEST; +} + +ATF_TC_WITH_CLEANUP(rtm_get_v4_lpm_success); +ATF_TC_HEAD(rtm_get_v4_lpm_success, tc) +{ + DESCRIBE_ROOT_TEST("Tests RTM_GET with address lookup on an existing prefix"); +} + +ATF_TC_BODY(rtm_get_v4_lpm_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv4(tc); + + prepare_route_message(rtm, RTM_GET, (struct sockaddr *)&c->net4, NULL, NULL); + + rtsock_send_rtm(c->rtsock_fd, rtm); + + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + /* + * RTM_GET: Report Metrics: len 312, pid: 67074, seq 1, errno 0, flags:<UP,DONE,PINNED> + * locks: inits: + * sockaddrs: <DST,GATEWAY,NETMASK,IFP,IFA> + * 10.0.0.0 link#1 255.255.255.0 vtnet0:52.54.0.42.f.ef 10.0.0.157 + */ + + verify_route_message(rtm, RTM_GET, (struct sockaddr *)&c->net4, + (struct sockaddr *)&c->mask4, NULL); + + verify_route_message_extra(rtm, c->ifindex, RTF_UP | RTF_DONE | RTF_PINNED); +} + +ATF_TC_CLEANUP(rtm_get_v4_lpm_success, tc) +{ + CLEANUP_AFTER_TEST; +} + + +ATF_TC_WITH_CLEANUP(rtm_get_v4_empty_dst_failure); +ATF_TC_HEAD(rtm_get_v4_empty_dst_failure, tc) +{ + + DESCRIBE_ROOT_TEST("Tests RTM_GET with empty DST addr"); +} + +ATF_TC_BODY(rtm_get_v4_empty_dst_failure, tc) +{ + DECLARE_TEST_VARS; + struct rtsock_config_options co; + + bzero(&co, sizeof(co)); + co.num_interfaces = 0; + + c = config_setup(tc,&co); + c->rtsock_fd = rtsock_setup_socket(); + + rtsock_prepare_route_message(rtm, RTM_GET, NULL, + (struct sockaddr *)&c->mask4, NULL); + rtsock_update_rtm_len(rtm); + + ATF_CHECK_ERRNO(EINVAL, write(c->rtsock_fd, rtm, rtm->rtm_msglen) == -1); +} + +ATF_TC_CLEANUP(rtm_get_v4_empty_dst_failure, tc) +{ + CLEANUP_AFTER_TEST; +} + +ATF_TC_WITH_CLEANUP(rtm_get_v4_hostbits_success); +ATF_TC_HEAD(rtm_get_v4_hostbits_success, tc) +{ + DESCRIBE_ROOT_TEST("Tests RTM_GET with prefix with some hosts-bits set"); +} + +ATF_TC_BODY(rtm_get_v4_hostbits_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv4(tc); + + /* Q the same prefix */ + rtsock_prepare_route_message(rtm, RTM_GET, (struct sockaddr *)&c->addr4, + (struct sockaddr *)&c->mask4, NULL); + rtsock_update_rtm_len(rtm); + + ATF_REQUIRE_ERRNO(0, true); + ATF_CHECK_ERRNO(0, write(c->rtsock_fd, rtm, rtm->rtm_msglen) > 0); +} + +ATF_TC_CLEANUP(rtm_get_v4_hostbits_success, tc) +{ + CLEANUP_AFTER_TEST; +} + +ATF_TC_WITH_CLEANUP(rtm_add_v4_gw_direct_success); +ATF_TC_HEAD(rtm_add_v4_gw_direct_success, tc) +{ + DESCRIBE_ROOT_TEST("Tests IPv4 route addition with directly-reachable GW specified by IP"); +} + +ATF_TC_BODY(rtm_add_v4_gw_direct_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv4(tc); + + /* Create IPv4 subnetwork with smaller prefix */ + struct sockaddr_in mask4; + struct sockaddr_in net4; + struct sockaddr_in gw4; + prepare_v4_network(c, &net4, &mask4, &gw4); + + prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net4, + (struct sockaddr *)&mask4, (struct sockaddr *)&gw4); + + rtsock_send_rtm(c->rtsock_fd, rtm); + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + /* + * RTM_ADD: Add Route: len 200, pid: 46068, seq 42, errno 0, flags:<GATEWAY,DONE,STATIC> + * locks: inits: + * sockaddrs: <DST,GATEWAY,NETMASK> + * 192.0.2.0 192.0.2.254 255.255.255.128 + */ + + verify_route_message(rtm, RTM_ADD, (struct sockaddr *)&net4, + (struct sockaddr *)&mask4, (struct sockaddr *)&gw4); + verify_route_message_extra(rtm, c->ifindex, + RTF_UP | RTF_DONE | RTF_GATEWAY | RTF_STATIC); +} + +ATF_TC_CLEANUP(rtm_add_v4_gw_direct_success, tc) +{ + CLEANUP_AFTER_TEST; +} + +RTM_DECLARE_ROOT_TEST(rtm_add_v4_no_rtf_host_success, + "Tests success with netmask sa and RTF_HOST inconsistency"); + +ATF_TC_BODY(rtm_add_v4_no_rtf_host_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv4(tc); + + /* Create IPv4 subnetwork with smaller prefix */ + struct sockaddr_in mask4; + struct sockaddr_in net4; + struct sockaddr_in gw4; + prepare_v4_network(c, &net4, &mask4, &gw4); + + prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net4, + NULL, (struct sockaddr *)&gw4); + rtsock_update_rtm_len(rtm); + + /* RTF_HOST is NOT specified, while netmask is empty */ + ATF_REQUIRE_ERRNO(0, true); + ATF_CHECK_ERRNO(0, write(c->rtsock_fd, rtm, rtm->rtm_msglen) > 0); +} + +ATF_TC_WITH_CLEANUP(rtm_del_v4_prefix_nogw_success); +ATF_TC_HEAD(rtm_del_v4_prefix_nogw_success, tc) +{ + DESCRIBE_ROOT_TEST("Tests IPv4 route removal without specifying gateway"); +} + +ATF_TC_BODY(rtm_del_v4_prefix_nogw_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv4(tc); + + /* Create IPv4 subnetwork with smaller prefix */ + struct sockaddr_in mask4; + struct sockaddr_in net4; + struct sockaddr_in gw4; + prepare_v4_network(c, &net4, &mask4, &gw4); + + prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net4, + (struct sockaddr *)&mask4, (struct sockaddr *)&gw4); + + rtsock_send_rtm(c->rtsock_fd, rtm); + + /* Route has been added successfully, try to delete it */ + prepare_route_message(rtm, RTM_DELETE, (struct sockaddr *)&net4, + (struct sockaddr *)&mask4, NULL); + + rtsock_send_rtm(c->rtsock_fd, rtm); + + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + /* + * RTM_DELETE: Delete Route: len 200, pid: 46417, seq 43, errno 0, flags: <GATEWAY,DONE,STATIC> + * sockaddrs: 0x7 <DST,GATEWAY,NETMASK> + * af=inet len=16 addr=192.0.2.0 hd={10, 02, 00{2}, C0, 00, 02, 00{9}} + * af=inet len=16 addr=192.0.2.254 hd={10, 02, 00{2}, C0, 00, 02, FE, 00{8}} + * af=inet len=16 addr=255.255.255.128 hd={10, 02, FF{5}, 80, 00{8}} + */ + verify_route_message(rtm, RTM_DELETE, (struct sockaddr *)&net4, + (struct sockaddr *)&mask4, (struct sockaddr *)&gw4); + + verify_route_message_extra(rtm, c->ifindex, RTF_DONE | RTF_GATEWAY | RTF_STATIC); +} + +ATF_TC_CLEANUP(rtm_del_v4_prefix_nogw_success, tc) +{ + CLEANUP_AFTER_TEST; +} + +RTM_DECLARE_ROOT_TEST(rtm_change_v4_gw_success, + "Tests IPv4 gateway change"); + +ATF_TC_BODY(rtm_change_v4_gw_success, tc) +{ + DECLARE_TEST_VARS; + struct rtsock_config_options co; + + bzero(&co, sizeof(co)); + co.num_interfaces = 2; + + c = config_setup(tc, &co); + jump_vnet(c, tc); + + ret = iface_turn_up(c->ifnames[0]); + ATF_REQUIRE_MSG(ret == 0, "Unable to turn up %s", c->ifnames[0]); + ret = iface_turn_up(c->ifnames[1]); + ATF_REQUIRE_MSG(ret == 0, "Unable to turn up %s", c->ifnames[1]); + + ret = iface_setup_addr(c->ifnames[0], c->addr4_str, c->plen4); + ATF_REQUIRE_MSG(ret == 0, "ifconfig failed"); + + /* Use 198.51.100.0/24 "TEST-NET-2" for the second interface */ + ret = iface_setup_addr(c->ifnames[1], "198.51.100.1", 24); + ATF_REQUIRE_MSG(ret == 0, "ifconfig failed"); + + c->rtsock_fd = rtsock_setup_socket(); + + /* Create IPv4 subnetwork with smaller prefix */ + struct sockaddr_in mask4; + struct sockaddr_in net4; + struct sockaddr_in gw4; + prepare_v4_network(c, &net4, &mask4, &gw4); + + prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net4, + (struct sockaddr *)&mask4, (struct sockaddr *)&gw4); + + rtsock_send_rtm(c->rtsock_fd, rtm); + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + verify_route_message(rtm, RTM_ADD, (struct sockaddr *)&net4, + (struct sockaddr *)&mask4, (struct sockaddr *)&gw4); + + /* Change gateway to the one on desiding on the other interface */ + inet_pton(AF_INET, "198.51.100.2", &gw4.sin_addr.s_addr); + prepare_route_message(rtm, RTM_CHANGE, (struct sockaddr *)&net4, + (struct sockaddr *)&mask4, (struct sockaddr *)&gw4); + rtsock_send_rtm(c->rtsock_fd, rtm); + + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + verify_route_message(rtm, RTM_CHANGE, (struct sockaddr *)&net4, + (struct sockaddr *)&mask4, (struct sockaddr *)&gw4); + + verify_route_message_extra(rtm, if_nametoindex(c->ifnames[1]), + RTF_UP | RTF_DONE | RTF_GATEWAY | RTF_STATIC); + + /* Verify the change has actually taken place */ + prepare_route_message(rtm, RTM_GET, (struct sockaddr *)&net4, + (struct sockaddr *)&mask4, NULL); + + rtsock_send_rtm(c->rtsock_fd, rtm); + + /* + * RTM_GET: len 200, pid: 3894, seq 44, errno 0, flags: <UP,GATEWAY,DONE,STATIC> + * sockaddrs: 0x7 <DST,GATEWAY,NETMASK> + * af=inet len=16 addr=192.0.2.0 hd={x10, x02, x00{2}, xC0, x00, x02, x00{9}} + * af=inet len=16 addr=198.51.100.2 hd={x10, x02, x00{2}, xC6, x33, x64, x02, x00{8}} + * af=inet len=16 addr=255.255.255.128 hd={x10, x02, xFF, xFF, xFF, xFF, xFF, x80, x00{8}} + */ + + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + verify_route_message_extra(rtm, if_nametoindex(c->ifnames[1]), + RTF_UP | RTF_DONE | RTF_GATEWAY | RTF_STATIC); + +} + +RTM_DECLARE_ROOT_TEST(rtm_change_v4_mtu_success, + "Tests IPv4 path mtu change"); + +ATF_TC_BODY(rtm_change_v4_mtu_success, tc) +{ + DECLARE_TEST_VARS; + + unsigned long test_mtu = 1442; + + c = presetup_ipv4(tc); + + /* Create IPv4 subnetwork with smaller prefix */ + struct sockaddr_in mask4; + struct sockaddr_in net4; + struct sockaddr_in gw4; + prepare_v4_network(c, &net4, &mask4, &gw4); + + prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net4, + (struct sockaddr *)&mask4, (struct sockaddr *)&gw4); + + rtsock_send_rtm(c->rtsock_fd, rtm); + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + /* Change MTU */ + prepare_route_message(rtm, RTM_CHANGE, (struct sockaddr *)&net4, + (struct sockaddr *)&mask4, NULL); + rtm->rtm_inits |= RTV_MTU; + rtm->rtm_rmx.rmx_mtu = test_mtu; + + rtsock_send_rtm(c->rtsock_fd, rtm); + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + verify_route_message(rtm, RTM_CHANGE, (struct sockaddr *)&net4, + (struct sockaddr *)&mask4, NULL); + + RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_rmx.rmx_mtu == test_mtu, + "expected mtu: %lu, got %lu", test_mtu, rtm->rtm_rmx.rmx_mtu); + + /* Verify the change has actually taken place */ + prepare_route_message(rtm, RTM_GET, (struct sockaddr *)&net4, + (struct sockaddr *)&mask4, NULL); + + rtsock_send_rtm(c->rtsock_fd, rtm); + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_rmx.rmx_mtu == test_mtu, + "expected mtu: %lu, got %lu", test_mtu, rtm->rtm_rmx.rmx_mtu); +} + +RTM_DECLARE_ROOT_TEST(rtm_change_v4_flags_success, + "Tests IPv4 path flags change"); + +ATF_TC_BODY(rtm_change_v4_flags_success, tc) +{ + DECLARE_TEST_VARS; + + uint32_t test_flags = RTF_PROTO1 | RTF_PROTO2 | RTF_PROTO3 | RTF_STATIC; + uint32_t desired_flags; + + c = presetup_ipv4(tc); + + /* Create IPv4 subnetwork with smaller prefix */ + struct sockaddr_in mask4; + struct sockaddr_in net4; + struct sockaddr_in gw4; + prepare_v4_network(c, &net4, &mask4, &gw4); + + prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net4, + (struct sockaddr *)&mask4, (struct sockaddr *)&gw4); + + /* Set test flags during route addition */ + desired_flags = RTF_UP | RTF_DONE | RTF_GATEWAY | test_flags; + rtm->rtm_flags |= test_flags; + rtsock_send_rtm(c->rtsock_fd, rtm); + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + /* Change flags */ + prepare_route_message(rtm, RTM_CHANGE, (struct sockaddr *)&net4, + (struct sockaddr *)&mask4, NULL); + rtm->rtm_flags &= ~test_flags; + desired_flags &= ~test_flags; + + rtsock_send_rtm(c->rtsock_fd, rtm); + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + /* Verify updated flags */ + verify_route_message_extra(rtm, c->ifindex, desired_flags | RTF_DONE); + + /* Verify the change has actually taken place */ + prepare_route_message(rtm, RTM_GET, (struct sockaddr *)&net4, + (struct sockaddr *)&mask4, NULL); + + rtsock_send_rtm(c->rtsock_fd, rtm); + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + verify_route_message_extra(rtm, c->ifindex, desired_flags | RTF_DONE); +} + + +ATF_TC_WITH_CLEANUP(rtm_add_v6_gu_gw_gu_direct_success); +ATF_TC_HEAD(rtm_add_v6_gu_gw_gu_direct_success, tc) +{ + DESCRIBE_ROOT_TEST("Tests IPv6 global unicast prefix addition with directly-reachable GU GW"); +} + +ATF_TC_BODY(rtm_add_v6_gu_gw_gu_direct_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv6(tc); + + /* Create IPv6 subnetwork with smaller prefix */ + struct sockaddr_in6 mask6; + struct sockaddr_in6 net6; + struct sockaddr_in6 gw6; + prepare_v6_network(c, &net6, &mask6, &gw6); + + prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net6, + (struct sockaddr *)&mask6, (struct sockaddr *)&gw6); + + rtsock_send_rtm(c->rtsock_fd, rtm); + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + /* + * RTM_ADD: Add Route: len 200, pid: 46068, seq 42, errno 0, flags:<GATEWAY,DONE,STATIC> + * locks: inits: + * sockaddrs: <DST,GATEWAY,NETMASK> + * 192.0.2.0 192.0.2.254 255.255.255.128 + */ + + verify_route_message(rtm, RTM_ADD, (struct sockaddr *)&net6, + (struct sockaddr *)&mask6, (struct sockaddr *)&gw6); + + verify_route_message_extra(rtm, c->ifindex, + RTF_UP | RTF_DONE | RTF_GATEWAY | RTF_STATIC); +} + +ATF_TC_CLEANUP(rtm_add_v6_gu_gw_gu_direct_success, tc) +{ + CLEANUP_AFTER_TEST; +} + +ATF_TC_WITH_CLEANUP(rtm_del_v6_gu_prefix_nogw_success); +ATF_TC_HEAD(rtm_del_v6_gu_prefix_nogw_success, tc) +{ + + DESCRIBE_ROOT_TEST("Tests IPv6 global unicast prefix removal without specifying gateway"); +} + +ATF_TC_BODY(rtm_del_v6_gu_prefix_nogw_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv6(tc); + + /* Create IPv6 subnetwork with smaller prefix */ + struct sockaddr_in6 mask6; + struct sockaddr_in6 net6; + struct sockaddr_in6 gw6; + prepare_v6_network(c, &net6, &mask6, &gw6); + + prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net6, + (struct sockaddr *)&mask6, (struct sockaddr *)&gw6); + + rtsock_send_rtm(c->rtsock_fd, rtm); + + /* Route has been added successfully, try to delete it */ + prepare_route_message(rtm, RTM_DELETE, (struct sockaddr *)&net6, + (struct sockaddr *)&mask6, NULL); + + rtsock_send_rtm(c->rtsock_fd, rtm); + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + /* + * RTM_DELETE: Delete Route: len 200, pid: 46417, seq 43, errno 0, flags: <GATEWAY,DONE,STATIC> + * sockaddrs: 0x7 <DST,GATEWAY,NETMASK> + * af=inet len=16 addr=192.0.2.0 hd={10, 02, 00{2}, C0, 00, 02, 00{9}} + * af=inet len=16 addr=192.0.2.254 hd={10, 02, 00{2}, C0, 00, 02, FE, 00{8}} + * af=inet len=16 addr=255.255.255.128 hd={10, 02, FF{5}, 80, 00{8}} + */ + + verify_route_message(rtm, RTM_DELETE, (struct sockaddr *)&net6, + (struct sockaddr *)&mask6, (struct sockaddr *)&gw6); + verify_route_message_extra(rtm, c->ifindex, RTF_DONE | RTF_GATEWAY | RTF_STATIC); +} + +ATF_TC_CLEANUP(rtm_del_v6_gu_prefix_nogw_success, tc) +{ + CLEANUP_AFTER_TEST; +} + +RTM_DECLARE_ROOT_TEST(rtm_change_v6_gw_success, + "Tests IPv6 gateway change"); + +ATF_TC_BODY(rtm_change_v6_gw_success, tc) +{ + DECLARE_TEST_VARS; + struct rtsock_config_options co; + + bzero(&co, sizeof(co)); + co.num_interfaces = 2; + + c = config_setup(tc, &co); + jump_vnet(c, tc); + + ret = iface_turn_up(c->ifnames[0]); + ATF_REQUIRE_MSG(ret == 0, "Unable to turn up %s", c->ifnames[0]); + ret = iface_turn_up(c->ifnames[1]); + ATF_REQUIRE_MSG(ret == 0, "Unable to turn up %s", c->ifnames[1]); + + ret = iface_enable_ipv6(c->ifnames[0]); + ATF_REQUIRE_MSG(ret == 0, "Unable to enable IPv6 on %s", c->ifnames[0]); + ret = iface_enable_ipv6(c->ifnames[1]); + ATF_REQUIRE_MSG(ret == 0, "Unable to enable IPv6 on %s", c->ifnames[1]); + + ret = iface_setup_addr(c->ifnames[0], c->addr6_str, c->plen6); + ATF_REQUIRE_MSG(ret == 0, "ifconfig failed"); + + ret = iface_setup_addr(c->ifnames[1], "2001:DB8:4242::1", 64); + ATF_REQUIRE_MSG(ret == 0, "ifconfig failed"); + + c->rtsock_fd = rtsock_setup_socket(); + + /* Create IPv6 subnetwork with smaller prefix */ + struct sockaddr_in6 mask6; + struct sockaddr_in6 net6; + struct sockaddr_in6 gw6; + prepare_v6_network(c, &net6, &mask6, &gw6); + + prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net6, + (struct sockaddr *)&mask6, (struct sockaddr *)&gw6); + + rtsock_send_rtm(c->rtsock_fd, rtm); + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + verify_route_message(rtm, RTM_ADD, (struct sockaddr *)&net6, + (struct sockaddr *)&mask6, (struct sockaddr *)&gw6); + + /* Change gateway to the one on residing on the other interface */ + inet_pton(AF_INET6, "2001:DB8:4242::4242", &gw6.sin6_addr); + prepare_route_message(rtm, RTM_CHANGE, (struct sockaddr *)&net6, + (struct sockaddr *)&mask6, (struct sockaddr *)&gw6); + rtsock_send_rtm(c->rtsock_fd, rtm); + + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + verify_route_message(rtm, RTM_CHANGE, (struct sockaddr *)&net6, + (struct sockaddr *)&mask6, (struct sockaddr *)&gw6); + + verify_route_message_extra(rtm, if_nametoindex(c->ifnames[1]), + RTF_UP | RTF_DONE | RTF_GATEWAY | RTF_STATIC); + + /* Verify the change has actually taken place */ + prepare_route_message(rtm, RTM_GET, (struct sockaddr *)&net6, + (struct sockaddr *)&mask6, NULL); + + rtsock_send_rtm(c->rtsock_fd, rtm); + + /* + * RTM_GET: len 248, pid: 2268, seq 44, errno 0, flags: <UP,GATEWAY,DONE,STATIC> + * sockaddrs: 0x7 <DST,GATEWAY,NETMASK> + * af=inet6 len=28 addr=2001:db8:: hd={x1C, x1C, x00{6}, x20, x01, x0D, xB8, x00{16}} + * af=inet6 len=28 addr=2001:db8:4242::4242 hd={x1C, x1C, x00{6}, x20, x01, x0D, xB8, x42, x42, x00{8}, x42, x42, x00{4}} + * af=inet6 len=28 addr=ffff:ffff:8000:: hd={x1C, x1C, xFF, xFF, xFF, xFF, xFF, xFF, xFF, xFF, xFF, xFF, x80, x00{15}} + */ + + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + verify_route_message_extra(rtm, if_nametoindex(c->ifnames[1]), + RTF_UP | RTF_DONE | RTF_GATEWAY | RTF_STATIC); +} + +RTM_DECLARE_ROOT_TEST(rtm_change_v6_mtu_success, + "Tests IPv6 path mtu change"); + +ATF_TC_BODY(rtm_change_v6_mtu_success, tc) +{ + DECLARE_TEST_VARS; + + unsigned long test_mtu = 1442; + + c = presetup_ipv6(tc); + + /* Create IPv6 subnetwork with smaller prefix */ + struct sockaddr_in6 mask6; + struct sockaddr_in6 net6; + struct sockaddr_in6 gw6; + prepare_v6_network(c, &net6, &mask6, &gw6); + + prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net6, + (struct sockaddr *)&mask6, (struct sockaddr *)&gw6); + + /* Send route add */ + rtsock_send_rtm(c->rtsock_fd, rtm); + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + /* Change MTU */ + prepare_route_message(rtm, RTM_CHANGE, (struct sockaddr *)&net6, + (struct sockaddr *)&mask6, NULL); + rtm->rtm_inits |= RTV_MTU; + rtm->rtm_rmx.rmx_mtu = test_mtu; + + rtsock_send_rtm(c->rtsock_fd, rtm); + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + verify_route_message(rtm, RTM_CHANGE, (struct sockaddr *)&net6, + (struct sockaddr *)&mask6, NULL); + + RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_rmx.rmx_mtu == test_mtu, + "expected mtu: %lu, got %lu", test_mtu, rtm->rtm_rmx.rmx_mtu); + + /* Verify the change has actually taken place */ + prepare_route_message(rtm, RTM_GET, (struct sockaddr *)&net6, + (struct sockaddr *)&mask6, NULL); + + rtsock_send_rtm(c->rtsock_fd, rtm); + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_rmx.rmx_mtu == test_mtu, + "expected mtu: %lu, got %lu", test_mtu, rtm->rtm_rmx.rmx_mtu); +} + +RTM_DECLARE_ROOT_TEST(rtm_change_v6_flags_success, + "Tests IPv6 path flags change"); + +ATF_TC_BODY(rtm_change_v6_flags_success, tc) +{ + DECLARE_TEST_VARS; + + uint32_t test_flags = RTF_PROTO1 | RTF_PROTO2 | RTF_PROTO3 | RTF_STATIC; + uint32_t desired_flags; + + c = presetup_ipv6(tc); + + /* Create IPv6 subnetwork with smaller prefix */ + struct sockaddr_in6 mask6; + struct sockaddr_in6 net6; + struct sockaddr_in6 gw6; + prepare_v6_network(c, &net6, &mask6, &gw6); + + prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net6, + (struct sockaddr *)&mask6, (struct sockaddr *)&gw6); + + /* Set test flags during route addition */ + desired_flags = RTF_UP | RTF_DONE | RTF_GATEWAY | test_flags; + rtm->rtm_flags |= test_flags; + rtsock_send_rtm(c->rtsock_fd, rtm); + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + /* Change flags */ + prepare_route_message(rtm, RTM_CHANGE, (struct sockaddr *)&net6, + (struct sockaddr *)&mask6, NULL); + rtm->rtm_flags &= ~test_flags; + desired_flags &= ~test_flags; + + rtsock_send_rtm(c->rtsock_fd, rtm); + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + /* Verify updated flags */ + verify_route_message_extra(rtm, c->ifindex, desired_flags | RTF_DONE); + + /* Verify the change has actually taken place */ + prepare_route_message(rtm, RTM_GET, (struct sockaddr *)&net6, + (struct sockaddr *)&mask6, NULL); + + rtsock_send_rtm(c->rtsock_fd, rtm); + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + verify_route_message_extra(rtm, c->ifindex, desired_flags | RTF_DONE); +} + +ATF_TC_WITH_CLEANUP(rtm_add_v4_temporal1_success); +ATF_TC_HEAD(rtm_add_v4_temporal1_success, tc) +{ + DESCRIBE_ROOT_TEST("Tests IPv4 route expiration with expire time set"); +} + +ATF_TC_BODY(rtm_add_v4_temporal1_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv4(tc); + + /* Create IPv4 subnetwork with smaller prefix */ + struct sockaddr_in mask4; + struct sockaddr_in net4; + struct sockaddr_in gw4; + prepare_v4_network(c, &net4, &mask4, &gw4); + + prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net4, + (struct sockaddr *)&mask4, (struct sockaddr *)&gw4); + + /* Set expire time to now */ + struct timeval tv; + gettimeofday(&tv, NULL); + rtm->rtm_rmx.rmx_expire = tv.tv_sec - 1; + rtm->rtm_inits |= RTV_EXPIRE; + + rtsock_send_rtm(c->rtsock_fd, rtm); + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + ATF_REQUIRE_MSG(rtm != NULL, "unable to get rtsock reply for RTM_ADD"); + RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_inits & RTV_EXPIRE, "RTV_EXPIRE not set"); + + /* The next should be route deletion */ + rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); + + verify_route_message(rtm, RTM_DELETE, (struct sockaddr *)&net4, + (struct sockaddr *)&mask4, (struct sockaddr *)&gw4); + + verify_route_message_extra(rtm, c->ifindex, + RTF_DONE | RTF_GATEWAY | RTF_STATIC); +} + +ATF_TC_CLEANUP(rtm_add_v4_temporal1_success, tc) +{ + CLEANUP_AFTER_TEST; +} + +ATF_TC_WITH_CLEANUP(rtm_add_v6_temporal1_success); +ATF_TC_HEAD(rtm_add_v6_temporal1_success, tc) +{ + DESCRIBE_ROOT_TEST("Tests IPv6 global unicast prefix addition with directly-reachable GU GW"); +} + +ATF_TC_BODY(rtm_add_v6_temporal1_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv6(tc); + + /* Create IPv6 subnetwork with smaller prefix */ + struct sockaddr_in6 mask6; + struct sockaddr_in6 net6; + struct sockaddr_in6 gw6; + prepare_v6_network(c, &net6, &mask6, &gw6); + + prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net6, + (struct sockaddr *)&mask6, (struct sockaddr *)&gw6); + + /* Set expire time to now */ + struct timeval tv; + gettimeofday(&tv, NULL); + rtm->rtm_rmx.rmx_expire = tv.tv_sec - 1; + rtm->rtm_inits |= RTV_EXPIRE; + + rtsock_send_rtm(c->rtsock_fd, rtm); + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + ATF_REQUIRE_MSG(rtm != NULL, "unable to get rtsock reply for RTM_ADD"); + RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_inits & RTV_EXPIRE, "RTV_EXPIRE not set"); + + /* The next should be route deletion */ + rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); + + verify_route_message(rtm, RTM_DELETE, (struct sockaddr *)&net6, + (struct sockaddr *)&mask6, (struct sockaddr *)&gw6); + + verify_route_message_extra(rtm, c->ifindex, + RTF_DONE | RTF_GATEWAY | RTF_STATIC); +} + +ATF_TC_CLEANUP(rtm_add_v6_temporal1_success, tc) +{ + CLEANUP_AFTER_TEST; +} + +/* Interface address messages tests */ + +RTM_DECLARE_ROOT_TEST(rtm_add_v6_gu_ifa_hostroute_success, + "Tests validness for /128 host route announce after ifaddr assignment"); + +ATF_TC_BODY(rtm_add_v6_gu_ifa_hostroute_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv6_iface(tc); + + c->rtsock_fd = rtsock_setup_socket(); + + ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6); + + /* + * There will be multiple. + * RTM_ADD without llinfo. + */ + + while (true) { + rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); + if ((rtm->rtm_type == RTM_ADD) && ((rtm->rtm_flags & RTF_LLINFO) == 0)) + break; + } + /* This should be a message for the host route */ + + verify_route_message(rtm, RTM_ADD, (struct sockaddr *)&c->addr6, NULL, NULL); + rtsock_validate_pid_kernel(rtm); + /* No netmask should be set */ + RTSOCK_ATF_REQUIRE_MSG(rtm, rtsock_find_rtm_sa(rtm, RTA_NETMASK) == NULL, "netmask is set"); + + /* gateway should be link sdl with ifindex of an address interface */ + verify_link_gateway(rtm, c->ifindex); + + int expected_rt_flags = RTF_UP | RTF_HOST | RTF_DONE | RTF_STATIC | RTF_PINNED; + verify_route_message_extra(rtm, if_nametoindex("lo0"), expected_rt_flags); +} + +RTM_DECLARE_ROOT_TEST(rtm_add_v6_gu_ifa_prefixroute_success, + "Tests validness for the prefix route announce after ifaddr assignment"); + +ATF_TC_BODY(rtm_add_v6_gu_ifa_prefixroute_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv6_iface(tc); + + c->rtsock_fd = rtsock_setup_socket(); + + ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6); + + /* + * Multiple RTM_ADD messages will be generated: + * 1) lladdr mapping (RTF_LLDATA) + * 2) host route (one w/o netmask) + * 3) prefix route + */ + + while (true) { + rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); + /* Find RTM_ADD with netmask - this should skip both host route and LLADDR */ + if ((rtm->rtm_type == RTM_ADD) && (rtsock_find_rtm_sa(rtm, RTA_NETMASK))) + break; + } + + /* This should be a message for the prefix route */ + verify_route_message(rtm, RTM_ADD, (struct sockaddr *)&c->net6, + (struct sockaddr *)&c->mask6, NULL); + + /* gateway should be link sdl with ifindex of an address interface */ + verify_link_gateway(rtm, c->ifindex); + + int expected_rt_flags = RTF_UP | RTF_DONE | RTF_PINNED; + verify_route_message_extra(rtm, c->ifindex, expected_rt_flags); +} + +RTM_DECLARE_ROOT_TEST(rtm_add_v6_gu_ifa_ordered_success, + "Tests ordering of the messages for IPv6 global unicast ifaddr assignment"); + +ATF_TC_BODY(rtm_add_v6_gu_ifa_ordered_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv6_iface(tc); + + c->rtsock_fd = rtsock_setup_socket(); + + ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6); + + int count = 0, tries = 0; + + enum msgtype { + MSG_IFADDR, + MSG_HOSTROUTE, + MSG_PREFIXROUTE, + MSG_MAX, + }; + + int msg_array[MSG_MAX]; + + bzero(msg_array, sizeof(msg_array)); + + while (count < 3 && tries < 20) { + rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); + tries++; + /* Classify */ + if (rtm->rtm_type == RTM_NEWADDR) { + RLOG("MSG_IFADDR: %d", count); + msg_array[MSG_IFADDR] = count++; + continue; + } + + /* Find RTM_ADD with netmask - this should skip both host route and LLADDR */ + if ((rtm->rtm_type == RTM_ADD) && (rtsock_find_rtm_sa(rtm, RTA_NETMASK))) { + RLOG("MSG_PREFIXROUTE: %d", count); + msg_array[MSG_PREFIXROUTE] = count++; + continue; + } + + if ((rtm->rtm_type == RTM_ADD) && ((rtm->rtm_flags & RTF_LLDATA) == 0)) { + RLOG("MSG_HOSTROUTE: %d", count); + msg_array[MSG_HOSTROUTE] = count++; + continue; + } + + RLOG("skipping msg type %s, try: %d", rtsock_print_cmdtype(rtm->rtm_type), + tries); + } + + /* TODO: verify multicast */ + ATF_REQUIRE_MSG(count == 3, "Received only %d/3 messages", count); + ATF_REQUIRE_MSG(msg_array[MSG_IFADDR] == 0, "ifaddr message is not the first"); +} + +RTM_DECLARE_ROOT_TEST(rtm_del_v6_gu_ifa_hostroute_success, + "Tests validness for /128 host route removal after ifaddr removal"); + +ATF_TC_BODY(rtm_del_v6_gu_ifa_hostroute_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv6_iface(tc); + + ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6); + + c->rtsock_fd = rtsock_setup_socket(); + + ret = iface_delete_addr(c->ifname, c->addr6_str); + + while (true) { + rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); + if ((rtm->rtm_type == RTM_DELETE) && + ((rtm->rtm_flags & RTF_LLINFO) == 0) && + rtsock_find_rtm_sa(rtm, RTA_NETMASK) == NULL) + break; + } + /* This should be a message for the host route */ + + verify_route_message(rtm, RTM_DELETE, (struct sockaddr *)&c->addr6, NULL, NULL); + rtsock_validate_pid_kernel(rtm); + /* No netmask should be set */ + RTSOCK_ATF_REQUIRE_MSG(rtm, rtsock_find_rtm_sa(rtm, RTA_NETMASK) == NULL, "netmask is set"); + + /* gateway should be link sdl with ifindex of an address interface */ + verify_link_gateway(rtm, c->ifindex); + + /* XXX: consider passing ifindex in rtm_index as done in RTM_ADD. */ + int expected_rt_flags = RTF_HOST | RTF_DONE | RTF_STATIC | RTF_PINNED; + RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_flags == expected_rt_flags, + "expected rtm flags: 0x%X, got 0x%X", expected_rt_flags, rtm->rtm_flags); +} + +RTM_DECLARE_ROOT_TEST(rtm_del_v6_gu_ifa_prefixroute_success, + "Tests validness for the prefix route removal after ifaddr assignment"); + +ATF_TC_BODY(rtm_del_v6_gu_ifa_prefixroute_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv6_iface(tc); + + ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6); + + c->rtsock_fd = rtsock_setup_socket(); + + ret = iface_delete_addr(c->ifname, c->addr6_str); + + while (true) { + rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); + /* Find RTM_DELETE with netmask - this should skip both host route and LLADDR */ + if ((rtm->rtm_type == RTM_DELETE) && (rtsock_find_rtm_sa(rtm, RTA_NETMASK))) + break; + } + + /* This should be a message for the prefix route */ + verify_route_message(rtm, RTM_DELETE, (struct sockaddr *)&c->net6, + (struct sockaddr *)&c->mask6, NULL); + + /* gateway should be link sdl with ifindex of an address interface */ + verify_link_gateway(rtm, c->ifindex); + + int expected_rt_flags = RTF_DONE | RTF_PINNED; + verify_route_message_extra(rtm, c->ifindex, expected_rt_flags); +} + +RTM_DECLARE_ROOT_TEST(rtm_add_v4_gu_ifa_prefixroute_success, + "Tests validness for the prefix route announce after ifaddr assignment"); + +ATF_TC_BODY(rtm_add_v4_gu_ifa_prefixroute_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv4_iface(tc); + + c->rtsock_fd = rtsock_setup_socket(); + + ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6); + + /* + * Multiple RTM_ADD messages will be generated: + * 1) lladdr mapping (RTF_LLDATA) + * 3) prefix route + */ + + while (true) { + rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); + /* Find RTM_ADD with netmask - this should skip both host route and LLADDR */ + if ((rtm->rtm_type == RTM_ADD) && (rtsock_find_rtm_sa(rtm, RTA_NETMASK))) + break; + } + + /* This should be a message for the prefix route */ + verify_route_message(rtm, RTM_ADD, (struct sockaddr *)&c->net4, + (struct sockaddr *)&c->mask4, NULL); + + /* gateway should be link sdl with ifindex of an address interface */ + verify_link_gateway(rtm, c->ifindex); + + int expected_rt_flags = RTF_UP | RTF_DONE | RTF_PINNED; + verify_route_message_extra(rtm, c->ifindex, expected_rt_flags); +} + +RTM_DECLARE_ROOT_TEST(rtm_add_v4_gu_ifa_ordered_success, + "Tests ordering of the messages for IPv4 unicast ifaddr assignment"); + +ATF_TC_BODY(rtm_add_v4_gu_ifa_ordered_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv4_iface(tc); + + c->rtsock_fd = rtsock_setup_socket(); + + ret = iface_setup_addr(c->ifname, c->addr4_str, c->plen4); + + int count = 0, tries = 0; + + enum msgtype { + MSG_IFADDR, + MSG_PREFIXROUTE, + MSG_MAX, + }; + + int msg_array[MSG_MAX]; + + bzero(msg_array, sizeof(msg_array)); + + while (count < 2 && tries < 20) { + rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); + tries++; + /* Classify */ + if (rtm->rtm_type == RTM_NEWADDR) { + RLOG("MSG_IFADDR: %d", count); + msg_array[MSG_IFADDR] = count++; + continue; + } + + /* Find RTM_ADD with netmask - this should skip both host route and LLADDR */ + if ((rtm->rtm_type == RTM_ADD) && (rtsock_find_rtm_sa(rtm, RTA_NETMASK))) { + RLOG("MSG_PREFIXROUTE: %d", count); + msg_array[MSG_PREFIXROUTE] = count++; + continue; + } + + RLOG("skipping msg type %s, try: %d", rtsock_print_cmdtype(rtm->rtm_type), + tries); + } + + /* TODO: verify multicast */ + ATF_REQUIRE_MSG(count == 2, "Received only %d/2 messages", count); + ATF_REQUIRE_MSG(msg_array[MSG_IFADDR] == 0, "ifaddr message is not the first"); +} + +RTM_DECLARE_ROOT_TEST(rtm_del_v4_gu_ifa_prefixroute_success, + "Tests validness for the prefix route removal after ifaddr assignment"); + +ATF_TC_BODY(rtm_del_v4_gu_ifa_prefixroute_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv4_iface(tc); + + + ret = iface_setup_addr(c->ifname, c->addr4_str, c->plen4); + + c->rtsock_fd = rtsock_setup_socket(); + + ret = iface_delete_addr(c->ifname, c->addr4_str); + + while (true) { + rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); + /* Find RTM_ADD with netmask - this should skip both host route and LLADDR */ + if ((rtm->rtm_type == RTM_DELETE) && (rtsock_find_rtm_sa(rtm, RTA_NETMASK))) + break; + } + + /* This should be a message for the prefix route */ + verify_route_message(rtm, RTM_DELETE, (struct sockaddr *)&c->net4, + (struct sockaddr *)&c->mask4, NULL); + + /* gateway should be link sdl with ifindex of an address interface */ + verify_link_gateway(rtm, c->ifindex); + + int expected_rt_flags = RTF_DONE | RTF_PINNED; + verify_route_message_extra(rtm, c->ifindex, expected_rt_flags); +} + + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, rtm_get_v4_exact_success); + ATF_TP_ADD_TC(tp, rtm_get_v4_lpm_success); + ATF_TP_ADD_TC(tp, rtm_get_v4_hostbits_success); + ATF_TP_ADD_TC(tp, rtm_get_v4_empty_dst_failure); + ATF_TP_ADD_TC(tp, rtm_add_v4_no_rtf_host_success); + ATF_TP_ADD_TC(tp, rtm_add_v4_gw_direct_success); + ATF_TP_ADD_TC(tp, rtm_del_v4_prefix_nogw_success); + ATF_TP_ADD_TC(tp, rtm_add_v6_gu_gw_gu_direct_success); + ATF_TP_ADD_TC(tp, rtm_del_v6_gu_prefix_nogw_success); + ATF_TP_ADD_TC(tp, rtm_change_v4_gw_success); + ATF_TP_ADD_TC(tp, rtm_change_v4_mtu_success); + ATF_TP_ADD_TC(tp, rtm_change_v4_flags_success); + ATF_TP_ADD_TC(tp, rtm_change_v6_gw_success); + ATF_TP_ADD_TC(tp, rtm_change_v6_mtu_success); + ATF_TP_ADD_TC(tp, rtm_change_v6_flags_success); + /* ifaddr tests */ + ATF_TP_ADD_TC(tp, rtm_add_v6_gu_ifa_hostroute_success); + ATF_TP_ADD_TC(tp, rtm_add_v6_gu_ifa_prefixroute_success); + ATF_TP_ADD_TC(tp, rtm_add_v6_gu_ifa_ordered_success); + ATF_TP_ADD_TC(tp, rtm_del_v6_gu_ifa_hostroute_success); + ATF_TP_ADD_TC(tp, rtm_del_v6_gu_ifa_prefixroute_success); + ATF_TP_ADD_TC(tp, rtm_add_v4_gu_ifa_ordered_success); + ATF_TP_ADD_TC(tp, rtm_del_v4_gu_ifa_prefixroute_success); + /* temporal routes */ + ATF_TP_ADD_TC(tp, rtm_add_v4_temporal1_success); + ATF_TP_ADD_TC(tp, rtm_add_v6_temporal1_success); + + return (atf_no_error()); +} + diff --git a/tests/sys/net/routing/test_rtsock_lladdr.c b/tests/sys/net/routing/test_rtsock_lladdr.c new file mode 100644 index 000000000000..072a4e31f7ce --- /dev/null +++ b/tests/sys/net/routing/test_rtsock_lladdr.c @@ -0,0 +1,416 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 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. + */ + +#include "rtsock_common.h" +#include "rtsock_config.h" + +static void +jump_vnet(struct rtsock_test_config *c, const atf_tc_t *tc) +{ + char vnet_name[512]; + + snprintf(vnet_name, sizeof(vnet_name), "vt-%s", atf_tc_get_ident(tc)); + RLOG("jumping to %s", vnet_name); + + vnet_switch_one(vnet_name, c->ifname); + + /* Update ifindex cache */ + c->ifindex = if_nametoindex(c->ifname); +} + +static inline struct rtsock_test_config * +presetup_ipv6(const atf_tc_t *tc) +{ + struct rtsock_test_config *c; + int ret; + + c = config_setup(tc, NULL); + + jump_vnet(c, tc); + + ret = iface_turn_up(c->ifname); + ATF_REQUIRE_MSG(ret == 0, "Unable to turn up %s", c->ifname); + ret = iface_enable_ipv6(c->ifname); + ATF_REQUIRE_MSG(ret == 0, "Unable to enable IPv6 on %s", c->ifname); + + c->rtsock_fd = rtsock_setup_socket(); + + return (c); +} + +static inline struct rtsock_test_config * +presetup_ipv4(const atf_tc_t *tc) +{ + struct rtsock_test_config *c; + int ret; + + c = config_setup(tc, NULL); + + jump_vnet(c, tc); + + /* assumes ifconfig doing IFF_UP */ + ret = iface_setup_addr(c->ifname, c->addr4_str, c->plen4); + ATF_REQUIRE_MSG(ret == 0, "ifconfig failed"); + + c->rtsock_fd = rtsock_setup_socket(); + + return (c); +} + +static void +prepare_route_message(struct rt_msghdr *rtm, int cmd, struct sockaddr *dst, + struct sockaddr *gw) +{ + + rtsock_prepare_route_message(rtm, cmd, dst, NULL, gw); + + rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA); +} + +/* TESTS */ +#define DECLARE_TEST_VARS \ + char buffer[2048], msg[512]; \ + ssize_t len; \ + int ret; \ + struct rtsock_test_config *c; \ + struct rt_msghdr *rtm = (struct rt_msghdr *)buffer; \ + struct sockaddr *sa; \ + \ + +#define DECLARE_CLEANUP_VARS \ + struct rtsock_test_config *c = config_setup(tc); \ + \ + +#define DESCRIBE_ROOT_TEST(_msg) config_describe_root_test(tc, _msg) +#define CLEANUP_AFTER_TEST config_generic_cleanup(tc) + +#define RTM_DECLARE_ROOT_TEST(_name, _descr) \ +ATF_TC_WITH_CLEANUP(_name); \ +ATF_TC_HEAD(_name, tc) \ +{ \ + DESCRIBE_ROOT_TEST(_descr); \ +} \ +ATF_TC_CLEANUP(_name, tc) \ +{ \ + CLEANUP_AFTER_TEST; \ +} + +RTM_DECLARE_ROOT_TEST(rtm_add_v6_ll_lle_success, "Tests addition of link-local IPv6 ND entry"); +ATF_TC_BODY(rtm_add_v6_ll_lle_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv6(tc); + + char str_buf[128]; + struct sockaddr_in6 sin6; + /* Interface here is optional. XXX: verify kernel side. */ + char *v6addr = "fe80::4242:4242"; + snprintf(str_buf, sizeof(str_buf), "%s%%%s", v6addr, c->ifname); + sa_convert_str_to_sa(str_buf, (struct sockaddr *)&sin6); + + struct sockaddr_dl ether; + snprintf(str_buf, sizeof(str_buf), "%s%%%s", c->remote_lladdr, c->ifname); + sa_convert_str_to_sa(str_buf, (struct sockaddr *)ðer); + + prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&sin6, (struct sockaddr *)ðer); + rtsock_send_rtm(c->rtsock_fd, rtm); + + /* + * Got message of size 240 on 2019-12-17 15:06:51 + * RTM_ADD: Add Route: len 240, pid: 0, seq 0, errno 0, flags: <UP,HOST,DONE,LLINFO> + * sockaddrs: 0x3 <DST,GATEWAY> + * af=inet6 len=28 addr=fe80::4242:4242 scope_id=3 if_name=tap4242 + * af=link len=54 sdl_index=3 if_name=tap4242 addr=52:54:00:14:E3:10 + */ + + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + sa = rtsock_find_rtm_sa(rtm, RTA_DST); + ret = sa_equal_msg(sa, (struct sockaddr *)&sin6, msg, sizeof(msg)); + RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "DST sa diff: %s", msg); + + sa = rtsock_find_rtm_sa(rtm, RTA_GATEWAY); + int sa_flags = SA_F_IGNORE_IFNAME | SA_F_IGNORE_IFTYPE | SA_F_IGNORE_MEMCMP; + ret = sa_equal_msg_flags(sa, (struct sockaddr *)ðer, msg, sizeof(msg), sa_flags); + RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "GATEWAY sa diff: %s", msg); + +#if 0 + /* Disable the check until https://reviews.freebsd.org/D22003 merge */ + /* Some additional checks to verify kernel has filled in interface data */ + struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa; + RTSOCK_ATF_REQUIRE_MSG(rtm, sdl->sdl_type > 0, "sdl_type not set"); +#endif +} + +RTM_DECLARE_ROOT_TEST(rtm_add_v6_gu_lle_success, "Tests addition of global IPv6 ND entry"); +ATF_TC_BODY(rtm_add_v6_gu_lle_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv6(tc); + + char str_buf[128]; + + struct sockaddr_in6 sin6; + sin6 = c->net6; +#define _s6_addr32 __u6_addr.__u6_addr32 + sin6.sin6_addr._s6_addr32[3] = htonl(0x42424242); +#undef _s6_addr32 + + struct sockaddr_dl ether; + snprintf(str_buf, sizeof(str_buf), "%s%%%s", c->remote_lladdr, c->ifname); + sa_convert_str_to_sa(str_buf, (struct sockaddr *)ðer); + + prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&sin6, (struct sockaddr *)ðer); + + rtsock_send_rtm(c->rtsock_fd, rtm); + + /* + * Got message of size 240 on 2019-12-17 14:56:43 + * RTM_ADD: Add Route: len 240, pid: 0, seq 0, errno 0, flags: <UP,HOST,DONE,LLINFO> + * sockaddrs: 0x3 <DST,GATEWAY> + * af=inet6 len=28 addr=2001:db8::4242:4242 + * af=link len=54 sdl_index=3 if_name=tap4242 addr=52:54:00:14:E3:10 + */ + + /* XXX: where is uRPF?! this should fail */ + + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + sa = rtsock_find_rtm_sa(rtm, RTA_DST); + ret = sa_equal_msg(sa, (struct sockaddr *)&sin6, msg, sizeof(msg)); + RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "DST sa diff: %s", msg); + + sa = rtsock_find_rtm_sa(rtm, RTA_GATEWAY); + int sa_flags = SA_F_IGNORE_IFNAME | SA_F_IGNORE_IFTYPE | SA_F_IGNORE_MEMCMP; + ret = sa_equal_msg_flags(sa, (struct sockaddr *)ðer, msg, sizeof(msg), sa_flags); + RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "GATEWAY sa diff: %s", msg); + +#if 0 + /* Disable the check until https://reviews.freebsd.org/D22003 merge */ + /* Some additional checks to verify kernel has filled in interface data */ + struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa; + RTSOCK_ATF_REQUIRE_MSG(rtm, sdl->sdl_type > 0, "sdl_type not set"); +#endif +} + +RTM_DECLARE_ROOT_TEST(rtm_add_v4_gu_lle_success, "Tests addition of IPv4 ARP entry"); +ATF_TC_BODY(rtm_add_v4_gu_lle_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv4(tc); + + char str_buf[128]; + + struct sockaddr_in sin; + sin = c->addr4; + /* Use the next IPv4 address after self */ + sin.sin_addr.s_addr = htonl(ntohl(sin.sin_addr.s_addr) + 1); + + struct sockaddr_dl ether; + snprintf(str_buf, sizeof(str_buf), "%s%%%s", c->remote_lladdr, c->ifname); + sa_convert_str_to_sa(str_buf, (struct sockaddr *)ðer); + + prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&sin, (struct sockaddr *)ðer); + + len = rtsock_send_rtm(c->rtsock_fd, rtm); + + /* + * RTM_ADD: Add Route: len 224, pid: 43131, seq 42, errno 0, flags: <HOST,DONE,LLINFO,STATIC> + * sockaddrs: 0x3 <DST,GATEWAY> + * af=inet len=16 addr=192.0.2.2 + * af=link len=54 sdl_index=3 if_name=tap4242 addr=52:54:00:14:E3:10 + */ + + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + sa = rtsock_find_rtm_sa(rtm, RTA_DST); + ret = sa_equal_msg(sa, (struct sockaddr *)&sin, msg, sizeof(msg)); + RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "DST sa diff: %s", msg); + + sa = rtsock_find_rtm_sa(rtm, RTA_GATEWAY); + int sa_flags = SA_F_IGNORE_IFNAME | SA_F_IGNORE_IFTYPE | SA_F_IGNORE_MEMCMP; + ret = sa_equal_msg_flags(sa, (struct sockaddr *)ðer, msg, sizeof(msg), sa_flags); + RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "GATEWAY sa diff: %s", msg); + + /* + * TODO: Currently kernel code does not set sdl_type, contrary to IPv6. + */ +} + +RTM_DECLARE_ROOT_TEST(rtm_del_v6_ll_lle_success, "Tests removal of link-local IPv6 ND entry"); +ATF_TC_BODY(rtm_del_v6_ll_lle_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv6(tc); + + char str_buf[128]; + + struct sockaddr_in6 sin6; + /* Interface here is optional. XXX: verify kernel side. */ + char *v6addr = "fe80::4242:4242"; + snprintf(str_buf, sizeof(str_buf), "%s%%%s", v6addr, c->ifname); + sa_convert_str_to_sa(str_buf, (struct sockaddr *)&sin6); + + struct sockaddr_dl ether; + snprintf(str_buf, sizeof(str_buf), "%s%%%s", c->remote_lladdr, c->ifname); + sa_convert_str_to_sa(str_buf, (struct sockaddr *)ðer); + + prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&sin6, (struct sockaddr *)ðer); + + rtsock_send_rtm(c->rtsock_fd, rtm); + + /* Successfully added an entry, let's try to remove it. */ + prepare_route_message(rtm, RTM_DELETE, (struct sockaddr *)&sin6, (struct sockaddr *)ðer); + + rtsock_send_rtm(c->rtsock_fd, rtm); + + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_type == RTM_DELETE, "rtm_type is not delete"); + + sa = rtsock_find_rtm_sa(rtm, RTA_DST); + ret = sa_equal_msg(sa, (struct sockaddr *)&sin6, msg, sizeof(msg)); + RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "DST sa diff: %s", msg); + + sa = rtsock_find_rtm_sa(rtm, RTA_GATEWAY); + int sa_flags = SA_F_IGNORE_IFNAME | SA_F_IGNORE_IFTYPE | SA_F_IGNORE_MEMCMP; + ret = sa_equal_msg_flags(sa, (struct sockaddr *)ðer, msg, sizeof(msg), sa_flags); + RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "GATEWAY sa diff: %s", msg); + + /* + * TODO: Currently kernel code does not set sdl_type on delete. + */ +} + +RTM_DECLARE_ROOT_TEST(rtm_del_v6_gu_lle_success, "Tests removal of global IPv6 ND entry"); +ATF_TC_BODY(rtm_del_v6_gu_lle_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv6(tc); + + char str_buf[128]; + + struct sockaddr_in6 sin6; + sin6 = c->net6; +#define _s6_addr32 __u6_addr.__u6_addr32 + sin6.sin6_addr._s6_addr32[3] = htonl(0x42424242); +#undef _s6_addr32 + + struct sockaddr_dl ether; + snprintf(str_buf, sizeof(str_buf), "%s%%%s", c->remote_lladdr, c->ifname); + sa_convert_str_to_sa(str_buf, (struct sockaddr *)ðer); + + prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&sin6, (struct sockaddr *)ðer); + + len = rtsock_send_rtm(c->rtsock_fd, rtm); + + /* Successfully added an entry, let's try to remove it. */ + prepare_route_message(rtm, RTM_DELETE, (struct sockaddr *)&sin6, (struct sockaddr *)ðer); + + rtsock_send_rtm(c->rtsock_fd, rtm); + + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_type == RTM_DELETE, "rtm_type is not delete"); + + sa = rtsock_find_rtm_sa(rtm, RTA_DST); + ret = sa_equal_msg(sa, (struct sockaddr *)&sin6, msg, sizeof(msg)); + RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "DST sa diff: %s", msg); + + sa = rtsock_find_rtm_sa(rtm, RTA_GATEWAY); + int sa_flags = SA_F_IGNORE_IFNAME | SA_F_IGNORE_IFTYPE | SA_F_IGNORE_MEMCMP; + ret = sa_equal_msg_flags(sa, (struct sockaddr *)ðer, msg, sizeof(msg), sa_flags); + RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "GATEWAY sa diff: %s", msg); + + /* + * TODO: Currently kernel code does not set sdl_type on delete. + */ +} + +RTM_DECLARE_ROOT_TEST(rtm_del_v4_gu_lle_success, "Tests removal of IPv4 ARP entry"); +ATF_TC_BODY(rtm_del_v4_gu_lle_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv4(tc); + + char str_buf[128]; + + struct sockaddr_in sin; + sin = c->addr4; + /* Use the next IPv4 address after self */ + sin.sin_addr.s_addr = htonl(ntohl(sin.sin_addr.s_addr) + 1); + + struct sockaddr_dl ether; + snprintf(str_buf, sizeof(str_buf), "%s%%%s", c->remote_lladdr, c->ifname); + sa_convert_str_to_sa(str_buf, (struct sockaddr *)ðer); + + prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&sin, (struct sockaddr *)ðer); + + rtsock_send_rtm(c->rtsock_fd, rtm); + + /* We successfully added an entry, let's try to remove it. */ + prepare_route_message(rtm, RTM_DELETE, (struct sockaddr *)&sin, (struct sockaddr *)ðer); + + rtsock_send_rtm(c->rtsock_fd, rtm); + + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_type == RTM_DELETE, "rtm_type is not delete"); + + sa = rtsock_find_rtm_sa(rtm, RTA_DST); + ret = sa_equal_msg(sa, (struct sockaddr *)&sin, msg, sizeof(msg)); + RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "DST sa diff: %s", msg); + + sa = rtsock_find_rtm_sa(rtm, RTA_GATEWAY); + int sa_flags = SA_F_IGNORE_IFNAME | SA_F_IGNORE_IFTYPE | SA_F_IGNORE_MEMCMP; + ret = sa_equal_msg_flags(sa, (struct sockaddr *)ðer, msg, sizeof(msg), sa_flags); + RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "GATEWAY sa diff: %s", msg); + + /* + * TODO: Currently kernel code does not set sdl_type, contrary to IPv6. + */ +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, rtm_add_v6_ll_lle_success); + ATF_TP_ADD_TC(tp, rtm_add_v6_gu_lle_success); + ATF_TP_ADD_TC(tp, rtm_add_v4_gu_lle_success); + ATF_TP_ADD_TC(tp, rtm_del_v6_ll_lle_success); + ATF_TP_ADD_TC(tp, rtm_del_v6_gu_lle_success); + ATF_TP_ADD_TC(tp, rtm_del_v4_gu_lle_success); + + return (atf_no_error()); +} + + diff --git a/tests/sys/net/routing/test_rtsock_multipath.py b/tests/sys/net/routing/test_rtsock_multipath.py new file mode 100755 index 000000000000..f4734d2f65bb --- /dev/null +++ b/tests/sys/net/routing/test_rtsock_multipath.py @@ -0,0 +1,271 @@ +import pytest +from atf_python.sys.net.rtsock import RtConst +from atf_python.sys.net.rtsock import Rtsock +from atf_python.sys.net.rtsock import RtsockRtMessage +from atf_python.sys.net.tools import ToolsHelper +from atf_python.sys.net.vnet import SingleVnetTestTemplate + + +class TestRtmMultipath(SingleVnetTestTemplate): + def setup_method(self, method): + method_name = method.__name__ + if "multipath4" in method_name: + self.IPV4_PREFIXES = ["192.0.2.1/24"] + self.PREFIX = "128.66.0.0/24" + elif "multipath6" in method_name: + self.IPV6_PREFIXES = ["2001:db8::1/64"] + self.PREFIX = "2001:db8:0:ddbb::/64" + super().setup_method(method) + self.rtsock = Rtsock() + + def get_prefix_routes(self): + family = "inet6" if ":" in self.PREFIX else "inet" + routes = ToolsHelper.get_routes(family) + return [r for r in routes if r["destination"] == self.PREFIX] + + @pytest.mark.parametrize( + "gws", + [ + pytest.param(["+.10=2", "+.5=3"], id="transition_multi"), + pytest.param(["+.10=2", "+.5=3", "-.10=2"], id="transition_single1"), + pytest.param(["+.10=2", "+.5=3", "-.5=3"], id="transition_single2"), + pytest.param( + ["+.10", "+.11", "+.50", "+.13", "+.145", "+.72"], id="correctness1" + ), + pytest.param( + ["+.10", "+.11", "+.50", "-.50", "+.145", "+.72"], id="correctness2" + ), + pytest.param(["+.10=1", "+.5=2"], id="weight1"), + pytest.param(["+.10=2", "+.5=7"], id="weight2"), + pytest.param(["+.10=13", "+.5=21"], id="weight3_max"), + pytest.param(["+.10=2", "+.5=3", "~.5=4"], id="change_new_weight1"), + pytest.param(["+.10=2", "+.5=3", "~.10=3"], id="change_new_weight2"), + pytest.param( + ["+.10=2", "+.5=3", "+.7=4", "~.10=3"], id="change_new_weight3" + ), + pytest.param(["+.10=2", "+.5=3", "~.5=3"], id="change_same_weight1"), + pytest.param( + ["+.10=2", "+.5=3", "+.7=4", "~.5=3"], id="change_same_weight2" + ), + ], + ) + @pytest.mark.require_user("root") + def test_rtm_multipath4(self, gws): + """Tests RTM_ADD with IPv4 dest transitioning to multipath""" + self._test_rtm_multipath(gws, "192.0.2") + + @pytest.mark.parametrize( + "gws", + [ + pytest.param(["+:10=2", "+:5=3"], id="transition_multi"), + pytest.param(["+:10=2", "+:5=3", "-:10=2"], id="transition_single1"), + pytest.param(["+:10=2", "+:5=3", "-:5=3"], id="transition_single2"), + pytest.param( + ["+:10", "+:11", "+:50", "+:13", "+:145", "+:72"], id="correctness1" + ), + pytest.param( + ["+:10", "+:11", "+:50", "-:50", "+:145", "+:72"], id="correctness2" + ), + pytest.param(["+:10=1", "+:5=2"], id="weight1"), + pytest.param(["+:10=2", "+:5=7"], id="weight2"), + pytest.param(["+:10=13", "+:5=21"], id="weight3_max"), + pytest.param(["+:10=13", "+:5=21"], id="weight3_max"), + pytest.param(["+:10=2", "+:5=3", "~:5=4"], id="change_new_weight1"), + pytest.param(["+:10=2", "+:5=3", "~:10=3"], id="change_new_weight2"), + pytest.param( + ["+:10=2", "+:5=3", "+:7=4", "~:10=3"], id="change_new_weight3" + ), + pytest.param(["+:10=2", "+:5=3", "~:5=3"], id="change_same_weight1"), + pytest.param( + ["+:10=2", "+:5=3", "+:7=4", "~:5=3"], id="change_same_weight2" + ), + ], + ) + @pytest.mark.require_user("root") + def test_rtm_multipath6(self, gws): + """Tests RTM_ADD with IPv6 dest transitioning to multipath""" + self._test_rtm_multipath(gws, "2001:db8:") + + def _test_rtm_multipath(self, gws, gw_prefix: str): + desired_map = {} + for gw_act in gws: + # GW format: <+-~>GW[=weight] + if "=" in gw_act: + arr = gw_act[1:].split("=") + weight = int(arr[1]) + gw = gw_prefix + arr[0] + else: + weight = None + gw = gw_prefix + gw_act[1:] + if gw_act[0] == "+": + msg = self.rtsock.new_rtm_add(self.PREFIX, gw) + desired_map[gw] = self.rtsock.get_weight(weight) + elif gw_act[0] == "-": + msg = self.rtsock.new_rtm_del(self.PREFIX, gw) + del desired_map[gw] + else: + msg = self.rtsock.new_rtm_change(self.PREFIX, gw) + desired_map[gw] = self.rtsock.get_weight(weight) + + msg.rtm_flags = RtConst.RTF_GATEWAY + if weight: + msg.rtm_inits |= RtConst.RTV_WEIGHT + msg.rtm_rmx.rmx_weight = weight + # Prepare SAs to check for + desired_sa = { + RtConst.RTA_DST: msg.get_sa(RtConst.RTA_DST), + RtConst.RTA_NETMASK: msg.get_sa(RtConst.RTA_NETMASK), + RtConst.RTA_GATEWAY: msg.get_sa(RtConst.RTA_GATEWAY), + } + self.rtsock.write_message(msg) + + data = self.rtsock.read_data(msg.rtm_seq) + msg_in = RtsockRtMessage.from_bytes(data) + msg_in.print_in_message() + msg_in.verify(msg.rtm_type, desired_sa) + assert msg_in.rtm_rmx.rmx_weight == self.rtsock.get_weight(weight) + + routes = self.get_prefix_routes() + derived_map = {r["gateway"]: r["weight"] for r in routes} + assert derived_map == desired_map + + @pytest.mark.require_user("root") + def test_rtm_multipath4_add_same_eexist(self): + """Tests adding same IPv4 gw to the multipath group (EEXIST)""" + gws = ["192.0.2.10", "192.0.2.11", "192.0.2.11"] + self._test_rtm_multipath_add_same_eexist(gws) + + @pytest.mark.require_user("root") + def test_rtm_multipath6_add_same_eexist(self): + """Tests adding same IPv4 gw to the multipath group (EEXIST)""" + gws = ["2001:db8::10", "2001:db8::11", "2001:db8::11"] + self._test_rtm_multipath_add_same_eexist(gws) + + def _test_rtm_multipath_add_same_eexist(self, gws): + for idx, gw in enumerate(gws): + msg = self.rtsock.new_rtm_add(self.PREFIX, gw) + msg.rtm_flags = RtConst.RTF_GATEWAY + try: + self.rtsock.write_message(msg) + except FileExistsError as e: + if idx != 2: + raise + print("Succcessfully raised {}".format(e)) + + @pytest.mark.require_user("root") + def test_rtm_multipath4_del_unknown_esrch(self): + """Tests deleting non-existing dest from the multipath group (ESRCH)""" + gws = ["192.0.2.10", "192.0.2.11"] + self._test_rtm_multipath_del_unknown_esrch(gws, "192.0.2.7") + + @pytest.mark.require_user("root") + def test_rtm_multipath6_del_unknown_esrch(self): + """Tests deleting non-existing dest from the multipath group (ESRCH)""" + gws = ["2001:db8::10", "2001:db8::11"] + self._test_rtm_multipath_del_unknown_esrch(gws, "2001:db8::7") + + @pytest.mark.require_user("root") + def _test_rtm_multipath_del_unknown_esrch(self, gws, target_gw): + for gw in gws: + msg = self.rtsock.new_rtm_add(self.PREFIX, gw) + msg.rtm_flags = RtConst.RTF_GATEWAY + self.rtsock.write_message(msg) + msg = self.rtsock.new_rtm_del(self.PREFIX, target_gw) + msg.rtm_flags = RtConst.RTF_GATEWAY + try: + self.rtsock.write_message(msg) + except ProcessLookupError as e: + print("Succcessfully raised {}".format(e)) + + @pytest.mark.require_user("root") + def test_rtm_multipath4_change_unknown_esrch(self): + """Tests changing non-existing dest in the multipath group (ESRCH)""" + gws = ["192.0.2.10", "192.0.2.11"] + self._test_rtm_multipath_change_unknown_esrch(gws, "192.0.2.7") + + @pytest.mark.require_user("root") + def test_rtm_multipath6_change_unknown_esrch(self): + """Tests changing non-existing dest in the multipath group (ESRCH)""" + gws = ["2001:db8::10", "2001:db8::11"] + self._test_rtm_multipath_change_unknown_esrch(gws, "2001:db8::7") + + @pytest.mark.require_user("root") + def _test_rtm_multipath_change_unknown_esrch(self, gws, target_gw): + for gw in gws: + msg = self.rtsock.new_rtm_add(self.PREFIX, gw) + msg.rtm_flags = RtConst.RTF_GATEWAY + self.rtsock.write_message(msg) + msg = self.rtsock.new_rtm_change(self.PREFIX, target_gw) + msg.rtm_flags = RtConst.RTF_GATEWAY + try: + self.rtsock.write_message(msg) + except ProcessLookupError as e: + print("Succcessfully raised {}".format(e)) + + @pytest.mark.require_user("root") + def test_rtm_multipath4_add_zero_weight(self): + """Tests RTM_ADD with dest transitioning to multipath""" + + desired_map = {} + for gw in ["192.0.2.10", "192.0.2.11", "192.0.2.13"]: + msg = self.rtsock.new_rtm_add(self.PREFIX, gw) + msg.rtm_flags = RtConst.RTF_GATEWAY + msg.rtm_rmx.rmx_weight = 0 + msg.rtm_inits |= RtConst.RTV_WEIGHT + self.rtsock.write_message(msg) + desired_map[gw] = self.rtsock.get_weight(0) + + routes = self.get_prefix_routes() + derived_map = {r["gateway"]: r["weight"] for r in routes} + assert derived_map == desired_map + + @pytest.mark.require_user("root") + def test_rtm_multipath4_getroute(self): + """Tests RTM_GET with exact prefix lookup on the multipath group""" + gws = ["192.0.2.10", "192.0.2.11", "192.0.2.13"] + return self._test_rtm_multipath_getroute(gws) + + @pytest.mark.require_user("root") + def test_rtm_multipath6_getroute(self): + """Tests RTM_GET with exact prefix lookup on the multipath group""" + gws = ["2001:db8::10", "2001:db8::11", "2001:db8::13"] + return self._test_rtm_multipath_getroute(gws) + + def _test_rtm_multipath_getroute(self, gws): + valid_gws = [] + for gw in gws: + msg = self.rtsock.new_rtm_add(self.PREFIX, gw) + msg.rtm_flags = RtConst.RTF_GATEWAY + self.rtsock.write_message(msg) + + desired_sa = { + RtConst.RTA_DST: msg.get_sa(RtConst.RTA_DST), + RtConst.RTA_NETMASK: msg.get_sa(RtConst.RTA_NETMASK), + } + valid_gws.append(msg.get_sa(RtConst.RTA_GATEWAY)) + + msg_get = RtsockRtMessage( + RtConst.RTM_GET, + self.rtsock.get_seq(), + msg.get_sa(RtConst.RTA_DST), + msg.get_sa(RtConst.RTA_NETMASK), + ) + self.rtsock.write_message(msg_get) + + data = self.rtsock.read_data(msg_get.rtm_seq) + msg_in = RtsockRtMessage.from_bytes(data) + msg_in.print_in_message() + msg_in.verify(RtConst.RTM_GET, desired_sa) + + # Additionally, check that the gateway is among the valid + # gateways + gw_found = False + gw_in = msg_in.get_sa(RtConst.RTA_GATEWAY) + for valid_gw in valid_gws: + try: + assert valid_gw == gw_in + gw_found = True + break + except AssertionError: + pass + assert gw_found is True diff --git a/tests/sys/net/routing/test_rtsock_ops.c b/tests/sys/net/routing/test_rtsock_ops.c new file mode 100644 index 000000000000..ab4be93cc1af --- /dev/null +++ b/tests/sys/net/routing/test_rtsock_ops.c @@ -0,0 +1,53 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 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. + */ + +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/socket.h> +#include <net/route.h> + +#include <atf-c.h> + +ATF_TC(socket_rtsock_openclose); +ATF_TC_HEAD(socket_rtsock_openclose, tc) +{ + atf_tc_set_md_var(tc, "descr", "test successful open/close"); +} + +ATF_TC_BODY(socket_rtsock_openclose, tc) +{ + int s = socket(PF_ROUTE, SOCK_RAW, 0); + ATF_CHECK(s >= 0); + ATF_CHECK_ERRNO(0, close(s) == 0); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, socket_rtsock_openclose); + + return (atf_no_error()); +} diff --git a/tests/sys/net/stp.py b/tests/sys/net/stp.py new file mode 100644 index 000000000000..dc6634fb7279 --- /dev/null +++ b/tests/sys/net/stp.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2021 Kristof Provost <kp@FreeBSD.org> +# +# 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. +# + +import argparse +import logging +logging.getLogger("scapy").setLevel(logging.CRITICAL) +import scapy.all as sp +import sys +import os +curdir = os.path.dirname(os.path.realpath(__file__)) +netpfil_common = curdir + "/../netpfil/common" +sys.path.append(netpfil_common) +from sniffer import Sniffer + +def check_stp(args, packet): + stp = packet.getlayer(sp.STP) + if stp is None: + return False + + if stp.rootmac != "00:0c:29:01:01:01": + return False + + # Ensure we don't get confused by valid STP packets generated by if_bridge + if (stp.maxage >= 6 and stp.maxage <= 40) and \ + (stp.hellotime >= 1 and stp.hellotime <= 2) and \ + (stp.fwddelay >= 4 and stp.fwddelay <= 30): + return False + + print("This packet should have been dropped") + print(packet.show()) + return True + +def invalid_stp(send_if): + llc = sp.Ether(src="00:0c:29:0b:91:0a", dst="01:80:C2:00:00:00") \ + / sp.LLC() + + # Bad maxage + stp = llc / sp.STP(proto=0, rootid=32768, rootmac="00:0c:29:01:01:01", \ + bridgeid=32768, bridgemac="00:0c:29:01:01:01", \ + portid=0x8007, maxage=41, hellotime=2, fwddelay=30) + sp.sendp(stp, iface=send_if, verbose=False) + stp = llc / sp.STP(proto=0, rootid=32768, rootmac="00:0c:29:01:01:01", \ + bridgeid=32768, bridgemac="00:0c:29:01:01:01", \ + portid=0x8007, maxage=5, hellotime=2, fwddelay=30) + sp.sendp(stp, iface=send_if, verbose=False) + + # Bad hellotime + stp = llc / sp.STP(proto=0, rootid=32768, rootmac="00:0c:29:01:01:01", \ + bridgeid=32768, bridgemac="00:0c:29:01:01:01", \ + portid=0x8007, maxage=40, hellotime=3, fwddelay=30) + sp.sendp(stp, iface=send_if, verbose=False) + stp = llc / sp.STP(proto=0, rootid=32768, rootmac="00:0c:29:01:01:01", \ + bridgeid=32768, bridgemac="00:0c:29:01:01:01", \ + portid=0x8007, maxage=40, hellotime=1, fwddelay=30) + sp.sendp(stp, iface=send_if, verbose=False) + + # Bad fwddelay + stp = llc / sp.STP(proto=0, rootid=32768, rootmac="00:0c:29:01:01:01", \ + bridgeid=32768, bridgemac="00:0c:29:01:01:01", \ + portid=0x8007, maxage=40, hellotime=2, fwddelay=31) + sp.sendp(stp, iface=send_if, verbose=False) + stp = llc / sp.STP(proto=0, rootid=32768, rootmac="00:0c:29:01:01:01", \ + bridgeid=32768, bridgemac="00:0c:29:01:01:01", \ + portid=0x8007, maxage=40, hellotime=2, fwddelay=3) + sp.sendp(stp, iface=send_if, verbose=False) + +def main(): + parser = argparse.ArgumentParser("stp.py", + description="STP test tool") + parser.add_argument('--sendif', nargs=1, + required=True, + help='The interface through which the packet(s) will be sent') + parser.add_argument('--recvif', nargs=1, + help='The interface on which to expect the ICMP echo request') + + args = parser.parse_args() + + sniffer = Sniffer(args, check_stp, args.recvif[0]) + + invalid_stp(args.sendif[0]) + + sniffer.join() + + # The 'correct' packet is a corrupt STP packet, so it shouldn't turn up. + if sniffer.correctPackets: + sys.exit(1) + +if __name__ == '__main__': + main() diff --git a/tests/sys/net/transient_tuntap.c b/tests/sys/net/transient_tuntap.c new file mode 100644 index 000000000000..b0cf43064317 --- /dev/null +++ b/tests/sys/net/transient_tuntap.c @@ -0,0 +1,54 @@ +/*- + * Copyright (c) 2024 Kyle Evans <kevans@FreeBSD.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +/* + * This test simply configures the tunnel as transient and exits. By the time + * we return, the tunnel should be gone because the last reference disappears. + */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <net/if_tun.h> +#include <net/if_tap.h> + +#include <assert.h> +#include <err.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> + +int +main(int argc, char *argv[]) +{ + unsigned long tunreq; + const char *tundev; + int one = 1, tunfd; + + assert(argc > 1); + tundev = argv[1]; + + tunfd = open(tundev, O_RDWR); + assert(tunfd >= 0); + + /* + * These are technically the same request, but we'll use the technically + * correct one just in case. + */ + if (strstr(tundev, "tun") != NULL) { + tunreq = TUNSTRANSIENT; + } else { + assert(strstr(tundev, "tap") != NULL); + tunreq = TAPSTRANSIENT; + } + + if (ioctl(tunfd, tunreq, &one) == -1) + err(1, "ioctl"); + + /* Final close should destroy the tunnel automagically. */ + close(tunfd); + + return (0); +} |
