]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
proto: use NFT_PAYLOAD_L4CSUM_PSEUDOHDR flag to mangle UDP checksum
authorPablo Neira Ayuso <pablo@netfilter.org>
Mon, 9 Sep 2024 10:48:33 +0000 (12:48 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Tue, 10 Sep 2024 13:52:38 +0000 (15:52 +0200)
There are two mechanisms to update the UDP checksum field:

 1) _CSUM_TYPE and _CSUM_OFFSET which specify the type of checksum
    (e.g. inet) and offset where it is located.
 2) use NFT_PAYLOAD_L4CSUM_PSEUDOHDR flag to use layer 4 kernel
    protocol parser.

The problem with 1) is that it is inconditional, that is, csum_type and
csum_offset cannot deal with zero UDP checksum.

Use NFT_PAYLOAD_L4CSUM_PSEUDOHDR flag instead since it relies on the
layer 4 kernel parser which skips updating zero UDP checksum.

Extend test coverage for the UDP mangling with and without zero
checksum.

Fixes: e6c9174e13b2 ("proto: add checksum key information to struct proto_desc")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
src/netlink_linearize.c
src/proto.c
tests/shell/testcases/packetpath/payload

index abda903bc59cff545e1ec47c88aa5d9db131ce39..77bc514932936430d2ad29c6b5e59d42308ea0f1 100644 (file)
@@ -1117,6 +1117,8 @@ static void netlink_gen_payload_stmt(struct netlink_linearize_ctx *ctx,
        }
        if ((expr->payload.base == PROTO_BASE_NETWORK_HDR && desc &&
             payload_needs_l4csum_update_pseudohdr(expr, desc)) ||
+           (expr->payload.base == PROTO_BASE_TRANSPORT_HDR && desc &&
+            desc == &proto_udp) ||
            expr->payload.base == PROTO_BASE_INNER_HDR)
                nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_FLAGS,
                                   NFT_PAYLOAD_L4CSUM_PSEUDOHDR);
index 553b6a447a7e78538661b85e1dbc3ccec41ca2a8..05ddb070662b48c5a5378c22477984375efb6f62 100644 (file)
@@ -535,8 +535,6 @@ const struct proto_desc proto_udp = {
        .name           = "udp",
        .id             = PROTO_DESC_UDP,
        .base           = PROTO_BASE_TRANSPORT_HDR,
-       .checksum_key   = UDPHDR_CHECKSUM,
-       .checksum_type  = NFT_PAYLOAD_CSUM_INET,
        .templates      = {
                [UDPHDR_SPORT]          = INET_SERVICE("sport", struct udphdr, source),
                [UDPHDR_DPORT]          = INET_SERVICE("dport", struct udphdr, dest),
index 1fb86aa94b17aa69d34dcee82fd736d306a4a0c8..83e0b7fc647acd2899f3c95dee4ac56c24a442cf 100755 (executable)
@@ -19,6 +19,28 @@ run_test()
        ns1_addr=$2
        ns2_addr=$3
        cidr=$4
+       mode=$5
+
+       case $mode in
+       "udp")
+               l4proto="udp"
+               udp_checksum="udp checksum != 0"
+               udp_zero_checksum=""
+               ;;
+       "udp-zero-checksum")
+               l4proto="udp"
+               udp_checksum="udp checksum 0"
+               udp_zero_checksum="udp checksum set 0"
+               ;;
+       "tcp")
+               l4proto="tcp"
+               udp_checksum=""
+               udp_zero_checksum=""
+               ;;
+       *)
+               echo "unexpected, incorrect mode"
+               exit 0
+       esac
 
        # socat needs square brackets, ie. [abcd::2]
        if [ $1 -eq 6 ]; then
@@ -54,16 +76,18 @@ RULESET="table netdev payload_netdev {
 
        chain ingress {
                type filter hook ingress device veth0 priority 0;
-               tcp dport 7777 counter name ingress
-               tcp dport 7778 tcp dport set 7779 counter name mangle_ingress
-               tcp dport 7779 counter name mangle_ingress_match
+               $udp_zero_checksum
+               $l4proto dport 7777 counter name ingress
+               $l4proto dport 7778 $l4proto dport set 7779 $udp_checksum counter name mangle_ingress
+               $l4proto dport 7779 counter name mangle_ingress_match
        }
 
        chain egress {
                type filter hook egress device veth0 priority 0;
-               tcp dport 8887 counter name egress
-               tcp dport 8888 tcp dport set 8889 counter name mangle_egress
-               tcp dport 8889 counter name mangle_egress_match
+               $udp_zero_checksum
+               $l4proto dport 8887 counter name egress
+               $l4proto dport 8888 $l4proto dport set 8889 $udp_checksum counter name mangle_egress
+               $l4proto dport 8889 counter name mangle_egress_match
        }
 }
 
