mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Andrea Mayer <andrea.mayer@uniroma2.it>
To: "David S. Miller" <davem@davemloft.net>,
	Eric Dumazet <edumazet@kernel.org>,
	Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
	Simon Horman <horms@kernel.org>, David Ahern <dsahern@kernel.org>,
	Ido Schimmel <idosch@nvidia.com>, Shuah Khan <shuah@kernel.org>
Cc: linux-kernel@vger.kernel.org, netdev@vger.kernel.org,
	linux-kselftest@vger.kernel.org, bpf@vger.kernel.org,
	David Lebrun <david.lebrun@uclouvain.be>,
	Daniel Borkmann <daniel@iogearbox.net>,
	Mathieu Xhonneux <m.xhonneux@gmail.com>,
	David Lebrun <dlebrun@google.com>,
	Hangbin Liu <hangbin.liu@linux.dev>,
	Stefano Salsano <stefano.salsano@uniroma2.it>,
	Andrea Mayer <andrea@common-net.org>,
	Andrea Mayer <andrea.mayer@uniroma2.it>
Subject: [PATCH net 6/6] selftests: seg6: add test for skb checksum with CHECKSUM_COMPLETE
Date: Sat, 31 Oct 2026 23:50:33 +0100	[thread overview]
Message-ID: <20261031225033.64-7-andrea.mayer@uniroma2.it> (raw)
In-Reply-To: <20261031225033.64-1-andrea.mayer@uniroma2.it>

Add a selftest for received packets with CHECKSUM_COMPLETE that the SRv6
code processes before they are delivered on the same node. The test checks
that, after the node changes the packet, UDP does not read a wrong
skb->csum.

The test uses a host and a router connected through a veth pair. Since veth
does not provide CHECKSUM_COMPLETE, a tc ct action on the router computes
skb->csum in software.

The host sends a UDP datagram to the router along five paths: no SRH (the
reference), an SRH with local segments, End.B6.Encaps with End.DT6, End
with the next-csid flavor, and End.BPF. Each datagram is expected to be
delivered, not dropped as a checksum error. Without the previous patches,
every path except the reference fails. With them, every path passes.

The host also sends a control datagram. It checks that the test detects a
wrong skb->csum after a change to the packet. After the ct action, a BPF
program on the router changes the Flow Label of the control datagram
without updating skb->csum, and UDP must drop it. If UDP accepts it, the
paths above may report a false pass, and the test fails.

Signed-off-by: Andrea Mayer <andrea.mayer@uniroma2.it>
---
 tools/testing/selftests/net/Makefile          |   1 +
 .../selftests/net/srv6_csum_complete.bpf.c    |  65 ++
 .../selftests/net/srv6_csum_complete_test.sh  | 642 ++++++++++++++++++
 3 files changed, 708 insertions(+)
 create mode 100644 tools/testing/selftests/net/srv6_csum_complete.bpf.c
 create mode 100755 tools/testing/selftests/net/srv6_csum_complete_test.sh

diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile
index df215e1a6262..16e9fbf889fd 100644
--- a/tools/testing/selftests/net/Makefile
+++ b/tools/testing/selftests/net/Makefile
@@ -92,6 +92,7 @@ TEST_PROGS := \
 	rxtimestamp.sh \
 	sctp_vrf.sh \
 	skf_net_off.sh \
+	srv6_csum_complete_test.sh \
 	srv6_encap_lookup_l3vpn_test.sh \
 	srv6_end_dt46_l3vpn_test.sh \
 	srv6_end_dt4_l3vpn_test.sh \
