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_SENDRECV
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_SENDRECV
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_SENDRECV
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_SENDRECV
369 }
370 };
371
372 static bool
tcpsig_bind_dest(const tcpsig_test_t * test,int sock,struct sockaddr_storage * dst)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
tcpsig_connect(const tcpsig_test_t * test,int port,int src,int dst,int * cfd,const struct sockaddr * addr)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, opt;
431 unsigned int optlen;
432 port_event_t pe;
433
434 if (listen(dst, 5) != 0) {
435 warn("TEST FAILED: %s: failed to listen", test->tt_desc);
436 return (false);
437 }
438
439 if (connect(src, addr, namelen) != 0 && errno != EINPROGRESS) {
440 if (errno == ECONNREFUSED &&
441 test->tt_pass == TCPSIG_CONNREFUSED) {
442 (void) printf("TEST PASSED: %s: connection refused\n",
443 test->tt_desc);
444 return (true);
445 }
446 warn("TEST FAILED: %s: failed to connect", test->tt_desc);
447 return (false);
448 }
449
450 if (port_associate(port, PORT_SOURCE_FD, src, POLLOUT, NULL) != 0) {
451 err(EXIT_FAILURE, "INTERNAL TEST FAILURE: %s: could not port "
452 "associate to watch connect", test->tt_desc);
453 }
454
455 if (port_get(port, &pe, &to) != 0) {
456 if (test->tt_pass == TCPSIG_NOCONNECT) {
457 (void) printf(
458 "TEST PASSED: %s: correctly failed to connect\n",
459 test->tt_desc);
460 return (true);
461 } else {
462 warn("TEST FAILED: %s: timed out waiting to connect",
463 test->tt_desc);
464 return (false);
465 }
466 }
467
468 if ((pe.portev_events & POLLOUT) == 0) {
469 warnx("TEST FAILED: %s: connect port event doesn't contain "
470 "POLLOUT, found 0x%x", test->tt_desc, pe.portev_events);
471 return (false);
472 }
473
474 /*
475 * Now make sure the listen socket is ready.
476 */
477 if (port_associate(port, PORT_SOURCE_FD, dst, POLLIN, NULL) != 0) {
478 err(EXIT_FAILURE, "INTERNAL TEST FAILURE: %s: could not port "
479 "associate to watch accept", test->tt_desc);
480 }
481
482 if (port_get(port, &pe, &to) != 0) {
483 warn("TEST FAILED: %s: timed out waiting to accept",
484 test->tt_desc);
485 return (false);
486 }
487
488 if ((pe.portev_events & POLLIN) == 0) {
489 warnx("TEST FAILED: %s: accept port event doesn't contain "
490 "POLLIN, found 0x%x", test->tt_desc, pe.portev_events);
491 return (false);
492 }
493
494 conn = accept4(dst, NULL, NULL, SOCK_NONBLOCK);
495 if (conn < 0) {
496 warn("TEST FAILED: %s: failed to get client connection",
497 test->tt_desc);
498 return (false);
499 }
500
501 optlen = sizeof (opt);
502 if (getsockopt(conn, IPPROTO_TCP, TCP_MD5SIG, &opt, &optlen) != 0) {
503 warn("TEST FAILED: %s: failed to retrieve accepted socket "
504 "TCP_MD5SIG option", test->tt_desc);
505 return (false);
506 }
507
508 if (optlen != sizeof (opt)) {
509 warn("TEST FAILED: %s: TCP_MD5SIG option has wrong length %d "
510 "(expected %ld).", test->tt_desc, optlen, sizeof (opt));
511 return (false);
512 }
513
514 /*
515 * For tests where the TCP MD5 option is not enabled on the source, but
516 * is on the destination, and where we expect the connection to
517 * succeed, we also expect that the socket option has been disabled on
518 * accept(). Check.
519 */
520 if (test->tt_enable_dst && !test->tt_enable_src &&
521 test->tt_pass == TCPSIG_SENDRECV && opt != 0) {
522 warnx("TEST FAILED: %s: TCP_MD5SIG is set and should not be",
523 test->tt_desc);
524 return (false);
525 } else if (test->tt_enable_src && opt == 0) {
526 warnx("TEST FAILED: %s: TCP_MD5SIG is not set and should be",
527 test->tt_desc);
528 return (false);
529 }
530
531 if (test->tt_pass != TCPSIG_SENDRECV &&
532 test->tt_pass != TCPSIG_NODATA) {
533 warnx("TEST FAILED: %s: expected connect to fail, but passed",
534 test->tt_desc);
535 return (false);
536 }
537
538 *cfd = conn;
539 return (true);
540 }
541
542 /*
543 * Attempt to send data with the tcpsigs set up appropriately. This might fail,
544 * hence our port_associate dance and unfortunately regrettable timeout.
545 */
546 static bool
tcpsig_sendrecv(const tcpsig_test_t * test,int port,int src,int dst)547 tcpsig_sendrecv(const tcpsig_test_t *test, int port, int src, int dst)
548 {
549 struct timespec to = { .tv_nsec = sock_to };
550 port_event_t pe;
551 uint32_t data;
552 ssize_t sret;
553
554 if (send(src, &msgdata, sizeof (msgdata), MSG_NOSIGNAL) !=
555 sizeof (msgdata)) {
556 warn("TEST FAILED: %s: failed to write message to socket",
557 test->tt_desc);
558 }
559
560 if (port_associate(port, PORT_SOURCE_FD, dst, POLLIN, NULL) != 0) {
561 err(EXIT_FAILURE, "INTERNAL TEST FAILURE: %s: could not port "
562 "associate to watch recv", test->tt_desc);
563 }
564
565 if (port_get(port, &pe, &to) != 0) {
566 if (test->tt_pass == TCPSIG_NODATA) {
567 (void) printf("TEST PASSED: %s: timed out waiting "
568 "for data\n", test->tt_desc);
569 return (true);
570 } else {
571 warn("TEST FAILED: %s: timed out waiting to recv",
572 test->tt_desc);
573 return (false);
574 }
575 }
576
577 if ((pe.portev_events & POLLIN) == 0) {
578 warnx("TEST FAILED: %s: receive port event doesn't contain "
579 "POLLIN, found 0x%x", test->tt_desc, pe.portev_events);
580 return (false);
581 }
582
583 sret = recv(dst, &data, sizeof (data), MSG_DONTWAIT);
584 if (sret != (ssize_t)sizeof (data)) {
585 warnx("TEST FAILED: %s: failed to receive data: %zx",
586 test->tt_desc, sret);
587 return (false);
588 }
589
590 if (test->tt_pass != TCPSIG_SENDRECV) {
591 warnx("TEST FAILED: %s: found data, despite expecting not to",
592 test->tt_desc);
593 return (false);
594 }
595
596 if (data != msgdata) {
597 warnx("TEST FAILED: %s: data mismatch: expected 0x%x, found "
598 "0x%x", test->tt_desc, msgdata, data);
599 return (false);
600 }
601
602 (void) printf("TEST PASSED: %s: successfully received data\n",
603 test->tt_desc);
604 return (true);
605 }
606
607 static bool
tcpsig_test_one(const tcpsig_test_t * test)608 tcpsig_test_one(const tcpsig_test_t *test)
609 {
610 int src = -1, dst = -1, cfd = -1, port = -1, tdst;
611 int x;
612 bool ret = true;
613 struct sockaddr_storage dst_addr;
614
615 if ((port = port_create()) < 0)
616 err(EXIT_FAILURE, "TEST FAILED: failed to create event port");
617
618 src = socket(test->tt_domain, SOCK_STREAM | SOCK_NONBLOCK, 0);
619 if (src < 0) {
620 warn("TEST FAILED: %s: failed to create source socket",
621 test->tt_desc);
622 ret = false;
623 goto cleanup;
624 }
625
626 x = test->tt_enable_src ? 1 : 0;
627 if (setsockopt(src, IPPROTO_TCP, TCP_MD5SIG, &x, sizeof (x)) != 0) {
628 warn("TEST FAILED: %s: failed to configure src MD5SIG option",
629 test->tt_desc);
630 ret = false;
631 goto cleanup;
632 }
633
634 dst = socket(test->tt_domain, SOCK_STREAM | SOCK_NONBLOCK, 0);
635 if (dst < 0) {
636 warn("TEST FAILED: %s: failed to create destination socket",
637 test->tt_desc);
638 ret = false;
639 goto cleanup;
640 }
641
642 x = test->tt_enable_dst ? 1 : 0;
643 if (setsockopt(dst, IPPROTO_TCP, TCP_MD5SIG, &x, sizeof (x)) != 0) {
644 warn("TEST FAILED: %s: failed to configure dst MD5SIG option",
645 test->tt_desc);
646 ret = false;
647 goto cleanup;
648 }
649
650 if (!tcpsig_bind_dest(test, dst, &dst_addr)) {
651 ret = false;
652 goto cleanup;
653 }
654
655 if (!tcpsig_connect(test, port, src, dst, &cfd,
656 (struct sockaddr *)&dst_addr)) {
657 ret = false;
658 goto cleanup;
659 }
660
661 if (test->tt_pass != TCPSIG_SENDRECV && test->tt_pass != TCPSIG_NODATA)
662 goto cleanup;
663
664 tdst = cfd;
665
666 if (!tcpsig_sendrecv(test, port, src, tdst)) {
667 ret = false;
668 goto cleanup;
669 }
670
671 cleanup:
672 if (port > -1)
673 (void) close(port);
674 if (src > -1) {
675 (void) shutdown(src, SHUT_RDWR);
676 (void) close(src);
677 }
678 if (dst > -1)
679 (void) close(dst);
680 if (cfd > -1)
681 (void) close(cfd);
682 return (ret);
683 }
684
685 int
main(int argc,char ** argv)686 main(int argc, char **argv)
687 {
688 size_t max = ARRAY_SIZE(tcpsig_tests) - 1;
689 int ret = EXIT_SUCCESS;
690
691 if (argc == 2) {
692 const char *errstr;
693 size_t idx;
694
695 idx = (size_t)strtonumx(argv[1], 0, max, &errstr, 0);
696 if (errstr != NULL) {
697 (void) fprintf(stderr, "Syntax: %s [test number]\n",
698 getprogname());
699 (void) fprintf(stderr,
700 "Test number is in the range [0-%u]\n", max);
701 (void) fprintf(stderr, "\nAvailable tests:\n");
702 for (size_t i = 0; i <= max; i++) {
703 (void) fprintf(stderr, " %5d - %s\n", i,
704 tcpsig_tests[i].tt_desc);
705 }
706 return (EXIT_FAILURE);
707 }
708
709 if (!tcpsig_test_one(&tcpsig_tests[idx]))
710 ret = EXIT_FAILURE;
711 } else {
712 for (size_t i = 0; i <= max; i++) {
713 if (!tcpsig_test_one(&tcpsig_tests[i]))
714 ret = EXIT_FAILURE;
715 }
716 if (ret == EXIT_SUCCESS)
717 (void) printf("All tests passed successfully\n");
718 }
719
720 return (ret);
721 }
722