@@ -77,32 +101,51 @@ table inet payload_inet {
 
        chain in {
                type filter hook input priority 0;
-               tcp dport 7770 counter name input
-               tcp dport 7771 tcp dport set 7772 counter name mangle_input
-               tcp dport 7772 counter name mangle_input_match
+               $udp_zero_checksum
+               $l4proto dport 7770 counter name input
+               $l4proto dport 7771 $l4proto dport set 7772 $udp_checksum counter name mangle_input
+               $l4proto dport 7772 counter name mangle_input_match
        }
 
        chain out {
                type filter hook output priority 0;
-               tcp dport 8880 counter name output
-               tcp dport 8881 tcp dport set 8882 counter name mangle_output
-               tcp dport 8882 counter name mangle_output_match
+               $udp_zero_checksum
+               $l4proto dport 8880 counter name output
+               $l4proto dport 8881 $l4proto dport set 8882 $udp_checksum counter name mangle_output
+               $l4proto dport 8882 counter name mangle_output_match
         }
 }"
 
        ip netns exec "$ns1" $NFT -f - <<< "$RULESET" || exit 1
 
-       ip netns exec "$ns1" socat -u STDIN TCP:$nsx2_addr:8887,connect-timeout=4 < /dev/null > /dev/null
-       ip netns exec "$ns1" socat -u STDIN TCP:$nsx2_addr:8888,connect-timeout=4 < /dev/null > /dev/null
+       case $l4proto in
+       "tcp")
+               ip netns exec "$ns1" socat -u STDIN TCP:$nsx2_addr:8887,connect-timeout=4 < /dev/null > /dev/null
+               ip netns exec "$ns1" socat -u STDIN TCP:$nsx2_addr:8888,connect-timeout=4 < /dev/null > /dev/null
 
-       ip netns exec "$ns1" socat -u STDIN TCP:$nsx2_addr:8880,connect-timeout=4 < /dev/null > /dev/null
-       ip netns exec "$ns1" socat -u STDIN TCP:$nsx2_addr:8881,connect-timeout=4 < /dev/null > /dev/null
+               ip netns exec "$ns1" socat -u STDIN TCP:$nsx2_addr:8880,connect-timeout=4 < /dev/null > /dev/null
+               ip netns exec "$ns1" socat -u STDIN TCP:$nsx2_addr:8881,connect-timeout=4 < /dev/null > /dev/null
 
-       ip netns exec "$ns2" socat -u STDIN TCP:$nsx1_addr:7777,connect-timeout=4 < /dev/null > /dev/null
-       ip netns exec "$ns2" socat -u STDIN TCP:$nsx1_addr:7778,connect-timeout=4 < /dev/null > /dev/null
+               ip netns exec "$ns2" socat -u STDIN TCP:$nsx1_addr:7777,connect-timeout=4 < /dev/null > /dev/null
+               ip netns exec "$ns2" socat -u STDIN TCP:$nsx1_addr:7778,connect-timeout=4 < /dev/null > /dev/null
 
-       ip netns exec "$ns2" socat -u STDIN TCP:$nsx1_addr:7770,connect-timeout=4 < /dev/null > /dev/null
-       ip netns exec "$ns2" socat -u STDIN TCP:$nsx1_addr:7771,connect-timeout=4 < /dev/null > /dev/null
+               ip netns exec "$ns2" socat -u STDIN TCP:$nsx1_addr:7770,connect-timeout=4 < /dev/null > /dev/null
+               ip netns exec "$ns2" socat -u STDIN TCP:$nsx1_addr:7771,connect-timeout=4 < /dev/null > /dev/null
+               ;;
+       "udp")
+               ip netns exec "$ns1" bash -c "echo 'AA' | socat -u STDIN UDP:$nsx2_addr:8887 > /dev/null"
+               ip netns exec "$ns1" bash -c "echo 'AA' | socat -u STDIN UDP:$nsx2_addr:8888 > /dev/null"
+
+               ip netns exec "$ns1" bash -c "echo 'AA' | socat -u STDIN UDP:$nsx2_addr:8880 > /dev/null"
+               ip netns exec "$ns1" bash -c "echo 'AA' | socat -u STDIN UDP:$nsx2_addr:8881 > /dev/null"
+
+               ip netns exec "$ns2" bash -c "echo 'AA' | socat -u STDIN UDP:$nsx1_addr:7777 > /dev/null"
+               ip netns exec "$ns2" bash -c "echo 'AA' | socat -u STDIN UDP:$nsx1_addr:7778 > /dev/null"
+
+               ip netns exec "$ns2" bash -c "echo 'AA' | socat -u STDIN UDP:$nsx1_addr:7770 > /dev/null"
+               ip netns exec "$ns2" bash -c "echo 'AA' | socat -u STDIN UDP:$nsx1_addr:7771 > /dev/null"
+               ;;
+       esac
 
        ip netns exec "$ns1" $NFT list ruleset
 
