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