selftests: net: make cmsg_so_mark ready for more options
authorJakub Kicinski <kuba@kernel.org>
Thu, 10 Feb 2022 00:36:43 +0000 (16:36 -0800)
committerDavid S. Miller <davem@davemloft.net>
Thu, 10 Feb 2022 15:04:51 +0000 (15:04 +0000)
Parametrize the code so that it can support UDP and ICMP
sockets in the future, and more cmsg types.

Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
tools/testing/selftests/net/cmsg_sender.c
tools/testing/selftests/net/cmsg_so_mark.sh

index 27f2804892a7ebcd2413ee4a19972a5f202546e7..4528ae638aea9cb39fad8b2b9e4539c6998b71ca 100644 (file)
@@ -1,6 +1,8 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 #include <errno.h>
+#include <error.h>
 #include <netdb.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <linux/types.h>
 #include <sys/socket.h>
 
-int main(int argc, const char **argv)
+enum {
+       ERN_SUCCESS = 0,
+       /* Well defined errors, callers may depend on these */
+       ERN_SEND = 1,
+       /* Informational, can reorder */
+       ERN_HELP,
+       ERN_SEND_SHORT,
+       ERN_SOCK_CREATE,
+       ERN_RESOLVE,
+       ERN_CMSG_WR,
+};
+
+struct options {
+       bool silent_send;
+       const char *host;
+       const char *service;
+       struct {
+               unsigned int type;
+       } sock;
+       struct {
+               bool ena;
+               unsigned int val;
+       } mark;
+} opt = {
+       .sock = {
+               .type   = SOCK_DGRAM,
+       },
+};
+
+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-m val  Set SO_MARK with given value\n"
+              "");
+       exit(ERN_HELP);
+}
+
+static void cs_parse_args(int argc, char *argv[])
+{
+       char o;
+
+       while ((o = getopt(argc, argv, "sm:")) != -1) {
+               switch (o) {
+               case 's':
+                       opt.silent_send = true;
+                       break;
+               case 'm':
+                       opt.mark.ena = true;
+                       opt.mark.val = atoi(optarg);
+                       break;
+               }
+       }
+
+       if (optind != argc - 2)
+               cs_usage(argv[0]);
+
+       opt.host = argv[optind];
+       opt.service = argv[optind + 1];
+}
+
+static void
+cs_write_cmsg(struct msghdr *msg, char *cbuf, size_t cbuf_sz)
 {
-       char cbuf[CMSG_SPACE(sizeof(__u32))];
-       struct addrinfo hints, *ai;
        struct cmsghdr *cmsg;
+       size_t cmsg_len;
+
+       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");
+
+               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 (cmsg_len)
+               msg->msg_controllen = cmsg_len;
+       else
+               msg->msg_control = NULL;
+}
+
+int main(int argc, char *argv[])
+{
+       struct addrinfo hints, *ai;
        struct iovec iov[1];
        struct msghdr msg;
-       int mark;
+       char cbuf[1024];
        int err;
        int fd;
 
-       if (argc != 4) {
-               fprintf(stderr, "Usage: %s <dst_ip> <port> <mark>\n", argv[0]);
-               return 1;
-       }
-       mark = atoi(argv[3]);
+       cs_parse_args(argc, argv);
 
        memset(&hints, 0, sizeof(hints));
        hints.ai_family = AF_UNSPEC;
-       hints.ai_socktype = SOCK_DGRAM;
+       hints.ai_socktype = opt.sock.type;
 
        ai = NULL;
-       err = getaddrinfo(argv[1], argv[2], &hints, &ai);
+       err = getaddrinfo(opt.host, opt.service, &hints, &ai);
        if (err) {
-               fprintf(stderr, "Can't resolve address: %s\n", strerror(errno));
-               return 1;
+               fprintf(stderr, "Can't resolve address [%s]:%s: %s\n",
+                       opt.host, opt.service, strerror(errno));
+               return ERN_SOCK_CREATE;
        }
 
        fd = socket(ai->ai_family, SOCK_DGRAM, IPPROTO_UDP);
        if (fd < 0) {
                fprintf(stderr, "Can't open socket: %s\n", strerror(errno));
                freeaddrinfo(ai);
-               return 1;
+               return ERN_RESOLVE;
        }
 
        iov[0].iov_base = "bla";
        iov[0].iov_len = 4;
 
+       memset(&msg, 0, sizeof(msg));
        msg.msg_name = ai->ai_addr;
        msg.msg_namelen = ai->ai_addrlen;
        msg.msg_iov = iov;
        msg.msg_iovlen = 1;
-       msg.msg_control = cbuf;
-       msg.msg_controllen = sizeof(cbuf);
 
-       cmsg = CMSG_FIRSTHDR(&msg);
-       cmsg->cmsg_level = SOL_SOCKET;
-       cmsg->cmsg_type = SO_MARK;
-       cmsg->cmsg_len = CMSG_LEN(sizeof(__u32));
-       *(__u32 *)CMSG_DATA(cmsg) = mark;
+       cs_write_cmsg(&msg, cbuf, sizeof(cbuf));
 
        err = sendmsg(fd, &msg, 0);
+       if (err < 0) {
+               if (!opt.silent_send)
+                       fprintf(stderr, "send failed: %s\n", strerror(errno));
+               err = ERN_SEND;
+       } else if (err != 4) {
+               fprintf(stderr, "short send\n");
+               err = ERN_SEND_SHORT;
+       } else {
+               err = ERN_SUCCESS;
+       }
 
        close(fd);
        freeaddrinfo(ai);
-       return err != 4;
+       return err;
 }
index 29a623aac74bf6f7cc2fc36b96c070896b01a608..841d706dc91bc3a8004356c0bb623f7c83e98168 100755 (executable)
@@ -41,14 +41,14 @@ check_result() {
     fi
 }
 
-ip netns exec $NS ./cmsg_sender $TGT4 1234 $((MARK + 1))
+ip netns exec $NS ./cmsg_sender -m $((MARK + 1)) $TGT4 1234
 check_result $? 0 "IPv4 pass"
-ip netns exec $NS ./cmsg_sender $TGT6 1234 $((MARK + 1))
+ip netns exec $NS ./cmsg_sender -m $((MARK + 1)) $TGT6 1234
 check_result $? 0 "IPv6 pass"
 
-ip netns exec $NS ./cmsg_sender $TGT4 1234 $MARK
+ip netns exec $NS ./cmsg_sender -s -m $MARK $TGT4 1234
 check_result $? 1 "IPv4 rejection"
-ip netns exec $NS ./cmsg_sender $TGT6 1234 $MARK
+ip netns exec $NS ./cmsg_sender -s -m $MARK $TGT6 1234
 check_result $? 1 "IPv6 rejection"
 
 # Summary