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 2024 Oxide Computer Company 14 */ 15 16 /* 17 * Basic set of tests for TCP_MD5SIG. The main design of this is to spin up 18 * connections on localhost that walk through different options and confirm 19 * that traffic either flows or is dropped according to the configuration. 20 */ 21 22 #include <err.h> 23 #include <port.h> 24 #include <stdlib.h> 25 #include <sys/types.h> 26 #include <sys/socket.h> 27 #include <netinet/in.h> 28 #include <netinet/tcp.h> 29 #include <arpa/inet.h> 30 #include <sys/sysmacros.h> 31 #include <stdbool.h> 32 #include <unistd.h> 33 #include <string.h> 34 #include <errno.h> 35 #include <sys/debug.h> 36 37 static hrtime_t sock_to = MSEC2NSEC(100); /* ms in ns */ 38 static const uint32_t msgdata = 0x7777; 39 40 /* 41 * Port setup - see tcpsig_init 42 */ 43 44 /* No SAs are configured */ 45 #define PORT_NOSA 24134 46 /* SAs exist in both directions, and the authentication keys match */ 47 #define PORT_BIDIR 24135 48 /* SAs exist in both directions, but the authentication keys don't match */ 49 #define PORT_MISMATCH 24136 50 /* A single SA exists in the outbound direction, none for inbound */ 51 #define PORT_OBSA 24137 52 /* A single SA exists in the inbound direction, none for outbound */ 53 #define PORT_IBSA 24138 54 55 typedef enum { 56 TCPSIG_SENDRECV, 57 TCPSIG_NOCONNECT, 58 TCPSIG_CONNREFUSED, 59 TCPSIG_NODATA 60 } tcpsig_pass_t; 61 62 typedef struct { 63 const char *tt_desc; 64 const int tt_domain; 65 const uint16_t tt_port; 66 const bool tt_enable_src; 67 const bool tt_enable_dst; 68 const tcpsig_pass_t tt_pass; 69 } tcpsig_test_t; 70 71 static const tcpsig_test_t tcpsig_tests[] = { 72 /* Tests using the port that (hopefully) has no SAs configured */ 73 { 74 .tt_desc = "IPv4 NOSA with MD5 enabled on both sides", 75 .tt_domain = PF_INET, 76 .tt_port = PORT_NOSA, 77 .tt_enable_src = true, 78 .tt_enable_dst = true, 79 .tt_pass = TCPSIG_CONNREFUSED 80 }, { 81 .tt_desc = "IPv4 NOSA with MD5 disabled on both sides", 82 .tt_domain = PF_INET, 83 .tt_port = PORT_NOSA, 84 .tt_enable_src = false, 85 .tt_enable_dst = false, 86 .tt_pass = TCPSIG_SENDRECV 87 }, { 88 .tt_desc = "IPv4 NOSA with MD5 enabled on src only", 89 .tt_domain = PF_INET, 90 .tt_port = PORT_NOSA, 91 .tt_enable_src = true, 92 .tt_enable_dst = false, 93 .tt_pass = TCPSIG_CONNREFUSED 94 }, { 95 .tt_desc = "IPv4 NOSA with MD5 enabled on dst only", 96 .tt_domain = PF_INET, 97 .tt_port = PORT_NOSA, 98 .tt_enable_src = false, 99 .tt_enable_dst = true, 100 .tt_pass = TCPSIG_NOCONNECT 101 }, 102 { 103 .tt_desc = "IPv6 NOSA with MD5 enabled on both sides", 104 .tt_domain = PF_INET6, 105 .tt_port = PORT_NOSA, 106 .tt_enable_src = true, 107 .tt_enable_dst = true, 108 .tt_pass = TCPSIG_CONNREFUSED 109 }, { 110 .tt_desc = "IPv6 NOSA with MD5 disabled on both sides", 111 .tt_domain = PF_INET6, 112 .tt_port = PORT_NOSA, 113 .tt_enable_src = false, 114 .tt_enable_dst = false, 115 .tt_pass = TCPSIG_SENDRECV 116 }, { 117 .tt_desc = "IPv6 NOSA with MD5 enabled on src only", 118 .tt_domain = PF_INET6, 119 .tt_port = PORT_NOSA, 120 .tt_enable_src = true, 121 .tt_enable_dst = false, 122 .tt_pass = TCPSIG_CONNREFUSED 123 }, { 124 .tt_desc = "IPv6 NOSA with MD5 enabled on dst only", 125 .tt_domain = PF_INET6, 126 .tt_port = PORT_NOSA, 127 .tt_enable_src = false, 128 .tt_enable_dst = true, 129 .tt_pass = TCPSIG_NOCONNECT 130 }, 131 /* Tests using the port that has bi-directional SAs configured */ 132 { 133 .tt_desc = "IPv4 BIDIR with MD5 enabled on both sides", 134 .tt_domain = PF_INET, 135 .tt_port = PORT_BIDIR, 136 .tt_enable_src = true, 137 .tt_enable_dst = true, 138 .tt_pass = TCPSIG_SENDRECV 139 }, { 140 .tt_desc = "IPv4 BIDIR with MD5 disabled on both sides", 141 .tt_domain = PF_INET, 142 .tt_port = PORT_BIDIR, 143 .tt_enable_src = false, 144 .tt_enable_dst = false, 145 .tt_pass = TCPSIG_SENDRECV 146 }, { 147 .tt_desc = "IPv4 BIDIR with MD5 enabled on src only", 148 .tt_domain = PF_INET, 149 .tt_port = PORT_BIDIR, 150 .tt_enable_src = true, 151 .tt_enable_dst = false, 152 .tt_pass = TCPSIG_NOCONNECT 153 }, { 154 .tt_desc = "IPv4 BIDIR with MD5 enabled on dst only", 155 .tt_domain = PF_INET, 156 .tt_port = PORT_BIDIR, 157 .tt_enable_src = false, 158 .tt_enable_dst = true, 159 .tt_pass = TCPSIG_NOCONNECT 160 }, { 161 .tt_desc = "IPv6 BIDIR with MD5 enabled on both sides", 162 .tt_domain = PF_INET6, 163 .tt_port = PORT_BIDIR, 164 .tt_enable_src = true, 165 .tt_enable_dst = true, 166 .tt_pass = TCPSIG_SENDRECV 167 }, { 168 .tt_desc = "IPv6 BIDIR with MD5 disabled on both sides", 169 .tt_domain = PF_INET6, 170 .tt_port = PORT_BIDIR, 171 .tt_enable_src = false, 172 .tt_enable_dst = false, 173 .tt_pass = TCPSIG_SENDRECV 174 }, { 175 .tt_desc = "IPv6 BIDIR with MD5 enabled on src only", 176 .tt_domain = PF_INET6, 177 .tt_port = PORT_BIDIR, 178 .tt_enable_src = true, 179 .tt_enable_dst = false, 180 .tt_pass = TCPSIG_NOCONNECT 181 }, { 182 .tt_desc = "IPv6 BIDIR with MD5 enabled on dst only", 183 .tt_domain = PF_INET6, 184 .tt_port = PORT_BIDIR, 185 .tt_enable_src = false, 186 .tt_enable_dst = true, 187 .tt_pass = TCPSIG_NOCONNECT 188 }, 189 /* Tests using the port with mismatching SA keys */ 190 { 191 /* 192 * Both sides of the connection have access to the two 193 * SAs and will use the correct key depending on the direction 194 * of the traffic. We therefore expect this to succeed. 195 * `tcpdump -M` can be used to verify that a different key is 196 * being used in each direction. 197 */ 198 .tt_desc = "IPv4 MISMATCH with MD5 enabled on both sides", 199 .tt_domain = PF_INET, 200 .tt_port = PORT_MISMATCH, 201 .tt_enable_src = true, 202 .tt_enable_dst = true, 203 .tt_pass = TCPSIG_SENDRECV 204 }, { 205 .tt_desc = "IPv4 MISMATCH with MD5 disabled on both sides", 206 .tt_domain = PF_INET, 207 .tt_port = PORT_MISMATCH, 208 .tt_enable_src = false, 209 .tt_enable_dst = false, 210 .tt_pass = TCPSIG_SENDRECV 211 }, { 212 .tt_desc = "IPv4 MISMATCH with MD5 enabled on src only", 213 .tt_domain = PF_INET, 214 .tt_port = PORT_MISMATCH, 215 .tt_enable_src = true, 216 .tt_enable_dst = false, 217 .tt_pass = TCPSIG_NOCONNECT 218 }, { 219 .tt_desc = "IPv4 MISMATCH with MD5 enabled on dst only", 220 .tt_domain = PF_INET, 221 .tt_port = PORT_MISMATCH, 222 .tt_enable_src = false, 223 .tt_enable_dst = true, 224 .tt_pass = TCPSIG_NOCONNECT 225 }, { 226 .tt_desc = "IPv6 MISMATCH with MD5 enabled on both sides", 227 .tt_domain = PF_INET6, 228 .tt_port = PORT_MISMATCH, 229 .tt_enable_src = true, 230 .tt_enable_dst = true, 231 .tt_pass = TCPSIG_SENDRECV 232 }, { 233 .tt_desc = "IPv6 MISMATCH with MD5 disabled on both sides", 234 .tt_domain = PF_INET6, 235 .tt_port = PORT_MISMATCH, 236 .tt_enable_src = false, 237 .tt_enable_dst = false, 238 .tt_pass = TCPSIG_SENDRECV 239 }, { 240 .tt_desc = "IPv6 MISMATCH with MD5 enabled on src only", 241 .tt_domain = PF_INET6, 242 .tt_port = PORT_MISMATCH, 243 .tt_enable_src = true, 244 .tt_enable_dst = false, 245 .tt_pass = TCPSIG_NOCONNECT 246 }, { 247 .tt_desc = "IPv6 MISMATCH with MD5 enabled on dst only", 248 .tt_domain = PF_INET6, 249 .tt_port = PORT_MISMATCH, 250 .tt_enable_src = false, 251 .tt_enable_dst = true, 252 .tt_pass = TCPSIG_NOCONNECT 253 }, 254 /* Tests using the port with only an outbound SA */ 255 { 256 .tt_desc = "IPv4 OBSA with MD5 enabled on both sides", 257 .tt_domain = PF_INET, 258 .tt_port = PORT_OBSA, 259 .tt_enable_src = true, 260 .tt_enable_dst = true, 261 .tt_pass = TCPSIG_NOCONNECT 262 }, { 263 .tt_desc = "IPv4 OBSA with MD5 disabled on both sides", 264 .tt_domain = PF_INET, 265 .tt_port = PORT_OBSA, 266 .tt_enable_src = false, 267 .tt_enable_dst = false, 268 .tt_pass = TCPSIG_SENDRECV 269 }, { 270 .tt_desc = "IPv4 OBSA with MD5 enabled on src only", 271 .tt_domain = PF_INET, 272 .tt_port = PORT_OBSA, 273 .tt_enable_src = true, 274 .tt_enable_dst = false, 275 .tt_pass = TCPSIG_NOCONNECT 276 }, { 277 .tt_desc = "IPv4 OBSA with MD5 enabled on dst only", 278 .tt_domain = PF_INET, 279 .tt_port = PORT_OBSA, 280 .tt_enable_src = false, 281 .tt_enable_dst = true, 282 .tt_pass = TCPSIG_NOCONNECT 283 }, { 284 .tt_desc = "IPv6 OBSA with MD5 enabled on both sides", 285 .tt_domain = PF_INET6, 286 .tt_port = PORT_OBSA, 287 .tt_enable_src = true, 288 .tt_enable_dst = true, 289 .tt_pass = TCPSIG_NOCONNECT 290 }, { 291 .tt_desc = "IPv6 OBSA with MD5 disabled on both sides", 292 .tt_domain = PF_INET6, 293 .tt_port = PORT_OBSA, 294 .tt_enable_src = false, 295 .tt_enable_dst = false, 296 .tt_pass = TCPSIG_SENDRECV 297 }, { 298 .tt_desc = "IPv6 OBSA with MD5 enabled on src only", 299 .tt_domain = PF_INET6, 300 .tt_port = PORT_OBSA, 301 .tt_enable_src = true, 302 .tt_enable_dst = false, 303 .tt_pass = TCPSIG_NOCONNECT 304 }, { 305 .tt_desc = "IPv6 OBSA with MD5 enabled on dst only", 306 .tt_domain = PF_INET6, 307 .tt_port = PORT_OBSA, 308 .tt_enable_src = false, 309 .tt_enable_dst = true, 310 .tt_pass = TCPSIG_NOCONNECT 311 }, 312 /* Tests using the port with only an inbound SA */ 313 { 314 .tt_desc = "IPv4 IBSA with MD5 enabled on both sides", 315 .tt_domain = PF_INET, 316 .tt_port = PORT_IBSA, 317 .tt_enable_src = true, 318 .tt_enable_dst = true, 319 .tt_pass = TCPSIG_CONNREFUSED 320 }, { 321 .tt_desc = "IPv4 IBSA with MD5 disabled on both sides", 322 .tt_domain = PF_INET, 323 .tt_port = PORT_IBSA, 324 .tt_enable_src = false, 325 .tt_enable_dst = false, 326 .tt_pass = TCPSIG_SENDRECV 327 }, { 328 .tt_desc = "IPv4 IBSA with MD5 enabled on src only", 329 .tt_domain = PF_INET, 330 .tt_port = PORT_IBSA, 331 .tt_enable_src = true, 332 .tt_enable_dst = false, 333 .tt_pass = TCPSIG_CONNREFUSED 334 }, { 335 .tt_desc = "IPv4 IBSA with MD5 enabled on dst only", 336 .tt_domain = PF_INET, 337 .tt_port = PORT_IBSA, 338 .tt_enable_src = false, 339 .tt_enable_dst = true, 340 .tt_pass = TCPSIG_NOCONNECT 341 }, { 342 .tt_desc = "IPv6 IBSA with MD5 enabled on both sides", 343 .tt_domain = PF_INET6, 344 .tt_port = PORT_IBSA, 345 .tt_enable_src = true, 346 .tt_enable_dst = true, 347 .tt_pass = TCPSIG_CONNREFUSED 348 }, { 349 .tt_desc = "IPv6 IBSA with MD5 disabled on both sides", 350 .tt_domain = PF_INET6, 351 .tt_port = PORT_IBSA, 352 .tt_enable_src = false, 353 .tt_enable_dst = false, 354 .tt_pass = TCPSIG_SENDRECV 355 }, { 356 .tt_desc = "IPv6 IBSA with MD5 enabled on src only", 357 .tt_domain = PF_INET6, 358 .tt_port = PORT_IBSA, 359 .tt_enable_src = true, 360 .tt_enable_dst = false, 361 .tt_pass = TCPSIG_CONNREFUSED 362 }, { 363 .tt_desc = "IPv6 IBSA with MD5 enabled on dst only", 364 .tt_domain = PF_INET6, 365 .tt_port = PORT_IBSA, 366 .tt_enable_src = false, 367 .tt_enable_dst = true, 368 .tt_pass = TCPSIG_NOCONNECT 369 } 370 }; 371 372 static bool 373 tcpsig_bind_dest(const tcpsig_test_t *test, int sock, 374 struct sockaddr_storage *dst) 375 { 376 socklen_t len; 377 struct sockaddr_storage addr; 378 379 (void) memset(&addr, 0, sizeof (struct sockaddr_storage)); 380 381 if (test->tt_domain == PF_INET) { 382 struct sockaddr_in *in = (struct sockaddr_in *)&addr; 383 in->sin_family = AF_INET; 384 in->sin_port = htons(test->tt_port); 385 if (inet_pton(AF_INET, "127.0.0.1", &in->sin_addr) != 1) { 386 warnx("TEST FAILED: %s: failed to convert 127.0.0.1 " 387 "to an IPv4 address", test->tt_desc); 388 return (false); 389 } 390 len = sizeof (struct sockaddr_in); 391 } else { 392 struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&addr; 393 in6->sin6_family = AF_INET6; 394 in6->sin6_port = htons(test->tt_port); 395 if (inet_pton(AF_INET6, "::1", &in6->sin6_addr) != 1) { 396 warnx("TEST FAILED: %s: failed to convert ::1 " 397 "to an IPv6 address", test->tt_desc); 398 return (false); 399 } 400 len = sizeof (struct sockaddr_in6); 401 } 402 403 if (bind(sock, (struct sockaddr *)&addr, len) != 0) { 404 warn("TEST FAILED: %s: failed to bind listen socket", 405 test->tt_desc); 406 return (false); 407 } 408 409 len = sizeof (struct sockaddr_storage); 410 if (getsockname(sock, (struct sockaddr *)dst, &len) != 0) { 411 warn("TEST FAILED: %s: failed to retrieve socket address ", 412 test->tt_desc); 413 return (false); 414 } 415 416 return (true); 417 } 418 419 /* 420 * Our job is to attempt to connect to the other end with our current settings. 421 * This may not work, so we use our port to get things ready just in case. 422 */ 423 static bool 424 tcpsig_connect(const tcpsig_test_t *test, int port, int src, int dst, int *cfd, 425 const struct sockaddr *addr) 426 { 427 struct timespec to = { .tv_nsec = sock_to }; 428 int namelen = test->tt_domain == PF_INET ? sizeof (struct sockaddr_in) : 429 sizeof (struct sockaddr_in6); 430 int conn; 431 port_event_t pe; 432 433 if (listen(dst, 5) != 0) { 434 warn("TEST FAILED: %s: failed to listen", test->tt_desc); 435 return (false); 436 } 437 438 if (connect(src, addr, namelen) != 0 && errno != EINPROGRESS) { 439 if (errno == ECONNREFUSED && 440 test->tt_pass == TCPSIG_CONNREFUSED) { 441 (void) printf("TEST PASSED: %s: connection refused\n", 442 test->tt_desc); 443 return (true); 444 } 445 warn("TEST FAILED: %s: failed to connect", test->tt_desc); 446 return (false); 447 } 448 449 if (port_associate(port, PORT_SOURCE_FD, src, POLLOUT, NULL) != 0) { 450 err(EXIT_FAILURE, "INTERNAL TEST FAILURE: %s: could not port " 451 "associate to watch connect", test->tt_desc); 452 } 453 454 if (port_get(port, &pe, &to) != 0) { 455 if (test->tt_pass == TCPSIG_NOCONNECT) { 456 (void) printf( 457 "TEST PASSED: %s: correctly failed to connect\n", 458 test->tt_desc); 459 return (true); 460 } else { 461 warn("TEST FAILED: %s: timed out waiting to connect", 462 test->tt_desc); 463 return (false); 464 } 465 } 466 467 if ((pe.portev_events & POLLOUT) == 0) { 468 warnx("TEST FAILED: %s: connect port event doesn't contain " 469 "POLLOUT, found 0x%x", test->tt_desc, pe.portev_events); 470 return (false); 471 } 472 473 /* 474 * Now make sure the listen socket is ready. 475 */ 476 if (port_associate(port, PORT_SOURCE_FD, dst, POLLIN, NULL) != 0) { 477 err(EXIT_FAILURE, "INTERNAL TEST FAILURE: %s: could not port " 478 "associate to watch accept", test->tt_desc); 479 } 480 481 if (port_get(port, &pe, &to) != 0) { 482 warn("TEST FAILED: %s: timed out waiting to accept", 483 test->tt_desc); 484 return (false); 485 } 486 487 if ((pe.portev_events & POLLIN) == 0) { 488 warnx("TEST FAILED: %s: accept port event doesn't contain " 489 "POLLIN, found 0x%x", test->tt_desc, pe.portev_events); 490 return (false); 491 } 492 493 conn = accept4(dst, NULL, NULL, SOCK_NONBLOCK); 494 if (conn < 0) { 495 warn("TEST FAILED: %s: failed to get client connection", 496 test->tt_desc); 497 return (false); 498 } 499 500 if (test->tt_pass != TCPSIG_SENDRECV && 501 test->tt_pass != TCPSIG_NODATA) { 502 warnx("TEST FAILED: %s: expected connect to fail, but passed", 503 test->tt_desc); 504 return (false); 505 } 506 507 *cfd = conn; 508 return (true); 509 } 510 511 /* 512 * Attempt to send data with the tcpsigs set up appropriately. This might fail, 513 * hence our port_associate dance and unfortunately regrettable timeout. 514 */ 515 static bool 516 tcpsig_sendrecv(const tcpsig_test_t *test, int port, int src, int dst) 517 { 518 struct timespec to = { .tv_nsec = sock_to }; 519 port_event_t pe; 520 uint32_t data; 521 ssize_t sret; 522 523 if (send(src, &msgdata, sizeof (msgdata), MSG_NOSIGNAL) != 524 sizeof (msgdata)) { 525 warn("TEST FAILED: %s: failed to write message to socket", 526 test->tt_desc); 527 } 528 529 if (port_associate(port, PORT_SOURCE_FD, dst, POLLIN, NULL) != 0) { 530 err(EXIT_FAILURE, "INTERNAL TEST FAILURE: %s: could not port " 531 "associate to watch recv", test->tt_desc); 532 } 533 534 if (port_get(port, &pe, &to) != 0) { 535 if (test->tt_pass == TCPSIG_NODATA) { 536 (void) printf("TEST PASSED: %s: timed out waiting " 537 "for data\n", test->tt_desc); 538 return (true); 539 } else { 540 warn("TEST FAILED: %s: timed out waiting to recv", 541 test->tt_desc); 542 return (false); 543 } 544 } 545 546 if ((pe.portev_events & POLLIN) == 0) { 547 warnx("TEST FAILED: %s: receive port event doesn't contain " 548 "POLLIN, found 0x%x", test->tt_desc, pe.portev_events); 549 return (false); 550 } 551 552 sret = recv(dst, &data, sizeof (data), MSG_DONTWAIT); 553 if (sret != (ssize_t)sizeof (data)) { 554 warnx("TEST FAILED: %s: failed to receive data: %zx", 555 test->tt_desc, sret); 556 return (false); 557 } 558 559 if (test->tt_pass != TCPSIG_SENDRECV) { 560 warnx("TEST FAILED: %s: found data, despite expecting not to", 561 test->tt_desc); 562 return (false); 563 } 564 565 if (data != msgdata) { 566 warnx("TEST FAILED: %s: data mismatch: expected 0x%x, found " 567 "0x%x", test->tt_desc, msgdata, data); 568 return (false); 569 } 570 571 (void) printf("TEST PASSED: %s: successfully received data\n", 572 test->tt_desc); 573 return (true); 574 } 575 576 static bool 577 tcpsig_test_one(const tcpsig_test_t *test) 578 { 579 int src = -1, dst = -1, cfd = -1, port = -1, tdst; 580 int x; 581 bool ret = true; 582 struct sockaddr_storage dst_addr; 583 584 if ((port = port_create()) < 0) 585 err(EXIT_FAILURE, "TEST FAILED: failed to create event port"); 586 587 src = socket(test->tt_domain, SOCK_STREAM | SOCK_NONBLOCK, 0); 588 if (src < 0) { 589 warn("TEST FAILED: %s: failed to create source socket", 590 test->tt_desc); 591 ret = false; 592 goto cleanup; 593 } 594 595 x = test->tt_enable_src ? 1 : 0; 596 if (setsockopt(src, IPPROTO_TCP, TCP_MD5SIG, &x, sizeof (x)) != 0) { 597 warn("TEST FAILED: %s: failed to configure src MD5SIG option", 598 test->tt_desc); 599 ret = false; 600 goto cleanup; 601 } 602 603 dst = socket(test->tt_domain, SOCK_STREAM | SOCK_NONBLOCK, 0); 604 if (dst < 0) { 605 warn("TEST FAILED: %s: failed to create destination socket", 606 test->tt_desc); 607 ret = false; 608 goto cleanup; 609 } 610 611 x = test->tt_enable_dst ? 1 : 0; 612 if (setsockopt(dst, IPPROTO_TCP, TCP_MD5SIG, &x, sizeof (x)) != 0) { 613 warn("TEST FAILED: %s: failed to configure dst MD5SIG option", 614 test->tt_desc); 615 ret = false; 616 goto cleanup; 617 } 618 619 if (!tcpsig_bind_dest(test, dst, &dst_addr)) { 620 ret = false; 621 goto cleanup; 622 } 623 624 if (!tcpsig_connect(test, port, src, dst, &cfd, 625 (struct sockaddr *)&dst_addr)) { 626 ret = false; 627 goto cleanup; 628 } 629 630 if (test->tt_pass != TCPSIG_SENDRECV && test->tt_pass != TCPSIG_NODATA) 631 goto cleanup; 632 633 tdst = cfd; 634 635 if (!tcpsig_sendrecv(test, port, src, tdst)) { 636 ret = false; 637 goto cleanup; 638 } 639 640 cleanup: 641 if (port > -1) 642 (void) close(port); 643 if (src > -1) { 644 (void) shutdown(src, SHUT_RDWR); 645 (void) close(src); 646 } 647 if (dst > -1) 648 (void) close(dst); 649 if (cfd > -1) 650 (void) close(cfd); 651 return (ret); 652 } 653 654 int 655 main(int argc, char **argv) 656 { 657 size_t max = ARRAY_SIZE(tcpsig_tests) - 1; 658 int ret = EXIT_SUCCESS; 659 660 if (argc == 2) { 661 const char *errstr; 662 size_t idx; 663 664 idx = (size_t)strtonumx(argv[1], 0, max, &errstr, 0); 665 if (errstr != NULL) { 666 (void) fprintf(stderr, "Syntax: %s [test number]\n", 667 getprogname()); 668 (void) fprintf(stderr, 669 "Test number is in the range [0-%u]\n", max); 670 (void) fprintf(stderr, "\nAvailable tests:\n"); 671 for (size_t i = 0; i <= max; i++) { 672 (void) fprintf(stderr, " %5d - %s\n", i, 673 tcpsig_tests[i].tt_desc); 674 } 675 return (EXIT_FAILURE); 676 } 677 678 if (!tcpsig_test_one(&tcpsig_tests[idx])) 679 ret = EXIT_FAILURE; 680 } else { 681 for (size_t i = 0; i <= max; i++) { 682 if (!tcpsig_test_one(&tcpsig_tests[i])) 683 ret = EXIT_FAILURE; 684 } 685 if (ret == EXIT_SUCCESS) 686 (void) printf("All tests passed successfully\n"); 687 } 688 689 return (ret); 690 } 691