1// SPDX-License-Identifier: GPL-2.0
2
3#define _GNU_SOURCE
4
5#include <assert.h>
6#include <errno.h>
7#include <fcntl.h>
8#include <limits.h>
9#include <string.h>
10#include <stdarg.h>
11#include <stdbool.h>
12#include <stdint.h>
13#include <inttypes.h>
14#include <stdio.h>
15#include <stdlib.h>
16#include <strings.h>
17#include <time.h>
18#include <unistd.h>
19
20#include <sys/socket.h>
21#include <sys/types.h>
22#include <sys/wait.h>
23
24#include <netdb.h>
25#include <netinet/in.h>
26
27#include <linux/tcp.h>
28
29static int pf = AF_INET;
30
31#ifndef IPPROTO_MPTCP
32#define IPPROTO_MPTCP 262
33#endif
34#ifndef SOL_MPTCP
35#define SOL_MPTCP 284
36#endif
37
38#ifndef MPTCP_INFO
39struct mptcp_info {
40 __u8 mptcpi_subflows;
41 __u8 mptcpi_add_addr_signal;
42 __u8 mptcpi_add_addr_accepted;
43 __u8 mptcpi_subflows_max;
44 __u8 mptcpi_add_addr_signal_max;
45 __u8 mptcpi_add_addr_accepted_max;
46 __u32 mptcpi_flags;
47 __u32 mptcpi_token;
48 __u64 mptcpi_write_seq;
49 __u64 mptcpi_snd_una;
50 __u64 mptcpi_rcv_nxt;
51 __u8 mptcpi_local_addr_used;
52 __u8 mptcpi_local_addr_max;
53 __u8 mptcpi_csum_enabled;
54 __u32 mptcpi_retransmits;
55 __u64 mptcpi_bytes_retrans;
56 __u64 mptcpi_bytes_sent;
57 __u64 mptcpi_bytes_received;
58 __u64 mptcpi_bytes_acked;
59};
60
61struct mptcp_subflow_data {
62 __u32 size_subflow_data; /* size of this structure in userspace */
63 __u32 num_subflows; /* must be 0, set by kernel */
64 __u32 size_kernel; /* must be 0, set by kernel */
65 __u32 size_user; /* size of one element in data[] */
66} __attribute__((aligned(8)));
67
68struct mptcp_subflow_addrs {
69 union {
70 __kernel_sa_family_t sa_family;
71 struct sockaddr sa_local;
72 struct sockaddr_in sin_local;
73 struct sockaddr_in6 sin6_local;
74 struct __kernel_sockaddr_storage ss_local;
75 };
76 union {
77 struct sockaddr sa_remote;
78 struct sockaddr_in sin_remote;
79 struct sockaddr_in6 sin6_remote;
80 struct __kernel_sockaddr_storage ss_remote;
81 };
82};
83
84#define MPTCP_INFO 1
85#define MPTCP_TCPINFO 2
86#define MPTCP_SUBFLOW_ADDRS 3
87#endif
88
89#ifndef MPTCP_FULL_INFO
90struct mptcp_subflow_info {
91 __u32 id;
92 struct mptcp_subflow_addrs addrs;
93};
94
95struct mptcp_full_info {
96 __u32 size_tcpinfo_kernel; /* must be 0, set by kernel */
97 __u32 size_tcpinfo_user;
98 __u32 size_sfinfo_kernel; /* must be 0, set by kernel */
99 __u32 size_sfinfo_user;
100 __u32 num_subflows; /* must be 0, set by kernel (real subflow count) */
101 __u32 size_arrays_user; /* max subflows that userspace is interested in;
102 * the buffers at subflow_info/tcp_info
103 * are respectively at least:
104 * size_arrays * size_sfinfo_user
105 * size_arrays * size_tcpinfo_user
106 * bytes wide
107 */
108 __aligned_u64 subflow_info;
109 __aligned_u64 tcp_info;
110 struct mptcp_info mptcp_info;
111};
112
113#define MPTCP_FULL_INFO 4
114#endif
115
116struct so_state {
117 struct mptcp_info mi;
118 struct mptcp_info last_sample;
119 struct tcp_info tcp_info;
120 struct mptcp_subflow_addrs addrs;
121 uint64_t mptcpi_rcv_delta;
122 uint64_t tcpi_rcv_delta;
123 bool pkt_stats_avail;
124};
125
126#ifndef MIN
127#define MIN(a, b) ((a) < (b) ? (a) : (b))
128#endif
129
130static void die_perror(const char *msg)
131{
132 perror(msg);
133 exit(1);
134}
135
136static void die_usage(int r)
137{
138 fprintf(stderr, "Usage: mptcp_sockopt [-6]\n");
139 exit(r);
140}
141
142static void xerror(const char *fmt, ...)
143{
144 va_list ap;
145
146 va_start(ap, fmt);
147 vfprintf(stderr, fmt, ap);
148 va_end(ap);
149 fputc('\n', stderr);
150 exit(1);
151}
152
153static const char *getxinfo_strerr(int err)
154{
155 if (err == EAI_SYSTEM)
156 return strerror(errno);
157
158 return gai_strerror(err);
159}
160
161static void xgetaddrinfo(const char *node, const char *service,
162 const struct addrinfo *hints,
163 struct addrinfo **res)
164{
165 int err = getaddrinfo(node, service, hints, res);
166
167 if (err) {
168 const char *errstr = getxinfo_strerr(err);
169
170 fprintf(stderr, "Fatal: getaddrinfo(%s:%s): %s\n",
171 node ? node : "", service ? service : "", errstr);
172 exit(1);
173 }
174}
175
176static int sock_listen_mptcp(const char * const listenaddr,
177 const char * const port)
178{
179 int sock = -1;
180 struct addrinfo hints = {
181 .ai_protocol = IPPROTO_TCP,
182 .ai_socktype = SOCK_STREAM,
183 .ai_flags = AI_PASSIVE | AI_NUMERICHOST
184 };
185
186 hints.ai_family = pf;
187
188 struct addrinfo *a, *addr;
189 int one = 1;
190
191 xgetaddrinfo(node: listenaddr, service: port, hints: &hints, res: &addr);
192 hints.ai_family = pf;
193
194 for (a = addr; a; a = a->ai_next) {
195 sock = socket(a->ai_family, a->ai_socktype, IPPROTO_MPTCP);
196 if (sock < 0)
197 continue;
198
199 if (-1 == setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one,
200 sizeof(one)))
201 perror("setsockopt");
202
203 if (bind(sock, a->ai_addr, a->ai_addrlen) == 0)
204 break; /* success */
205
206 perror("bind");
207 close(sock);
208 sock = -1;
209 }
210
211 freeaddrinfo(addr);
212
213 if (sock < 0)
214 xerror(fmt: "could not create listen socket");
215
216 if (listen(sock, 20))
217 die_perror(msg: "listen");
218
219 return sock;
220}
221
222static int sock_connect_mptcp(const char * const remoteaddr,
223 const char * const port, int proto)
224{
225 struct addrinfo hints = {
226 .ai_protocol = IPPROTO_TCP,
227 .ai_socktype = SOCK_STREAM,
228 };
229 struct addrinfo *a, *addr;
230 int sock = -1;
231
232 hints.ai_family = pf;
233
234 xgetaddrinfo(node: remoteaddr, service: port, hints: &hints, res: &addr);
235 for (a = addr; a; a = a->ai_next) {
236 sock = socket(a->ai_family, a->ai_socktype, proto);
237 if (sock < 0)
238 continue;
239
240 if (connect(sock, a->ai_addr, a->ai_addrlen) == 0)
241 break; /* success */
242
243 die_perror(msg: "connect");
244 }
245
246 if (sock < 0)
247 xerror(fmt: "could not create connect socket");
248
249 freeaddrinfo(addr);
250 return sock;
251}
252
253static void parse_opts(int argc, char **argv)
254{
255 int c;
256
257 while ((c = getopt(argc, argv, "h6")) != -1) {
258 switch (c) {
259 case 'h':
260 die_usage(r: 0);
261 break;
262 case '6':
263 pf = AF_INET6;
264 break;
265 default:
266 die_usage(r: 1);
267 break;
268 }
269 }
270}
271
272static void do_getsockopt_bogus_sf_data(int fd, int optname)
273{
274 struct mptcp_subflow_data good_data;
275 struct bogus_data {
276 struct mptcp_subflow_data d;
277 char buf[2];
278 } bd;
279 socklen_t olen, _olen;
280 int ret;
281
282 memset(&bd, 0, sizeof(bd));
283 memset(&good_data, 0, sizeof(good_data));
284
285 olen = sizeof(good_data);
286 good_data.size_subflow_data = olen;
287
288 ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen);
289 assert(ret < 0); /* 0 size_subflow_data */
290 assert(olen == sizeof(good_data));
291
292 bd.d = good_data;
293
294 ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen);
295 assert(ret == 0);
296 assert(olen == sizeof(good_data));
297 assert(bd.d.num_subflows == 1);
298 assert(bd.d.size_kernel > 0);
299 assert(bd.d.size_user == 0);
300
301 bd.d = good_data;
302 _olen = rand() % olen;
303 olen = _olen;
304 ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen);
305 assert(ret < 0); /* bogus olen */
306 assert(olen == _olen); /* must be unchanged */
307
308 bd.d = good_data;
309 olen = sizeof(good_data);
310 bd.d.size_kernel = 1;
311 ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen);
312 assert(ret < 0); /* size_kernel not 0 */
313
314 bd.d = good_data;
315 olen = sizeof(good_data);
316 bd.d.num_subflows = 1;
317 ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen);
318 assert(ret < 0); /* num_subflows not 0 */
319
320 /* forward compat check: larger struct mptcp_subflow_data on 'old' kernel */
321 bd.d = good_data;
322 olen = sizeof(bd);
323 bd.d.size_subflow_data = sizeof(bd);
324
325 ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &olen);
326 assert(ret == 0);
327
328 /* olen must be truncated to real data size filled by kernel: */
329 assert(olen == sizeof(good_data));
330
331 assert(bd.d.size_subflow_data == sizeof(bd));
332
333 bd.d = good_data;
334 bd.d.size_subflow_data += 1;
335 bd.d.size_user = 1;
336 olen = bd.d.size_subflow_data + 1;
337 _olen = olen;
338
339 ret = getsockopt(fd, SOL_MPTCP, optname, &bd, &_olen);
340 assert(ret == 0);
341
342 /* no truncation, kernel should have filled 1 byte of optname payload in buf[1]: */
343 assert(olen == _olen);
344
345 assert(bd.d.size_subflow_data == sizeof(good_data) + 1);
346 assert(bd.buf[0] == 0);
347}
348
349static void do_getsockopt_mptcp_info(struct so_state *s, int fd, size_t w)
350{
351 struct mptcp_info i;
352 socklen_t olen;
353 int ret;
354
355 olen = sizeof(i);
356 ret = getsockopt(fd, SOL_MPTCP, MPTCP_INFO, &i, &olen);
357
358 if (ret < 0)
359 die_perror(msg: "getsockopt MPTCP_INFO");
360
361 s->pkt_stats_avail = olen >= sizeof(i);
362
363 s->last_sample = i;
364 if (s->mi.mptcpi_write_seq == 0)
365 s->mi = i;
366
367 assert(s->mi.mptcpi_write_seq + w == i.mptcpi_write_seq);
368
369 s->mptcpi_rcv_delta = i.mptcpi_rcv_nxt - s->mi.mptcpi_rcv_nxt;
370}
371
372static void do_getsockopt_tcp_info(struct so_state *s, int fd, size_t r, size_t w)
373{
374 struct my_tcp_info {
375 struct mptcp_subflow_data d;
376 struct tcp_info ti[2];
377 } ti;
378 int ret, tries = 5;
379 socklen_t olen;
380
381 do {
382 memset(&ti, 0, sizeof(ti));
383
384 ti.d.size_subflow_data = sizeof(struct mptcp_subflow_data);
385 ti.d.size_user = sizeof(struct tcp_info);
386 olen = sizeof(ti);
387
388 ret = getsockopt(fd, SOL_MPTCP, MPTCP_TCPINFO, &ti, &olen);
389 if (ret < 0)
390 xerror(fmt: "getsockopt MPTCP_TCPINFO (tries %d, %m)");
391
392 assert(olen <= sizeof(ti));
393 assert(ti.d.size_kernel > 0);
394 assert(ti.d.size_user ==
395 MIN(ti.d.size_kernel, sizeof(struct tcp_info)));
396 assert(ti.d.num_subflows == 1);
397
398 assert(olen > (socklen_t)sizeof(struct mptcp_subflow_data));
399 olen -= sizeof(struct mptcp_subflow_data);
400 assert(olen == ti.d.size_user);
401
402 s->tcp_info = ti.ti[0];
403
404 if (ti.ti[0].tcpi_bytes_sent == w &&
405 ti.ti[0].tcpi_bytes_received == r)
406 goto done;
407
408 if (r == 0 && ti.ti[0].tcpi_bytes_sent == w &&
409 ti.ti[0].tcpi_bytes_received) {
410 s->tcpi_rcv_delta = ti.ti[0].tcpi_bytes_received;
411 goto done;
412 }
413
414 /* wait and repeat, might be that tx is still ongoing */
415 sleep(1);
416 } while (tries-- > 0);
417
418 xerror("tcpi_bytes_sent %" PRIu64 ", want %zu. tcpi_bytes_received %" PRIu64 ", want %zu",
419 ti.ti[0].tcpi_bytes_sent, w, ti.ti[0].tcpi_bytes_received, r);
420
421done:
422 do_getsockopt_bogus_sf_data(fd, MPTCP_TCPINFO);
423}
424
425static void do_getsockopt_subflow_addrs(struct so_state *s, int fd)
426{
427 struct sockaddr_storage remote, local;
428 socklen_t olen, rlen, llen;
429 int ret;
430 struct my_addrs {
431 struct mptcp_subflow_data d;
432 struct mptcp_subflow_addrs addr[2];
433 } addrs;
434
435 memset(&addrs, 0, sizeof(addrs));
436 memset(&local, 0, sizeof(local));
437 memset(&remote, 0, sizeof(remote));
438
439 addrs.d.size_subflow_data = sizeof(struct mptcp_subflow_data);
440 addrs.d.size_user = sizeof(struct mptcp_subflow_addrs);
441 olen = sizeof(addrs);
442
443 ret = getsockopt(fd, SOL_MPTCP, MPTCP_SUBFLOW_ADDRS, &addrs, &olen);
444 if (ret < 0)
445 die_perror(msg: "getsockopt MPTCP_SUBFLOW_ADDRS");
446
447 assert(olen <= sizeof(addrs));
448 assert(addrs.d.size_kernel > 0);
449 assert(addrs.d.size_user ==
450 MIN(addrs.d.size_kernel, sizeof(struct mptcp_subflow_addrs)));
451 assert(addrs.d.num_subflows == 1);
452
453 assert(olen > (socklen_t)sizeof(struct mptcp_subflow_data));
454 olen -= sizeof(struct mptcp_subflow_data);
455 assert(olen == addrs.d.size_user);
456
457 llen = sizeof(local);
458 ret = getsockname(fd, (struct sockaddr *)&local, &llen);
459 if (ret < 0)
460 die_perror(msg: "getsockname");
461 rlen = sizeof(remote);
462 ret = getpeername(fd, (struct sockaddr *)&remote, &rlen);
463 if (ret < 0)
464 die_perror(msg: "getpeername");
465
466 assert(rlen > 0);
467 assert(rlen == llen);
468
469 assert(remote.ss_family == local.ss_family);
470
471 assert(memcmp(p: &local, q: &addrs.addr[0].ss_local, size: sizeof(local)) == 0);
472 assert(memcmp(p: &remote, q: &addrs.addr[0].ss_remote, size: sizeof(remote)) == 0);
473 s->addrs = addrs.addr[0];
474
475 memset(&addrs, 0, sizeof(addrs));
476
477 addrs.d.size_subflow_data = sizeof(struct mptcp_subflow_data);
478 addrs.d.size_user = sizeof(sa_family_t);
479 olen = sizeof(addrs.d) + sizeof(sa_family_t);
480
481 ret = getsockopt(fd, SOL_MPTCP, MPTCP_SUBFLOW_ADDRS, &addrs, &olen);
482 assert(ret == 0);
483 assert(olen == sizeof(addrs.d) + sizeof(sa_family_t));
484
485 assert(addrs.addr[0].sa_family == pf);
486 assert(addrs.addr[0].sa_family == local.ss_family);
487
488 assert(memcmp(p: &local, q: &addrs.addr[0].ss_local, size: sizeof(local)) != 0);
489 assert(memcmp(p: &remote, q: &addrs.addr[0].ss_remote, size: sizeof(remote)) != 0);
490
491 do_getsockopt_bogus_sf_data(fd, MPTCP_SUBFLOW_ADDRS);
492}
493
494static void do_getsockopt_mptcp_full_info(struct so_state *s, int fd)
495{
496 size_t data_size = sizeof(struct mptcp_full_info);
497 struct mptcp_subflow_info sfinfo[2];
498 struct tcp_info tcp_info[2];
499 struct mptcp_full_info mfi;
500 socklen_t olen;
501 int ret;
502
503 memset(&mfi, 0, data_size);
504 memset(tcp_info, 0, sizeof(tcp_info));
505 memset(sfinfo, 0, sizeof(sfinfo));
506
507 mfi.size_tcpinfo_user = sizeof(struct tcp_info);
508 mfi.size_sfinfo_user = sizeof(struct mptcp_subflow_info);
509 mfi.size_arrays_user = 2;
510 mfi.subflow_info = (unsigned long)&sfinfo[0];
511 mfi.tcp_info = (unsigned long)&tcp_info[0];
512 olen = data_size;
513
514 ret = getsockopt(fd, SOL_MPTCP, MPTCP_FULL_INFO, &mfi, &olen);
515 if (ret < 0) {
516 if (errno == EOPNOTSUPP) {
517 perror("MPTCP_FULL_INFO test skipped");
518 return;
519 }
520 xerror(fmt: "getsockopt MPTCP_FULL_INFO");
521 }
522
523 assert(olen <= data_size);
524 assert(mfi.size_tcpinfo_kernel > 0);
525 assert(mfi.size_tcpinfo_user ==
526 MIN(mfi.size_tcpinfo_kernel, sizeof(struct tcp_info)));
527 assert(mfi.size_sfinfo_kernel > 0);
528 assert(mfi.size_sfinfo_user ==
529 MIN(mfi.size_sfinfo_kernel, sizeof(struct mptcp_subflow_info)));
530 assert(mfi.num_subflows == 1);
531
532 /* Tolerate future extension to mptcp_info struct and running newer
533 * test on top of older kernel.
534 * Anyway any kernel supporting MPTCP_FULL_INFO must at least include
535 * the following in mptcp_info.
536 */
537 assert(olen > (socklen_t)__builtin_offsetof(struct mptcp_full_info, tcp_info));
538 assert(mfi.mptcp_info.mptcpi_subflows == 0);
539 assert(mfi.mptcp_info.mptcpi_bytes_sent == s->last_sample.mptcpi_bytes_sent);
540 assert(mfi.mptcp_info.mptcpi_bytes_received == s->last_sample.mptcpi_bytes_received);
541
542 assert(sfinfo[0].id == 1);
543 assert(tcp_info[0].tcpi_bytes_sent == s->tcp_info.tcpi_bytes_sent);
544 assert(tcp_info[0].tcpi_bytes_received == s->tcp_info.tcpi_bytes_received);
545 assert(!memcmp(p: &sfinfo->addrs, q: &s->addrs, size: sizeof(struct mptcp_subflow_addrs)));
546}
547
548static void do_getsockopts(struct so_state *s, int fd, size_t r, size_t w)
549{
550 do_getsockopt_mptcp_info(s, fd, w);
551
552 do_getsockopt_tcp_info(s, fd, r, w);
553
554 do_getsockopt_subflow_addrs(s, fd);
555
556 if (r)
557 do_getsockopt_mptcp_full_info(s, fd);
558}
559
560static void connect_one_server(int fd, int pipefd)
561{
562 char buf[4096], buf2[4096];
563 size_t len, i, total;
564 struct so_state s;
565 bool eof = false;
566 ssize_t ret;
567
568 memset(&s, 0, sizeof(s));
569
570 len = rand() % (sizeof(buf) - 1);
571
572 if (len < 128)
573 len = 128;
574
575 for (i = 0; i < len ; i++) {
576 buf[i] = rand() % 26;
577 buf[i] += 'A';
578 }
579
580 buf[i] = '\n';
581
582 do_getsockopts(s: &s, fd, r: 0, w: 0);
583
584 /* un-block server */
585 ret = read(pipefd, buf2, 4);
586 assert(ret == 4);
587 close(pipefd);
588
589 assert(strncmp(buf2, "xmit", 4) == 0);
590
591 ret = write(fd, buf, len);
592 if (ret < 0)
593 die_perror(msg: "write");
594
595 if (ret != (ssize_t)len)
596 xerror(fmt: "short write");
597
598 total = 0;
599 do {
600 ret = read(fd, buf2 + total, sizeof(buf2) - total);
601 if (ret < 0)
602 die_perror(msg: "read");
603 if (ret == 0) {
604 eof = true;
605 break;
606 }
607
608 total += ret;
609 } while (total < len);
610
611 if (total != len)
612 xerror(fmt: "total %lu, len %lu eof %d\n", total, len, eof);
613
614 if (memcmp(p: buf, q: buf2, size: len))
615 xerror(fmt: "data corruption");
616
617 if (s.tcpi_rcv_delta)
618 assert(s.tcpi_rcv_delta <= total);
619
620 do_getsockopts(s: &s, fd, r: ret, w: ret);
621
622 if (eof)
623 total += 1; /* sequence advances due to FIN */
624
625 assert(s.mptcpi_rcv_delta == (uint64_t)total);
626 close(fd);
627}
628
629static void process_one_client(int fd, int pipefd)
630{
631 ssize_t ret, ret2, ret3;
632 struct so_state s;
633 char buf[4096];
634
635 memset(&s, 0, sizeof(s));
636 do_getsockopts(s: &s, fd, r: 0, w: 0);
637
638 ret = write(pipefd, "xmit", 4);
639 assert(ret == 4);
640
641 ret = read(fd, buf, sizeof(buf));
642 if (ret < 0)
643 die_perror(msg: "read");
644
645 assert(s.mptcpi_rcv_delta <= (uint64_t)ret);
646
647 if (s.tcpi_rcv_delta)
648 assert(s.tcpi_rcv_delta == (uint64_t)ret);
649
650 ret2 = write(fd, buf, ret);
651 if (ret2 < 0)
652 die_perror(msg: "write");
653
654 /* wait for hangup */
655 ret3 = read(fd, buf, 1);
656 if (ret3 != 0)
657 xerror(fmt: "expected EOF, got %lu", ret3);
658
659 do_getsockopts(s: &s, fd, r: ret, w: ret2);
660 if (s.mptcpi_rcv_delta != (uint64_t)ret + 1)
661 xerror("mptcpi_rcv_delta %" PRIu64 ", expect %" PRIu64, s.mptcpi_rcv_delta, ret + 1, s.mptcpi_rcv_delta - ret);
662
663 /* be nice when running on top of older kernel */
664 if (s.pkt_stats_avail) {
665 if (s.last_sample.mptcpi_bytes_sent != ret2)
666 xerror("mptcpi_bytes_sent %" PRIu64 ", expect %" PRIu64,
667 s.last_sample.mptcpi_bytes_sent, ret2,
668 s.last_sample.mptcpi_bytes_sent - ret2);
669 if (s.last_sample.mptcpi_bytes_received != ret)
670 xerror("mptcpi_bytes_received %" PRIu64 ", expect %" PRIu64,
671 s.last_sample.mptcpi_bytes_received, ret,
672 s.last_sample.mptcpi_bytes_received - ret);
673 if (s.last_sample.mptcpi_bytes_acked != ret)
674 xerror("mptcpi_bytes_acked %" PRIu64 ", expect %" PRIu64,
675 s.last_sample.mptcpi_bytes_acked, ret2,
676 s.last_sample.mptcpi_bytes_acked - ret2);
677 }
678
679 close(fd);
680}
681
682static int xaccept(int s)
683{
684 int fd = accept(s, NULL, 0);
685
686 if (fd < 0)
687 die_perror(msg: "accept");
688
689 return fd;
690}
691
692static int server(int pipefd)
693{
694 int fd = -1, r;
695
696 switch (pf) {
697 case AF_INET:
698 fd = sock_listen_mptcp(listenaddr: "127.0.0.1", port: "15432");
699 break;
700 case AF_INET6:
701 fd = sock_listen_mptcp(listenaddr: "::1", port: "15432");
702 break;
703 default:
704 xerror(fmt: "Unknown pf %d\n", pf);
705 break;
706 }
707
708 r = write(pipefd, "conn", 4);
709 assert(r == 4);
710
711 alarm(15);
712 r = xaccept(s: fd);
713
714 process_one_client(fd: r, pipefd);
715
716 return 0;
717}
718
719static void test_ip_tos_sockopt(int fd)
720{
721 uint8_t tos_in, tos_out;
722 socklen_t s;
723 int r;
724
725 tos_in = rand() & 0xfc;
726 r = setsockopt(fd, SOL_IP, IP_TOS, &tos_in, sizeof(tos_out));
727 if (r != 0)
728 die_perror(msg: "setsockopt IP_TOS");
729
730 tos_out = 0;
731 s = sizeof(tos_out);
732 r = getsockopt(fd, SOL_IP, IP_TOS, &tos_out, &s);
733 if (r != 0)
734 die_perror(msg: "getsockopt IP_TOS");
735
736 if (tos_in != tos_out)
737 xerror("tos %x != %x socklen_t %d\n", tos_in, tos_out, s);
738
739 if (s != 1)
740 xerror(fmt: "tos should be 1 byte");
741
742 s = 0;
743 r = getsockopt(fd, SOL_IP, IP_TOS, &tos_out, &s);
744 if (r != 0)
745 die_perror(msg: "getsockopt IP_TOS 0");
746 if (s != 0)
747 xerror(fmt: "expect socklen_t == 0");
748
749 s = -1;
750 r = getsockopt(fd, SOL_IP, IP_TOS, &tos_out, &s);
751 if (r != -1 && errno != EINVAL)
752 die_perror(msg: "getsockopt IP_TOS did not indicate -EINVAL");
753 if (s != -1)
754 xerror(fmt: "expect socklen_t == -1");
755}
756
757static int client(int pipefd)
758{
759 int fd = -1;
760
761 alarm(15);
762
763 switch (pf) {
764 case AF_INET:
765 fd = sock_connect_mptcp(remoteaddr: "127.0.0.1", port: "15432", IPPROTO_MPTCP);
766 break;
767 case AF_INET6:
768 fd = sock_connect_mptcp(remoteaddr: "::1", port: "15432", IPPROTO_MPTCP);
769 break;
770 default:
771 xerror(fmt: "Unknown pf %d\n", pf);
772 }
773
774 test_ip_tos_sockopt(fd);
775
776 connect_one_server(fd, pipefd);
777
778 return 0;
779}
780
781static pid_t xfork(void)
782{
783 pid_t p = fork();
784
785 if (p < 0)
786 die_perror(msg: "fork");
787
788 return p;
789}
790
791static int rcheck(int wstatus, const char *what)
792{
793 if (WIFEXITED(wstatus)) {
794 if (WEXITSTATUS(wstatus) == 0)
795 return 0;
796 fprintf(stderr, "%s exited, status=%d\n", what, WEXITSTATUS(wstatus));
797 return WEXITSTATUS(wstatus);
798 } else if (WIFSIGNALED(wstatus)) {
799 xerror(fmt: "%s killed by signal %d\n", what, WTERMSIG(wstatus));
800 } else if (WIFSTOPPED(wstatus)) {
801 xerror(fmt: "%s stopped by signal %d\n", what, WSTOPSIG(wstatus));
802 }
803
804 return 111;
805}
806
807static void init_rng(void)
808{
809 int fd = open("/dev/urandom", O_RDONLY);
810
811 if (fd >= 0) {
812 unsigned int foo;
813 ssize_t ret;
814
815 /* can't fail */
816 ret = read(fd, &foo, sizeof(foo));
817 assert(ret == sizeof(foo));
818
819 close(fd);
820 srand(foo);
821 } else {
822 srand(time(NULL));
823 }
824}
825
826int main(int argc, char *argv[])
827{
828 int e1, e2, wstatus;
829 pid_t s, c, ret;
830 int pipefds[2];
831
832 parse_opts(argc, argv);
833
834 init_rng();
835
836 e1 = pipe(pipefds);
837 if (e1 < 0)
838 die_perror(msg: "pipe");
839
840 s = xfork();
841 if (s == 0)
842 return server(pipefd: pipefds[1]);
843
844 close(pipefds[1]);
845
846 /* wait until server bound a socket */
847 e1 = read(pipefds[0], &e1, 4);
848 assert(e1 == 4);
849
850 c = xfork();
851 if (c == 0)
852 return client(pipefd: pipefds[0]);
853
854 close(pipefds[0]);
855
856 ret = waitpid(s, &wstatus, 0);
857 if (ret == -1)
858 die_perror(msg: "waitpid");
859 e1 = rcheck(wstatus, what: "server");
860 ret = waitpid(c, &wstatus, 0);
861 if (ret == -1)
862 die_perror(msg: "waitpid");
863 e2 = rcheck(wstatus, what: "client");
864
865 return e1 ? e1 : e2;
866}
867

source code of linux/tools/testing/selftests/net/mptcp/mptcp_sockopt.c