selftests: net: cmsg_sender: support icmp and raw sockets
authorJakub Kicinski <kuba@kernel.org>
Thu, 10 Feb 2022 00:36:44 +0000 (16:36 -0800)
committerDavid S. Miller <davem@davemloft.net>
Thu, 10 Feb 2022 15:04:51 +0000 (15:04 +0000)
Support sending fake ICMP(v6) messages and UDP via RAW sockets.

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

index 4528ae6..edb8c42 100644 (file)
@@ -7,7 +7,10 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <linux/icmp.h>
+#include <linux/icmpv6.h>
 #include <linux/types.h>
+#include <linux/udp.h>
 #include <sys/socket.h>
 
 enum {
@@ -27,7 +30,9 @@ struct options {
        const char *host;
        const char *service;
        struct {
+               unsigned int family;
                unsigned int type;
+               unsigned int proto;
        } sock;
        struct {
                bool ena;
@@ -35,7 +40,9 @@ struct options {
        } mark;
 } opt = {
        .sock = {
+               .family = AF_UNSPEC,
                .type   = SOCK_DGRAM,
+               .proto  = IPPROTO_UDP,
        },
 };
 
@@ -44,6 +51,10 @@ 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-4/-6   Force IPv4 / IPv6 only\n"
+              "\t\t-p prot Socket protocol\n"
+              "\t\t        (u = UDP (default); i = ICMP; r = RAW)\n"
+              "\n"
               "\t\t-m val  Set SO_MARK with given value\n"
               "");
        exit(ERN_HELP);
@@ -53,11 +64,29 @@ static void cs_parse_args(int argc, char *argv[])
 {
        char o;
 
-       while ((o = getopt(argc, argv, "sm:")) != -1) {
+       while ((o = getopt(argc, argv, "46sp:m:")) != -1) {
                switch (o) {
                case 's':
                        opt.silent_send = true;
                        break;
+               case '4':
+                       opt.sock.family = AF_INET;
+                       break;
+               case '6':
+                       opt.sock.family = AF_INET6;
+                       break;
+               case 'p':
+                       if (*optarg == 'u' || *optarg == 'U') {
+                               opt.sock.proto = IPPROTO_UDP;
+                       } else if (*optarg == 'i' || *optarg == 'I') {
+                               opt.sock.proto = IPPROTO_ICMP;
+                       } else if (*optarg == 'r') {
+                               opt.sock.type = SOCK_RAW;
+                       } else {
+                               printf("Error: unknown protocol: %s\n", optarg);
+                               cs_usage(argv[0]);
+                       }
+                       break;
                case 'm':
                        opt.mark.ena = true;
                        opt.mark.val = atoi(optarg);
@@ -101,6 +130,7 @@ cs_write_cmsg(struct msghdr *msg, char *cbuf, size_t cbuf_sz)
 
 int main(int argc, char *argv[])
 {
+       char buf[] = "blablablabla";
        struct addrinfo hints, *ai;
        struct iovec iov[1];
        struct msghdr msg;
@@ -111,26 +141,42 @@ int main(int argc, char *argv[])
        cs_parse_args(argc, argv);
 
        memset(&hints, 0, sizeof(hints));
-       hints.ai_family = AF_UNSPEC;
-       hints.ai_socktype = opt.sock.type;
+       hints.ai_family = opt.sock.family;
 
        ai = NULL;
        err = getaddrinfo(opt.host, opt.service, &hints, &ai);
        if (err) {
-               fprintf(stderr, "Can't resolve address [%s]:%s: %s\n",
-                       opt.host, opt.service, strerror(errno));
+               fprintf(stderr, "Can't resolve address [%s]:%s\n",
+                       opt.host, opt.service);
                return ERN_SOCK_CREATE;
        }
 
-       fd = socket(ai->ai_family, SOCK_DGRAM, IPPROTO_UDP);
+       if (ai->ai_family == AF_INET6 && opt.sock.proto == IPPROTO_ICMP)
+               opt.sock.proto = IPPROTO_ICMPV6;
+
+       fd = socket(ai->ai_family, opt.sock.type, opt.sock.proto);
        if (fd < 0) {
                fprintf(stderr, "Can't open socket: %s\n", strerror(errno));
                freeaddrinfo(ai);
                return ERN_RESOLVE;
        }
 
-       iov[0].iov_base = "bla";
-       iov[0].iov_len = 4;
+       if (opt.sock.proto == IPPROTO_ICMP) {
+               buf[0] = ICMP_ECHO;
+               buf[1] = 0;
+       } else if (opt.sock.proto == IPPROTO_ICMPV6) {
+               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 sockaddr_in6 *sin6 = (void *)ai->ai_addr;;
+
+               memcpy(buf, &hdr, sizeof(hdr));
+               sin6->sin6_port = htons(opt.sock.proto);
+       }
+
+       iov[0].iov_base = buf;
+       iov[0].iov_len = sizeof(buf);
 
        memset(&msg, 0, sizeof(msg));
        msg.msg_name = ai->ai_addr;
@@ -145,7 +191,7 @@ int main(int argc, char *argv[])
                if (!opt.silent_send)
                        fprintf(stderr, "send failed: %s\n", strerror(errno));
                err = ERN_SEND;
-       } else if (err != 4) {
+       } else if (err != sizeof(buf)) {
                fprintf(stderr, "short send\n");
                err = ERN_SEND_SHORT;
        } else {