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	DEBUG(x) if (debug) printf x
50 
51 #define	TESTPORT	32123
52 
53 #define	RT_RECVTOS	0x1
54 #define	RT_RECVTTL	0x2
55 #define	RT_RECVPKTINFO	0x4
56 #define	RT_RECVMASK	0x7
57 
58 #define	RT_SETTOS	0x10
59 #define	RT_SETTTL	0x20
60 #define	RT_STREAM	0x40
61 #define	RT_SKIP		0x80
62 
63 typedef struct recvmsg_test {
64 	char *name;		/* Name of the test */
65 	uint8_t tos;		/* TOS to set */
66 	uint8_t ttl;		/* TTL to set */
67 	uint8_t flags;		/* Test flags, RT_ */
68 } recvmsg_test_t;
69 
70 static recvmsg_test_t tests[] = {
71 	{
72 		.name = "baseline",
73 		.flags = 0,
74 	},
75 
76 	/* Combinations of receive flags */
77 	{
78 		.name = "recv TOS",
79 		.flags = RT_RECVTOS,
80 	},
81 
82 	{
83 		.name = "recv TTL",
84 		.flags = RT_RECVTTL,
85 	},
86 
87 	{
88 		.name = "recv PKTINFO",
89 		.flags = RT_RECVPKTINFO,
90 	},
91 
92 	{
93 		.name = "recv TOS,TTL",
94 		.flags = RT_RECVTOS | RT_RECVTTL,
95 	},
96 
97 	{
98 		.name = "recv TTL,PKTINFO",
99 		.flags = RT_RECVTTL | RT_RECVPKTINFO,
100 	},
101 
102 	{
103 		.name = "recv TOS,PKTINFO",
104 		.flags = RT_RECVTOS | RT_RECVPKTINFO,
105 	},
106 
107 	{
108 		.name = "recv TOS,TTL,PKTINFO",
109 		.flags = RT_RECVTOS | RT_RECVTTL | RT_RECVPKTINFO,
110 	},
111 
112 	/* Manually set TTL and TOS */
113 
114 	{
115 		.name = "set TOS,TTL",
116 		.flags = RT_SETTOS | RT_SETTTL,
117 		.ttl = 11,
118 		.tos = 0xe0
119 	},
120 
121 	{
122 		.name = "set/recv TOS,TTL",
123 		.flags = RT_SETTOS | RT_SETTTL | RT_RECVTOS | RT_RECVTTL,
124 		.ttl = 32,
125 		.tos = 0x48
126 	},
127 
128 	{
129 		.name = "set TOS,TTL, recv PKTINFO",
130 		.flags = RT_SETTOS | RT_SETTTL | RT_RECVPKTINFO,
131 		.ttl = 173,
132 		.tos = 0x78
133 	},
134 
135 	{
136 		.name = "set TOS,TTL, recv TOS,TTL,PKTINFO",
137 		.flags = RT_SETTOS | RT_SETTTL | RT_RECVTOS | RT_RECVTTL |
138 		    RT_RECVPKTINFO,
139 		.ttl = 54,
140 		.tos = 0x90
141 	},
142 
143 	/* STREAM socket */
144 
145 	{
146 		.name = "STREAM set TOS",
147 		.flags = RT_STREAM | RT_SETTOS,
148 		.tos = 0xe0
149 	},
150 
151 	/*
152 	 * The ancillary data are not returned for the loopback TCP path,
153 	 * so these tests are skipped by default.
154 	 * To run them, use two different zones (or machines) and run:
155 	 *	recvmsg.64 -s 'test name'
156 	 * on the first, and:
157 	 *	recvmsg.64 -c <first machine IP> 'test name'
158 	 * on the second.
159 	 */
160 	{
161 		.name = "STREAM recv TOS",
162 		.flags = RT_STREAM | RT_RECVTOS | RT_SKIP,
163 	},
164 
165 	{
166 		.name = "STREAM set/recv TOS",
167 		.flags = RT_STREAM | RT_SETTOS | RT_RECVTOS | RT_SKIP,
168 		.tos = 0x48
169 	},
170 
171 	/* End of tests */
172 
173 	{
174 		.name = NULL
175 	}
176 };
177 
178 static boolean_t
servertest(recvmsg_test_t * t)179 servertest(recvmsg_test_t *t)
180 {
181 	struct sockaddr_in addr;
182 	boolean_t pass = _B_TRUE;
183 	int sockfd, readfd, acceptfd = -1, c = 1;
184 
185 	DEBUG(("\nserver %s: starting\n", t->name));
186 
187 	sockfd = socket(AF_INET,
188 	    t->flags & RT_STREAM ? SOCK_STREAM : SOCK_DGRAM, 0);
189 	if (sockfd == -1)
190 		err(EXIT_FAILURE, "failed to create server socket");
191 
192 	addr.sin_family = AF_INET;
193 	addr.sin_addr.s_addr = INADDR_ANY;
194 	addr.sin_port = htons(TESTPORT);
195 
196 	if (bind(sockfd, (struct sockaddr *)&addr, sizeof (addr)) == -1)
197 		err(EXIT_FAILURE, "server socket bind failed");
198 
199 	if (t->flags & RT_RECVTOS) {
200 		DEBUG((" : setting RECVTOS\n"));
201 		if (setsockopt(sockfd, IPPROTO_IP, IP_RECVTOS, &c,
202 		    sizeof (c)) == -1) {
203 			printf("[FAIL] %s - "
204 			    "couldn't set TOS on server socket: %s\n",
205 			    t->name, strerror(errno));
206 			pass = _B_FALSE;
207 		}
208 	}
209 
210 	if (t->flags & RT_RECVTTL) {
211 		DEBUG((" : setting RECVTTL\n"));
212 		if (setsockopt(sockfd, 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 		DEBUG((" : setting RECVPKTINFO\n"));
223 		if (setsockopt(sockfd, IPPROTO_IP, IP_PKTINFO, &c,
224 		    sizeof (c)) == -1) {
225 			printf("[FAIL] %s - "
226 			    "couldn't set PKTINFO on server socket: %s\n",
227 			    t->name, strerror(errno));
228 			pass = _B_FALSE;
229 		}
230 	}
231 
232 	if (t->flags & RT_STREAM) {
233 		if (listen(sockfd, 1) == -1)
234 			err(EXIT_FAILURE, "Could not listen on sever socket");
235 	}
236 
237 	/* Signal the client that the server is ready for the next test */
238 	if (debug)
239 		printf(" : signalling client\n");
240 	(void) pthread_mutex_lock(&mutex);
241 	server_ready = _B_TRUE;
242 	(void) pthread_cond_signal(&cv);
243 	(void) pthread_mutex_unlock(&mutex);
244 
245 	if (t->flags & RT_STREAM) {
246 		struct sockaddr_in caddr;
247 		socklen_t sl = sizeof (caddr);
248 
249 		if ((acceptfd = accept(sockfd, (struct sockaddr *)&caddr,
250 		    &sl)) == -1) {
251 			err(EXIT_FAILURE, "socket accept failed");
252 		}
253 		readfd = acceptfd;
254 	} else {
255 		readfd = sockfd;
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 	DEBUG((" : waiting for message\n"));
276 
277 	r = recvmsg(readfd, &msg, 0);
278 	if (r <= 0) {
279 		printf("[FAIL] %s - recvmsg returned %d (%s)\n",
280 		    t->name, r, strerror(errno));
281 		pass = _B_FALSE;
282 		goto out;
283 	}
284 
285 	DEBUG((" : recvmsg returned %d (flags=0x%x, controllen=%d)\n",
286 	    r, msg.msg_flags, msg.msg_controllen));
287 
288 	if (r != strlen(t->name)) {
289 		printf("[FAIL] %s - got '%.*s' (%d bytes), expected '%s'\n",
290 		    t->name, r, buf, r, t->name);
291 		pass = _B_FALSE;
292 	}
293 
294 	DEBUG((" : Received '%.*s'\n", r, buf));
295 
296 	if (msg.msg_flags != 0) {
297 		printf("[FAIL] %s - received flags 0x%x\n",
298 		    t->name, msg.msg_flags);
299 		pass = _B_FALSE;
300 	}
301 
302 	uint8_t flags = 0;
303 
304 	for (struct cmsghdr *cm = CMSG_FIRSTHDR(&msg); cm != NULL;
305 	    cm = CMSG_NXTHDR(&msg, cm)) {
306 		uint8_t d;
307 
308 		DEBUG((" : >> Got cmsg %x/%x - length %u\n",
309 		    cm->cmsg_level, cm->cmsg_type, cm->cmsg_len));
310 
311 		if (cm->cmsg_level != IPPROTO_IP)
312 			continue;
313 
314 		switch (cm->cmsg_type) {
315 		case IP_PKTINFO:
316 			flags |= RT_RECVPKTINFO;
317 			if (debug) {
318 				struct in_pktinfo *pi =
319 				    (struct in_pktinfo *)CMSG_DATA(cm);
320 				printf(" : ifIndex: %u\n", pi->ipi_ifindex);
321 			}
322 			break;
323 		case IP_RECVTTL:
324 			if (cm->cmsg_len != CMSG_LEN(sizeof (uint8_t))) {
325 				printf(
326 				    "[FAIL] %s - cmsg_len was %u expected %u\n",
327 				    t->name, cm->cmsg_len,
328 				    CMSG_LEN(sizeof (uint8_t)));
329 				pass = _B_FALSE;
330 				break;
331 			}
332 			flags |= RT_RECVTTL;
333 			memcpy(&d, CMSG_DATA(cm), sizeof (d));
334 			DEBUG((" : RECVTTL = %u\n", d));
335 			if (t->flags & RT_SETTTL && d != t->ttl) {
336 				printf("[FAIL] %s - TTL was %u, expected %u\n",
337 				    t->name, d, t->ttl);
338 				pass = _B_FALSE;
339 			}
340 			break;
341 		case IP_RECVTOS:
342 			if (cm->cmsg_len != CMSG_LEN(sizeof (uint8_t))) {
343 				printf(
344 				    "[FAIL] %s - cmsg_len was %u expected %u\n",
345 				    t->name, cm->cmsg_len,
346 				    CMSG_LEN(sizeof (uint8_t)));
347 				pass = _B_FALSE;
348 				break;
349 			}
350 			flags |= RT_RECVTOS;
351 			memcpy(&d, CMSG_DATA(cm), sizeof (d));
352 			DEBUG((" : RECVTOS = %u\n", d));
353 			if (t->flags & RT_SETTOS && d != t->tos) {
354 				printf("[FAIL] %s - TOS was %u, expected %u\n",
355 				    t->name, d, t->tos);
356 				pass = _B_FALSE;
357 			}
358 			break;
359 		}
360 	}
361 
362 	if ((t->flags & RT_RECVMASK) != flags) {
363 		printf("[FAIL] %s - Did not receive everything expected, "
364 		    "flags %#x vs. %#x\n", t->name,
365 		    flags, t->flags & RT_RECVMASK);
366 		pass = _B_FALSE;
367 	}
368 
369 	/* Wait for the client to finish */
370 	(void) pthread_mutex_lock(&cmutex);
371 	while (!client_done)
372 		(void) pthread_cond_wait(&ccv, &cmutex);
373 	client_done = _B_FALSE;
374 	(void) pthread_mutex_unlock(&cmutex);
375 
376 out:
377 	if (acceptfd != -1)
378 		(void) close(acceptfd);
379 	(void) close(sockfd);
380 
381 	if (pass)
382 		printf("[PASS] %s\n", t->name);
383 
384 	return (pass);
385 }
386 
387 static int
server(const char * test)388 server(const char *test)
389 {
390 	int ret = EXIT_SUCCESS;
391 	recvmsg_test_t *t;
392 
393 	for (t = tests; t->name != NULL; t++) {
394 		if (test != NULL) {
395 			if (strcmp(test, t->name) != 0)
396 				continue;
397 			client_done = _B_TRUE;
398 			return (servertest(t));
399 		}
400 		if (t->flags & RT_SKIP) {
401 			printf("[SKIP] %s - (requires two separate zones)\n",
402 			    t->name);
403 			continue;
404 		}
405 		if (!servertest(t))
406 			ret = EXIT_FAILURE;
407 	}
408 
409 	return (ret);
410 }
411 
412 static void
clienttest(recvmsg_test_t * t)413 clienttest(recvmsg_test_t *t)
414 {
415 	struct sockaddr_in addr;
416 	int s, ret;
417 
418 	DEBUG(("client %s: starting\n", t->name));
419 
420 	s = socket(AF_INET, t->flags & RT_STREAM ? SOCK_STREAM : SOCK_DGRAM, 0);
421 	if (s == -1)
422 		err(EXIT_FAILURE, "failed to create client socket");
423 
424 	addr.sin_family = AF_INET;
425 	addr.sin_addr.s_addr = testip;
426 	addr.sin_port = htons(TESTPORT);
427 
428 	if (t->flags & RT_STREAM) {
429 		if (connect(s, (struct sockaddr *)&addr, sizeof (addr)) == -1)
430 			err(EXIT_FAILURE, "failed to connect to server");
431 	}
432 
433 	if (t->flags & RT_SETTOS) {
434 		int c = t->tos;
435 
436 		DEBUG(("client %s: setting TOS = 0x%x\n", t->name, c));
437 		if (setsockopt(s, IPPROTO_IP, IP_TOS, &c, sizeof (c)) == -1)
438 			err(EXIT_FAILURE, "could not set TOS on client socket");
439 	}
440 
441 	if (t->flags & RT_SETTTL) {
442 		int c = t->ttl;
443 
444 		DEBUG(("client %s: setting TTL = 0x%x\n", t->name, c));
445 		if (setsockopt(s, IPPROTO_IP, IP_TTL, &c, sizeof (c)) == -1)
446 			err(EXIT_FAILURE, "could not set TTL on client socket");
447 	}
448 
449 	DEBUG(("client %s: sending\n", t->name));
450 
451 	if (t->flags & RT_STREAM) {
452 		ret = send(s, t->name, strlen(t->name), 0);
453 		shutdown(s, SHUT_RDWR);
454 	} else {
455 		ret = sendto(s, t->name, strlen(t->name), 0,
456 		    (struct sockaddr *)&addr, sizeof (addr));
457 	}
458 
459 	if (ret == -1)
460 		err(EXIT_FAILURE, "sendto failed to send data to server");
461 
462 	DEBUG(("client %s: done\n", t->name));
463 
464 	close(s);
465 }
466 
467 static void *
client(void * arg)468 client(void *arg)
469 {
470 	char *test = (char *)arg;
471 	recvmsg_test_t *t;
472 
473 	for (t = tests; t->name != NULL; t++) {
474 		if (test != NULL) {
475 			if (strcmp(test, t->name) != 0)
476 				continue;
477 			clienttest(t);
478 			return (NULL);
479 		}
480 		if (t->flags & RT_SKIP)
481 			continue;
482 		/* Wait for the server to be ready to receive */
483 		(void) pthread_mutex_lock(&mutex);
484 		while (!server_ready)
485 			(void) pthread_cond_wait(&cv, &mutex);
486 		server_ready = _B_FALSE;
487 		(void) pthread_mutex_unlock(&mutex);
488 		clienttest(t);
489 		/* Tell the server we are done */
490 		(void) pthread_mutex_lock(&cmutex);
491 		client_done = _B_TRUE;
492 		(void) pthread_cond_signal(&ccv);
493 		(void) pthread_mutex_unlock(&cmutex);
494 	}
495 
496 	return (NULL);
497 }
498 
499 int
main(int argc,const char ** argv)500 main(int argc, const char **argv)
501 {
502 	int ret = EXIT_SUCCESS;
503 	pthread_t cthread;
504 
505 	if (argc > 1 && strcmp(argv[1], "-d") == 0) {
506 		debug = _B_TRUE;
507 		argc--, argv++;
508 	}
509 
510 	/* -c <server IP> <test name> */
511 	if (argc == 4 && strcmp(argv[1], "-c") == 0) {
512 		testip = inet_addr(argv[2]);
513 		printf("TEST IP: %s\n", argv[2]);
514 		if (testip == INADDR_NONE) {
515 			err(EXIT_FAILURE,
516 			    "Could not parse destination IP address");
517 		}
518 		client((void *)argv[3]);
519 		return (ret);
520 	}
521 
522 	/* -s <test name> */
523 	if (argc == 3 && strcmp(argv[1], "-s") == 0)
524 		return (server(argv[2]));
525 
526 	testip = inet_addr("127.0.0.1");
527 	if (testip == INADDR_NONE)
528 		err(EXIT_FAILURE, "Could not parse destination IP address");
529 
530 	if (pthread_create(&cthread, NULL, client, NULL) == -1)
531 		err(EXIT_FAILURE, "Could not create client thread");
532 
533 	ret = server(NULL);
534 
535 	if (pthread_join(cthread, NULL) != 0)
536 		err(EXIT_FAILURE, "join client thread failed");
537 
538 	return (ret);
539 }
540