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 IP_MINTTL and IPV6_MINHOPCOUNT. The main design of
18 * this is to spin up connections on localhost that walk through different
19 * socket types and confirm that we can use the corresponding socket option and
20 * that we receive traffic when the TTL is set and not otherwise.
21 */
22
23 #include <err.h>
24 #include <port.h>
25 #include <stdlib.h>
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <netinet/in.h>
29 #include <netinet/sctp.h>
30 #include <arpa/inet.h>
31 #include <sys/sysmacros.h>
32 #include <stdbool.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <errno.h>
36 #include <sys/debug.h>
37
38 /*
39 * The IP protocols 0xfd-0xfe are reserved for experiments. This is the safest
40 * IP protocol for us to use then in our testing for raw sockets.
41 */
42 #define TTL_IPPROTO_EXP 0xfd
43
44 static hrtime_t tt_sock_to = MSEC2NSEC(100); /* ms in ns */
45 static const uint32_t tt_msg = 0x7777;
46
47 typedef enum {
48 TTL_SENDRECV,
49 TTL_NOCONNECT,
50 TTL_NODATA
51 } ttl_pass_t;
52
53 typedef struct {
54 const char *tt_desc;
55 int tt_domain;
56 int tt_type;
57 int tt_ttl;
58 int tt_proto;
59 int tt_minttl;
60 ttl_pass_t tt_pass;
61 } ttl_test_t;
62
63 static const ttl_test_t ttl_tests[] = {
64 {
65 .tt_desc = "IPv4 TCP TTL/MIN: unset/0",
66 .tt_domain = PF_INET,
67 .tt_type = SOCK_STREAM,
68 .tt_ttl = 0,
69 .tt_minttl = 0,
70 .tt_pass = TTL_SENDRECV
71 }, {
72 .tt_desc = "IPv4 TCP TTL/MIN: 200/100",
73 .tt_domain = PF_INET,
74 .tt_type = SOCK_STREAM,
75 .tt_ttl = 200,
76 .tt_minttl = 100,
77 .tt_pass = TTL_SENDRECV
78 }, {
79 .tt_desc = "IPv4 TCP TTL/MIN: 255/255",
80 .tt_domain = PF_INET,
81 .tt_type = SOCK_STREAM,
82 .tt_ttl = 255,
83 .tt_minttl = 255,
84 .tt_pass = TTL_SENDRECV
85 }, {
86 .tt_desc = "IPv4 TCP TTL/MIN: 23/169",
87 .tt_domain = PF_INET,
88 .tt_type = SOCK_STREAM,
89 .tt_ttl = 23,
90 .tt_minttl = 169,
91 .tt_pass = TTL_NOCONNECT
92 }, {
93 .tt_desc = "IPv4 TCP TTL/MIN: 254/255",
94 .tt_domain = PF_INET,
95 .tt_type = SOCK_STREAM,
96 .tt_ttl = 254,
97 .tt_minttl = 255,
98 .tt_pass = TTL_NOCONNECT
99 }, {
100 .tt_desc = "IPv4 UDP TTL/MIN: unset/0",
101 .tt_domain = PF_INET,
102 .tt_type = SOCK_DGRAM,
103 .tt_ttl = 0,
104 .tt_minttl = 0,
105 .tt_pass = TTL_SENDRECV
106 }, {
107 .tt_desc = "IPv4 UDP TTL/MIN: 200/100",
108 .tt_domain = PF_INET,
109 .tt_type = SOCK_DGRAM,
110 .tt_ttl = 200,
111 .tt_minttl = 100,
112 .tt_pass = TTL_SENDRECV
113 }, {
114 .tt_desc = "IPv4 UDP TTL/MIN: 255/255",
115 .tt_domain = PF_INET,
116 .tt_type = SOCK_DGRAM,
117 .tt_ttl = 255,
118 .tt_minttl = 255,
119 .tt_pass = TTL_SENDRECV
120 }, {
121 .tt_desc = "IPv4 UDP TTL/MIN: 23/169",
122 .tt_domain = PF_INET,
123 .tt_type = SOCK_DGRAM,
124 .tt_ttl = 23,
125 .tt_minttl = 169,
126 .tt_pass = TTL_NODATA
127 }, {
128 .tt_desc = "IPv4 UDP TTL/MIN: 254/255",
129 .tt_domain = PF_INET,
130 .tt_type = SOCK_DGRAM,
131 .tt_ttl = 254,
132 .tt_minttl = 255,
133 .tt_pass = TTL_NODATA
134 }, {
135 .tt_desc = "IPv4 SCTP TTL/MIN: unset/0",
136 .tt_domain = PF_INET,
137 .tt_type = SOCK_STREAM,
138 .tt_proto = IPPROTO_SCTP,
139 .tt_ttl = 0,
140 .tt_minttl = 0,
141 .tt_pass = TTL_SENDRECV
142 }, {
143 .tt_desc = "IPv4 SCTP TTL/MIN: 200/100",
144 .tt_domain = PF_INET,
145 .tt_type = SOCK_STREAM,
146 .tt_proto = IPPROTO_SCTP,
147 .tt_ttl = 200,
148 .tt_minttl = 100,
149 .tt_pass = TTL_SENDRECV
150 }, {
151 .tt_desc = "IPv4 SCTP TTL/MIN: 255/255",
152 .tt_domain = PF_INET,
153 .tt_type = SOCK_STREAM,
154 .tt_proto = IPPROTO_SCTP,
155 .tt_ttl = 255,
156 .tt_minttl = 255,
157 .tt_pass = TTL_SENDRECV
158 }, {
159 .tt_desc = "IPv4 SCTP TTL/MIN: 23/169",
160 .tt_domain = PF_INET,
161 .tt_type = SOCK_STREAM,
162 .tt_proto = IPPROTO_SCTP,
163 .tt_ttl = 23,
164 .tt_minttl = 169,
165 .tt_pass = TTL_NOCONNECT
166 }, {
167 .tt_desc = "IPv4 SCTP TTL/MIN: 254/255",
168 .tt_domain = PF_INET,
169 .tt_type = SOCK_STREAM,
170 .tt_proto = IPPROTO_SCTP,
171 .tt_ttl = 254,
172 .tt_minttl = 255,
173 .tt_pass = TTL_NOCONNECT
174 }, {
175 .tt_desc = "IPv4 RAW (0xfd) TTL/MIN: unset/0",
176 .tt_domain = PF_INET,
177 .tt_type = SOCK_RAW,
178 .tt_proto = TTL_IPPROTO_EXP,
179 .tt_ttl = 0,
180 .tt_minttl = 0,
181 .tt_pass = TTL_SENDRECV
182 }, {
183 .tt_desc = "IPv4 RAW (0xfd) TTL/MIN: 200/100",
184 .tt_domain = PF_INET,
185 .tt_type = SOCK_RAW,
186 .tt_proto = TTL_IPPROTO_EXP,
187 .tt_ttl = 200,
188 .tt_minttl = 100,
189 .tt_pass = TTL_SENDRECV
190 }, {
191 .tt_desc = "IPv4 RAW (0xfd) TTL/MIN: 255/255",
192 .tt_domain = PF_INET,
193 .tt_type = SOCK_RAW,
194 .tt_proto = TTL_IPPROTO_EXP,
195 .tt_ttl = 255,
196 .tt_minttl = 255,
197 .tt_pass = TTL_SENDRECV
198 }, {
199 .tt_desc = "IPv4 RAW (0xfd) TTL/MIN: 23/169",
200 .tt_domain = PF_INET,
201 .tt_type = SOCK_RAW,
202 .tt_proto = TTL_IPPROTO_EXP,
203 .tt_ttl = 23,
204 .tt_minttl = 169,
205 .tt_pass = TTL_NODATA
206 }, {
207 .tt_desc = "IPv4 RAW (0xfd) TTL/MIN: 254/255",
208 .tt_domain = PF_INET,
209 .tt_type = SOCK_RAW,
210 .tt_proto = TTL_IPPROTO_EXP,
211 .tt_ttl = 254,
212 .tt_minttl = 255,
213 .tt_pass = TTL_NODATA
214 }, {
215 .tt_desc = "IPv6 TCP TTL/MIN: unset/0",
216 .tt_domain = PF_INET6,
217 .tt_type = SOCK_STREAM,
218 .tt_ttl = 0,
219 .tt_minttl = 0,
220 .tt_pass = TTL_SENDRECV
221 }, {
222 .tt_desc = "IPv6 TCP TTL/MIN: 200/100",
223 .tt_domain = PF_INET6,
224 .tt_type = SOCK_STREAM,
225 .tt_ttl = 200,
226 .tt_minttl = 100,
227 .tt_pass = TTL_SENDRECV
228 }, {
229 .tt_desc = "IPv6 TCP TTL/MIN: 255/255",
230 .tt_domain = PF_INET6,
231 .tt_type = SOCK_STREAM,
232 .tt_ttl = 255,
233 .tt_minttl = 255,
234 .tt_pass = TTL_SENDRECV
235 }, {
236 .tt_desc = "IPv6 CTP TTL/MIN: 23/169",
237 .tt_domain = PF_INET6,
238 .tt_type = SOCK_STREAM,
239 .tt_ttl = 23,
240 .tt_minttl = 169,
241 .tt_pass = TTL_NOCONNECT
242 }, {
243 .tt_desc = "IPv6 CTP TTL/MIN: 254/255",
244 .tt_domain = PF_INET6,
245 .tt_type = SOCK_STREAM,
246 .tt_ttl = 254,
247 .tt_minttl = 255,
248 .tt_pass = TTL_NOCONNECT
249 }, {
250 .tt_desc = "IPv6 UDP TTL/MIN: unset/0",
251 .tt_domain = PF_INET6,
252 .tt_type = SOCK_DGRAM,
253 .tt_ttl = 0,
254 .tt_minttl = 0,
255 .tt_pass = TTL_SENDRECV
256 }, {
257 .tt_desc = "IPv6 UDP TTL/MIN: 200/100",
258 .tt_domain = PF_INET6,
259 .tt_type = SOCK_DGRAM,
260 .tt_ttl = 200,
261 .tt_minttl = 100,
262 .tt_pass = TTL_SENDRECV
263 }, {
264 .tt_desc = "IPv6 UDP TTL/MIN: 255/255",
265 .tt_domain = PF_INET6,
266 .tt_type = SOCK_DGRAM,
267 .tt_ttl = 255,
268 .tt_minttl = 255,
269 .tt_pass = TTL_SENDRECV
270 }, {
271 .tt_desc = "IPv6 UDP TTL/MIN: 23/169",
272 .tt_domain = PF_INET6,
273 .tt_type = SOCK_DGRAM,
274 .tt_ttl = 23,
275 .tt_minttl = 169,
276 .tt_pass = TTL_NODATA
277 }, {
278 .tt_desc = "IPv6 UDP TTL/MIN: 254/255",
279 .tt_domain = PF_INET6,
280 .tt_type = SOCK_DGRAM,
281 .tt_ttl = 254,
282 .tt_minttl = 255,
283 .tt_pass = TTL_NODATA
284 }, {
285 .tt_desc = "IPv6 SCTP TTL/MIN: unset/0",
286 .tt_domain = PF_INET6,
287 .tt_type = SOCK_STREAM,
288 .tt_proto = IPPROTO_SCTP,
289 .tt_ttl = 0,
290 .tt_minttl = 0,
291 .tt_pass = TTL_SENDRECV
292 }, {
293 .tt_desc = "IPv6 SCTP TTL/MIN: 200/100",
294 .tt_domain = PF_INET6,
295 .tt_type = SOCK_STREAM,
296 .tt_proto = IPPROTO_SCTP,
297 .tt_ttl = 200,
298 .tt_minttl = 100,
299 .tt_pass = TTL_SENDRECV
300 }, {
301 .tt_desc = "IPv6 SCTP TTL/MIN: 255/255",
302 .tt_domain = PF_INET6,
303 .tt_type = SOCK_STREAM,
304 .tt_proto = IPPROTO_SCTP,
305 .tt_ttl = 255,
306 .tt_minttl = 255,
307 .tt_pass = TTL_SENDRECV
308 }, {
309 .tt_desc = "IPv6 SCTP TTL/MIN: 23/169",
310 .tt_domain = PF_INET6,
311 .tt_type = SOCK_STREAM,
312 .tt_proto = IPPROTO_SCTP,
313 .tt_ttl = 23,
314 .tt_minttl = 169,
315 .tt_pass = TTL_NOCONNECT
316 }, {
317 .tt_desc = "IPv6 SCTP TTL/MIN: 254/255",
318 .tt_domain = PF_INET6,
319 .tt_type = SOCK_STREAM,
320 .tt_proto = IPPROTO_SCTP,
321 .tt_ttl = 254,
322 .tt_minttl = 255,
323 .tt_pass = TTL_NOCONNECT
324 }, {
325 .tt_desc = "IPv6 RAW (0xfd) TTL/MIN: unset/0",
326 .tt_domain = PF_INET6,
327 .tt_type = SOCK_RAW,
328 .tt_proto = TTL_IPPROTO_EXP,
329 .tt_ttl = 0,
330 .tt_minttl = 0,
331 .tt_pass = TTL_SENDRECV
332 }, {
333 .tt_desc = "IPv6 RAW (0xfd) TTL/MIN: 200/100",
334 .tt_domain = PF_INET6,
335 .tt_type = SOCK_RAW,
336 .tt_proto = TTL_IPPROTO_EXP,
337 .tt_ttl = 200,
338 .tt_minttl = 100,
339 .tt_pass = TTL_SENDRECV
340 }, {
341 .tt_desc = "IPv6 RAW (0xfd) TTL/MIN: 255/255",
342 .tt_domain = PF_INET6,
343 .tt_type = SOCK_RAW,
344 .tt_proto = TTL_IPPROTO_EXP,
345 .tt_ttl = 255,
346 .tt_minttl = 255,
347 .tt_pass = TTL_SENDRECV
348 }, {
349 .tt_desc = "IPv6 RAW (0xfd) TTL/MIN: 23/169",
350 .tt_domain = PF_INET6,
351 .tt_type = SOCK_RAW,
352 .tt_proto = TTL_IPPROTO_EXP,
353 .tt_ttl = 23,
354 .tt_minttl = 169,
355 .tt_pass = TTL_NODATA
356 }, {
357 .tt_desc = "IPv6 RAW (0xfd) TTL/MIN: 254/255",
358 .tt_domain = PF_INET6,
359 .tt_type = SOCK_RAW,
360 .tt_proto = TTL_IPPROTO_EXP,
361 .tt_ttl = 254,
362 .tt_minttl = 255,
363 .tt_pass = TTL_NODATA
364 }
365 };
366
367 static bool
ttl_bind_dest(const ttl_test_t * test,int sock,struct sockaddr_storage * dst)368 ttl_bind_dest(const ttl_test_t *test, int sock, struct sockaddr_storage *dst)
369 {
370 socklen_t len;
371 struct sockaddr_storage addr;
372
373 (void) memset(&addr, 0, sizeof (struct sockaddr_storage));
374
375 if (test->tt_domain == PF_INET) {
376 struct sockaddr_in *in = (struct sockaddr_in *)&addr;
377 in->sin_family = AF_INET;
378 in->sin_port = htons(0);
379 if (inet_pton(AF_INET, "127.0.0.1", &in->sin_addr) != 1) {
380 warnx("TEST FAILED: %s: failed to convert 127.0.0.1 "
381 "to an IPv4 address", test->tt_desc);
382 return (false);
383 }
384 len = sizeof (struct sockaddr_in);
385 } else {
386 struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&addr;
387 in6->sin6_family = AF_INET6;
388 in6->sin6_port = htons(0);
389 if (inet_pton(AF_INET6, "::1", &in6->sin6_addr) != 1) {
390 warnx("TEST FAILED: %s: failed to convert ::1 "
391 "to an IPv6 address", test->tt_desc);
392 return (false);
393 }
394 len = sizeof (struct sockaddr_in6);
395 }
396
397 if (bind(sock, (struct sockaddr *)&addr, len) != 0) {
398 warn("TEST FAILED: %s: failed to bind listen socket",
399 test->tt_desc);
400 return (false);
401 }
402
403 len = sizeof (struct sockaddr_storage);
404 if (getsockname(sock, (struct sockaddr *)dst, &len) != 0) {
405 warn("TEST FAILED: %s: failed to retrieve socket address ",
406 test->tt_desc);
407 return (false);
408 }
409
410 return (true);
411 }
412
413 /*
414 * Our job is to attempt to connect to the other end with our current settings.
415 * This may not work, so we use our port to get things ready just in case.
416 */
417 static bool
ttl_connect(const ttl_test_t * test,int port,int src,int dst,int * cfd,const struct sockaddr * addr)418 ttl_connect(const ttl_test_t *test, int port, int src, int dst, int *cfd,
419 const struct sockaddr *addr)
420 {
421 struct timespec to = { .tv_nsec = tt_sock_to };
422 int namelen = test->tt_domain == PF_INET ? sizeof (struct sockaddr_in) :
423 sizeof (struct sockaddr_in6);
424 int conn;
425 port_event_t pe;
426
427 if (listen(dst, 5) != 0) {
428 warn("TEST FAILED: %s: failed to listen", test->tt_desc);
429 return (false);
430 }
431
432 if (connect(src, addr, namelen) != 0 && errno != EINPROGRESS) {
433 warn("TEST FAILED: %s: failed to connect", test->tt_desc);
434 return (false);
435 }
436
437 if (port_associate(port, PORT_SOURCE_FD, src, POLLOUT, NULL) != 0) {
438 err(EXIT_FAILURE, "INTERNAL TEST FAILURE: %s: could not port "
439 "associate to watch connect", test->tt_desc);
440 }
441
442 if (port_get(port, &pe, &to) != 0) {
443 if (test->tt_pass == TTL_NOCONNECT) {
444 (void) printf("TEST PASSED: %s: correctly failed to "
445 "connect\n", test->tt_desc);
446 return (true);
447 } else {
448 warn("TEST FAILED: %s: timed out waiting to connect",
449 test->tt_desc);
450 return (false);
451 }
452 }
453
454 if ((pe.portev_events & POLLOUT) == 0) {
455 warnx("TEST FAILED: %s: connect port event doesn't contain "
456 "POLLOUT, found 0x%x", test->tt_desc, pe.portev_events);
457 return (false);
458 }
459
460 /*
461 * Now make sure the listen socket is ready.
462 */
463 if (port_associate(port, PORT_SOURCE_FD, dst, POLLIN, NULL) != 0) {
464 err(EXIT_FAILURE, "INTERNAL TEST FAILURE: %s: could not port "
465 "associate to watch accept", test->tt_desc);
466 }
467
468 if (port_get(port, &pe, &to) != 0) {
469 warn("TEST FAILED: %s: timed out waiting to accept",
470 test->tt_desc);
471 return (false);
472 }
473
474 if ((pe.portev_events & POLLIN) == 0) {
475 warnx("TEST FAILED: %s: accept port event doesn't contain "
476 "POLLIN, found 0x%x", test->tt_desc, pe.portev_events);
477 return (false);
478 }
479
480 conn = accept4(dst, NULL, NULL, SOCK_NONBLOCK);
481 if (conn < 0) {
482 warn("TEST FAILED: %s: failed to get client connection",
483 test->tt_desc);
484 return (false);
485 }
486
487 if (test->tt_pass != TTL_SENDRECV) {
488 warnx("TEST FAILED: %s: expected connect to fail, but passed",
489 test->tt_desc);
490 return (false);
491 }
492
493 *cfd = conn;
494 return (true);
495 }
496
497 static bool
ttl_check_ancil(const ttl_test_t * test,const struct msghdr * msg)498 ttl_check_ancil(const ttl_test_t *test, const struct msghdr *msg)
499 {
500 int level, ttlopt;
501
502 if (test->tt_domain == PF_INET) {
503 level = IPPROTO_IP;
504 ttlopt = IP_RECVTTL;
505 } else {
506 level = IPPROTO_IPV6;
507 ttlopt = IPV6_HOPLIMIT;
508 }
509
510 if (msg->msg_controllen != CMSG_SPACE(sizeof (int))) {
511 warnx("TEST FAILED: %s: expected %u bytes of ancillary "
512 "data, found %u", test->tt_desc, CMSG_SPACE(sizeof (int)),
513 msg->msg_controllen);
514 return (false);
515 }
516
517 for (const struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
518 cmsg = CMSG_NXTHDR(msg, cmsg)) {
519 int val;
520
521 if (cmsg->cmsg_level != level || cmsg->cmsg_type != ttlopt)
522 continue;
523 (void) memcpy(&val, CMSG_DATA(cmsg), sizeof (int));
524 if (test->tt_ttl != 0 && val != test->tt_ttl) {
525 warnx("TEST FAILED: %s: TTL/HLIM mismatch: expected "
526 "0x%x, found 0x%x", test->tt_desc, test->tt_ttl,
527 val);
528 return (false);
529 }
530
531 (void) printf("TEST PASSED: %s: TTL/HLIM is correct\n",
532 test->tt_desc);
533 return (true);
534 }
535
536 warnx("TEST FAILED: %s: failed to find TTL/HLIM in ancillary options",
537 test->tt_desc);
538 return (false);
539 }
540
541 /*
542 * Attempt to send data with the TTLs set up appropriately. This might fail,
543 * hence our port_associate dance and unfortunately regrettable timeout.
544 */
545 static bool
ttl_sendrecv(const ttl_test_t * test,int port,int src,int dst,struct sockaddr * addr)546 ttl_sendrecv(const ttl_test_t *test, int port, int src, int dst,
547 struct sockaddr *addr)
548 {
549 struct timespec to = { .tv_nsec = tt_sock_to };
550 int namelen = test->tt_domain == PF_INET ? sizeof (struct sockaddr_in) :
551 sizeof (struct sockaddr_in6);
552 uint8_t ancil[CMSG_SPACE(sizeof (int)) * 2];
553 port_event_t pe;
554 struct msghdr msg;
555 uint32_t data;
556 struct iovec iov;
557 ssize_t sret;
558
559 if (sendto(src, &tt_msg, sizeof (tt_msg), MSG_NOSIGNAL, addr,
560 namelen) != sizeof (tt_msg)) {
561 warn("TEST FAILED: %s: failed to write message to socket",
562 test->tt_desc);
563 }
564
565 if (port_associate(port, PORT_SOURCE_FD, dst, POLLIN, NULL) != 0) {
566 err(EXIT_FAILURE, "INTERNAL TEST FAILURE: %s: could not port "
567 "associate to watch recv", test->tt_desc);
568 }
569
570 if (port_get(port, &pe, &to) != 0) {
571 if (test->tt_pass == TTL_NODATA) {
572 (void) printf("TEST PASSED: %s: timed out waiting "
573 "for data\n", test->tt_desc);
574 return (true);
575 } else {
576 warn("TEST FAILED: %s: timed out waiting to recv",
577 test->tt_desc);
578 return (false);
579 }
580 }
581
582 if ((pe.portev_events & POLLIN) == 0) {
583 warnx("TEST FAILED: %s: receive port event doesn't contain "
584 "POLLIN, found 0x%x", test->tt_desc, pe.portev_events);
585 return (false);
586 }
587
588 (void) memset(&msg, 0, sizeof (msg));
589 iov.iov_base = (void *)&data;
590 iov.iov_len = sizeof (data);
591 msg.msg_iov = &iov;
592 msg.msg_iovlen = 1;
593 msg.msg_control = ancil;
594 msg.msg_controllen = sizeof (ancil);
595
596 sret = recvmsg(dst, &msg, MSG_DONTWAIT);
597 if (sret != (ssize_t)sizeof (data)) {
598 warnx("TEST FAILED: %s: failed to receive data: %zx",
599 test->tt_desc, sret);
600 return (false);
601 }
602
603 if (test->tt_pass != TTL_SENDRECV) {
604 warnx("TEST FAILED: %s: found data, despite expecting not to",
605 test->tt_desc);
606 return (false);
607 }
608
609 /*
610 * We skip testing the data on raw sockets so we can ignore having to
611 * parse the IPv4 or IPv6 headers.
612 */
613 if (data != tt_msg && test->tt_type != SOCK_RAW) {
614 warnx("TEST FAILED: %s: data mismatch: expected 0x%x, found "
615 "0x%x", test->tt_desc, tt_msg, data);
616 return (false);
617 }
618
619 if (test->tt_type == SOCK_DGRAM && !ttl_check_ancil(test, &msg)) {
620 return (false);
621 }
622
623 (void) printf("TEST PASSED: %s: Successfully received data\n",
624 test->tt_desc);
625 return (true);
626 }
627
628 static bool
ttl_test_one(const ttl_test_t * test)629 ttl_test_one(const ttl_test_t *test)
630 {
631 int src = -1, dst = -1, cfd = -1, port = -1, tdst;
632 int level, ttlopt, minttlopt, recvopt, en = 1;
633 bool ret = true;
634 struct sockaddr_storage dst_addr;
635
636 if ((port = port_create()) < 0) {
637 err(EXIT_FAILURE, "TEST FAILED: failed to create event port");
638 }
639
640 src = socket(test->tt_domain, test->tt_type | SOCK_NONBLOCK,
641 test->tt_proto);
642 if (src < 0) {
643 warn("TEST FAILED: %s: failed to create source socket",
644 test->tt_desc);
645 ret = false;
646 goto cleanup;
647 }
648
649 dst = socket(test->tt_domain, test->tt_type | SOCK_NONBLOCK,
650 test->tt_proto);
651 if (dst < 0) {
652 warn("TEST FAILED: %s: failed to create destination socket",
653 test->tt_desc);
654 ret = false;
655 goto cleanup;
656 }
657
658 if (!ttl_bind_dest(test, dst, &dst_addr)) {
659 ret = false;
660 goto cleanup;
661 }
662
663 if (test->tt_domain == PF_INET) {
664 level = IPPROTO_IP;
665 ttlopt = IP_TTL;
666 minttlopt = IP_MINTTL;
667 recvopt = IP_RECVTTL;
668 } else {
669 level = IPPROTO_IPV6;
670 ttlopt = IPV6_UNICAST_HOPS;
671 minttlopt = IPV6_MINHOPCOUNT;
672 recvopt = IPV6_RECVHOPLIMIT;
673 }
674
675 if (test->tt_ttl > 0 && setsockopt(src, level, ttlopt, &test->tt_ttl,
676 sizeof (int)) != 0) {
677 warn("TEST FAILED: %s: failed to set TTL/HLIM to %d",
678 test->tt_desc, test->tt_ttl);
679 ret = false;
680 goto cleanup;
681 }
682
683 if (setsockopt(dst, level, minttlopt, &test->tt_minttl,
684 sizeof (int)) != 0) {
685 warn("TEST FAILED: %s: failed to set min TTL/HLIM to %d",
686 test->tt_desc, test->tt_minttl);
687 ret = false;
688 goto cleanup;
689 }
690
691 if (test->tt_type == SOCK_DGRAM && setsockopt(dst, level, recvopt, &en,
692 sizeof (int)) != 0) {
693 warn("TEST FAILED: %s failed to enable receiving the TTL",
694 test->tt_desc);
695 ret = false;
696 goto cleanup;
697 }
698
699 if (test->tt_type != SOCK_DGRAM && test->tt_type != SOCK_RAW) {
700 if (!ttl_connect(test, port, src, dst, &cfd,
701 (struct sockaddr *)&dst_addr)) {
702 ret = false;
703 goto cleanup;
704 }
705 if (test->tt_pass != TTL_SENDRECV) {
706 goto cleanup;
707 }
708 tdst = cfd;
709 } else {
710 tdst = dst;
711 }
712
713 if (!ttl_sendrecv(test, port, src, tdst,
714 (struct sockaddr *)&dst_addr)) {
715 ret = false;
716 goto cleanup;
717 }
718
719 cleanup:
720 if (port > -1)
721 (void) close(port);
722 if (src > -1)
723 (void) close(src);
724 if (dst > -1)
725 (void) close(dst);
726 if (cfd > -1)
727 (void) close(cfd);
728 return (ret);
729 }
730
731 int
main(void)732 main(void)
733 {
734 int ret = EXIT_SUCCESS;
735
736 for (size_t i = 0; i < ARRAY_SIZE(ttl_tests); i++) {
737 if (!ttl_test_one(&ttl_tests[i])) {
738 ret = EXIT_FAILURE;
739 }
740 }
741
742 if (ret == EXIT_SUCCESS) {
743 (void) printf("All tests passed successfully\n");
744 }
745
746 return (ret);
747 }
748