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