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