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 }