netpfil tests: Add functions for testing routing scenarios

Many pf tests use identical setup where one jail is a router and
optionally another jail is a server. Add functions to create such jails
for IPv6 and IPv4 and functions to perform tests on such setup.

Add tests using those functions: scrub actions, routing table, tcp
sequence number modulation.

Reviewed by:	kp
Sponsored by:	InnoGames GmbH
Differential Revision:	https://reviews.freebsd.org/D38126
This commit is contained in:
Kristof Provost 2023-03-04 14:35:24 +01:00
parent f5b204adf4
commit 38e2d5db2a
6 changed files with 567 additions and 45 deletions

View File

@ -7,7 +7,6 @@ TESTS_SUBDIRS+= ioctl
ATF_TESTS_SH+= altq \
anchor \
checksum \
dup \
ether \
forward \
@ -18,6 +17,7 @@ ATF_TESTS_SH+= altq \
macro \
map_e \
match \
modulate \
names \
nat \
pass_block \
@ -27,7 +27,9 @@ ATF_TESTS_SH+= altq \
rdr \
ridentifier \
route_to \
rtable \
rules_counter \
scrub \
set_skip \
set_tos \
src_track \

View File

@ -327,6 +327,43 @@ reassemble_cleanup()
pft_cleanup
}
atf_test_case "no_df" "cleanup"
no_df_head()
{
atf_set descr 'Test removing of DF flag'
atf_set require.user root
}
no_df_body()
{
setup_router_server_ipv4
# Tester can send long packets which will get fragmented by the router.
# Replies from server will come in fragments which might get
# reassembled resulting in a long reply packet sent back to tester.
ifconfig ${epair_tester}a mtu 9000
jexec router ifconfig ${epair_tester}b mtu 9000
jexec router ifconfig ${epair_server}a mtu 1500
jexec server ifconfig ${epair_server}b mtu 1500
# Sanity check.
ping_server_check_reply exit:0 --ping-type=icmp
# Enable packet reassembly with clearing of the no-df flag.
pft_set_rules router \
"scrub all fragment reassemble no-df" \
"block" \
"pass inet proto icmp all icmp-type echoreq"
# Ping with non-fragmentable packets.
# pf will strip the DF flag resulting in fragmentation and packets
# getting properly forwarded.
ping_server_check_reply exit:0 --ping-type=icmp --send-length=2000 --send-flags DF
}
no_df_cleanup()
{
pft_cleanup
}
atf_init_test_cases()
{
atf_add_test_case "too_many_fragments"
@ -336,4 +373,5 @@ atf_init_test_cases()
atf_add_test_case "overindex"
atf_add_test_case "overlimit"
atf_add_test_case "reassemble"
atf_add_test_case "no_df"
}

View File

