selftests/net: expand cmsg_ip with MSG_MORE
authorWillem de Bruijn <willemb@google.com>
Fri, 7 Mar 2025 03:34:10 +0000 (22:34 -0500)
committerJakub Kicinski <kuba@kernel.org>
Mon, 10 Mar 2025 20:13:04 +0000 (13:13 -0700)
UDP send with MSG_MORE takes a slightly different path than the
lockless fast path.

For completeness, add coverage to this case too.

Pass MSG_MORE on the initial sendmsg, then follow up with a zero byte
write to unplug the cork.

Unrelated: also add two missing endlines in usage().

Signed-off-by: Willem de Bruijn <willemb@google.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Link: https://patch.msgid.link/20250307033620.411611-4-willemdebruijn.kernel@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
tools/testing/selftests/net/cmsg_ip.sh
tools/testing/selftests/net/cmsg_sender.c

index 2a52520aca327484161724fa0be54078da2f97f2..b55680e081ad78a7482975f76363ecac03ca24fb 100755 (executable)
@@ -50,8 +50,9 @@ check_result() {
 # IPV6_DONTFRAG
 for ovr in setsock cmsg both diff; do
     for df in 0 1; do
-       for p in u i r; do
+       for p in u i r; do
            [ $p == "u" ] && prot=UDP
+           [ $p == "U" ] && prot=UDP
            [ $p == "i" ] && prot=ICMP
            [ $p == "r" ] && prot=RAW
 
@@ -81,8 +82,9 @@ test_dscp() {
     ip $IPVER -netns $NS route add table 300 prohibit any
 
     for ovr in setsock cmsg both diff; do
-       for p in u i r; do
+       for p in u i r; do
            [ $p == "u" ] && prot=UDP
+           [ $p == "U" ] && prot=UDP
            [ $p == "i" ] && prot=ICMP
            [ $p == "r" ] && prot=RAW
 
@@ -134,8 +136,9 @@ test_ttl_hoplimit() {
     local -r LIM=4
 
     for ovr in setsock cmsg both diff; do
-       for p in u i r; do
+       for p in u i r; do
            [ $p == "u" ] && prot=UDP
+           [ $p == "U" ] && prot=UDP
            [ $p == "i" ] && prot=ICMP
            [ $p == "r" ] && prot=RAW
 
@@ -166,7 +169,7 @@ test_ttl_hoplimit -4 $TGT4 ttl
 test_ttl_hoplimit -6 $TGT6 hlim
 
 # IPV6 exthdr
-for p in u i r; do
+for p in u i r; do
     # Very basic "does it crash" test
     for h in h d r; do
        $NSEXE ./cmsg_sender -p $p -6 -H $h $TGT6 1234
index 19bd8499031bf46a2057caebd7ce997444cfef7f..a825e628aee73504d25867be2dda00e89c30e16c 100644 (file)
@@ -33,6 +33,7 @@ enum {
        ERN_RECVERR,
        ERN_CMSG_RD,
        ERN_CMSG_RCV,
+       ERN_SEND_MORE,
 };
 
 struct option_cmsg_u32 {
@@ -46,6 +47,7 @@ struct options {
        const char *service;
        unsigned int size;
        unsigned int num_pkt;
+       bool msg_more;
        struct {
                unsigned int mark;
                unsigned int dontfrag;
@@ -94,7 +96,8 @@ static void __attribute__((noreturn)) cs_usage(const char *bin)
               "\t\t-S      send() size\n"
               "\t\t-4/-6   Force IPv4 / IPv6 only\n"
               "\t\t-p prot Socket protocol\n"
-              "\t\t        (u = UDP (default); i = ICMP; r = RAW)\n"
+              "\t\t        (u = UDP (default); i = ICMP; r = RAW;\n"
+              "\t\t         U = UDP with MSG_MORE)\n"
               "\n"
               "\t\t-m val  Set SO_MARK with given value\n"
               "\t\t-M val  Set SO_MARK via setsockopt\n"
@@ -109,8 +112,8 @@ static void __attribute__((noreturn)) cs_usage(const char *bin)
               "\t\t-l val  Set TTL/HOPLIMIT via cmsg\n"
               "\t\t-L val  Set TTL/HOPLIMIT via setsockopt\n"
               "\t\t-H type Add an IPv6 header option\n"
-              "\t\t        (h = HOP; d = DST; r = RTDST)"
-              "");
+              "\t\t        (h = HOP; d = DST; r = RTDST)\n"
+              "\n");
        exit(ERN_HELP);
 }
 
@@ -133,8 +136,11 @@ static void cs_parse_args(int argc, char *argv[])
                        opt.sock.family = AF_INET6;
                        break;
                case 'p':
-                       if (*optarg == 'u' || *optarg == 'U') {
+                       if (*optarg == 'u') {
                                opt.sock.proto = IPPROTO_UDP;
+                       } else if (*optarg == 'U') {
+                               opt.sock.proto = IPPROTO_UDP;
+                               opt.msg_more = true;
                        } else if (*optarg == 'i' || *optarg == 'I') {
                                opt.sock.proto = IPPROTO_ICMP;
                        } else if (*optarg == 'r') {
@@ -531,7 +537,7 @@ int main(int argc, char *argv[])
        cs_write_cmsg(fd, &msg, cbuf, sizeof(cbuf));
 
        for (i = 0; i < opt.num_pkt; i++) {
-               err = sendmsg(fd, &msg, 0);
+               err = sendmsg(fd, &msg, opt.msg_more ? MSG_MORE : 0);
                if (err < 0) {
                        if (!opt.silent_send)
                                fprintf(stderr, "send failed: %s\n", strerror(errno));
@@ -542,6 +548,14 @@ int main(int argc, char *argv[])
                        err = ERN_SEND_SHORT;
                        goto err_out;
                }
+               if (opt.msg_more) {
+                       err = write(fd, NULL, 0);
+                       if (err < 0) {
+                               fprintf(stderr, "send more: %s\n", strerror(errno));
+                               err = ERN_SEND_MORE;
+                               goto err_out;
+                       }
+               }
        }
        err = ERN_SUCCESS;