OSDN Git Service

selftests: net: test IPV6_DONTFRAG
authorJakub Kicinski <kuba@kernel.org>
Thu, 17 Feb 2022 01:21:17 +0000 (17:21 -0800)
committerDavid S. Miller <davem@davemloft.net>
Thu, 17 Feb 2022 14:22:09 +0000 (14:22 +0000)
Test setting IPV6_DONTFRAG via setsockopt and cmsg
across socket types.

Output without the kernel support (this series):

    Case DONTFRAG ICMP setsock returned 0, expected 1
    Case DONTFRAG ICMP cmsg returned 0, expected 1
    Case DONTFRAG ICMP both returned 0, expected 1
    Case DONTFRAG ICMP diff returned 0, expected 1
  FAIL - 4/24 cases failed

Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
tools/testing/selftests/net/cmsg_ipv6.sh [new file with mode: 0755]
tools/testing/selftests/net/cmsg_sender.c

diff --git a/tools/testing/selftests/net/cmsg_ipv6.sh b/tools/testing/selftests/net/cmsg_ipv6.sh
new file mode 100755 (executable)
index 0000000..fb5a8ab
--- /dev/null
@@ -0,0 +1,65 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+NS=ns
+IP6=2001:db8:1::1/64
+TGT6=2001:db8:1::2
+
+cleanup()
+{
+    ip netns del $NS
+}
+
+trap cleanup EXIT
+
+NSEXE="ip netns exec $NS"
+
+# Namespaces
+ip netns add $NS
+
+$NSEXE sysctl -w net.ipv4.ping_group_range='0 2147483647' > /dev/null
+
+# Connectivity
+ip -netns $NS link add type dummy
+ip -netns $NS link set dev dummy0 up
+ip -netns $NS addr add $IP6 dev dummy0
+
+# Test
+BAD=0
+TOTAL=0
+
+check_result() {
+    ((TOTAL++))
+    if [ $1 -ne $2 ]; then
+       echo "  Case $3 returned $1, expected $2"
+       ((BAD++))
+    fi
+}
+
+# IPV6_DONTFRAG
+for ovr in setsock cmsg both diff; do
+    for df in 0 1; do
+       for p in u i r; do
+           [ $p == "u" ] && prot=UDP
+           [ $p == "i" ] && prot=ICMP
+           [ $p == "r" ] && prot=RAW
+
+           [ $ovr == "setsock" ] && m="-F $df"
+           [ $ovr == "cmsg" ]    && m="-f $df"
+           [ $ovr == "both" ]    && m="-F $df -f $df"
+           [ $ovr == "diff" ]    && m="-F $((1 - df)) -f $df"
+
+           $NSEXE ./cmsg_sender -s -S 2000 -6 -p $p $m $TGT6 1234
+           check_result $? $df "DONTFRAG $prot $ovr"
+       done
+    done
+done
+
+# Summary
+if [ $BAD -ne 0 ]; then
+    echo "FAIL - $BAD/$TOTAL cases failed"
+    exit 1
+else
+    echo "OK"
+    exit 0
+fi
index efa617b..844ca61 100644 (file)
@@ -33,22 +33,26 @@ enum {
        ERN_CMSG_RCV,
 };
 