@ -1,6 +1,8 @@
# $FreeBSD$
#
# SPDX-License-Identifier: BSD-2-Clause-FreeBSD
#
# Copyright (c) 2020 Kristof Provost <kp@FreeBSD.org>
# Copyright (c) 2023 Kajetan Staszkiewicz <vegetga@tuxpowered.net>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
@ -25,62 +27,54 @@
. $(atf_get_srcdir)/utils.subr
common_dir=$(atf_get_srcdir)/../common
atf_test_case "unaligned" "cleanup"
unaligned_head()
atf_test_case "modulate_v4" "cleanup"
modulate_v4_head()
{
atf_set descr 'Test unaligned checksum updates'
atf_set descr 'IPv4 TCP sequence number modulation'
atf_set require.user root
atf_set require.progs scapy
}
unaligned_body()
modulate_v4_body()
{
pft_init
setup_router_dummy_ipv4
epair_in=$(vnet_mkepair)
epair_out=$(vnet_mkepair)
vnet_mkjail alcatraz ${epair_in}b ${epair_out}a
ifconfig ${epair_in}a 192.0.2.2/24 up
route add -net 198.51.100.0/24 192.0.2.1
jexec alcatraz ifconfig ${epair_in}b 192.0.2.1/24 up
jexec alcatraz sysctl net.inet.ip.forwarding=1
jexec alcatraz ifconfig ${epair_out}a 198.51.100.1/24 up
jexec alcatraz arp -s 198.51.100.2 00:01:02:03:04:05
ifconfig ${epair_out}b up
jexec alcatraz pfctl -e
pft_set_rules alcatraz \
"scrub on ${epair_in}b reassemble tcp max-mss 1200"
# Check aligned
atf_check -s exit:0 ${common_dir}/pft_ping.py \
--sendif ${epair_in}a \
--to 198.51.100.2 \
--recvif ${epair_out}b \
--ping-type tcpsyn
# And unaligned
atf_check -s exit:0 ${common_dir}/pft_ping.py \
--sendif ${epair_in}a \
--to 198.51.100.2 \
--recvif ${epair_out}b \
--ping-type tcpsyn \
--send-tcpopt-unaligned
pft_set_rules router \
"pass in on ${epair_tester}b modulate state"
ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-seq 42 # Sanity check
ping_dummy_check_request exit:1 --ping-type=tcpsyn --send-seq 42 --expect-seq 42
}
unaligned_cleanup()
modulate_v4_cleanup()
{
pft_cleanup
}
atf_test_case "modulate_v6" "cleanup"
modulate_v6_head()
{
atf_set descr 'IPv6 TCP sequence number modulation'
atf_set require.user root
atf_set require.progs scapy
}
modulate_v6_body()
{
setup_router_dummy_ipv6
pft_set_rules router \
"pass in on ${epair_tester}b modulate state"
ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-seq 42 # Sanity check
ping_dummy_check_request exit:1 --ping-type=tcpsyn --send-seq 42 --expect-seq 42
}
modulate_v6_cleanup()
{
pft_cleanup
}
atf_init_test_cases()
{
atf_add_test_case "unaligned"
atf_add_test_case "modulate_v4"
atf_add_test_case "modulate_v6"
}

View File

@ -0,0 +1,131 @@
# $FreeBSD$
#
# SPDX-License-Identifier: BSD-2-Clause-FreeBSD
#
# Copyright (c) 2023 Kajetan Staszkiewicz <vegetga@tuxpowered.net>
#
# 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_test_case "forward_v4" "cleanup"
forward_v4_head()
{
atf_set descr 'Test IPv4 forwarding with rtable'
atf_set require.user root
atf_set require.progs scapy
}
forward_v4_body()
{
setup_router_server_ipv4
# Sanity check
ping_server_check_reply exit:0
jexec router sysctl net.fibs=2
jexec router ifconfig ${epair_server}a fib 1
jexec router route del -net ${net_server}
jexec router route add -fib 1 -net ${net_server} -iface ${epair_server}a
# Sanity check
ping_server_check_reply exit:1
# This rule is not enough.
# Echo requests will be properly forwarded but replies can't be routed back.
pft_set_rules router \
"pass in on ${epair_tester}b inet proto icmp all icmp-type echoreq rtable 1"
ping_server_check_reply exit:1
# Allow replies coming back to the tester properly via stateful filtering post-routing.
pft_set_rules router \
"pass in on ${epair_tester}b inet proto icmp all icmp-type echoreq rtable 1" \
"pass out on ${epair_server}a inet proto icmp all icmp-type echoreq rtable 0"
ping_server_check_reply exit:0
# Allow replies coming back to the tester properly via provding extra routes in rtable 1
pft_set_rules router \
"pass in on ${epair_tester}b inet proto icmp all icmp-type echoreq rtable 1"
jexec router route add -fib 1 -net ${net_tester} -iface ${epair_tester}b
ping_server_check_reply exit:0
}
forward_v4_cleanup()
{
pft_cleanup
}
atf_test_case "forward_v6" "cleanup"
forward_v6_head()
{
atf_set descr 'Test IPv6 forwarding with rtable'
atf_set require.user root
atf_set require.progs scapy
}
forward_v6_body()
{
setup_router_server_ipv6
# Sanity check
ping_server_check_reply exit:0
jexec router sysctl net.fibs=2
jexec router ifconfig ${epair_server}a fib 1
jexec router route del -6 ${net_server}
jexec router route add -fib 1 -6 ${net_server} -iface ${epair_server}a
# Sanity check
ping_server_check_reply exit:1
# This rule is not enough.
# Echo requests will be properly forwarded but replies can't be routed back.
pft_set_rules router \
"pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
"pass in on ${epair_tester}b inet6 proto icmp6 icmp6-type echoreq"
ping_server_check_reply exit:1
# Allow replies coming back to the tester properly via stateful filtering post-routing.
pft_set_rules router \
"pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
"pass in on ${epair_tester}b inet6 proto icmp6 icmp6-type echoreq rtable 1" \
"pass out on ${epair_server}a inet6 proto icmp6 icmp6-type echoreq rtable 0"
ping_server_check_reply exit:0
# Allow replies coming back to the tester properly via provding extra routes in rtable 1
pft_set_rules router \
"pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
"pass in on ${epair_tester}b inet6 proto icmp6 icmp6-type echoreq rtable 1"
jexec router route add -fib 1 -6 ${net_tester} -iface ${epair_tester}b
ping_server_check_reply exit:0
}
forward_v6_cleanup()
{
pft_cleanup
}
atf_init_test_cases()
{
atf_add_test_case "forward_v4"
atf_add_test_case "forward_v6"
}

