ipfw: fix IPv6 flow label matching

* do not require just only ip6 proto for flow-id opcode in ipfw(8).
  ipv6-icmp, tcp, udp should be fine too.
* fix off-by-one bug leading to out-of-bounds read.
* apply IPV6_FLOWLABEL_MASK before comparison in flow6id_match(),
  so flow-id opcode will match a specified flow label. No need to
  take protocol version and traffic class into account.
* add the test to verify that opcode is working correctly.

Reviewed by:	pouria
Obtained from:	Yandex LLC
MFC after:	1 week
Differential Revision:	https://reviews.freebsd.org/D56869
This commit is contained in:
Boris Lytochkin
2026-05-12 10:44:10 +03:00
committed by Andrey V. Elsukov
parent e26b5e0749
commit 3d39eadcde
5 changed files with 95 additions and 6 deletions
+12 -1
View File
@@ -63,6 +63,7 @@ def prepare_ipv6(send_params):
dst_address = send_params.get('dst_address')
hlim = send_params.get('hlim')
tc = send_params.get('tc')
fl = send_params.get('fl')
ip6 = sp.IPv6(dst=dst_address)
if src_address:
ip6.src = src_address
@@ -70,6 +71,8 @@ def prepare_ipv6(send_params):
ip6.hlim = hlim
if tc:
ip6.tc = tc
if fl:
ip6.fl = fl
return ip6
@@ -224,6 +227,7 @@ def check_ipv6(expect_params, packet):
flags = expect_params.get('flags')
hlim = expect_params.get('hlim')
tc = expect_params.get('tc')
fl = expect_params.get('fl')
ip6 = packet.getlayer(sp.IPv6)
if not ip6:
LOGGER.debug('Packet is not IPv6!')
@@ -245,6 +249,9 @@ def check_ipv6(expect_params, packet):
if tc and ip6.tc != tc:
LOGGER.debug(f'Wrong TC value {ip6.tc}, expected {tc}')
return False
if fl and ip6.fl != fl:
LOGGER.debug(f'Wrong Flow Label value {ip6.fl}, expected {fl}')
return False
return True
@@ -635,6 +642,8 @@ def parse_args():
help='ICMP Echo Request payload size')
parser_send.add_argument('--send-tc', type=int,
help='IPv6 Traffic Class or IPv4 DiffServ / ToS')
parser_send.add_argument('--send-fl', type=int,
help='IPv6 Flow label')
parser_send.add_argument('--send-tcpopt-unaligned', action='store_true',
help='Include unaligned TCP options')
parser_send.add_argument('--send-nop', action='store_true',
@@ -652,6 +661,8 @@ def parse_args():
help='TCP sequence number')
parser_expect.add_argument('--expect-tc', type=int,
help='IPv6 Traffic Class or IPv4 DiffServ / ToS')
parser_expect.add_argument('--expect-fl', type=int,
help='IPv6 Flow Label')
parser.add_argument('-v', '--verbose', action='store_true',
help=('Enable verbose logging. Apart of potentially useful information '
@@ -673,7 +684,7 @@ def main():
send_params = {}
expect_params = {}
for param_name in (
'flags', 'hlim', 'length', 'mss', 'seq', 'tc', 'frag_length',
'flags', 'hlim', 'length', 'mss', 'seq', 'tc', 'fl', 'frag_length',
'sport', 'dport',
):
param_arg = vars(args).get(f'send_{param_name}')
+1
View File
@@ -4,6 +4,7 @@ TESTSDIR= ${TESTSBASE}/sys/netpfil/ipfw
ATF_TESTS_SH+= fwd \
divert \
ipv6-flow-id \
log \
lookup \
table
+78
View File
@@ -0,0 +1,78 @@
#
# Copyright (c) 2026 Boris Lytochkin
#
# SPDX-License-Identifier: BSD-2-Clause
#
common_dir="$(atf_get_srcdir)/../common"
. ${common_dir}/utils.subr
NC="nc -w 1 -dnN"
setup_network_v6()
{
epair="$1"
ifconfig ${epair}a inet6 2001:db8:42::1/64 up no_dad -ifdisabled
vnet_mkjail alcatraz ${epair}b
ifconfig -j alcatraz ${epair}b inet6 2001:db8:42::2/64 up no_dad -ifdisabled
jexec alcatraz /usr/sbin/inetd -p /dev/null $(atf_get_srcdir)/lookup_inetd.conf
# Sanity checks
atf_check -s exit:0 -o ignore ping6 -i .1 -c 3 -s 1200 2001:db8:42::2
atf_check -o "inline:GOOD 82\n" ${NC} 2001:db8:42::2 82
}
atf_test_case "ipv6fl" "cleanup"
ipv6fl_head()
{
atf_set descr 'flow-id test'
atf_set require.user root
atf_set require.progs python3 scapy
}
ipv6fl_body()
{
firewall_init "ipfw"
epair=$(vnet_mkepair)
setup_network_v6 ${epair}
# Check if the firewall is able to match exact IPv6 flow label
firewall_config "alcatraz" ipfw ipfw \
"ipfw -q add 100 allow ip6 from any to any flow-id 0xbaad" \
"ipfw -q add 200 deny ipv6-icmp from any to any icmp6types 128 in"
# Check Flow Label matches
atf_check -s exit:0 ${common_dir}/pft_ping.py \
--sendif ${epair}a \
--fromaddr 2001:db8:42::1 \
--to 2001:db8:42::2 \
--send-fl $((0xbaad)) \
--replyif ${epair}a
# Check Flow Label mismatch
atf_check -s exit:1 ${common_dir}/pft_ping.py \
--sendif ${epair}a \
--fromaddr 2001:db8:42::1 \
--to 2001:db8:42::2 \
--send-fl $((0xf001)) \
--replyif ${epair}a
}
ipv6fl_cleanup()
{
firewall_cleanup $1
}
atf_init_test_cases()
{
atf_add_test_case "ipv6fl"
}