+struct option_cmsg_u32 {
+       bool ena;
+       unsigned int val;
+};
+
 struct options {
        bool silent_send;
        const char *host;
        const char *service;
+       unsigned int size;
        struct {
                unsigned int mark;
+               unsigned int dontfrag;
        } sockopt;
        struct {
                unsigned int family;
                unsigned int type;
                unsigned int proto;
        } sock;
-       struct {
-               bool ena;
-               unsigned int val;
-       } mark;
+       struct option_cmsg_u32 mark;
        struct {
                bool ena;
                unsigned int delay;
@@ -56,7 +60,11 @@ struct options {
        struct {
                bool ena;
        } ts;
+       struct {
+               struct option_cmsg_u32 dontfrag;
+       } v6;
 } opt = {
+       .size = 13,
        .sock = {
                .family = AF_UNSPEC,
                .type   = SOCK_DGRAM,
@@ -72,6 +80,7 @@ static void __attribute__((noreturn)) cs_usage(const char *bin)
        printf("Usage: %s [opts] <dst host> <dst port / service>\n", bin);
        printf("Options:\n"
               "\t\t-s      Silent send() failures\n"
+              "\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"
@@ -80,6 +89,8 @@ static void __attribute__((noreturn)) cs_usage(const char *bin)
               "\t\t-M val  Set SO_MARK via setsockopt\n"
               "\t\t-d val  Set SO_TXTIME with given delay (usec)\n"
               "\t\t-t      Enable time stamp reporting\n"
+              "\t\t-f val  Set don't fragment via cmsg\n"
+              "\t\t-F val  Set don't fragment via setsockopt\n"
               "");
        exit(ERN_HELP);
 }
@@ -88,11 +99,14 @@ static void cs_parse_args(int argc, char *argv[])
 {
        char o;
 
-       while ((o = getopt(argc, argv, "46sp:m:M:d:t")) != -1) {
+       while ((o = getopt(argc, argv, "46sS:p:m:M:d:tf:F:")) != -1) {
                switch (o) {
                case 's':
                        opt.silent_send = true;
                        break;
+               case 'S':
+                       opt.size = atoi(optarg);
+                       break;
                case '4':
                        opt.sock.family = AF_INET;
                        break;
@@ -126,6 +140,13 @@ static void cs_parse_args(int argc, char *argv[])
                case 't':
                        opt.ts.ena = true;
                        break;
+               case 'f':
+                       opt.v6.dontfrag.ena = true;
+                       opt.v6.dontfrag.val = atoi(optarg);
+                       break;
+               case 'F':
+                       opt.sockopt.dontfrag = atoi(optarg);
+                       break;
                }
        }
 
@@ -136,6 +157,38 @@ static void cs_parse_args(int argc, char *argv[])
        opt.service = argv[optind + 1];
 }
 
+static void memrnd(void *s, size_t n)
+{
+       int *dword = s;
+       char *byte;
+
+       for (; n >= 4; n -= 4)
+               *dword++ = rand();
+       byte = (void *)dword;
+       while (n--)
+               *byte++ = rand();
+}
+
+static void
+ca_write_cmsg_u32(char *cbuf, size_t cbuf_sz, size_t *cmsg_len,
+                 int level, int optname, struct option_cmsg_u32 *uopt)
+{
+       struct cmsghdr *cmsg;
+
+       if (!uopt->ena)
+               return;
+
+       cmsg = (struct cmsghdr *)(cbuf + *cmsg_len);
+       *cmsg_len += CMSG_SPACE(sizeof(__u32));
+       if (cbuf_sz < *cmsg_len)
+               error(ERN_CMSG_WR, EFAULT, "cmsg buffer too small");
+
+       cmsg->cmsg_level = level;
+       cmsg->cmsg_type = optname;
+       cmsg->cmsg_len = CMSG_LEN(sizeof(__u32));
+       *(__u32 *)CMSG_DATA(cmsg) = uopt->val;
+}
+
 static void
 cs_write_cmsg(int fd, struct msghdr *msg, char *cbuf, size_t cbuf_sz)
 {
@@ -145,17 +198,11 @@ cs_write_cmsg(int fd, struct msghdr *msg, char *cbuf, size_t cbuf_sz)
        msg->msg_control = cbuf;
        cmsg_len = 0;
 
-       if (opt.mark.ena) {
-               cmsg = (struct cmsghdr *)(cbuf + cmsg_len);
-               cmsg_len += CMSG_SPACE(sizeof(__u32));
-               if (cbuf_sz < cmsg_len)
-                       error(ERN_CMSG_WR, EFAULT, "cmsg buffer too small");
+       ca_write_cmsg_u32(cbuf, cbuf_sz, &cmsg_len,
+                         SOL_SOCKET, SO_MARK, &opt.mark);
+       ca_write_cmsg_u32(cbuf, cbuf_sz, &cmsg_len,
+                         SOL_IPV6, IPV6_DONTFRAG, &opt.v6.dontfrag);
 
-               cmsg->cmsg_level = SOL_SOCKET;
-               cmsg->cmsg_type = SO_MARK;
-               cmsg->cmsg_len = CMSG_LEN(sizeof(__u32));
-               *(__u32 *)CMSG_DATA(cmsg) = opt.mark.val;
-       }
        if (opt.txtime.ena) {
                struct sock_txtime so_txtime = {
                        .clockid = CLOCK_MONOTONIC,
@@ -286,18 +333,33 @@ cs_read_cmsg(int fd, struct msghdr *msg, char *cbuf, size_t cbuf_sz)
        }
 }
 
+static void ca_set_sockopts(int fd)
+{
+       if (opt.sockopt.mark &&
+           setsockopt(fd, SOL_SOCKET, SO_MARK,
+                      &opt.sockopt.mark, sizeof(opt.sockopt.mark)))
+               error(ERN_SOCKOPT, errno, "setsockopt SO_MARK");
+       if (opt.sockopt.dontfrag &&
+           setsockopt(fd, SOL_IPV6, IPV6_DONTFRAG,
+                      &opt.sockopt.dontfrag, sizeof(opt.sockopt.dontfrag)))
+               error(ERN_SOCKOPT, errno, "setsockopt IPV6_DONTFRAG");
+}
+
 int main(int argc, char *argv[])
 {
-       char buf[] = "blablablabla";
        struct addrinfo hints, *ai;
        struct iovec iov[1];
        struct msghdr msg;
        char cbuf[1024];
+       char *buf;
        int err;
        int fd;
 
        cs_parse_args(argc, argv);
 
+       buf = malloc(opt.size);
+       memrnd(buf, opt.size);
+
        memset(&hints, 0, sizeof(hints));
        hints.ai_family = opt.sock.family;
 
@@ -326,17 +388,14 @@ int main(int argc, char *argv[])
                buf[0] = ICMPV6_ECHO_REQUEST;
                buf[1] = 0;
        } else if (opt.sock.type == SOCK_RAW) {
-               struct udphdr hdr = { 1, 2, htons(sizeof(buf)), 0 };
+               struct udphdr hdr = { 1, 2, htons(opt.size), 0 };
                struct sockaddr_in6 *sin6 = (void *)ai->ai_addr;;
 
                memcpy(buf, &hdr, sizeof(hdr));
                sin6->sin6_port = htons(opt.sock.proto);
        }
 
-       if (opt.sockopt.mark &&
-           setsockopt(fd, SOL_SOCKET, SO_MARK,
-                      &opt.sockopt.mark, sizeof(opt.sockopt.mark)))
-               error(ERN_SOCKOPT, errno, "setsockopt SO_MARK");
+       ca_set_sockopts(fd);
 
        if (clock_gettime(CLOCK_REALTIME, &time_start_real))
                error(ERN_GETTIME, errno, "gettime REALTIME");
@@ -344,7 +403,7 @@ int main(int argc, char *argv[])
                error(ERN_GETTIME, errno, "gettime MONOTONIC");
 
        iov[0].iov_base = buf;
-       iov[0].iov_len = sizeof(buf);
+       iov[0].iov_len = opt.size;
 
        memset(&msg, 0, sizeof(msg));
        msg.msg_name = ai->ai_addr;
@@ -360,7 +419,7 @@ int main(int argc, char *argv[])
                        fprintf(stderr, "send failed: %s\n", strerror(errno));
                err = ERN_SEND;
                goto err_out;
-       } else if (err != sizeof(buf)) {
+       } else if (err != (int)opt.size) {
                fprintf(stderr, "short send\n");
                err = ERN_SEND_SHORT;
                goto err_out;