View File

@ -0,0 +1,221 @@
# $FreeBSD$
#
# SPDX-License-Identifier: BSD-2-Clause-FreeBSD
#
# Copyright (c) 2020 Kristof Provost <kp@FreeBSD.org>
# Copyright (c) 2023 Kajetan Staszkiewicz <vegeta@tuxpowered.net>
#
# 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_test_case "max_mss_v4" "cleanup"
max_mss_v4_head()
{
atf_set descr 'Test IPv4 scrub "mss" rule'
atf_set require.user root
atf_set require.progs scapy
}
max_mss_v4_body()
{
setup_router_dummy_ipv4
pft_set_rules router "scrub on ${epair_tester}b max-mss 1300"
# Check aligned
ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-mss=1400 --expect-mss=1300
# And unaligned
ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-mss=1400 --expect-mss=1300 \
--send-tcpopt-unaligned
}
max_mss_v4_cleanup()
{
pft_cleanup
}
atf_test_case "max_mss_v6" "cleanup"
max_mss_v6_head()
{
atf_set descr 'Test IPv6 scrub "mss" rule'
atf_set require.user root
atf_set require.progs scapy
}
max_mss_v6_body()
{
setup_router_dummy_ipv6
pft_set_rules router "scrub on ${epair_tester}b max-mss 1300"
# Check aligned
ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-mss=1400 --expect-mss=1300
# And unaligned
ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-mss=1400 --expect-mss=1300 \
--send-tcpopt-unaligned
}
max_mss_v6_cleanup()
{
pft_cleanup
}
atf_test_case "set_tos_v4" "cleanup"
set_tos_v4_head()
{
atf_set descr 'Test IPv4 scub "set-tos" rule'
atf_set require.user root
atf_set require.progs scapy
}
set_tos_v4_body()
{
setup_router_dummy_ipv4
pft_set_rules router "scrub on ${epair_tester}b set-tos 0x42"
ping_dummy_check_request exit:0 --send-tc=0 --expect-tc=66
}
set_tos_v4_cleanup()
{
pft_cleanup
}
atf_test_case "set_tos_v6" "cleanup"
set_tos_v6_head()
{
atf_set descr 'Test IPv6 scub "set-tos" rule'
atf_set require.user root
atf_set require.progs scapy
}
set_tos_v6_body()
{
setup_router_dummy_ipv6
pft_set_rules router "scrub on ${epair_tester}b set-tos 0x42"
ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-tc=0 --expect-tc=66
}
set_tos_v6_cleanup()
{
pft_cleanup
}
atf_test_case "min_ttl_v4" "cleanup"
min_ttl_v4_head()
{
atf_set descr 'Test IPv4 scub "min-ttl" rule'
atf_set require.user root
atf_set require.progs scapy
}
min_ttl_v4_body()
{
setup_router_dummy_ipv4
pft_set_rules router "scrub on ${epair_tester}b min-ttl 50"
ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-hlim=40 --expect-hlim=49
}
min_ttl_v4_cleanup()
{
pft_cleanup
}
atf_test_case "min_ttl_v6" "cleanup"
min_ttl_v6_head()
{
atf_set descr 'Test IPv6 scub "min-ttl" rule'
atf_set require.user root
atf_set require.progs scapy
}
min_ttl_v6_body()
{
setup_router_dummy_ipv6
pft_set_rules router "scrub on ${epair_tester}b min-ttl 50"
ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-hlim=40 --expect-hlim=49
}
min_ttl_v6_cleanup()
{
pft_cleanup
}
atf_test_case "no_scrub_v4" "cleanup"
no_scrub_v4_head()
{
atf_set descr 'Test IPv4 "no scrub" rule'
atf_set require.user root
atf_set require.progs scapy
}
no_scrub_v4_body()
{
setup_router_dummy_ipv4
pft_set_rules router\
"no scrub on ${epair_tester}b to ${net_server_host_server}"
"scrub on ${epair_tester}b set-tos 0x42"
ping_dummy_check_request exit:0 --send-tc=0 --expect-tc=0
}
no_scrub_v4_cleanup()
{
pft_cleanup
}
atf_test_case "no_scrub_v6" "cleanup"
no_scrub_v6_head()
{
atf_set descr 'Test IPv6 "no scrub" rule'
atf_set require.user root
atf_set require.progs scapy
}
no_scrub_v6_body()
{
setup_router_dummy_ipv6
pft_set_rules router \
"no scrub on ${epair_tester}b to ${net_server_host_server}"
"scrub on ${epair_tester}b set-tos 0x42"
ping_dummy_check_request exit:0 --send-tc=0 --expect-tc=0
}
no_scrub_v6_cleanup()
{
pft_cleanup
}
atf_init_test_cases()
{
atf_add_test_case "max_mss_v4"
atf_add_test_case "max_mss_v6"
atf_add_test_case "set_tos_v4"
atf_add_test_case "set_tos_v6"
atf_add_test_case "min_ttl_v4"
atf_add_test_case "min_ttl_v6"
atf_add_test_case "no_scrub_v4"
atf_add_test_case "no_scrub_v6"
}

