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 --- 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@kernel.org/ 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] \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"