diff --git a/tools/testing/selftests/net/srv6_csum_complete.bpf.c b/tools/testing/selftests/net/srv6_csum_complete.bpf.c
new file mode 100644
index 000000000000..281b0361f976
--- /dev/null
+++ b/tools/testing/selftests/net/srv6_csum_complete.bpf.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Author: Andrea Mayer <andrea.mayer@uniroma2.it>
+ */
+#include <stddef.h>
+#include <linux/bpf.h>
+#include <linux/if_ether.h>
+#include <linux/ipv6.h>
+#include <linux/pkt_cls.h>
+#include <linux/seg6.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_endian.h>
+
+/* On the tc ingress, skb->data points to the Ethernet header */
+#define TC_IPV6_OFF		ETH_HLEN
+/* Byte with the lowest 8 bits of the Flow Label */
+#define TC_FLOW_LBL_OFF		(TC_IPV6_OFF + \
+				 offsetof(struct ipv6hdr, flow_lbl) + 2)
+/* Bit of the Flow Label changed in the control datagram */
+#define CTRL_FLOW_LBL_BIT	0x01
+
+/* In End.BPF, skb->data points to the IPv6 header, followed by the SRH */
+#define SRH_TAG_OFF		(sizeof(struct ipv6hdr) + \
+				 offsetof(struct ipv6_sr_hdr, tag))
+/* Value written in the SRH Tag, test case (vi) */
+#define SRH_TAG			0x1234
+
+/* Test case (v) of srv6_csum_complete_test.sh. The program runs on the tc
+ * ingress of rt, after the ct action, only for the control datagram. It
+ * changes one bit of the Flow Label, without updating the receive checksum
+ * (skb->csum). The Flow Label is not part of the UDP checksum, which stays
+ * correct. UDP must drop the datagram because the receive checksum no longer
+ * matches the packet.
+ */
+SEC("tc")
+int ctrl_flow_label(struct __sk_buff *skb)
+{
+	__u8 lbl;
+
+	/* Read the byte with the lowest 8 bits of the Flow Label */
+	if (bpf_skb_load_bytes(skb, TC_FLOW_LBL_OFF, &lbl, sizeof(lbl)))
+		return TC_ACT_SHOT;
+
+	lbl ^= CTRL_FLOW_LBL_BIT;
+
+	/* Write the byte back, flags 0 do not update the receive checksum */
+	if (bpf_skb_store_bytes(skb, TC_FLOW_LBL_OFF, &lbl, sizeof(lbl), 0))
+		return TC_ACT_SHOT;
+
+	return TC_ACT_OK;
+}
+
+/* Test case (vi), the End.BPF program writes the SRH Tag */
+SEC("lwt_seg6local")
+int end_bpf_tag(struct __sk_buff *skb)
+{
+	__u16 tag = bpf_htons(SRH_TAG);
+
+	if (bpf_lwt_seg6_store_bytes(skb, SRH_TAG_OFF, &tag, sizeof(tag)))
+		return BPF_DROP;
+
+	return BPF_OK;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/net/srv6_csum_complete_test.sh b/tools/testing/selftests/net/srv6_csum_complete_test.sh
new file mode 100755
index 000000000000..0b9c92931362
--- /dev/null
+++ b/tools/testing/selftests/net/srv6_csum_complete_test.sh
@@ -0,0 +1,642 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# author: Andrea Mayer <andrea.mayer@uniroma2.it>
+
+# This test checks the receive checksum of an SRv6 packet that is processed and
+# then delivered on the same node. The node changes the Segments Left and the
+# destination address of the packet, and can also change the Hop Limit.
+#
+# The receive checksum (CHECKSUM_COMPLETE) is the checksum of the whole packet,
+# kept with the received packet. When the node changes the packet, it must also
+# update this sum, or discard it.
+#
+# Network topology
+# ================
+#
+# The test uses a host (hs) and a router (rt) connected through a veth pair. In
+# each test case, hs sends one UDP datagram to an address of rt.
+#
+# +--------------------+                    +--------------------------+
+# |        hs          |                    |           rt             |
+# |                    |                    |                          |
+# |  veth-hs           +--------------------+ veth-rt                  |
+# |  fd00::1/64        |                    | fd00::2/64               |
+# |  tx checksum off   |                    | tc ingress: ct, then bpf |
+# +--------------------+                    |                          |
+#                                           | lo: fc00::1 ... fc00::4  |
+#                                           |     cafe::1 ... cafe::6  |
+#                                           +--------------------------+
+#
+# Receive checksum
+# ================
+#
+# veth does not provide the receive checksum. For this reason, a tc ct action
+# runs on the ingress of rt. The ct action checks the UDP checksum, and in this
+# way computes the receive checksum in software. If the receive checksum does
+# not match the packet, UDP drops the datagram as a checksum error.
+#
+# The ct action computes the receive checksum when hs computes the UDP checksum
+# in software, before the datagram crosses the veth. For this reason, hs has
+# transmit checksumming off.
+#
+# Each datagram is longer than 76 bytes, UDP header included. For datagrams of
+# 76 bytes or less, UDP computes the checksum again from the packet, and accepts
+# the datagram even with a wrong receive checksum.
+#
+# UDP counters
+# ============
+#
+# Each datagram goes to a port with no listener. If the UDP checksum check
+# passes, rt counts the datagram in Udp6NoPorts. If it fails, rt counts it in
+# Udp6InCsumErrors.
+#
+# Control datagram
+# ================
+#
+# A control datagram checks that the test detects a wrong receive checksum after
+# a change to the packet. After the ct action, a tc bpf filter on rt changes one
+# bit of its Flow Label, without updating the receive checksum. The Flow Label
+# is not part of the UDP checksum, which stays correct. UDP must drop the
+# datagram because the receive checksum no longer matches the packet. If UDP
+# accepts it, the test cannot detect a wrong receive checksum. The other test
+# cases may then pass even when their receive checksum is wrong, and for this
+# reason the test fails.
+#
+# Routes and tc filters
+# =====================
+#
+# hs: encap (main table)
+# +---------+-----------------------------------------------------+
+# | dst     | action                                              |
+# +---------+-----------------------------------------------------+
+# | cafe::1 | via fd00::2                                         |
+# | cafe::2 | encap seg6 mode inline segs fc00::1,fc00::2         |
+# | cafe::3 | encap seg6 mode inline segs fcbb:0:0200::           |
+# | cafe::4 | encap seg6 mode inline segs fcbb:0:0100:0200::      |
+# | cafe::5 | encap seg6 mode inline segs fc00::3,fc00::4         |
+# | cafe::6 | encap seg6 mode inline segs fc00::b0,fcbb:0:0200::  |
+# +---------+-----------------------------------------------------+
+#
+# rt: SRv6 behaviors (main table)
+# +------------------+------------------------------------------------+
+# | SID              | action                                         |
+# +------------------+------------------------------------------------+
+# | fcbb:0:0100::/48 | End flavors next-csid lblen 32 nflen 16        |
+# | fcbb:0:0200::/48 | End.B6.Encaps srh segs fc00::d6                |
+# | fc00::d6         | End.DT6 table 255 (local)                      |
+# | fc00::b0         | End.BPF, the program writes the SRH Tag        |
+# +------------------+------------------------------------------------+
+#
+# rt: tc ingress of veth-rt
+# +-------+------+---------------------------------------------------+
+# | chain | prio | filter                                            |
+# +-------+------+---------------------------------------------------+
+# | 0     | 1    | matchall, action ct, goto chain 1                 |
+# | 1     | 1    | flower dst_ip fc00::3, goto chain 2               |
+# | 2     | 1    | bpf, changes one bit of the Flow Label            |
+# +-------+------+---------------------------------------------------+
+#
+# Test cases
+# ==========
+#
+# hs applies the SRv6 Policies in inline mode, i.e. the original destination
+# becomes the last SID of the SID List.
+#
+#   i) cafe::1, no SRH, used as a reference.
+#
+#  ii) cafe::2, SID List=fc00::1,fc00::2,cafe::2
+#      fc00::1 and fc00::2 are addresses of rt. rt processes the SRH twice. Each
+#      time, it also decrements the Hop Limit.
+#
+# iii) cafe::3, SID List=fcbb:0:0200::,cafe::3
+#      End.B6.Encaps advances the SRH and adds an outer IPv6 header with an SRH.
+#      The outer header goes to the End.DT6 SID fc00::d6, which removes it.
+#
+#  iv) cafe::4, SID List=fcbb:0:0100:0200::,cafe::4
+#      End with the NEXT-C-SID flavor changes the DA to the next C-SID,
+#      fcbb:0:0200::. Then the datagram follows the same path as in (iii).
+#
+#   v) cafe::5, SID List=fc00::3,fc00::4,cafe::5, control.
+#      The tc flower filter matches the first segment, fc00::3, and the tc bpf
+#      filter changes the Flow Label. Then rt processes the SRH as in (ii).
+#
+#  vi) cafe::6, SID List=fc00::b0,fcbb:0:0200::,cafe::6
+#      End.BPF writes the SRH Tag. Then the datagram follows the same path as in
+#      (iii).
+#
+# In the test case (v), the datagram is expected to be dropped as a checksum
+# error. In all the other test cases, the datagram is expected to be delivered
+# on rt.
+
+# shellcheck source=lib.sh
+source lib.sh
+
+readonly IPv6_RT_NETWORK=fd00
+readonly IPv6_DST_NETWORK=cafe
+readonly SID_LOCATOR=fc00
+readonly CSID_BLOCK=fcbb:0
+readonly CSID_LBLEN=32
+readonly CSID_NFLEN=16
+readonly CSID_BLOCK_FUNC_LEN=$((CSID_LBLEN + CSID_NFLEN))
+readonly B6_FUNC=0200
+readonly CSID_FUNC=0100
+readonly DT6_FUNC=d6
+readonly BPF_FUNC=b0
+readonly DUMMY_DEVNAME=dum0
+readonly UDP_PORT=9
+readonly UDP_PAYLOAD_LEN=200
+readonly WAIT_TIMEOUT_SEC=5
+
+# destination of the control datagram on the veth, test case (v)
+readonly CTRL_DST="${SID_LOCATOR}::3"
+# tc chain of the flower filter that selects the control datagram
+readonly CTRL_CHAIN=1
+# tc chain of the bpf filter that changes the Flow Label
+readonly BPF_CHAIN=2
+
+readonly BPF_FILE=srv6_csum_complete.bpf.o
+readonly BPF_TC_SEC=tc
+readonly BPF_END_SEC=lwt_seg6local
+
+# return values of check_udp_delivery()
+readonly UDP_DELIVERED=0
+readonly UDP_LOST=1
+readonly UDP_CSUM_ERROR=2
+
+SETUP_ERR=1
+
+ret=${ksft_skip}
+nsuccess=0
+nfail=0
+
+log_test()
+{
+	log_test_expected "$1" "$2" "$3"
+}
+
+print_log_test_results()
+{
+	printf "\nTests passed: %3d\n" "${nsuccess}"
+	printf "Tests failed: %3d\n"   "${nfail}"
+
+	# when a test fails, the value of 'ret' is set to 1 (error code).
+	# Conversely, when all tests are passed successfully, the 'ret' value
+	# is set to 0 (success code).
+	if [ "${ret}" -ne 1 ]; then
+		ret=0
+	fi
+}
+
+log_section()
+{
+	echo
+	echo "################################################################################"
+	echo "TEST SECTION: $*"
+	echo "################################################################################"
+}
+
+cleanup()
+{
+	cleanup_all_ns
+
+	# check whether the setup phase was completed successfully or not. In
+	# case of an error during the setup phase of the testing environment,
+	# the selftest is considered as "skipped".
+	if [ "${SETUP_ERR}" -ne 0 ]; then
+		echo "SKIP: Setting up the testing environment failed"
+		exit "${ksft_skip}"
+	fi
+
+	exit "${ret}"
+}
+
+# add on hs the SRv6 Policy for cafe::<id>, in inline mode
+# args:
+#  $1 - destination id
+#  $2 - segments, comma separated
+set_hs_policy()
+{
+	local dstid="$1"
+	local segs="$2"
+
+	ip -netns "${NS_HS}" -6 route add "${IPv6_DST_NETWORK}::${dstid}/128" \
+		encap seg6 mode inline segs "${segs}" \
+		via "${IPv6_RT_NETWORK}::2" dev veth-hs
+}
+
+setup_hs()
+{
+	ip -netns "${NS_HS}" link set veth-hs up
+	ip -netns "${NS_HS}" addr add "${IPv6_RT_NETWORK}::1/64" \
+		dev veth-hs nodad
+
+	# with transmit checksumming on, conntrack on rt does not compute the
+	# receive checksum
+	ip netns exec "${NS_HS}" ethtool -K veth-hs tx off >/dev/null
+
+	# routes to the SIDs, used after the SRH is inserted
+	ip -netns "${NS_HS}" -6 route add "${SID_LOCATOR}::/16" \
+		via "${IPv6_RT_NETWORK}::2" dev veth-hs
+	ip -netns "${NS_HS}" -6 route add "${CSID_BLOCK}::/${CSID_LBLEN}" \
+		via "${IPv6_RT_NETWORK}::2" dev veth-hs
+
+	# test case (i), the reference, no SRH
+	ip -netns "${NS_HS}" -6 route add "${IPv6_DST_NETWORK}::1/128" \
+		via "${IPv6_RT_NETWORK}::2" dev veth-hs
+
+	# the SRv6 Policies of the test cases (ii) to (vi)
+	set_hs_policy 2 "${SID_LOCATOR}::1,${SID_LOCATOR}::2"
+	set_hs_policy 3 "${CSID_BLOCK}:${B6_FUNC}::"
+	set_hs_policy 4 "${CSID_BLOCK}:${CSID_FUNC}:${B6_FUNC}::"
+	set_hs_policy 5 "${SID_LOCATOR}::3,${SID_LOCATOR}::4"
+	set_hs_policy 6 "${SID_LOCATOR}::${BPF_FUNC},${CSID_BLOCK}:${B6_FUNC}::"
+}
+
+setup_rt()
+{
+	local dstid
+	local dev
+
+	ip -netns "${NS_RT}" link set veth-rt up
+	ip -netns "${NS_RT}" addr add "${IPv6_RT_NETWORK}::2/64" \
+		dev veth-rt nodad
+
+	# local segments of the test case (ii)
+	ip -netns "${NS_RT}" addr add "${SID_LOCATOR}::1/128" dev lo nodad
+	ip -netns "${NS_RT}" addr add "${SID_LOCATOR}::2/128" dev lo nodad
+	# local segments of the test case (v)
+	ip -netns "${NS_RT}" addr add "${SID_LOCATOR}::3/128" dev lo nodad
+	ip -netns "${NS_RT}" addr add "${SID_LOCATOR}::4/128" dev lo nodad
+	for dstid in 1 2 3 4 5 6; do
+		ip -netns "${NS_RT}" addr add \
+			"${IPv6_DST_NETWORK}::${dstid}/128" dev lo nodad
+	done
+
+	for dev in all veth-rt lo; do
+		ip netns exec "${NS_RT}" sysctl -wq \
+			net.ipv6.conf."${dev}".seg6_enabled=1
+	done
+
+	# SRv6 behaviors used by the test cases (iii), (iv) and (vi)
+	ip -netns "${NS_RT}" -6 route \
+		add "${CSID_BLOCK}:${CSID_FUNC}::/${CSID_BLOCK_FUNC_LEN}" \
+		encap seg6local action End flavors next-csid \
+		lblen "${CSID_LBLEN}" nflen "${CSID_NFLEN}" dev veth-rt
+	ip -netns "${NS_RT}" -6 route \
+		add "${CSID_BLOCK}:${B6_FUNC}::/${CSID_BLOCK_FUNC_LEN}" \
+		encap seg6local action End.B6.Encaps \
+		srh segs "${SID_LOCATOR}::${DT6_FUNC}" dev veth-rt
+	ip -netns "${NS_RT}" -6 route add "${SID_LOCATOR}::${DT6_FUNC}/128" \
+		encap seg6local action End.DT6 table 255 dev veth-rt
+	ip -netns "${NS_RT}" -6 route add "${SID_LOCATOR}::${BPF_FUNC}/128" \
+		encap seg6local action End.BPF endpoint obj "${BPF_FILE}" \
+		sec "${BPF_END_SEC}" dev veth-rt
+
+	tc -netns "${NS_RT}" qdisc add dev veth-rt clsact
+	# the bpf filter changes the Flow Label of the control datagram
+	tc -netns "${NS_RT}" filter add dev veth-rt ingress \
+		chain "${BPF_CHAIN}" prio 1 protocol ipv6 \
+		bpf object-file "${BPF_FILE}" section "${BPF_TC_SEC}" \
+		direct-action
+	# the control datagram goes to the bpf filter
+	tc -netns "${NS_RT}" filter add dev veth-rt ingress \
+		chain "${CTRL_CHAIN}" prio 1 protocol ipv6 \
+		flower dst_ip "${CTRL_DST}" \
+		action goto chain "${BPF_CHAIN}"
+	# every packet goes to the ct action, which computes the receive
+	# checksum, then to the chain of the flower filter
+	tc -netns "${NS_RT}" filter add dev veth-rt ingress prio 1 \
+		protocol ipv6 matchall \
+		action ct pipe action goto chain "${CTRL_CHAIN}"
+	ip netns exec "${NS_RT}" sysctl -wq \
+		net.netfilter.nf_conntrack_checksum=1
+}
+
+setup()
+{
+	setup_ns NS_HS NS_RT
+
+	ip -netns "${NS_HS}" link add veth-hs type veth \
+		peer name veth-rt netns "${NS_RT}"
+
+	setup_hs
+	setup_rt
+
+	# testing environment was set up successfully
+	SETUP_ERR=0
+}
+
+# print the value of a counter of rt in /proc/net/snmp6
+# args:
+#  $1 - counter name in /proc/net/snmp6
+get_rt_udp6_counter()
+{
+	local name="$1"
+
+	ip netns exec "${NS_RT}" cat /proc/net/snmp6 | \
+		awk -v k="${name}" '$1 == k { print $2 }'
+}
+
+# number of datagrams that UDP on rt counted in Udp6NoPorts or Udp6InCsumErrors
+get_rt_udp6_received()
+{
+	echo $(( $(get_rt_udp6_counter Udp6NoPorts) +
+		 $(get_rt_udp6_counter Udp6InCsumErrors) ))
+}
+
+check_hs_rt_connectivity()
+{
+	ip netns exec "${NS_HS}" ping -c 1 -W 1 "${IPv6_RT_NETWORK}::2" \
+		>/dev/null 2>&1
+}
+
+check_and_log_hs_rt_connectivity()
+{
+	check_hs_rt_connectivity
+	log_test $? 0 "Connectivity: hs -> rt"
+}
+
+connectivity_tests()
+{
+	log_section "IPv6 connectivity test between host and router"
+
+	check_and_log_hs_rt_connectivity
+}
+
+# Send one UDP datagram from hs to a port with no listener on rt.
+# args:
+#  $1 - destination id
+# return:
+#  - UDP_DELIVERED if rt counts the datagram as delivered
+#  - UDP_CSUM_ERROR if rt drops it for a bad checksum
+#  - UDP_LOST if the datagram cannot be sent, or if it does not arrive
+check_udp_delivery()
+{
+	local dstid="$1"
+	local noports
+	local payload
+	local errors
+	local recv
+	local dst
+
+	dst="${IPv6_DST_NETWORK}::${dstid}"
+	noports="$(get_rt_udp6_counter Udp6NoPorts)"
+	errors="$(get_rt_udp6_counter Udp6InCsumErrors)"
+	recv=$((noports + errors))
+	payload="$(printf '%*s' "${UDP_PAYLOAD_LEN}" '')"
+
+	# send the datagram with the /dev/udp redirection of bash
+	if ! ip netns exec "${NS_HS}" bash -c "printf '%s' '${payload}' > \
+			/dev/udp/${dst}/${UDP_PORT}"; then
+		return "${UDP_LOST}"
+	fi
+
+	# wait until rt counts the datagram in Udp6NoPorts or Udp6InCsumErrors
+	if ! slowwait "${WAIT_TIMEOUT_SEC}" until_counter_is \
+			">= $((recv + 1))" get_rt_udp6_received >/dev/null; then
+		return "${UDP_LOST}"
+	fi
+
+	if [ "$(get_rt_udp6_counter Udp6InCsumErrors)" -ne "${errors}" ]; then
+		return "${UDP_CSUM_ERROR}"
+	fi
+
+	if [ "$(get_rt_udp6_counter Udp6NoPorts)" -ne "$((noports + 1))" ]; then
+		return "${UDP_LOST}"
+	fi
+
+	return "${UDP_DELIVERED}"
+}
+
+# check the delivery of a datagram to cafe::<id> and log the result
+# args:
+#  $1 - destination id
+#  $2 - expected return value of check_udp_delivery()
+#  $3 - description of the path
+check_and_log_udp_delivery()
+{
+	local dstid="$1"
+	local expected="$2"
+	local desc="$3"
+
+	check_udp_delivery "${dstid}"
+	log_test $? "${expected}" \
+		"UDP: hs -> ${IPv6_DST_NETWORK}::${dstid} (${desc})"
+}
+
+udp_delivery_tests()
+{
+	log_section "UDP delivery test with CHECKSUM_COMPLETE"
+
+	check_and_log_udp_delivery 1 "${UDP_DELIVERED}" "no SRH"
+	check_and_log_udp_delivery 2 "${UDP_DELIVERED}" "SRH, local segments"
+	check_and_log_udp_delivery 3 "${UDP_DELIVERED}" "End.B6.Encaps, End.DT6"
+	check_and_log_udp_delivery 4 "${UDP_DELIVERED}" \
+		"End next-csid, End.B6.Encaps, End.DT6"
+	check_and_log_udp_delivery 5 "${UDP_CSUM_ERROR}" "control, csum error"
+	check_and_log_udp_delivery 6 "${UDP_DELIVERED}" \
+		"End.BPF, End.B6.Encaps, End.DT6"
+}
+
+test_command_or_ksft_skip()
+{
+	local cmd="$1"
+
+	if [ ! -x "$(command -v "${cmd}")" ]; then
+		echo "SKIP: Could not run test without \"${cmd}\" tool"
+		exit "${ksft_skip}"
+	fi
+}
+
+test_dummy_dev_or_ksft_skip()
+{
+	local test_netns
+
+	setup_ns test_netns
+
+	modprobe dummy &>/dev/null || true
+	if ! ip -netns "${test_netns}" link add "${DUMMY_DEVNAME}" \
+			type dummy; then
+		cleanup_ns "${test_netns}"
+		echo "SKIP: dummy dev not supported"
+		exit "${ksft_skip}"
+	fi
+
+	cleanup_ns "${test_netns}"
+}
+
+test_iproute2_supp_or_ksft_skip()
+{
+	if ! ip route help 2>&1 | grep -qo next-csid; then
+		echo "SKIP: Missing SRv6 next-csid flavor support in iproute2"
+		exit "${ksft_skip}"
+	fi
+}
+
+test_kernel_supp_or_ksft_skip()
+{
+	local test_netns
+
+	setup_ns test_netns
+
+	ip -netns "${test_netns}" link add "${DUMMY_DEVNAME}" type dummy
+	ip -netns "${test_netns}" link set "${DUMMY_DEVNAME}" up
+
+	if ! ip -netns "${test_netns}" -6 route \
+			add "${CSID_BLOCK}::/${CSID_LBLEN}" encap seg6local \
+			action End flavors next-csid dev "${DUMMY_DEVNAME}" \
+			2>/dev/null; then
+		cleanup_ns "${test_netns}"
+		echo "SKIP: SRv6 next-csid flavor not supported"
+		exit "${ksft_skip}"
+	fi
+
+	cleanup_ns "${test_netns}"
+}
+
+test_veth_tx_csum_off_or_ksft_skip()
+{
+	local test_netns
+
+	setup_ns test_netns
+
+	if ! ip -netns "${test_netns}" link add veth-t0 type veth \
+			peer name veth-t1 2>/dev/null; then
+		cleanup_ns "${test_netns}"
+		echo "SKIP: veth dev not supported"
+		exit "${ksft_skip}"
+	fi
+
+	# setup_hs() turns off transmit checksumming on veth-hs. If it stays on,
+	# conntrack does not compute the receive checksum and the test cannot
+	# see the error.
+	ip netns exec "${test_netns}" ethtool -K veth-t0 tx off >/dev/null 2>&1
+
+	if ! ip netns exec "${test_netns}" ethtool -k veth-t0 | \
+			grep -q "^tx-checksumming: off"; then
+		cleanup_ns "${test_netns}"
+		echo "SKIP: Cannot turn off transmit checksumming on veth"
+		exit "${ksft_skip}"
+	fi
+
+	cleanup_ns "${test_netns}"
+}
+
+test_bpf_file_or_ksft_skip()
+{
+	if [ ! -f "${BPF_FILE}" ]; then
+		echo "SKIP: Missing ${BPF_FILE}, run 'make' first"
+		exit "${ksft_skip}"
+	fi
+}
+
+test_tc_or_ksft_skip()
+{
+	local test_netns
+
+	setup_ns test_netns
+
+	ip -netns "${test_netns}" link add "${DUMMY_DEVNAME}" type dummy
+
+	if ! tc -netns "${test_netns}" qdisc add dev "${DUMMY_DEVNAME}" \
+			clsact 2>/dev/null; then
+		cleanup_ns "${test_netns}"
+		echo "SKIP: tc clsact qdisc not supported"
+		exit "${ksft_skip}"
+	fi
+
+	if ! tc -netns "${test_netns}" filter add dev "${DUMMY_DEVNAME}" \
+			ingress chain "${BPF_CHAIN}" protocol ipv6 \
+			bpf object-file "${BPF_FILE}" section "${BPF_TC_SEC}" \
+			direct-action 2>/dev/null; then
+		cleanup_ns "${test_netns}"
+		echo "SKIP: tc bpf filter not supported"
+		exit "${ksft_skip}"
+	fi
+
+	if ! tc -netns "${test_netns}" filter add dev "${DUMMY_DEVNAME}" \
+			ingress chain "${CTRL_CHAIN}" protocol ipv6 \
+			flower dst_ip "${CTRL_DST}" \
+			action goto chain "${BPF_CHAIN}" 2>/dev/null; then
+		cleanup_ns "${test_netns}"
+		echo "SKIP: tc flower filter or goto chain action not supported"
+		exit "${ksft_skip}"
+	fi
+
+	if ! tc -netns "${test_netns}" filter add dev "${DUMMY_DEVNAME}" \
+			ingress protocol ipv6 matchall \
+			action ct pipe action goto chain "${CTRL_CHAIN}" \
+			2>/dev/null; then
+		cleanup_ns "${test_netns}"
+		echo "SKIP: tc matchall filter or ct action not supported"
+		exit "${ksft_skip}"
+	fi
+
+	# setup_rt() turns on nf_conntrack_checksum. If it is off, conntrack
+	# does not check the UDP checksum and does not compute the receive
+	# checksum. The test cannot see the error.
+	if ! ip netns exec "${test_netns}" sysctl -wq \
+			net.netfilter.nf_conntrack_checksum=1 2>/dev/null; then
+		cleanup_ns "${test_netns}"
+		echo "SKIP: nf_conntrack_checksum sysctl not available"
+		exit "${ksft_skip}"
+	fi
+
+	cleanup_ns "${test_netns}"
+}
+
+test_end_bpf_or_ksft_skip()
+{
+	local test_netns
+
+	setup_ns test_netns
+
+	ip -netns "${test_netns}" link add "${DUMMY_DEVNAME}" type dummy
+	ip -netns "${test_netns}" link set "${DUMMY_DEVNAME}" up
+
+	if ! ip -netns "${test_netns}" -6 route \
+			add "${SID_LOCATOR}::${BPF_FUNC}/128" encap seg6local \
+			action End.BPF endpoint obj "${BPF_FILE}" \
+			sec "${BPF_END_SEC}" dev "${DUMMY_DEVNAME}" \
+			2>/dev/null; then
+		cleanup_ns "${test_netns}"
+		echo "SKIP: SRv6 End.BPF not supported"
+		exit "${ksft_skip}"
+	fi
+
+	cleanup_ns "${test_netns}"
+}
+
+if [ "$(id -u)" -ne 0 ]; then
+	echo "SKIP: Need root privileges"
+	exit "${ksft_skip}"
+fi
+
+# required programs to carry out this selftest
+test_command_or_ksft_skip ip
+test_command_or_ksft_skip tc
+test_command_or_ksft_skip ethtool
+test_command_or_ksft_skip ping
+test_command_or_ksft_skip sysctl
+test_command_or_ksft_skip awk
+test_command_or_ksft_skip grep
+
+test_dummy_dev_or_ksft_skip
+test_iproute2_supp_or_ksft_skip
+test_kernel_supp_or_ksft_skip
+test_veth_tx_csum_off_or_ksft_skip
+test_bpf_file_or_ksft_skip
+test_tc_or_ksft_skip
+test_end_bpf_or_ksft_skip
+
+set -e
+trap cleanup EXIT
+
+setup
+set +e
+
+connectivity_tests
+udp_delivery_tests
+
+print_log_test_results
-- 
2.43.0


  parent reply	other threads:[~2026-10-31 22:52 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-31 22:50 [PATCH net 0/6] seg6: fix skb checksum updates " Andrea Mayer
2026-10-31 22:50 ` [PATCH net 1/6] seg6: fix skb checksum update helpers Andrea Mayer
2026-11-03  1:25   ` Hangbin Liu
2026-10-31 22:50 ` [PATCH net 2/6] seg6: update skb checksum when ipv6_srh_rcv() decrements the Hop Limit Andrea Mayer
2026-11-03  1:27   ` Hangbin Liu
2026-10-31 22:50 ` [PATCH net 3/6] seg6: update skb checksum when SRv6 behaviors advance to the next SID Andrea Mayer
2026-11-03  2:15   ` Hangbin Liu
2026-10-31 22:50 ` [PATCH net 4/6] seg6: discard CHECKSUM_COMPLETE after the End.BPF program Andrea Mayer
2026-11-03  2:28   ` Hangbin Liu
2026-10-31 22:50 ` [PATCH net 5/6] seg6: account for the MAC header in skb checksum on L2 encapsulation Andrea Mayer
2026-11-03  2:37   ` Hangbin Liu
2026-10-31 22:50 ` Andrea Mayer [this message]
2026-11-03  3:26   ` [PATCH net 6/6] selftests: seg6: add test for skb checksum with CHECKSUM_COMPLETE Hangbin Liu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261031225033.64-7-andrea.mayer@uniroma2.it \
    --to=andrea.mayer@uniroma2.it \
    --cc=andrea@common-net.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=davem@davemloft.net \
    --cc=david.lebrun@uclouvain.be \
    --cc=dlebrun@google.com \
    --cc=dsahern@kernel.org \
    --cc=edumazet@kernel.org \
    --cc=hangbin.liu@linux.dev \
    --cc=horms@kernel.org \
    --cc=idosch@nvidia.com \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=m.xhonneux@gmail.com \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=shuah@kernel.org \
    --cc=stefano.salsano@uniroma2.it \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®