View File

@ -4,6 +4,7 @@
# SPDX-License-Identifier: BSD-2-Clause-FreeBSD
#
# Copyright (c) 2017 Kristof Provost <kp@FreeBSD.org>
# Copyright (c) 2023 Kajetan Staszkiewicz <vegeta@tuxpowered.net>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
@ -27,6 +28,7 @@
# SUCH DAMAGE.
. $(atf_get_srcdir)/../../common/vnet.subr
common_dir=$(atf_get_srcdir)/../common
pft_onerror()
{
@ -153,3 +155,137 @@ altq_cleanup()
{
pft_cleanup
}
# Create a bare router jail.
# This function lacks target configuration.
setup_router_ipv4()
{
pft_init
epair_tester=$(vnet_mkepair)
epair_server=$(vnet_mkepair)
net_tester=192.0.2.0/24
net_tester_mask=24
net_tester_host_router=192.0.2.1
net_tester_host_tester=192.0.2.2
net_server=198.51.100.0/24
net_server_mask=24
net_server_host_router=198.51.100.1
net_server_host_server=198.51.100.2
vnet_mkjail router ${epair_tester}b ${epair_server}a
ifconfig ${epair_tester}a ${net_tester_host_tester}/${net_tester_mask} up
route add -net ${net_server} ${net_tester_host_router}
jexec router ifconfig ${epair_tester}b ${net_tester_host_router}/${net_tester_mask} up
jexec router sysctl net.inet.ip.forwarding=1
jexec router ifconfig ${epair_server}a ${net_server_host_router}/${net_server_mask} up
jexec router pfctl -e
}
# Create a router jail.
# The target for tests does not exist but a static ARP entry does
# so packets to it can be properly routed.
setup_router_dummy_ipv4()
{
setup_router_ipv4
jexec router arp -s ${net_server_host_server} 00:01:02:03:04:05
ifconfig ${epair_server}b up
}
# Create a router and a server jail.
# The server is capable of responding to pings from the tester.
setup_router_server_ipv4()
{
setup_router_ipv4
vnet_mkjail server ${epair_server}b
jexec server ifconfig ${epair_server}b ${net_server_host_server}/${net_server_mask} up
jexec server route add -net ${net_tester} ${net_server_host_router}
jexec server nc -4l 666 &
sleep 1 # Give nc time to start and listen
}
# Create a bare router jail.
# This function lacks target configuration.
setup_router_ipv6()
{
pft_init
epair_tester=$(vnet_mkepair)
epair_server=$(vnet_mkepair)
net_tester=2001:db8:42::/64
net_tester_mask=64
net_tester_host_router=2001:db8:42::1
net_tester_host_tester=2001:db8:42::2
net_server=2001:db8:43::/64
net_server_mask=64
net_server_host_router=2001:db8:43::1
net_server_host_server=2001:db8:43::2
vnet_mkjail router ${epair_tester}b ${epair_server}a
ifconfig ${epair_tester}a inet6 ${net_tester_host_tester}/${net_tester_mask}up no_dad
route add -6 ${net_server} ${net_tester_host_router}
jexec router ifconfig ${epair_tester}b inet6 ${net_tester_host_router}/${net_tester_mask} up no_dad
jexec router sysctl net.inet6.ip6.forwarding=1
jexec router ifconfig ${epair_server}a inet6 ${net_server_host_router}/${net_server_mask} up no_dad
jexec router pfctl -e
}
# Create a router jail.
# The target for tests does not exist but a static NDP entry does
# so packets to it can be properly routed.
setup_router_dummy_ipv6()
{
setup_router_ipv6
jexec router ndp -s ${net_server_host_server} 00:01:02:03:04:05
ifconfig ${epair_server}b up
}
# Create a router and a server jail.
# The server is capable of responding to pings from tester.
setup_router_server_ipv6()
{
setup_router_ipv6
vnet_mkjail server ${epair_server}b
jexec server ifconfig ${epair_server}b inet6 ${net_server_host_server}/${net_server_mask} up no_dad
jexec server route add -6 ${net_tester} ${net_server_host_router}
jexec server nc -6l 666 &
sleep 1 # Give nc time to start and listen
}
# Ping the dummy static NDP target.
# Check for pings being forwarded through the router towards the target.
ping_dummy_check_request()
{
exit_condition=$1
shift
params=$@
atf_check -s ${exit_condition} ${common_dir}/pft_ping.py \
--sendif ${epair_tester}a \
--to ${net_server_host_server} \
--recvif ${epair_server}b \
$params
}
# Ping the server jail.
# Check for responses coming back throught the router back to the tester.
ping_server_check_reply()
{
exit_condition=$1
shift
params=$@
atf_check -s ${exit_condition} ${common_dir}/pft_ping.py \
--sendif ${epair_tester}a \
--to ${net_server_host_server} \
--replyif ${epair_tester}a \
$params
}