1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet at
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 
  12 /*
  13  * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
  14  */
  15 
  16 /*
  17  * Test ancillary data receipt via recvmsg()
  18  */
  19 
  20 #include <stdio.h>
  21 #include <errno.h>
  22 #include <fcntl.h>
  23 #include <signal.h>
  24 #include <stdlib.h>
  25 #include <string.h>
  26 #include <strings.h>
  27 #include <unistd.h>
  28 
  29 #include <sys/types.h>
  30 #include <sys/param.h>
  31 #include <sys/socket.h>
  32 #include <sys/stat.h>
  33 #include <netinet/in.h>
  34 #include <arpa/inet.h>
  35 #include <sys/wait.h>
  36 #include <pthread.h>
  37 #include <err.h>
  38 
  39 static boolean_t debug;
  40 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
  41 static pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
  42 static pthread_mutex_t cmutex = PTHREAD_MUTEX_INITIALIZER;
  43 static pthread_cond_t ccv = PTHREAD_COND_INITIALIZER;
  44 static boolean_t server_ready = _B_FALSE;
  45 static boolean_t client_done = _B_FALSE;
  46 
  47 static in_addr_t testip;
  48 
  49 #define TESTPORT        32123
  50 
  51 #define RT_RECVTOS      0x1
  52 #define RT_RECVTTL      0x2
  53 #define RT_RECVPKTINFO  0x4
  54 #define RT_RECVMASK     0x7
  55 
  56 #define RT_SETTOS       0x10
  57 #define RT_SETTTL       0x20
  58 #define RT_STREAM       0x40
  59 #define RT_SKIP         0x80
  60 
  61 typedef struct recvmsg_test {
  62         char *name;             /* Name of the test */
  63         uint8_t tos;            /* TOS to set */
  64         uint8_t ttl;            /* TTL to set */
  65         uint8_t flags;          /* Test flags, RT_ */
  66 } recvmsg_test_t;
  67 
  68 static recvmsg_test_t tests[] = {
  69         {
  70                 .name = "baseline",
  71                 .flags = 0,
  72         },
  73 
  74         /* Combinations of receive flags */
  75         {
  76                 .name = "recv TOS",
  77                 .flags = RT_RECVTOS,
  78         },
  79 
  80         {
  81                 .name = "recv TTL",
  82                 .flags = RT_RECVTTL,
  83         },
  84 
  85         {
  86                 .name = "recv PKTINFO",
  87                 .flags = RT_RECVPKTINFO,
  88         },
  89 
  90         {
  91                 .name = "recv TOS,TTL",
  92                 .flags = RT_RECVTOS | RT_RECVTTL,
  93         },
  94 
  95         {
  96                 .name = "recv TTL,PKTINFO",
  97                 .flags = RT_RECVTTL | RT_RECVPKTINFO,
  98         },
  99 
 100         {
 101                 .name = "recv TOS,PKTINFO",
 102                 .flags = RT_RECVTOS | RT_RECVPKTINFO,
 103         },
 104 
 105         {
 106                 .name = "recv TOS,TTL,PKTINFO",
 107                 .flags = RT_RECVTOS | RT_RECVTTL | RT_RECVPKTINFO,
 108         },
 109 
 110         /* Manually set TTL and TOS */
 111 
 112         {
 113                 .name = "set TOS,TTL",
 114                 .flags = RT_SETTOS | RT_SETTTL,
 115                 .ttl = 11,
 116                 .tos = 0xe0
 117         },
 118 
 119         {
 120                 .name = "set/recv TOS,TTL",
 121                 .flags = RT_SETTOS | RT_SETTTL | RT_RECVTOS | RT_RECVTTL,
 122                 .ttl = 32,
 123                 .tos = 0x48
 124         },
 125 
 126         {
 127                 .name = "set TOS,TTL, recv PKTINFO",
 128                 .flags = RT_SETTOS | RT_SETTTL | RT_RECVPKTINFO,
 129                 .ttl = 173,
 130                 .tos = 0x78
 131         },
 132 
 133         {
 134                 .name = "set TOS,TTL, recv TOS,TTL,PKTINFO",
 135                 .flags = RT_SETTOS | RT_SETTTL | RT_RECVTOS | RT_RECVTTL |
 136                     RT_RECVPKTINFO,
 137                 .ttl = 54,
 138                 .tos = 0x90
 139         },
 140 
 141         /* STREAM socket */
 142 
 143         {
 144                 .name = "STREAM set TOS",
 145                 .flags = RT_STREAM | RT_SETTOS,
 146                 .tos = 0xe0
 147         },
 148 
 149         /*
 150          * The ancillary data are not returned for the loopback TCP path,
 151          * so these tests are skipped by default.
 152          * To run them, use two different zones (or machines) and run:
 153          *      recvmsg.64 -s 'test name'
 154          * on the first, and:
 155          *      recvmsg.64 -c <first machine IP> 'test name'
 156          * on the second.
 157          */
 158         {
 159                 .name = "STREAM recv TOS",
 160                 .flags = RT_STREAM | RT_RECVTOS | RT_SKIP,
 161         },
 162 
 163         {
 164                 .name = "STREAM set/recv TOS",
 165                 .flags = RT_STREAM | RT_SETTOS | RT_RECVTOS | RT_SKIP,
 166                 .tos = 0x48
 167         },
 168 
 169         /* End of tests */
 170 
 171         {
 172                 .name = NULL
 173         }
 174 };
 175 
 176 static boolean_t
 177 servertest(recvmsg_test_t *t)
 178 {
 179         struct sockaddr_in addr;
 180         boolean_t pass = _B_TRUE;
 181         int s, a = -1, c = 1;
 182 
 183         if (debug)
 184                 printf("\nserver %s: starting\n", t->name);
 185 
 186         s = socket(AF_INET, t->flags & RT_STREAM ? SOCK_STREAM : SOCK_DGRAM, 0);
 187         if (s == -1)
 188                 err(EXIT_FAILURE, "failed to create server socket");
 189 
 190         addr.sin_family = AF_INET;
 191         addr.sin_addr.s_addr = INADDR_ANY;
 192         addr.sin_port = htons(TESTPORT);
 193 
 194         if (bind(s, (struct sockaddr *)&addr, sizeof (addr)) == -1)
 195                 err(EXIT_FAILURE, "server socket bind failed");
 196 
 197         if (t->flags & RT_RECVTOS) {
 198                 if (debug)
 199                         printf(" : setting RECVTOS\n");
 200                 if (setsockopt(s, IPPROTO_IP, IP_RECVTOS, &c,
 201                     sizeof (c)) == -1) {
 202                         printf("[FAIL] %s - "
 203                             "couldn't set TOS on server socket: %s\n",
 204                             t->name, strerror(errno));
 205                         pass = _B_FALSE;
 206                 }
 207         }
 208 
 209         if (t->flags & RT_RECVTTL) {
 210                 if (debug)
 211                         printf(" : setting RECVTTL\n");
 212                 if (setsockopt(s, IPPROTO_IP, IP_RECVTTL, &c,
 213                     sizeof (c)) == -1) {
 214                         printf("[FAIL] %s - "
 215                             "couldn't set TTL on server socket: %s\n",
 216                             t->name, strerror(errno));
 217                         pass = _B_FALSE;
 218                 }
 219         }
 220 
 221         if (t->flags & RT_RECVPKTINFO) {
 222                 if (debug)
 223                         printf(" : setting RECVPKTINFO\n");
 224                 if (setsockopt(s, IPPROTO_IP, IP_PKTINFO, &c,
 225                     sizeof (c)) == -1) {
 226                         printf("[FAIL] %s - "
 227                             "couldn't set PKTINFO on server socket: %s\n",
 228                             t->name, strerror(errno));
 229                         pass = _B_FALSE;
 230                 }
 231         }
 232 
 233         if (t->flags & RT_STREAM) {
 234                 if (listen(s, 1) == -1)
 235                         err(EXIT_FAILURE, "Could not listen on sever socket");
 236         }
 237 
 238         /* Signal the client that the server is ready for the next test */
 239         if (debug)
 240                 printf(" : signalling client\n");
 241         (void) pthread_mutex_lock(&mutex);
 242         server_ready = _B_TRUE;
 243         (void) pthread_cond_signal(&cv);
 244         (void) pthread_mutex_unlock(&mutex);
 245 
 246         if (t->flags & RT_STREAM) {
 247                 struct sockaddr_in caddr;
 248                 socklen_t sl = sizeof (caddr);
 249                 int t;
 250 
 251                 if ((a = accept(s, (struct sockaddr *)&caddr, &sl)) == -1)
 252                         err(EXIT_FAILURE, "socket accept failed");
 253                 t = s;
 254                 s = a;
 255                 a = t;
 256         }
 257 
 258         /* Receive the datagram */
 259 
 260         struct msghdr msg;
 261         char buf[0x100];
 262         char cbuf[CMSG_SPACE(0x400)];
 263         struct iovec iov[1] = {0};
 264         ssize_t r;
 265 
 266         iov[0].iov_base = buf;
 267         iov[0].iov_len = sizeof (buf);
 268 
 269         bzero(&msg, sizeof (msg));
 270         msg.msg_iov = iov;
 271         msg.msg_iovlen = 1;
 272         msg.msg_control = cbuf;
 273         msg.msg_controllen = sizeof (cbuf);
 274 
 275         if (debug)
 276                 printf(" : waiting for message\n");
 277 
 278         r = recvmsg(s, &msg, 0);
 279         if (r <= 0) {
 280                 printf("[FAIL] %s - recvmsg returned %d (%s)\n",
 281                     t->name, r, strerror(errno));
 282                 pass = _B_FALSE;
 283                 goto out;
 284         }
 285 
 286         if (debug) {
 287                 printf(" : recvmsg returned %d (flags=0x%x, controllen=%d)\n",
 288                     r, msg.msg_flags, msg.msg_controllen);
 289         }
 290 
 291         if (r != strlen(t->name)) {
 292                 printf("[FAIL] %s - got '%.*s' (%d bytes), expected '%s'\n",
 293                     t->name, r, buf, r, t->name);
 294                 pass = _B_FALSE;
 295         }
 296 
 297         if (debug)
 298                 printf(" : Received '%.*s'\n", r, buf);
 299 
 300         if (msg.msg_flags != 0) {
 301                 printf("[FAIL] %s - received flags 0x%x\n",
 302                     t->name, msg.msg_flags);
 303                 pass = _B_FALSE;
 304         }
 305 
 306         uint8_t flags = 0;
 307 
 308         for (struct cmsghdr *cm = CMSG_FIRSTHDR(&msg); cm != NULL;
 309             cm = CMSG_NXTHDR(&msg, cm)) {
 310                 uint8_t d;
 311 
 312                 if (debug) {
 313                         printf(" : >> Got cmsg %x/%x - length %u\n",
 314                             cm->cmsg_level, cm->cmsg_type, cm->cmsg_len);
 315                 }
 316 
 317                 if (cm->cmsg_level != IPPROTO_IP)
 318                         continue;
 319 
 320                 switch (cm->cmsg_type) {
 321                 case IP_PKTINFO:
 322                         flags |= RT_RECVPKTINFO;
 323                         if (debug) {
 324                                 struct in_pktinfo *pi =
 325                                     (struct in_pktinfo *)CMSG_DATA(cm);
 326                                 printf(" : ifIndex: %u\n", pi->ipi_ifindex);
 327                         }
 328                         break;
 329                 case IP_RECVTTL:
 330                         if (cm->cmsg_len != CMSG_LEN(sizeof (uint8_t))) {
 331                                 printf(
 332                                     "[FAIL] %s - cmsg_len was %u expected %u\n",
 333                                     t->name, cm->cmsg_len,
 334                                     CMSG_LEN(sizeof (uint8_t)));
 335                                 pass = _B_FALSE;
 336                                 break;
 337                         }
 338                         flags |= RT_RECVTTL;
 339                         memcpy(&d, CMSG_DATA(cm), sizeof (d));
 340                         if (debug)
 341                                 printf(" : RECVTTL = %u\n", d);
 342                         if (t->flags & RT_SETTTL && d != t->ttl) {
 343                                 printf("[FAIL] %s - TTL was %u, expected %u\n",
 344                                     t->name, d, t->ttl);
 345                                 pass = _B_FALSE;
 346                         }
 347                         break;
 348                 case IP_RECVTOS:
 349                         if (cm->cmsg_len != CMSG_LEN(sizeof (uint8_t))) {
 350                                 printf(
 351                                     "[FAIL] %s - cmsg_len was %u expected %u\n",
 352                                     t->name, cm->cmsg_len,
 353                                     CMSG_LEN(sizeof (uint8_t)));
 354                                 pass = _B_FALSE;
 355                                 break;
 356                         }
 357                         flags |= RT_RECVTOS;
 358                         memcpy(&d, CMSG_DATA(cm), sizeof (d));
 359                         if (debug)
 360                                 printf(" : RECVTOS = %u\n", d);
 361                         if (t->flags & RT_SETTOS && d != t->tos) {
 362                                 printf("[FAIL] %s - TOS was %u, expected %u\n",
 363                                     t->name, d, t->tos);
 364                                 pass = _B_FALSE;
 365                         }
 366                         break;
 367                 }
 368         }
 369 
 370         if ((t->flags & RT_RECVMASK) != flags) {
 371                 printf("[FAIL] %s - Did not receive everything expected, "
 372                     "flags %#x vs. %#x\n", t->name,
 373                     flags, t->flags & RT_RECVMASK);
 374                 pass = _B_FALSE;
 375         }
 376 
 377         /* Wait for the client to finish */
 378         (void) pthread_mutex_lock(&cmutex);
 379         while (!client_done)
 380                 (void) pthread_cond_wait(&ccv, &cmutex);
 381         client_done = _B_FALSE;
 382         (void) pthread_mutex_unlock(&cmutex);
 383 
 384 out:
 385         (void) close(s);
 386         if (a != -1)
 387                 (void) close(a);
 388 
 389         if (pass)
 390                 printf("[PASS] %s\n", t->name);
 391 
 392         return (pass);
 393 }
 394 
 395 static int
 396 server(const char *test)
 397 {
 398         int ret = EXIT_SUCCESS;
 399         recvmsg_test_t *t;
 400 
 401         for (t = tests; t->name != NULL; t++) {
 402                 if (test != NULL) {
 403                         if (strcmp(test, t->name) != 0)
 404                                 continue;
 405                         client_done = _B_TRUE;
 406                         return (servertest(t));
 407                 }
 408                 if (t->flags & RT_SKIP) {
 409                         printf("[SKIP] %s - (requires two separate zones)\n",
 410                             t->name);
 411                         continue;
 412                 }
 413                 if (!servertest(t))
 414                         ret = EXIT_FAILURE;
 415         }
 416 
 417         return (ret);
 418 }
 419 
 420 static void
 421 clienttest(recvmsg_test_t *t)
 422 {
 423         struct sockaddr_in addr;
 424         int s, ret;
 425 
 426         if (debug)
 427                 printf("client %s: starting\n", t->name);
 428 
 429         s = socket(AF_INET, t->flags & RT_STREAM ? SOCK_STREAM : SOCK_DGRAM, 0);
 430         if (s == -1)
 431                 err(EXIT_FAILURE, "failed to create client socket");
 432 
 433         addr.sin_family = AF_INET;
 434         addr.sin_addr.s_addr = testip;
 435         addr.sin_port = htons(TESTPORT);
 436 
 437         if (t->flags & RT_STREAM) {
 438                 if (connect(s, (struct sockaddr *)&addr, sizeof (addr)) == -1)
 439                         err(EXIT_FAILURE, "failed to connect to server");
 440         }
 441 
 442         if (t->flags & RT_SETTOS) {
 443                 int c = t->tos;
 444 
 445                 if (debug)
 446                         printf("client %s: setting TOS = 0x%x\n", t->name, c);
 447                 if (setsockopt(s, IPPROTO_IP, IP_TOS, &c, sizeof (c)) == -1)
 448                         err(EXIT_FAILURE, "could not set TOS on client socket");
 449         }
 450 
 451         if (t->flags & RT_SETTTL) {
 452                 int c = t->ttl;
 453 
 454                 if (debug)
 455                         printf("client %s: setting TTL = 0x%x\n", t->name, c);
 456                 if (setsockopt(s, IPPROTO_IP, IP_TTL, &c, sizeof (c)) == -1)
 457                         err(EXIT_FAILURE, "could not set TTL on client socket");
 458         }
 459 
 460         if (debug)
 461                 printf("client %s: sending\n", t->name);
 462 
 463         if (t->flags & RT_STREAM) {
 464                 ret = send(s, t->name, strlen(t->name), 0);
 465                 shutdown(s, SHUT_RDWR);
 466         } else {
 467                 ret = sendto(s, t->name, strlen(t->name), 0,
 468                     (struct sockaddr *)&addr, sizeof (addr));
 469         }
 470 
 471         if (ret == -1)
 472                 err(EXIT_FAILURE, "sendto failed to send data to server");
 473 
 474         if (debug)
 475                 printf("client %s: done\n", t->name);
 476 
 477         close(s);
 478 }
 479 
 480 static void *
 481 client(void *arg)
 482 {
 483         char *test = (char *)arg;
 484         recvmsg_test_t *t;
 485 
 486         for (t = tests; t->name != NULL; t++) {
 487                 if (test != NULL) {
 488                         if (strcmp(test, t->name) != 0)
 489                                 continue;
 490                         clienttest(t);
 491                         return (NULL);
 492                 }
 493                 if (t->flags & RT_SKIP)
 494                         continue;
 495                 /* Wait for the server to be ready to receive */
 496                 (void) pthread_mutex_lock(&mutex);
 497                 while (!server_ready)
 498                         (void) pthread_cond_wait(&cv, &mutex);
 499                 server_ready = _B_FALSE;
 500                 (void) pthread_mutex_unlock(&mutex);
 501                 clienttest(t);
 502                 /* Tell the server we are done */
 503                 (void) pthread_mutex_lock(&cmutex);
 504                 client_done = _B_TRUE;
 505                 (void) pthread_cond_signal(&ccv);
 506                 (void) pthread_mutex_unlock(&cmutex);
 507         }
 508 
 509         return (NULL);
 510 }
 511 
 512 int
 513 main(int argc, const char **argv)
 514 {
 515         int ret = EXIT_SUCCESS;
 516         pthread_t cthread;
 517 
 518         if (argc > 1 && strcmp(argv[1], "-d") == 0) {
 519                 debug = _B_TRUE;
 520                 argc--, argv++;
 521         }
 522 
 523         /* -c <server IP> <test name> */
 524         if (argc == 4 && strcmp(argv[1], "-c") == 0) {
 525                 testip = inet_addr(argv[2]);
 526                 printf("TEST IP: %s\n", argv[2]);
 527                 if (testip == INADDR_NONE) {
 528                         err(EXIT_FAILURE,
 529                             "Could not parse destination IP address");
 530                 }
 531                 client((void *)argv[3]);
 532                 return (ret);
 533         }
 534 
 535         /* -s <test name> */
 536         if (argc == 3 && strcmp(argv[1], "-s") == 0)
 537                 return (server(argv[2]));
 538 
 539         testip = inet_addr("127.0.0.1");
 540         if (testip == INADDR_NONE)
 541                 err(EXIT_FAILURE, "Could not parse destination IP address");
 542 
 543         if (pthread_create(&cthread, NULL, client, NULL) == -1)
 544                 err(EXIT_FAILURE, "Could not create client thread");
 545 
 546         ret = server(NULL);
 547 
 548         if (pthread_join(cthread, NULL) != 0)
 549                 err(EXIT_FAILURE, "join client thread failed");
 550 
 551         return (ret);
 552 }