Date: Tue, 12 May 2026 07:48:45 +0000 From: Andrey V. Elsukov <ae@FreeBSD.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org Cc: Boris Lytochkin <lytboris@gmail.com> Subject: git: 3d39eadcdeb3 - main - ipfw: fix IPv6 flow label matching Message-ID: <6a02db5d.23ba8.7e41e5d9@gitrepo.freebsd.org>
index | next in thread | raw e-mail
The branch main has been updated by ae: URL: https://cgit.FreeBSD.org/src/commit/?id=3d39eadcdeb301e95abdc94b1ad5d1255fa0f446 commit 3d39eadcdeb301e95abdc94b1ad5d1255fa0f446 Author: Boris Lytochkin <lytboris@gmail.com> AuthorDate: 2026-05-12 07:44:10 +0000 Commit: Andrey V. Elsukov <ae@FreeBSD.org> CommitDate: 2026-05-12 07:44:10 +0000 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 --- sbin/ipfw/ipfw2.c | 5 +-- sys/netpfil/ipfw/ip_fw2.c | 4 +- tests/sys/netpfil/common/pft_ping.py | 13 +++++- tests/sys/netpfil/ipfw/Makefile | 1 + tests/sys/netpfil/ipfw/ipv6-flow-id.sh | 78 ++++++++++++++++++++++++++++++++++ 5 files changed, 95 insertions(+), 6 deletions(-) diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c index e0e1339a1dce..06a1ee937cd7 100644 --- a/sbin/ipfw/ipfw2.c +++ b/sbin/ipfw/ipfw2.c @@ -5515,10 +5515,7 @@ read_options: break; case TOK_FLOWID: - if (proto != IPPROTO_IPV6 ) - errx( EX_USAGE, "flow-id filter is active " - "only for ipv6 protocol\n"); - fill_flow6( (ipfw_insn_u32 *) cmd, *av, cblen); + fill_flow6(insntod(cmd, u32), *av, cblen); av++; break; diff --git a/sys/netpfil/ipfw/ip_fw2.c b/sys/netpfil/ipfw/ip_fw2.c index 10690920e6fd..133aeeb969a6 100644 --- a/sys/netpfil/ipfw/ip_fw2.c +++ b/sys/netpfil/ipfw/ip_fw2.c @@ -780,7 +780,9 @@ static int flow6id_match(int curr_flow, ipfw_insn_u32 *cmd) { int i; - for (i=0; i <= cmd->o.arg1; ++i) + /* Mask proto version and traffic class out before comparing flow-id */ + curr_flow &= ntohl(IPV6_FLOWLABEL_MASK); + for (i=0; i < cmd->o.arg1; ++i) if (curr_flow == cmd->d[i]) return 1; return 0; diff --git a/tests/sys/netpfil/common/pft_ping.py b/tests/sys/netpfil/common/pft_ping.py index a2a1d9c7f4ec..ada4f8607ab0 100644 --- a/tests/sys/netpfil/common/pft_ping.py +++ b/tests/sys/netpfil/common/pft_ping.py @@ -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}') diff --git a/tests/sys/netpfil/ipfw/Makefile b/tests/sys/netpfil/ipfw/Makefile index 52f898050267..0be870945891 100644 --- a/tests/sys/netpfil/ipfw/Makefile +++ b/tests/sys/netpfil/ipfw/Makefile @@ -4,6 +4,7 @@ TESTSDIR= ${TESTSBASE}/sys/netpfil/ipfw ATF_TESTS_SH+= fwd \ divert \ + ipv6-flow-id \ log \ lookup \ table diff --git a/tests/sys/netpfil/ipfw/ipv6-flow-id.sh b/tests/sys/netpfil/ipfw/ipv6-flow-id.sh new file mode 100644 index 000000000000..fd3ef2f6cd81 --- /dev/null +++ b/tests/sys/netpfil/ipfw/ipv6-flow-id.sh @@ -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" +}home | help
Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a02db5d.23ba8.7e41e5d9>
