[PATCH net v2 2/2] selftests: net: exercise SO_MARK on connected sockets
From: Xia Yan
Date: Wed Nov 04 2026 - 04:25:04 EST
cmsg_so_mark.sh sends to an explicit destination without connecting the
socket, so it does not exercise the route cached by connect(). This left
the IPv6 SO_MARK cache bug uncovered.
Add a cmsg_sender option to connect before sending and omit msg_name in
that mode. Keep the existing cases and add connected IPv4 and IPv6 UDP,
ping and raw sends with a per-message mark equal to the socket mark,
prohibited by a policy rule, or explicitly zero.
On UML, the added IPv6 UDP and ping cases with the prohibited mark fail
before the kernel fix. With the fix, all 54 cases pass.
Signed-off-by: Xia Yan <yanxia@xxxxxxxxxxxxxxxxxxxx>
---
v2:
- New patch; add 18 connected-socket cases alongside the existing 36.
The test gap was noted during automated review of v1:
https://lore.kernel.org/netdev/179371875338.488856.4546363285391899557@xxxxxxxxxx/
Tested the same selftest on baseline, v1 and v2 UML kernels: baseline
fails 2/54 cases; v1/v2 pass all 54. Both versions of cmsg_sender build
with GCC 12.3.1. ShellCheck 0.11.0 reports the same 13 existing SC2086
diagnostics before and after the change.
tools/testing/selftests/net/cmsg_sender.c | 21 ++++++++++++++++++---
tools/testing/selftests/net/cmsg_so_mark.sh | 16 ++++++++++++++++
2 files changed, 34 insertions(+), 3 deletions(-)
diff --git a/tools/testing/selftests/net/cmsg_sender.c b/tools/testing/selftests/net/cmsg_sender.c
--- a/tools/testing/selftests/net/cmsg_sender.c
+++ b/tools/testing/selftests/net/cmsg_sender.c
@@ -34,6 +34,7 @@
ERN_CMSG_RD,
ERN_CMSG_RCV,
ERN_SEND_MORE,
+ ERN_CONNECT,
};
struct option_cmsg_u32 {
@@ -48,6 +49,7 @@
unsigned int size;
unsigned int num_pkt;
bool msg_more;
+ bool connected;
struct {
unsigned int mark;
unsigned int dontfrag;
@@ -93,6 +95,7 @@
printf("Usage: %s [opts] <dst host> <dst port / service>\n", bin);
printf("Options:\n"
"\t\t-s Silent send() failures\n"
+ "\t\t-k Connect before sending\n"
"\t\t-S send() size\n"
"\t\t-4/-6 Force IPv4 / IPv6 only\n"
"\t\t-p prot Socket protocol\n"
@@ -121,8 +124,12 @@
{
int o;
- while ((o = getopt(argc, argv, "46sS:p:P:m:M:n:d:tf:F:c:C:l:L:H:Q:")) != -1) {
+ while ((o = getopt(argc, argv,
+ "46ksS:p:P:m:M:n:d:tf:F:c:C:l:L:H:Q:")) != -1) {
switch (o) {
+ case 'k':
+ opt.connected = true;
+ break;
case 's':
opt.silent_send = true;
break;
@@ -521,6 +528,12 @@
ca_set_sockopts(fd);
+ if (opt.connected && connect(fd, ai->ai_addr, ai->ai_addrlen)) {
+ fprintf(stderr, "Can't connect socket: %s\n", strerror(errno));
+ err = ERN_CONNECT;
+ goto err_out;
+ }
+
if (clock_gettime(CLOCK_REALTIME, &time_start_real))
error(ERN_GETTIME, errno, "gettime REALTIME");
if (clock_gettime(CLOCK_MONOTONIC, &time_start_mono))
@@ -530,8 +543,10 @@
iov[0].iov_len = opt.size;
memset(&msg, 0, sizeof(msg));
- msg.msg_name = ai->ai_addr;
- msg.msg_namelen = ai->ai_addrlen;
+ if (!opt.connected) {
+ msg.msg_name = ai->ai_addr;
+ msg.msg_namelen = ai->ai_addrlen;
+ }
msg.msg_iov = iov;
msg.msg_iovlen = 1;
diff --git a/tools/testing/selftests/net/cmsg_so_mark.sh b/tools/testing/selftests/net/cmsg_so_mark.sh
--- a/tools/testing/selftests/net/cmsg_so_mark.sh
+++ b/tools/testing/selftests/net/cmsg_so_mark.sh
@@ -68,6 +68,22 @@
done
done
+# Check per-message marks after connect() has populated the route cache.
+for i in 4 6; do
+ [ "$i" = 4 ] && TGT=$TGT4 || TGT=$TGT6
+
+ for p in u i r; do
+ for mark in "$((MARK + 1))" "$MARK" 0; do
+ expected=0
+ [ "$mark" -eq "$MARK" ] && expected=1
+
+ ip netns exec "$NS" ./cmsg_sender -"$i" -p "$p" -k \
+ -M "$((MARK + 1))" -m "$mark" -s "$TGT" 1234
+ check_result "$?" "$expected" "IPv$i $p connected mark $mark"
+ done
+ done
+done
+
# Summary
if [ $BAD -ne 0 ]; then
echo "FAIL - $BAD/$TOTAL cases failed"