@@ -149,26 +192,39 @@ RULESET="table bridge payload_bridge {
 
        chain in {
                type filter hook input priority 0;
-               tcp dport 7770 counter name input
-               tcp dport 7771 tcp dport set 7772 counter name mangle_input
-               tcp dport 7772 counter name mangle_input_match
+               $udp_zero_checksum
+               $l4proto dport 7770 counter name input
+               $l4proto dport 7771 $l4proto dport set 7772 $udp_checksum counter name mangle_input
+               $l4proto dport 7772 counter name mangle_input_match
        }
 
        chain out {
                type filter hook output priority 0;
-               tcp dport 8880 counter name output
-               tcp dport 8881 tcp dport set 8882 counter name mangle_output
-               tcp dport 8882 counter name mangle_output_match
+               $udp_zero_checksum
+               $l4proto dport 8880 counter name output
+               $l4proto dport 8881 $l4proto dport set 8882 $udp_checksum counter name mangle_output
+               $l4proto dport 8882 counter name mangle_output_match
         }
 }"
 
        ip netns exec "$ns1" $NFT -f - <<< "$RULESET" || exit 1
 
-       ip netns exec "$ns1" socat -u STDIN TCP:$nsx2_addr:8880,connect-timeout=4 < /dev/null > /dev/null
-       ip netns exec "$ns1" socat -u STDIN TCP:$nsx2_addr:8881,connect-timeout=4 < /dev/null > /dev/null
+       case $l4proto in
+       "tcp")
+               ip netns exec "$ns1" socat -u STDIN TCP:$nsx2_addr:8880,connect-timeout=4 < /dev/null > /dev/null
+               ip netns exec "$ns1" socat -u STDIN TCP:$nsx2_addr:8881,connect-timeout=4 < /dev/null > /dev/null
+
+               ip netns exec "$ns2" socat -u STDIN TCP:$nsx1_addr:7770,connect-timeout=4 < /dev/null > /dev/null
+               ip netns exec "$ns2" socat -u STDIN TCP:$nsx1_addr:7771,connect-timeout=4 < /dev/null > /dev/null
+               ;;
+       "udp")
+               ip netns exec "$ns1" bash -c "echo 'AA' | socat -u STDIN UDP:$nsx2_addr:8880 > /dev/null"
+               ip netns exec "$ns1" bash -c "echo 'AA' | socat -u STDIN UDP:$nsx2_addr:8881 > /dev/null"
 
-       ip netns exec "$ns2" socat -u STDIN TCP:$nsx1_addr:7770,connect-timeout=4 < /dev/null > /dev/null
-       ip netns exec "$ns2" socat -u STDIN TCP:$nsx1_addr:7771,connect-timeout=4 < /dev/null > /dev/null
+               ip netns exec "$ns2" bash -c "echo 'AA' | socat -u STDIN UDP:$nsx1_addr:7770 > /dev/null"
+               ip netns exec "$ns2" bash -c "echo 'AA' | socat -u STDIN UDP:$nsx1_addr:7771 > /dev/null"
+               ;;
+       esac
 
        ip netns exec "$ns1" $NFT list ruleset
 
@@ -180,7 +236,16 @@ RULESET="table bridge payload_bridge {
        ip netns exec "$ns1" $NFT list counter bridge payload_bridge mangle_output_match | grep -q "packets 0" && exit 1
 }
 
-run_test "4" "10.141.10.2" "10.141.10.3" "24"
+run_test "4" "10.141.10.2" "10.141.10.3" "24" "tcp"
+cleanup
+run_test 6 "abcd::2" "abcd::3" "64" "tcp"
+cleanup
+run_test "4" "10.141.10.2" "10.141.10.3" "24" "udp"
+cleanup
+run_test 6 "abcd::2" "abcd::3" "64" "udp"
+cleanup
+run_test "4" "10.141.10.2" "10.141.10.3" "24" "udp-zero-checksum"
 cleanup
-run_test 6 "abcd::2" "abcd::3" "64"
+run_test 6 "abcd::2" "abcd::3" "64" "udp-zero-checksum"
 # trap calls cleanup
+exit 0