diff --git a/sys/netpfil/pf/pf_norm.c b/sys/netpfil/pf/pf_norm.c --- a/sys/netpfil/pf/pf_norm.c +++ b/sys/netpfil/pf/pf_norm.c @@ -157,7 +157,7 @@ #ifdef INET6 static int pf_reassemble6(struct mbuf **, struct ip6_hdr *, struct ip6_frag *, uint16_t, uint16_t, u_short *); -static void pf_scrub_ip6(struct mbuf **, uint8_t); +static void pf_scrub_ip6(struct mbuf **, uint32_t, uint8_t, uint8_t); #endif /* INET6 */ #define DPFPRINTF(x) do { \ @@ -1283,7 +1283,7 @@ if (sizeof(struct ip6_hdr) + plen > m->m_pkthdr.len) goto shortpkt; - pf_scrub_ip6(&m, r->min_ttl); + pf_scrub_ip6(&m, r->rule_flag, r->min_ttl, r->set_tos); return (PF_PASS); @@ -2032,7 +2032,7 @@ #ifdef INET6 static void -pf_scrub_ip6(struct mbuf **m0, u_int8_t min_ttl) +pf_scrub_ip6(struct mbuf **m0, u_int32_t flags, u_int8_t min_ttl, u_int8_t tos) { struct mbuf *m = *m0; struct ip6_hdr *h = mtod(m, struct ip6_hdr *); @@ -2040,5 +2040,11 @@ /* Enforce a minimum ttl, may cause endless packet loops */ if (min_ttl && h->ip6_hlim < min_ttl) h->ip6_hlim = min_ttl; + + /* Enforce tos. Set traffic class bits */ + if (flags & PFRULE_SET_TOS) { + h->ip6_flow &= IPV6_FLOWLABEL_MASK | IPV6_VERSION_MASK; + h->ip6_flow |= htonl((tos | IPV6_ECN(h)) << 20); + } } #endif diff --git a/tests/sys/netpfil/common/pft_ping.py b/tests/sys/netpfil/common/pft_ping.py --- a/tests/sys/netpfil/common/pft_ping.py +++ b/tests/sys/netpfil/common/pft_ping.py @@ -115,6 +115,14 @@ if icmp.data != PAYLOAD_MAGIC: return False + # Wait to check expectations until we've established this is the packet we + # sent. + if args.expect_tc: + if ip.tc != int(args.expect_tc[0]): + print("Unexpected traffic class value %d, expected %d" \ + % (ip.tc, int(args.expect_tc[0]))) + return False + return True def check_ping_reply(args, packet): @@ -147,6 +155,12 @@ if raw.load != PAYLOAD_MAGIC: return False + if args.expect_tos: + if ip.tos != int(args.expect_tos[0]): + print("Unexpected ToS value %d, expected %d" \ + % (ip.tos, int(args.expect_tos[0]))) + return False + return True def check_ping6_reply(args, packet): @@ -170,6 +184,12 @@ print("data mismatch") return False + if args.expect_tc: + if ip.tc != int(args.expect_tc[0]): + print("Unexpected traffic class value %d, expected %d" \ + % (ip.tc, int(args.expect_tc[0]))) + return False + return True def ping(send_if, dst_ip, args): @@ -192,8 +212,11 @@ ip6 = sp.IPv6(dst=dst_ip) icmp = sp.ICMPv6EchoRequest(data=sp.raw(PAYLOAD_MAGIC)) + if args.send_tc: + ip6.tc = int(args.send_tc[0]) + if args.fromaddr: - ip.src = args.fromaddr[0] + ip6.src = args.fromaddr[0] req = ether / ip6 / icmp sp.sendp(req, iface=send_if, verbose=False) @@ -274,10 +297,14 @@ # Packet settings parser.add_argument('--send-tos', nargs=1, help='Set the ToS value for the transmitted packet') + parser.add_argument('--send-tc', nargs=1, + help='Set the traffic class value for the transmitted packet') # Expectations parser.add_argument('--expect-tos', nargs=1, help='The expected ToS value in the received packet') + parser.add_argument('--expect-tc', nargs=1, + help='The expected traffic class value in the received packet') args = parser.parse_args() diff --git a/tests/sys/netpfil/pf/set_tos.sh b/tests/sys/netpfil/pf/set_tos.sh --- a/tests/sys/netpfil/pf/set_tos.sh +++ b/tests/sys/netpfil/pf/set_tos.sh @@ -4,6 +4,8 @@ # # Copyright (c) 2017 Kristof Provost # +# Copyright (c) 2021 Samuel Robinette +# # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: @@ -114,7 +116,93 @@ pft_cleanup } +atf_test_case "v6" "cleanup" +v6_head() +{ + atf_set descr 'set-tos6 test' + atf_set require.user root + + # We need scapy to be installed for out test scripts to work + atf_set require.progs scapy +} + +v6_body() +{ + pft_init + + epair=$(vnet_mkepair) + ifconfig ${epair}a inet6 add 2001:db8:192::1 + vnet_mkjail alcatraz ${epair}b + jexec alcatraz ifconfig ${epair}b inet6 add 2001:db8:192::2 + + route -6 add 2001:db8:192::2 2001:db8:192::1 + jexec alcatraz route -6 add 2001:db8:192::1 2001:db8:192::2 + + jexec alcatraz pfctl -e + + # No change is done if not requested + pft_set_rules alcatraz "scrub out proto ipv6-icmp" + atf_check -s exit:1 -o ignore -e ignore ${common_dir}/pft_ping.py \ + --ip6 \ + --sendif ${epair}a \ + --to 2001:db8:192::2 \ + --replyif ${epair}a \ + --expect-tc 42 + + # The requested ToS is set + pft_set_rules alcatraz "scrub out proto ipv6-icmp set-tos 42" + atf_check -s exit:0 -o ignore -e ignore ${common_dir}/pft_ping.py \ + --ip6 \ + --sendif ${epair}a \ + --to 2001:db8:192::2 \ + --replyif ${epair}a \ + --expect-tc 42 + + # ToS is not changed if the scrub rule does not match + pft_set_rules alcatraz "scrub out from 2001:db8:192::3 set-tos 42" + atf_check -s exit:1 -o ignore -e ignore ${common_dir}/pft_ping.py \ + --ip6 \ + --sendif ${epair}a \ + --to 2001:db8:192::2 \ + --replyif ${epair}a \ + --expect-tc 42 + + # Multiple scrub rules match as expected + pft_set_rules alcatraz "scrub out from 2001:db8:192::3 set-tos 13" \ + "scrub out proto ipv6-icmp set-tos 14" + atf_check -s exit:0 -o ignore -e ignore ${common_dir}/pft_ping.py \ + --ip6 \ + --sendif ${epair}a \ + --to 2001:db8:192::2 \ + --replyif ${epair}a \ + --expect-tc 14 + + # And this works even if the packet already has ToS values set + atf_check -s exit:0 -o ignore -e ignore ${common_dir}/pft_ping.py \ + --ip6 \ + --sendif ${epair}a \ + --to 2001:db8:192::2 \ + --replyif ${epair}a \ + --send-tc 42 \ + --expect-tc 14 + + # ToS values are unmolested if the packets do not match a scrub rule + pft_set_rules alcatraz "scrub out from 2001:db8:192::3 set-tos 13" + atf_check -s exit:0 -o ignore -e ignore ${common_dir}/pft_ping.py \ + --ip6 \ + --sendif ${epair}a \ + --to 2001:db8:192::2 \ + --replyif ${epair}a \ + --expect-tc 0 +} + +v6_cleanup() +{ + pft_cleanup +} + atf_init_test_cases() { atf_add_test_case "v4" + atf_add_test_case "v6" }