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