diff mbox series

[nft,2/2] proto: use NFT_PAYLOAD_L4CSUM_PSEUDOHDR flag to mangle UDP checksum

Message ID 20240909131339.290879-2-pablo@netfilter.org
State Accepted
Headers show
Series [nft,1/2] tests: shell: stabilize packetpath/payload | expand

Commit Message

Pablo Neira Ayuso Sept. 9, 2024, 1:13 p.m. UTC
There are two mechanisms to update the UDP checksum field:

 1) _CSUM_TYPE and _CSUM_OFFSET, which specifies 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 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                  |   2 +
 src/proto.c                              |   2 -
 tests/shell/testcases/packetpath/payload | 129 +++++++++++++++++------
 3 files changed, 99 insertions(+), 34 deletions(-)
diff mbox series

Patch

diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index abda903bc59c..77bc51493293 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -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);
diff --git a/src/proto.c b/src/proto.c
index 553b6a447a7e..05ddb070662b 100644
--- a/src/proto.c
+++ b/src/proto.c
@@ -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),
diff --git a/tests/shell/testcases/packetpath/payload b/tests/shell/testcases/packetpath/payload
index 1fb86aa94b17..83e0b7fc647a 100755
--- a/tests/shell/testcases/packetpath/payload
+++ b/tests/shell/testcases/packetpath/payload
@@ -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