]> git.ipfire.org Git - thirdparty/util-linux.git/blame - tests/helpers/test_mkfds.c
tests: (mkfds) add netlink factory
[thirdparty/util-linux.git] / tests / helpers / test_mkfds.c
CommitLineData
fd81d3a5 1/*
794c09fa 2 * test_mkfds - make various file descriptors
fd81d3a5
MY
3 *
4 * Written by Masatake YAMATO <yamato@redhat.com>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it would be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
bce240a7 20#include <arpa/inet.h>
cb16b318 21#include <ctype.h>
5c265705 22#include <dirent.h>
031ff45c 23#include <errno.h>
d85c66d8 24#include <fcntl.h>
fd81d3a5 25#include <getopt.h>
bce240a7
MY
26#include <linux/if_ether.h>
27#include <linux/if_packet.h>
4763f067 28#include <linux/netlink.h>
c5eb81b3 29#include <linux/sockios.h> /* SIOCGSKNS */
bce240a7 30#include <net/if.h>
20d97930
MY
31#include <netinet/in.h>
32#include <netinet/tcp.h>
55529a26 33#include <sched.h>
fd81d3a5
MY
34#include <signal.h>
35#include <stdbool.h>
36#include <stdio.h>
37#include <stdlib.h>
bce240a7 38#include <string.h>
4d51adc5 39#include <sys/inotify.h>
c5eb81b3 40#include <sys/ioctl.h>
bce240a7 41#include <sys/mman.h>
863a6544 42#include <sys/prctl.h>
caa97697 43#include <sys/select.h>
d827da23 44#include <sys/socket.h>
ba8c749f 45#include <sys/syscall.h>
fd81d3a5 46#include <sys/types.h>
d827da23 47#include <sys/un.h>
bce240a7 48#include <sys/user.h>
fd81d3a5
MY
49#include <unistd.h>
50
51#include "c.h"
52#include "nls.h"
13b7739a 53#include "xalloc.h"
fd81d3a5 54
2182cd22
MY
55#define EXIT_ENOSYS 17
56#define EXIT_EPERM 18
57#define EXIT_ENOPROTOOPT 19
40853fe4 58#define EXIT_EPROTONOSUPPORT 20
2182cd22 59
fd81d3a5
MY
60#define _U_ __attribute__((__unused__))
61
ba8c749f
MY
62static int pidfd_open(pid_t pid, unsigned int flags);
63
fd81d3a5
MY
64static void __attribute__((__noreturn__)) usage(FILE *out, int status)
65{
66 fputs(USAGE_HEADER, out);
13b7739a 67 fprintf(out, _(" %s [options] FACTORY FD... [PARAM=VAL...]\n"), program_invocation_short_name);
fd81d3a5
MY
68
69 fputs(USAGE_OPTIONS, out);
4f3a2a77
MY
70 fputs(_(" -l, --list list available file descriptor factories and exit\n"), out);
71 fputs(_(" -I, --parameters <factory> list parameters the factory takes\n"), out);
72 fputs(_(" -r, --comm <name> rename self\n"), out);
73 fputs(_(" -q, --quiet don't print pid(s)\n"), out);
74 fputs(_(" -c, --dont-pause don't pause after making fd(s)\n"), out);
fd81d3a5
MY
75
76 fputs(USAGE_SEPARATOR, out);
77 fputs(_("Examples:\n"), out);
0dfd6614 78 fprintf(out, _("Using 3, open /etc/group:\n\n $ %s ro-regular-file 3 file=/etc/group\n\n"),
fd81d3a5 79 program_invocation_short_name);
0dfd6614 80 fprintf(out, _("Using 3 and 4, make a pipe:\n\n $ %s pipe-no-fork 3 4\n\n"),
fd81d3a5
MY
81 program_invocation_short_name);
82
83 exit(status);
84}
85
13b7739a
MY
86union value {
87 const char *string;
cb9c4cab 88 long integer;
91d159bf 89 unsigned long uinteger;
e2475629 90 bool boolean;
13b7739a
MY
91};
92
93enum ptype {
94 PTYPE_STRING,
cb9c4cab 95 PTYPE_INTEGER,
91d159bf 96 PTYPE_UINTEGER,
e2475629 97 PTYPE_BOOLEAN,
13b7739a
MY
98};
99
100struct ptype_class {
101 const char *name;
102
103 /* Covert to a string representation.
104 * A caller must free the returned value with free(3) after using. */
105 char *(*sprint)(const union value *value);
106
107 /* Convert from a string. If ARG is NULL, use DEFV instead.
108 * A caller must free the returned value with the free method
109 * after using. */
110 union value (*read)(const char *arg, const union value *defv);
111
112 /* Free the value returned from the read method. */
113 void (*free)(union value value);
114};
115
116#define ARG_STRING(A) (A.v.string)
cb9c4cab 117#define ARG_INTEGER(A) (A.v.integer)
91d159bf 118#define ARG_UINTEGER(A) (A.v.uinteger)
e2475629 119#define ARG_BOOLEAN(A) (A.v.boolean)
13b7739a
MY
120struct arg {
121 union value v;
122 void (*free)(union value value);
123};
124
125struct parameter {
126 const char *name;
127 const enum ptype type;
128 const char *desc;
129 union value defv; /* Default value */
130};
131
132static char *string_sprint(const union value *value)
133{
134 return xstrdup(value->string);
135}
136
137static union value string_read(const char *arg, const union value *defv)
138{
139 return (union value){ .string = xstrdup(arg?: defv->string) };
140}
141
142static void string_free(union value value)
143{
144 free((void *)value.string);
145}
146
cb9c4cab
MY
147static char *integer_sprint(const union value *value)
148{
149 char *str = NULL;
150 xasprintf(&str, "%ld", value->integer);
151 return str;
152}
153
154static union value integer_read(const char *arg, const union value *defv)
155{
156 char *ep;
157 union value r;
158
159 if (!arg)
160 return *defv;
161
162 errno = 0;
163 r.integer = strtol(arg, &ep, 10);
164 if (errno)
165 err(EXIT_FAILURE, _("fail to make a number from %s"), arg);
166 else if (*ep != '\0')
167 errx(EXIT_FAILURE, _("garbage at the end of number: %s"), arg);
168 return r;
169}
170
171static void integer_free(union value value _U_)
172{
173 /* Do nothing */
174}
175
91d159bf
MY
176static char *uinteger_sprint(const union value *value)
177{
178 char *str = NULL;
179 xasprintf(&str, "%lu", value->uinteger);
180 return str;
181}
182
183static union value uinteger_read(const char *arg, const union value *defv)
184{
185 char *ep;
186 union value r;
187
188 if (!arg)
189 return *defv;
190
191 errno = 0;
192 r.uinteger = strtoul(arg, &ep, 10);
193 if (errno)
194 err(EXIT_FAILURE, _("fail to make a number from %s"), arg);
195 else if (*ep != '\0')
196 errx(EXIT_FAILURE, _("garbage at the end of number: %s"), arg);
197 return r;
198}
199
200static void uinteger_free(union value value _U_)
201{
202 /* Do nothing */
203}
204
e2475629
MY
205static char *boolean_sprint(const union value *value)
206{
207 return xstrdup(value->boolean? "true": "false");
208}
209
210static union value boolean_read(const char *arg, const union value *defv)
211{
212 union value r;
213
214 if (!arg)
215 return *defv;
216
217 if (strcasecmp(arg, "true") == 0
218 || strcmp(arg, "1") == 0
219 || strcasecmp(arg, "yes") == 0
220 || strcasecmp(arg, "y") == 0)
221 r.boolean = true;
222 else
223 r.boolean = false;
224 return r;
225}
226
227static void boolean_free(union value value _U_)
228{
229 /* Do nothing */
230}
cb9c4cab 231
13b7739a
MY
232struct ptype_class ptype_classes [] = {
233 [PTYPE_STRING] = {
234 .name = "string",
235 .sprint = string_sprint,
236 .read = string_read,
237 .free = string_free,
238 },
cb9c4cab
MY
239 [PTYPE_INTEGER] = {
240 .name = "integer",
241 .sprint = integer_sprint,
242 .read = integer_read,
243 .free = integer_free,
244 },
4763f067
MY
245 [PTYPE_UINTEGER] = {
246 .name = "uinteger",
247 .sprint = uinteger_sprint,
248 .read = uinteger_read,
249 .free = uinteger_free,
250 },
e2475629
MY
251 [PTYPE_BOOLEAN] = {
252 .name = "boolean",
253 .sprint = boolean_sprint,
254 .read = boolean_read,
255 .free = boolean_free,
256 },
13b7739a
MY
257};
258
ae9ff1cd
MY
259static struct arg decode_arg(const char *pname,
260 const struct parameter *parameters,
261 int argc, char **argv)
262{
263 char *v = NULL;
264 size_t len = strlen(pname);
265 const struct parameter *p = NULL;
266 struct arg arg;
267
268 while (parameters->name) {
269 if (strcmp(pname, parameters->name) == 0) {
270 p = parameters;
271 break;
272 }
273 parameters++;
274 }
275 if (p == NULL)
276 errx(EXIT_FAILURE, _("no such parameter: %s"), pname);
277
278 for (int i = 0; i < argc; i++) {
279 if (strncmp(pname, argv[i], len) == 0) {
280 v = argv[i] + len;
281 if (*v == '=') {
282 v++;
283 break;
284 } else if (*v == '\0')
285 errx(EXIT_FAILURE,
286 _("no value given for \"%s\" parameter"),
287 pname);
288 else
289 v = NULL;
290 }
291 }
292 arg.v = ptype_classes [p->type].read (v, &p->defv);
293 arg.free = ptype_classes [p->type].free;
294 return arg;
295}
296
297static void free_arg(struct arg *arg)
298{
299 arg->free(arg->v);
300}
301
fd81d3a5
MY
302struct fdesc {
303 int fd;
304 void (*close)(int, void *);
305 void *data;
306};
307
308struct factory {
309 const char *name; /* [-a-zA-Z0-9_]+ */
310 const char *desc;
311 bool priv; /* the root privilege is needed to make fd(s) */
d14e9efd 312#define MAX_N 5
fd81d3a5 313 int N; /* the number of fds this factory makes */
d14e9efd 314 int EX_N; /* fds made optionally */
67aab5a5
MY
315 void *(*make)(const struct factory *, struct fdesc[], int, char **);
316 void (*free)(const struct factory *, void *);
271bc510 317 void (*report)(const struct factory *, void *, FILE *);
13b7739a 318 const struct parameter * params;
fd81d3a5
MY
319};
320
321static void close_fdesc(int fd, void *data _U_)
322{
323 close(fd);
324}
325
67aab5a5
MY
326static void *open_ro_regular_file(const struct factory *factory, struct fdesc fdescs[],
327 int argc, char ** argv)
fd81d3a5 328{
ae9ff1cd 329 struct arg file = decode_arg("file", factory->params, argc, argv);
6d201bcb 330 struct arg offset = decode_arg("offset", factory->params, argc, argv);
fd81d3a5 331
ae9ff1cd 332 int fd = open(ARG_STRING(file), O_RDONLY);
fd81d3a5 333 if (fd < 0)
ae9ff1cd
MY
334 err(EXIT_FAILURE, "failed to open: %s", ARG_STRING(file));
335 free_arg(&file);
fd81d3a5 336
6d201bcb
MY
337 if (ARG_INTEGER(offset) != 0) {
338 if (lseek(fd, (off_t)ARG_INTEGER(offset), SEEK_CUR) < 0) {
339 int e = errno;
340 close(fd);
341 errno = e;
342 err(EXIT_FAILURE, "failed to seek 0 -> %ld", ARG_INTEGER(offset));
343 }
344 }
345 free_arg(&offset);
346
3ebd5939
MY
347 if (fd != fdescs[0].fd) {
348 if (dup2(fd, fdescs[0].fd) < 0) {
349 int e = errno;
350 close(fd);
351 errno = e;
352 err(EXIT_FAILURE, "failed to dup %d -> %d", fd, fdescs[0].fd);
353 }
fd81d3a5 354 close(fd);
fd81d3a5
MY
355 }
356
357 fdescs[0] = (struct fdesc){
358 .fd = fdescs[0].fd,
359 .close = close_fdesc,
360 .data = NULL
361 };
67aab5a5
MY
362
363 return NULL;
fd81d3a5
MY
364}
365
67aab5a5
MY
366static void *make_pipe(const struct factory *factory, struct fdesc fdescs[],
367 int argc, char ** argv)
fd81d3a5
MY
368{
369 int pd[2];
d85c66d8
MY
370 int nonblock_flags[2] = {0, 0};
371 struct arg nonblock = decode_arg("nonblock", factory->params, argc, argv);
372 if (strlen(ARG_STRING(nonblock)) != 2) {
373 errx(EXIT_FAILURE, "string value for %s has unexpected length: %s",
374 "nonblock", ARG_STRING(nonblock));
375 }
376
9aa68bf4
MY
377 /* Make extra pipe descriptors for making pipe objects connected
378 * with fds more than 2.
379 * See https://github.com/util-linux/util-linux/pull/1622
380 * about the background of the requirement. */
381 struct arg rdup = decode_arg("rdup", factory->params, argc, argv);
382 struct arg wdup = decode_arg("wdup", factory->params, argc, argv);
383 int xpd[2];
384 xpd [0] = ARG_INTEGER(rdup);
385 xpd [1] = ARG_INTEGER(wdup);
386
d85c66d8
MY
387 for (int i = 0; i < 2; i++) {
388 if (ARG_STRING(nonblock)[i] == '-')
389 continue;
390 if ((i == 0 && ARG_STRING(nonblock)[i] == 'r')
391 || (i == 1 && ARG_STRING(nonblock)[i] == 'w'))
392 nonblock_flags[i] = 1;
393 else
394 errx(EXIT_FAILURE, "unexpected value %c for the %s fd of %s",
395 ARG_STRING(nonblock)[i],
396 (i == 0)? "read": "write",
397 "nonblock");
398 }
399 free_arg(&nonblock);
400
fd81d3a5
MY
401 if (pipe(pd) < 0)
402 err(EXIT_FAILURE, "failed to make pipe");
403
d85c66d8
MY
404 for (int i = 0; i < 2; i++) {
405 if (nonblock_flags[i]) {
406 int flags = fcntl(pd[i], F_GETFL);
407 if (fcntl(pd[i], F_SETFL, flags|O_NONBLOCK) < 0) {
408 int e = errno;
409 close(pd[0]);
410 close(pd[1]);
411 errno = e;
412 errx(EXIT_FAILURE, "failed to set NONBLOCK flag to the %s fd",
413 (i == 0)? "read": "write");
414 }
415 }
416 }
417
fd81d3a5 418 for (int i = 0; i < 2; i++) {
3ebd5939
MY
419 if (pd[i] != fdescs[i].fd) {
420 if (dup2(pd[i], fdescs[i].fd) < 0) {
421 int e = errno;
422 close(pd[0]);
423 close(pd[1]);
424 errno = e;
425 err(EXIT_FAILURE, "failed to dup %d -> %d",
426 pd[i], fdescs[i].fd);
427 }
428 close(pd[i]);
fd81d3a5
MY
429 }
430 fdescs[i] = (struct fdesc){
431 .fd = fdescs[i].fd,
432 .close = close_fdesc,
433 .data = NULL
434 };
435 }
9aa68bf4
MY
436
437 /* Make extra pipe descriptors. */
438 for (int i = 0; i < 2; i++) {
439 if (xpd[i] >= 0) {
440 if (dup2(fdescs[i].fd, xpd[i]) < 0) {
441 int e = errno;
442 close(fdescs[0].fd);
443 close(fdescs[1].fd);
444 if (i > 0 && xpd[0] >= 0)
445 close(xpd[0]);
446 errno = e;
447 err(EXIT_FAILURE, "failed to dup %d -> %d",
448 fdescs[i].fd, xpd[i]);
449 }
450 fdescs[i + 2] = (struct fdesc){
451 .fd = xpd[i],
452 .close = close_fdesc,
453 .data = NULL
454 };
455 }
456 }
67aab5a5
MY
457
458 return NULL;
fd81d3a5
MY
459}
460
5c265705
MY
461static void close_dir(int fd, void *data)
462{
463 DIR *dp = data;
464 if (dp)
465 closedir(dp);
466 else
467 close_fdesc(fd, NULL);
468}
469
67aab5a5
MY
470static void *open_directory(const struct factory *factory, struct fdesc fdescs[],
471 int argc, char ** argv)
77c70070 472{
8c670091 473 struct arg dir = decode_arg("dir", factory->params, argc, argv);
5c265705
MY
474 struct arg dentries = decode_arg("dentries", factory->params, argc, argv);
475 DIR *dp = NULL;
77c70070 476
8c670091 477 int fd = open(ARG_STRING(dir), O_RDONLY|O_DIRECTORY);
77c70070 478 if (fd < 0)
8c670091
MY
479 err(EXIT_FAILURE, "failed to open: %s", ARG_STRING(dir));
480 free_arg(&dir);
77c70070 481
3ebd5939
MY
482 if (fd != fdescs[0].fd) {
483 if (dup2(fd, fdescs[0].fd) < 0) {
484 int e = errno;
485 close(fd);
486 errno = e;
487 err(EXIT_FAILURE, "failed to dup %d -> %d", fd, fdescs[0].fd);
488 }
77c70070 489 close(fd);
77c70070
MY
490 }
491
5c265705
MY
492 if (ARG_INTEGER(dentries) > 0) {
493 dp = fdopendir(fdescs[0].fd);
494 if (dp == NULL) {
495 int e = errno;
496 close(fdescs[0].fd);
497 errno = e;
498 err(EXIT_FAILURE, "failed to make DIR* from fd: %s", ARG_STRING(dir));
499 }
500 for (int i = 0; i < ARG_INTEGER(dentries); i++) {
501 struct dirent *d = readdir(dp);
502 if (!d) {
503 int e = errno;
504 closedir(dp);
505 errno = e;
506 err(EXIT_FAILURE, "failed in readdir(3)");
507 }
508 }
509 }
510 free_arg(&dentries);
511
77c70070
MY
512 fdescs[0] = (struct fdesc){
513 .fd = fdescs[0].fd,
5c265705
MY
514 .close = close_dir,
515 .data = dp
77c70070 516 };
67aab5a5
MY
517
518 return NULL;
77c70070
MY
519}
520
67aab5a5
MY
521static void *open_rw_chrdev(const struct factory *factory, struct fdesc fdescs[],
522 int argc, char ** argv)
7c06963e
MY
523{
524 struct arg chrdev = decode_arg("chrdev", factory->params, argc, argv);
525 int fd = open(ARG_STRING(chrdev), O_RDWR);
526 if (fd < 0)
527 err(EXIT_FAILURE, "failed to open: %s", ARG_STRING(chrdev));
528 free_arg(&chrdev);
529
6a2c79e7
MY
530 if (fd != fdescs[0].fd) {
531 if (dup2(fd, fdescs[0].fd) < 0) {
532 int e = errno;
533 close(fd);
534 errno = e;
535 err(EXIT_FAILURE, "failed to dup %d -> %d", fd, fdescs[0].fd);
536 }
537 close(fd);
538 }
539
7c06963e 540 fdescs[0] = (struct fdesc){
6a2c79e7 541 .fd = fdescs[0].fd,
7c06963e
MY
542 .close = close_fdesc,
543 .data = NULL
544 };
67aab5a5
MY
545
546 return NULL;
7c06963e
MY
547}
548
67aab5a5
MY
549static void *make_socketpair(const struct factory *factory, struct fdesc fdescs[],
550 int argc, char ** argv)
d827da23
MY
551{
552 int sd[2];
553 struct arg socktype = decode_arg("socktype", factory->params, argc, argv);
554 int isocktype;
555 if (strcmp(ARG_STRING(socktype), "STREAM") == 0)
556 isocktype = SOCK_STREAM;
557 else if (strcmp(ARG_STRING(socktype), "DGRAM") == 0)
558 isocktype = SOCK_DGRAM;
559 else if (strcmp(ARG_STRING(socktype), "SEQPACKET") == 0)
560 isocktype = SOCK_SEQPACKET;
561 else
562 errx(EXIT_FAILURE,
563 "unknown socket type for socketpair(AF_UNIX,...): %s",
564 ARG_STRING(socktype));
565 free_arg(&socktype);
566
567 if (socketpair(AF_UNIX, isocktype, 0, sd) < 0)
568 err(EXIT_FAILURE, "failed to make socket pair");
569
570 for (int i = 0; i < 2; i++) {
3ebd5939
MY
571 if (sd[i] != fdescs[i].fd) {
572 if (dup2(sd[i], fdescs[i].fd) < 0) {
573 int e = errno;
574 close(sd[0]);
575 close(sd[1]);
576 errno = e;
577 err(EXIT_FAILURE, "failed to dup %d -> %d",
578 sd[i], fdescs[i].fd);
579 }
580 close(sd[i]);
d827da23
MY
581 }
582 fdescs[i] = (struct fdesc){
583 .fd = fdescs[i].fd,
584 .close = close_fdesc,
585 .data = NULL
586 };
587 }
67aab5a5
MY
588
589 return NULL;
d827da23
MY
590}
591
67aab5a5
MY
592static void *open_with_opath(const struct factory *factory, struct fdesc fdescs[],
593 int argc, char ** argv)
21c70c29
MY
594{
595 struct arg path = decode_arg("path", factory->params, argc, argv);
596 int fd = open(ARG_STRING(path), O_PATH|O_NOFOLLOW);
597 if (fd < 0)
598 err(EXIT_FAILURE, "failed to open with O_PATH: %s", ARG_STRING(path));
599 free_arg(&path);
600
6a2c79e7
MY
601 if (fd != fdescs[0].fd) {
602 if (dup2(fd, fdescs[0].fd) < 0) {
603 int e = errno;
604 close(fd);
605 errno = e;
606 err(EXIT_FAILURE, "failed to dup %d -> %d", fd, fdescs[0].fd);
607 }
608 close(fd);
609 }
610
21c70c29 611 fdescs[0] = (struct fdesc){
6a2c79e7 612 .fd = fdescs[0].fd,
21c70c29
MY
613 .close = close_fdesc,
614 .data = NULL
615 };
67aab5a5
MY
616
617 return NULL;
21c70c29
MY
618}
619
67aab5a5 620static void *open_ro_blkdev(const struct factory *factory, struct fdesc fdescs[],
cb16b318
MY
621 int argc, char ** argv)
622{
623 struct arg blkdev = decode_arg("blkdev", factory->params, argc, argv);
624 int fd = open(ARG_STRING(blkdev), O_RDONLY);
625 if (fd < 0)
626 err(EXIT_FAILURE, "failed to open: %s", ARG_STRING(blkdev));
627 free_arg(&blkdev);
628
6a2c79e7
MY
629 if (fd != fdescs[0].fd) {
630 if (dup2(fd, fdescs[0].fd) < 0) {
631 int e = errno;
632 close(fd);
633 errno = e;
634 err(EXIT_FAILURE, "failed to dup %d -> %d", fd, fdescs[0].fd);
635 }
636 close(fd);
637 }
638
cb16b318 639 fdescs[0] = (struct fdesc){
6a2c79e7 640 .fd = fdescs[0].fd,
cb16b318
MY
641 .close = close_fdesc,
642 .data = NULL,
643 };
67aab5a5
MY
644
645 return NULL;
cb16b318
MY
646}
647
bce240a7
MY
648static int make_packet_socket(int socktype, const char *interface)
649{
650 int sd;
651 struct sockaddr_ll addr;
652
653 sd = socket(AF_PACKET, socktype, htons(ETH_P_ALL));
654 if (sd < 0)
655 err(EXIT_FAILURE, "failed to make a socket with AF_PACKET");
656
657 if (interface == NULL)
658 return sd; /* Just making a socket */
659
660 memset(&addr, 0, sizeof(struct sockaddr_ll));
661 addr.sll_family = AF_PACKET;
662 addr.sll_protocol = ETH_P_ALL;
663 addr.sll_ifindex = if_nametoindex(interface);
664 if (addr.sll_ifindex == 0) {
665 int e = errno;
666 close(sd);
667 errno = e;
668 err(EXIT_FAILURE,
669 "failed to get the interface index for %s", interface);
670 }
671 if (bind(sd, (struct sockaddr *)&addr, sizeof(struct sockaddr_ll)) < 0) {
672 int e = errno;
673 close(sd);
674 errno = e;
675 err(EXIT_FAILURE,
676 "failed to get the interface index for %s", interface);
677 }
678
679 return sd;
680}
681
682struct munmap_data {
683 void *ptr;
684 size_t len;
685};
686
687static void close_fdesc_after_munmap(int fd, void *data)
688{
689 struct munmap_data *munmap_data = data;
690 munmap(munmap_data->ptr, munmap_data->len);
691 free(data);
692 close(fd);
693}
694
67aab5a5
MY
695static void *make_mmapped_packet_socket(const struct factory *factory, struct fdesc fdescs[],
696 int argc, char ** argv)
bce240a7
MY
697{
698 int sd;
699 struct arg socktype = decode_arg("socktype", factory->params, argc, argv);
700 struct arg interface = decode_arg("interface", factory->params, argc, argv);
701
702 int isocktype;
703 const char *sinterface;
704 struct tpacket_req req;
705 struct munmap_data *munmap_data;
706
707 if (strcmp(ARG_STRING(socktype), "DGRAM") == 0)
708 isocktype = SOCK_DGRAM;
709 else if (strcmp(ARG_STRING(socktype), "RAW") == 0)
710 isocktype = SOCK_RAW;
711 else
712 errx(EXIT_FAILURE,
713 "unknown socket type for socket(AF_PACKET,...): %s",
714 ARG_STRING(socktype));
715 free_arg(&socktype);
716
717 sinterface = ARG_STRING(interface);
718 sd = make_packet_socket(isocktype, sinterface);
719 free_arg(&interface);
720
721 /* Specify the spec of ring buffers.
722 *
723 * ref.
724 * - linux/Documentation/networking/packet_mmap.rst
725 * - https://sites.google.com/site/packetmmap/home
726 */
fbb894b2
KZ
727 req.tp_block_size = getpagesize();
728 req.tp_frame_size = getpagesize();
bce240a7
MY
729 req.tp_block_nr = 1;
730 req.tp_frame_nr = 1;
731 if (setsockopt(sd, SOL_PACKET, PACKET_TX_RING, (char *)&req, sizeof(req)) < 0) {
732 int e = errno;
733 close(sd);
734 errno = e;
da74e6e5
MY
735 err((errno == ENOPROTOOPT? EXIT_ENOPROTOOPT: EXIT_FAILURE),
736 "failed to specify a buffer spec to a packet socket");
bce240a7
MY
737 }
738
739 munmap_data = malloc(sizeof (*munmap_data));
740 if (munmap_data == NULL) {
741 close(sd);
742 errx(EXIT_FAILURE, "memory exhausted");
743 }
d244d77b 744 munmap_data->len = (size_t) req.tp_block_size * req.tp_block_nr;
bce240a7
MY
745 munmap_data->ptr = mmap(NULL, munmap_data->len, PROT_WRITE, MAP_SHARED, sd, 0);
746 if (munmap_data->ptr == MAP_FAILED) {
747 int e = errno;
748 close(sd);
749 free(munmap_data);
750 errno = e;
751 err(EXIT_FAILURE, "failed to do mmap a packet socket");
752 }
753
754 if (sd != fdescs[0].fd) {
755 if (dup2(sd, fdescs[0].fd) < 0) {
756 int e = errno;
757 close(sd);
758 munmap(munmap_data->ptr, munmap_data->len);
759 free(munmap_data);
760 errno = e;
761 err(EXIT_FAILURE, "failed to dup %d -> %d", sd, fdescs[0].fd);
762 }
763 close(sd);
764 }
765
766 fdescs[0] = (struct fdesc){
767 .fd = fdescs[0].fd,
768 .close = close_fdesc_after_munmap,
769 .data = munmap_data,
770 };
67aab5a5
MY
771
772 return NULL;
bce240a7
MY
773}
774
67aab5a5
MY
775static void *make_pidfd(const struct factory *factory, struct fdesc fdescs[],
776 int argc, char ** argv)
ba8c749f
MY
777{
778 struct arg target_pid = decode_arg("target-pid", factory->params, argc, argv);
779 pid_t pid = ARG_INTEGER(target_pid);
780
781 int fd = pidfd_open(pid, 0);
782 if (fd < 0)
3de730ad
MY
783 err((errno == ENOSYS? EXIT_ENOSYS: EXIT_FAILURE),
784 "failed in pidfd_open(%d)", (int)pid);
ba8c749f
MY
785 free_arg(&target_pid);
786
787 if (fd != fdescs[0].fd) {
788 if (dup2(fd, fdescs[0].fd) < 0) {
789 int e = errno;
790 close(fd);
791 errno = e;
792 err(EXIT_FAILURE, "failed to dup %d -> %d", fd, fdescs[0].fd);
793 }
794 close(fd);
795 }
796
797 fdescs[0] = (struct fdesc){
798 .fd = fdescs[0].fd,
799 .close = close_fdesc,
800 .data = NULL
801 };
67aab5a5
MY
802
803 return NULL;
ba8c749f
MY
804}
805
67aab5a5
MY
806static void *make_inotify_fd(const struct factory *factory _U_, struct fdesc fdescs[],
807 int argc _U_, char ** argv _U_)
4d51adc5
MY
808{
809 int fd = inotify_init();
810 if (fd < 0)
811 err(EXIT_FAILURE, "failed in inotify_init()");
812
813 if (fd != fdescs[0].fd) {
814 if (dup2(fd, fdescs[0].fd) < 0) {
815 int e = errno;
816 close(fd);
817 errno = e;
818 err(EXIT_FAILURE, "failed to dup %d -> %d", fd, fdescs[0].fd);
819 }
820 close(fd);
821 }
822
823 fdescs[0] = (struct fdesc){
824 .fd = fdescs[0].fd,
825 .close = close_fdesc,
826 .data = NULL
827 };
67aab5a5
MY
828
829 return NULL;
4d51adc5
MY
830}
831
a234bad9
MY
832static void close_unix_socket(int fd, void *data)
833{
834 char *path = data;
835 close(fd);
836 if (path) {
837 unlink(path);
838 free(path);
839 }
840}
841
223063b1
MY
842static void *make_unix_stream_core(const struct factory *factory, struct fdesc fdescs[],
843 int argc, char ** argv, int type, const char *typestr)
a234bad9
MY
844{
845 struct arg path = decode_arg("path", factory->params, argc, argv);
846 const char *spath = ARG_STRING(path);
847
848 struct arg backlog = decode_arg("backlog", factory->params, argc, argv);
849 int ibacklog = ARG_INTEGER(path);
850
851 struct arg abstract = decode_arg("abstract", factory->params, argc, argv);
852 bool babstract = ARG_BOOLEAN(abstract);
853
854 struct arg server_shutdown = decode_arg("server-shutdown", factory->params, argc, argv);
855 int iserver_shutdown = ARG_INTEGER(server_shutdown);
856 struct arg client_shutdown = decode_arg("client-shutdown", factory->params, argc, argv);
857 int iclient_shutdown = ARG_INTEGER(client_shutdown);
858
eca0c148 859 int ssd, csd, asd; /* server, client, and accepted socket descriptors */
a234bad9
MY
860 struct sockaddr_un un;
861 size_t un_len = sizeof(un);
862
863 memset(&un, 0, sizeof(un));
864 un.sun_family = AF_UNIX;
865 if (babstract) {
866 strncpy(un.sun_path + 1, spath, sizeof(un.sun_path) - 1 - 1);
867 size_t pathlen = strlen(spath);
868 if (sizeof(un.sun_path) - 1 > pathlen)
869 un_len = sizeof(un) - sizeof(un.sun_path) + 1 + pathlen;
870 } else
871 strncpy(un.sun_path, spath, sizeof(un.sun_path) - 1 );
872
873 free_arg(&client_shutdown);
874 free_arg(&server_shutdown);
875 free_arg(&abstract);
876 free_arg(&backlog);
877 free_arg(&path);
878
879 if (iserver_shutdown < 0 || iserver_shutdown > 3)
880 errx(EXIT_FAILURE, "the server shudown specification in unexpected range");
881 if (iclient_shutdown < 0 || iclient_shutdown > 3)
882 errx(EXIT_FAILURE, "the client shudown specification in unexpected range");
883
223063b1 884 ssd = socket(AF_UNIX, type, 0);
a234bad9
MY
885 if (ssd < 0)
886 err(EXIT_FAILURE,
223063b1 887 "failed to make a socket with AF_UNIX + SOCK_%s (server side)", typestr);
a234bad9
MY
888 if (ssd != fdescs[0].fd) {
889 if (dup2(ssd, fdescs[0].fd) < 0) {
890 int e = errno;
891 close(ssd);
892 errno = e;
893 err(EXIT_FAILURE, "failed to dup %d -> %d", ssd, fdescs[0].fd);
894 }
895 close(ssd);
896 ssd = fdescs[0].fd;
897 }
898
899 fdescs[0] = (struct fdesc){
900 .fd = fdescs[0].fd,
901 .close = close_unix_socket,
902 .data = NULL,
903 };
904
905 if (!babstract)
906 unlink(un.sun_path);
907 if (bind(ssd, (const struct sockaddr *)&un, un_len) < 0) {
908 int e = errno;
909 close(ssd);
910 errno = e;
911 err(EXIT_FAILURE, "failed to bind a socket for listening");
912 }
913
914 if (!babstract)
915 fdescs[0].data = xstrdup(un.sun_path);
916 if (listen(ssd, ibacklog) < 0) {
917 int e = errno;
918 close_unix_socket(ssd, fdescs[0].data);
919 errno = e;
920 err(EXIT_FAILURE, "failed to listen a socket");
921 }
922
223063b1 923 csd = socket(AF_UNIX, type, 0);
a234bad9
MY
924 if (csd < 0)
925 err(EXIT_FAILURE,
223063b1 926 "failed to make a socket with AF_UNIX + SOCK_%s (client side)", typestr);
a234bad9
MY
927 if (csd != fdescs[1].fd) {
928 if (dup2(csd, fdescs[1].fd) < 0) {
929 int e = errno;
930 close(csd);
931 close_unix_socket(ssd, fdescs[0].data);
932 errno = e;
933 err(EXIT_FAILURE, "failed to dup %d -> %d", csd, fdescs[1].fd);
934 }
935 close(csd);
936 csd = fdescs[1].fd;
937 }
938
939 fdescs[1] = (struct fdesc){
940 .fd = fdescs[1].fd,
941 .close = close_fdesc,
942 .data = NULL,
943 };
944
945 if (connect(csd, (const struct sockaddr *)&un, un_len) < 0) {
946 int e = errno;
947 close_fdesc(csd, NULL);
948 close_unix_socket(ssd, fdescs[0].data);
949 errno = e;
950 err(EXIT_FAILURE, "failed to connect a socket to the listening socket");
951 }
952
953 if (!babstract)
954 unlink(un.sun_path);
955
956 asd = accept(ssd, NULL, NULL);
957 if (asd < 0) {
958 int e = errno;
959 close_fdesc(csd, NULL);
960 close_unix_socket(ssd, fdescs[0].data);
961 errno = e;
962 err(EXIT_FAILURE, "failed to accept a socket from the listening socket");
963 }
964 if (asd != fdescs[2].fd) {
965 if (dup2(asd, fdescs[2].fd) < 0) {
966 int e = errno;
967 close(asd);
968 close_fdesc(csd, NULL);
969 close_unix_socket(ssd, fdescs[0].data);
970 errno = e;
971 err(EXIT_FAILURE, "failed to dup %d -> %d", asd, fdescs[2].fd);
972 }
973 close(asd);
974 asd = fdescs[2].fd;
975 }
976
977 if (iserver_shutdown & (1 << 0))
978 shutdown(asd, SHUT_RD);
979 if (iserver_shutdown & (1 << 1))
980 shutdown(asd, SHUT_WR);
981 if (iclient_shutdown & (1 << 0))
982 shutdown(csd, SHUT_RD);
983 if (iclient_shutdown & (1 << 1))
984 shutdown(csd, SHUT_WR);
985
986 return NULL;
987}
988
223063b1
MY
989static void *make_unix_stream(const struct factory *factory, struct fdesc fdescs[],
990 int argc, char ** argv)
991{
992 struct arg type = decode_arg("type", factory->params, argc, argv);
993 const char *stype = ARG_STRING(type);
994
995 int typesym;
996 const char *typestr;
997
998 if (strcmp(stype, "stream") == 0) {
999 typesym = SOCK_STREAM;
1000 typestr = "STREAM";
1001 } else if (strcmp(stype, "seqpacket") == 0) {
1002 typesym = SOCK_SEQPACKET;
1003 typestr = "SEQPACKET";
1004 } else
1005 errx(EXIT_FAILURE, _("unknown unix socket type: %s"), stype);
1006
1007 free_arg(&type);
1008
1009 return make_unix_stream_core(factory, fdescs, argc, argv, typesym, typestr);
1010}
1011
2229a4b4
MY
1012static void *make_unix_dgram(const struct factory *factory, struct fdesc fdescs[],
1013 int argc, char ** argv)
1014{
1015 struct arg path = decode_arg("path", factory->params, argc, argv);
1016 const char *spath = ARG_STRING(path);
1017
1018 struct arg abstract = decode_arg("abstract", factory->params, argc, argv);
1019 bool babstract = ARG_BOOLEAN(abstract);
1020
1021 int ssd, csd; /* server and client socket descriptors */
1022
1023 struct sockaddr_un un;
1024 size_t un_len = sizeof(un);
1025
1026 memset(&un, 0, sizeof(un));
1027 un.sun_family = AF_UNIX;
1028 if (babstract) {
1029 strncpy(un.sun_path + 1, spath, sizeof(un.sun_path) - 1 - 1);
1030 size_t pathlen = strlen(spath);
1031 if (sizeof(un.sun_path) - 1 > pathlen)
1032 un_len = sizeof(un) - sizeof(un.sun_path) + 1 + pathlen;
1033 } else
1034 strncpy(un.sun_path, spath, sizeof(un.sun_path) - 1 );
1035
1036 free_arg(&abstract);
1037 free_arg(&path);
1038
1039 ssd = socket(AF_UNIX, SOCK_DGRAM, 0);
1040 if (ssd < 0)
1041 err(EXIT_FAILURE,
1042 "failed to make a socket with AF_UNIX + SOCK_DGRAM (server side)");
1043 if (ssd != fdescs[0].fd) {
1044 if (dup2(ssd, fdescs[0].fd) < 0) {
1045 int e = errno;
1046 close(ssd);
1047 errno = e;
1048 err(EXIT_FAILURE, "failed to dup %d -> %d", ssd, fdescs[0].fd);
1049 }
1050 close(ssd);
1051 ssd = fdescs[0].fd;
1052 }
1053
1054 fdescs[0] = (struct fdesc){
1055 .fd = fdescs[0].fd,
1056 .close = close_unix_socket,
1057 .data = NULL,
1058 };
1059
1060 if (!babstract)
1061 unlink(un.sun_path);
1062 if (bind(ssd, (const struct sockaddr *)&un, un_len) < 0) {
1063 int e = errno;
1064 close(ssd);
1065 errno = e;
1066 err(EXIT_FAILURE, "failed to bind a socket for server");
1067 }
1068
1069 if (!babstract)
1070 fdescs[0].data = xstrdup(un.sun_path);
1071 csd = socket(AF_UNIX, SOCK_DGRAM, 0);
1072 if (csd < 0)
1073 err(EXIT_FAILURE,
1074 "failed to make a socket with AF_UNIX + SOCK_DGRAM (client side)");
1075 if (csd != fdescs[1].fd) {
1076 if (dup2(csd, fdescs[1].fd) < 0) {
1077 int e = errno;
1078 close(csd);
1079 close_unix_socket(ssd, fdescs[0].data);
1080 errno = e;
1081 err(EXIT_FAILURE, "failed to dup %d -> %d", csd, fdescs[1].fd);
1082 }
1083 close(csd);
1084 csd = fdescs[1].fd;
1085 }
1086
1087 fdescs[1] = (struct fdesc){
1088 .fd = fdescs[1].fd,
1089 .close = close_fdesc,
1090 .data = NULL,
1091 };
1092
1093 if (connect(csd, (const struct sockaddr *)&un, un_len) < 0) {
1094 int e = errno;
1095 close_fdesc(csd, NULL);
1096 close_unix_socket(ssd, fdescs[0].data);
1097 errno = e;
1098 err(EXIT_FAILURE, "failed to connect a socket to the server socket");
1099 }
1100
1101 if (!babstract)
1102 unlink(un.sun_path);
1103
1104 return NULL;
1105}
1106
55529a26
MY
1107static void *make_unix_in_new_netns(const struct factory *factory, struct fdesc fdescs[],
1108 int argc, char ** argv)
1109{
1110 struct arg type = decode_arg("type", factory->params, argc, argv);
1111 const char *stype = ARG_STRING(type);
1112
1113 struct arg path = decode_arg("path", factory->params, argc, argv);
1114 const char *spath = ARG_STRING(path);
1115
1116 struct arg abstract = decode_arg("abstract", factory->params, argc, argv);
1117 bool babstract = ARG_BOOLEAN(abstract);
1118
1119 int typesym;
1120 const char *typestr;
1121
1122 struct sockaddr_un un;
1123 size_t un_len = sizeof(un);
1124
1125 int self_netns, tmp_netns, sd;
1126
1127 if (strcmp(stype, "stream") == 0) {
1128 typesym = SOCK_STREAM;
1129 typestr = "STREAM";
1130 } else if (strcmp(stype, "seqpacket") == 0) {
1131 typesym = SOCK_SEQPACKET;
1132 typestr = "SEQPACKET";
1133 } else if (strcmp(stype, "dgram") == 0) {
1134 typesym = SOCK_DGRAM;
1135 typestr = "DGRAM";
1136 } else {
1137 free_arg(&abstract);
1138 free_arg(&path);
1139 free_arg(&type);
1140 errx(EXIT_FAILURE, _("unknown unix socket type: %s"), stype);
1141 }
1142
1143 memset(&un, 0, sizeof(un));
1144 un.sun_family = AF_UNIX;
1145 if (babstract) {
1146 strncpy(un.sun_path + 1, spath, sizeof(un.sun_path) - 1 - 1);
1147 size_t pathlen = strlen(spath);
1148 if (sizeof(un.sun_path) - 1 > pathlen)
1149 un_len = sizeof(un) - sizeof(un.sun_path) + 1 + pathlen;
1150 } else
1151 strncpy(un.sun_path, spath, sizeof(un.sun_path) - 1 );
1152
1153 free_arg(&abstract);
1154 free_arg(&path);
1155 free_arg(&type);
1156
1157 self_netns = open("/proc/self/ns/net", O_RDONLY);
1158 if (self_netns < 0)
1159 err(EXIT_FAILURE, _("failed to open /proc/self/ns/net"));
1160 if (self_netns != fdescs[0].fd) {
1161 if (dup2(self_netns, fdescs[0].fd) < 0) {
1162 int e = errno;
1163 close(self_netns);
1164 errno = e;
1165 err(EXIT_FAILURE, "failed to dup %d -> %d", self_netns, fdescs[0].fd);
1166 }
1167 close(self_netns);
1168 self_netns = fdescs[0].fd;
1169 }
1170
1171 fdescs[0] = (struct fdesc){
1172 .fd = fdescs[0].fd,
1173 .close = close_fdesc,
1174 .data = NULL,
1175 };
1176
1177 if (unshare(CLONE_NEWNET) < 0) {
1178 int e = errno;
1179 close_fdesc(self_netns, NULL);
1180 errno = e;
4203b76b
MY
1181 err((errno == EPERM? EXIT_EPERM: EXIT_FAILURE),
1182 "failed in unshare");
55529a26
MY
1183 }
1184
1185 tmp_netns = open("/proc/self/ns/net", O_RDONLY);
1186 if (tmp_netns < 0) {
1187 int e = errno;
1188 close_fdesc(self_netns, NULL);
1189 errno = e;
1190 err(EXIT_FAILURE, _("failed to open /proc/self/ns/net for the new netns"));
1191 }
1192 if (tmp_netns != fdescs[1].fd) {
1193 if (dup2(tmp_netns, fdescs[1].fd) < 0) {
1194 int e = errno;
1195 close_fdesc(self_netns, NULL);
1196 close(tmp_netns);
1197 errno = e;
1198 err(EXIT_FAILURE, "failed to dup %d -> %d", tmp_netns, fdescs[1].fd);
1199 }
1200 close(tmp_netns);
1201 tmp_netns = fdescs[1].fd;
1202 }
1203
1204 fdescs[1] = (struct fdesc){
1205 .fd = fdescs[1].fd,
1206 .close = close_fdesc,
1207 .data = NULL,
1208 };
1209
1210 sd = socket(AF_UNIX, typesym, 0);
1211 if (sd < 0) {
1212 int e = errno;
1213 close_fdesc(self_netns, NULL);
1214 close_fdesc(tmp_netns, NULL);
1215 errno = e;
1216 err(EXIT_FAILURE,
1217 _("failed to make a socket with AF_UNIX + SOCK_%s"),
1218 typestr);
1219 }
1220
1221 if (sd != fdescs[2].fd) {
1222 if (dup2(sd, fdescs[2].fd) < 0) {
1223 int e = errno;
1224 close_fdesc(self_netns, NULL);
1225 close_fdesc(tmp_netns, NULL);
1226 close(sd);
1227 errno = e;
1228 err(EXIT_FAILURE, "failed to dup %d -> %d", sd, fdescs[2].fd);
1229 }
1230 close(sd);
1231 sd = fdescs[2].fd;
1232 }
1233
1234 fdescs[2] = (struct fdesc){
1235 .fd = fdescs[2].fd,
1236 .close = close_unix_socket,
1237 .data = NULL,
1238 };
1239
1240 if (!babstract)
1241 unlink(un.sun_path);
1242 if (bind(sd, (const struct sockaddr *)&un, un_len) < 0) {
1243 int e = errno;
1244 close_fdesc(self_netns, NULL);
1245 close_fdesc(tmp_netns, NULL);
1246 close_unix_socket(sd, NULL);
1247 errno = e;
1248 err(EXIT_FAILURE, "failed to bind a socket");
1249 }
1250
1251 if (!babstract)
1252 fdescs[2].data = xstrdup(un.sun_path);
1253
1254 if (typesym != SOCK_DGRAM) {
1255 if (listen(sd, 1) < 0) {
1256 int e = errno;
1257 close_fdesc(self_netns, NULL);
1258 close_fdesc(tmp_netns, NULL);
1259 close_unix_socket(sd, fdescs[2].data);
1260 errno = e;
1261 err(EXIT_FAILURE, "failed to listen a socket");
1262 }
1263 }
1264
1265 if (setns(self_netns, CLONE_NEWNET) < 0) {
1266 int e = errno;
1267 close_fdesc(self_netns, NULL);
1268 close_fdesc(tmp_netns, NULL);
1269 close_unix_socket(sd, fdescs[2].data);
1270 errno = e;
1271 err(EXIT_FAILURE, "failed to swich back to the original net namespace");
1272 }
1273
1274 return NULL;
1275}
1276
ca517a11
MY
1277static void *make_tcp_common(const struct factory *factory, struct fdesc fdescs[],
1278 int argc, char ** argv,
1279 int family,
1280 void (*init_addr)(struct sockaddr *, unsigned short),
1281 size_t addr_size,
1282 struct sockaddr * sin, struct sockaddr * cin)
20d97930
MY
1283{
1284 struct arg server_port = decode_arg("server-port", factory->params, argc, argv);
1285 unsigned short iserver_port = (unsigned short)ARG_INTEGER(server_port);
1286 struct arg client_port = decode_arg("client-port", factory->params, argc, argv);
1287 unsigned short iclient_port = (unsigned short)ARG_INTEGER(client_port);
1288
20d97930
MY
1289 int ssd, csd, asd;
1290
1291 const int y = 1;
1292
1293 free_arg(&server_port);
1294 free_arg(&client_port);
1295
ca517a11 1296 ssd = socket(family, SOCK_STREAM, 0);
20d97930
MY
1297 if (ssd < 0)
1298 err(EXIT_FAILURE,
1299 _("failed to make a tcp socket for listening"));
1300
1301 if (setsockopt(ssd, SOL_SOCKET,
1302 SO_REUSEADDR, (const char *)&y, sizeof(y)) < 0) {
1303 int e = errno;
1304 close(ssd);
1305 errno = e;
1306 err(EXIT_FAILURE, "failed to setsockopt(SO_REUSEADDR)");
1307 }
1308
1309 if (ssd != fdescs[0].fd) {
1310 if (dup2(ssd, fdescs[0].fd) < 0) {
1311 int e = errno;
1312 close(ssd);
1313 errno = e;
1314 err(EXIT_FAILURE, "failed to dup %d -> %d", ssd, fdescs[0].fd);
1315 }
1316 close(ssd);
1317 ssd = fdescs[0].fd;
1318 }
1319
ca517a11
MY
1320 init_addr(sin, iserver_port);
1321 if (bind(ssd, sin, addr_size) < 0) {
20d97930
MY
1322 int e = errno;
1323 close(ssd);
1324 errno = e;
1325 err(EXIT_FAILURE, "failed to bind a listening socket");
1326 }
1327
1328 if (listen(ssd, 1) < 0) {
1329 int e = errno;
1330 close(ssd);
1331 errno = e;
1332 err(EXIT_FAILURE, "failed to listen a socket");
1333 }
1334
ca517a11 1335 csd = socket(family, SOCK_STREAM, 0);
20d97930
MY
1336 if (csd < 0) {
1337 int e = errno;
1338 close(ssd);
1339 errno = e;
1340 err(EXIT_FAILURE,
1341 _("failed to make a tcp client socket"));
1342 }
1343
1344 if (setsockopt(csd, SOL_SOCKET,
1345 SO_REUSEADDR, (const char *)&y, sizeof(y)) < 0) {
1346 int e = errno;
1347 close(ssd);
1348 close(csd);
1349 errno = e;
1350 err(EXIT_FAILURE, "failed to setsockopt(SO_REUSEADDR)");
1351 }
1352
1353 if (csd != fdescs[1].fd) {
1354 if (dup2(csd, fdescs[1].fd) < 0) {
1355 int e = errno;
1356 close(ssd);
1357 close(csd);
1358 errno = e;
1359 err(EXIT_FAILURE, "failed to dup %d -> %d", csd, fdescs[1].fd);
1360 }
1361 close(csd);
1362 csd = fdescs[1].fd;
1363 }
1364
ca517a11
MY
1365 init_addr(cin, iclient_port);
1366 if (bind(csd, cin, addr_size) < 0) {
20d97930
MY
1367 int e = errno;
1368 close(ssd);
1369 close(csd);
1370 errno = e;
1371 err(EXIT_FAILURE, "failed to bind a client socket");
1372 }
1373
ca517a11 1374 if (connect(csd, sin, addr_size) < 0) {
20d97930
MY
1375 int e = errno;
1376 close(ssd);
1377 close(csd);
1378 errno = e;
d8c6094f 1379 err(EXIT_FAILURE, "failed to connect a client socket to the server socket");
20d97930
MY
1380 }
1381
1382 asd = accept(ssd, NULL, NULL);
1383 if (asd < 0) {
1384 int e = errno;
1385 close(ssd);
1386 close(csd);
1387 errno = e;
1388 err(EXIT_FAILURE, "failed to accept a socket from the listening socket");
1389 }
1390 if (asd != fdescs[2].fd) {
1391 if (dup2(asd, fdescs[2].fd) < 0) {
1392 int e = errno;
1393 close(ssd);
1394 close(csd);
1395 errno = e;
1396 err(EXIT_FAILURE, "failed to dup %d -> %d", asd, fdescs[2].fd);
1397 }
1398 close(asd);
1399 asd = fdescs[2].fd;
1400 }
1401
1402 fdescs[0] = (struct fdesc) {
1403 .fd = fdescs[0].fd,
1404 .close = close_fdesc,
1405 .data = NULL,
1406 };
1407 fdescs[1] = (struct fdesc) {
1408 .fd = fdescs[1].fd,
1409 .close = close_fdesc,
1410 .data = NULL,
1411 };
1412 fdescs[2] = (struct fdesc) {
1413 .fd = fdescs[2].fd,
1414 .close = close_fdesc,
1415 .data = NULL,
1416 };
1417
1418 return NULL;
1419}
1420
ca517a11
MY
1421static void tcp_init_addr(struct sockaddr *addr, unsigned short port)
1422{
1423 struct sockaddr_in *in = (struct sockaddr_in *)addr;
1424 memset(in, 0, sizeof(*in));
1425 in->sin_family = AF_INET;
1426 in->sin_port = htons(port);
1427 in->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
1428}
1429
1430static void *make_tcp(const struct factory *factory, struct fdesc fdescs[],
1431 int argc, char ** argv)
1432{
1433 struct sockaddr_in sin, cin;
1434 return make_tcp_common(factory, fdescs, argc, argv,
1435 AF_INET,
1436 tcp_init_addr, sizeof(sin),
1437 (struct sockaddr *)&sin, (struct sockaddr *)&cin);
1438}
1439
e568c794
MY
1440static void *make_udp_common(const struct factory *factory, struct fdesc fdescs[],
1441 int argc, char ** argv,
1442 int family,
1443 void (*init_addr)(struct sockaddr *, unsigned short),
1444 size_t addr_size,
1445 struct sockaddr * sin, struct sockaddr * cin)
abefebd7 1446{
6c9e370e
MY
1447 struct arg lite = decode_arg("lite", factory->params, argc, argv);
1448 bool blite = ARG_BOOLEAN(lite);
1449
abefebd7
MY
1450 struct arg server_port = decode_arg("server-port", factory->params, argc, argv);
1451 unsigned short iserver_port = (unsigned short)ARG_INTEGER(server_port);
1452 struct arg client_port = decode_arg("client-port", factory->params, argc, argv);
1453 unsigned short iclient_port = (unsigned short)ARG_INTEGER(client_port);
1454
1455 struct arg server_do_bind = decode_arg("server-do-bind", factory->params, argc, argv);
1456 bool bserver_do_bind = ARG_BOOLEAN(server_do_bind);
1457 struct arg client_do_bind = decode_arg("client-do-bind", factory->params, argc, argv);
1458 bool bclient_do_bind = ARG_BOOLEAN(client_do_bind);
1459 struct arg client_do_connect = decode_arg("client-do-connect", factory->params, argc, argv);
1460 bool bclient_do_connect = ARG_BOOLEAN(client_do_connect);
1461
abefebd7
MY
1462 int ssd, csd;
1463
1464 const int y = 1;
1465
1466 free_arg(&client_do_connect);
1467 free_arg(&client_do_bind);
1468 free_arg(&server_do_bind);
1469 free_arg(&server_port);
1470 free_arg(&client_port);
6c9e370e 1471 free_arg(&lite);
abefebd7 1472
6c9e370e 1473 ssd = socket(family, SOCK_DGRAM, blite? IPPROTO_UDPLITE: 0);
abefebd7
MY
1474 if (ssd < 0)
1475 err(EXIT_FAILURE,
1476 _("failed to make a udp socket for server"));
1477
1478 if (setsockopt(ssd, SOL_SOCKET,
1479 SO_REUSEADDR, (const char *)&y, sizeof(y)) < 0) {
1480 int e = errno;
1481 close(ssd);
1482 errno = e;
1483 err(EXIT_FAILURE, "failed to setsockopt(SO_REUSEADDR)");
1484 }
1485
1486 if (ssd != fdescs[0].fd) {
1487 if (dup2(ssd, fdescs[0].fd) < 0) {
1488 int e = errno;
1489 close(ssd);
1490 errno = e;
1491 err(EXIT_FAILURE, "failed to dup %d -> %d", ssd, fdescs[0].fd);
1492 }
1493 close(ssd);
1494 ssd = fdescs[0].fd;
1495 }
1496
e568c794 1497 init_addr(sin, iserver_port);
abefebd7 1498 if (bserver_do_bind) {
e568c794 1499 if (bind(ssd, sin, addr_size) < 0) {
abefebd7
MY
1500 int e = errno;
1501 close(ssd);
1502 errno = e;
d8c6094f 1503 err(EXIT_FAILURE, "failed to bind a server socket");
abefebd7
MY
1504 }
1505 }
1506
6c9e370e 1507 csd = socket(family, SOCK_DGRAM, blite? IPPROTO_UDPLITE: 0);
abefebd7
MY
1508 if (csd < 0) {
1509 int e = errno;
1510 close(ssd);
1511 errno = e;
1512 err(EXIT_FAILURE,
1513 _("failed to make a udp client socket"));
1514 }
1515
1516 if (setsockopt(csd, SOL_SOCKET,
1517 SO_REUSEADDR, (const char *)&y, sizeof(y)) < 0) {
1518 int e = errno;
1519 close(ssd);
1520 close(csd);
1521 errno = e;
1522 err(EXIT_FAILURE, "failed to setsockopt(SO_REUSEADDR)");
1523 }
1524
1525 if (csd != fdescs[1].fd) {
1526 if (dup2(csd, fdescs[1].fd) < 0) {
1527 int e = errno;
1528 close(ssd);
1529 close(csd);
1530 errno = e;
1531 err(EXIT_FAILURE, "failed to dup %d -> %d", csd, fdescs[1].fd);
1532 }
1533 close(csd);
1534 csd = fdescs[1].fd;
1535 }
1536
1537 if (bclient_do_bind) {
e568c794
MY
1538 init_addr(cin, iclient_port);
1539 if (bind(csd, cin, addr_size) < 0) {
abefebd7
MY
1540 int e = errno;
1541 close(ssd);
1542 close(csd);
1543 errno = e;
1544 err(EXIT_FAILURE, "failed to bind a client socket");
1545 }
1546 }
1547
1548 if (bclient_do_connect) {
e568c794 1549 if (connect(csd, sin, addr_size) < 0) {
abefebd7
MY
1550 int e = errno;
1551 close(ssd);
1552 close(csd);
1553 errno = e;
d8c6094f 1554 err(EXIT_FAILURE, "failed to connect a client socket to the server socket");
abefebd7
MY
1555 }
1556 }
1557
1558 fdescs[0] = (struct fdesc) {
1559 .fd = fdescs[0].fd,
1560 .close = close_fdesc,
1561 .data = NULL,
1562 };
1563 fdescs[1] = (struct fdesc) {
1564 .fd = fdescs[1].fd,
1565 .close = close_fdesc,
1566 .data = NULL,
1567 };
1568
1569 return NULL;
1570}
1571
e568c794
MY
1572static void *make_udp(const struct factory *factory, struct fdesc fdescs[],
1573 int argc, char ** argv)
1574{
1575 struct sockaddr_in sin, cin;
1576 return make_udp_common(factory, fdescs, argc, argv,
1577 AF_INET,
1578 tcp_init_addr, sizeof(sin),
1579 (struct sockaddr *)&sin, (struct sockaddr *)&cin);
1580}
1581
0e07fc2d
MY
1582static void *make_raw_common(const struct factory *factory, struct fdesc fdescs[],
1583 int argc, char ** argv,
1584 int family,
1585 void (*init_addr)(struct sockaddr *, bool),
1586 size_t addr_size,
1587 struct sockaddr * sin)
0188afb3
MY
1588{
1589 struct arg protocol = decode_arg("protocol", factory->params, argc, argv);
1590 int iprotocol = ARG_INTEGER(protocol);
1591 int ssd;
0188afb3
MY
1592
1593 free_arg(&protocol);
1594
0e07fc2d 1595 ssd = socket(family, SOCK_RAW, iprotocol);
0188afb3
MY
1596 if (ssd < 0)
1597 err(EXIT_FAILURE,
1598 _("failed to make a udp socket for server"));
1599
1600 if (ssd != fdescs[0].fd) {
1601 if (dup2(ssd, fdescs[0].fd) < 0) {
1602 int e = errno;
1603 close(ssd);
1604 errno = e;
1605 err(EXIT_FAILURE, "failed to dup %d -> %d", ssd, fdescs[0].fd);
1606 }
1607 close(ssd);
1608 ssd = fdescs[0].fd;
1609 }
1610
0e07fc2d
MY
1611 init_addr(sin, false);
1612 if (bind(ssd, sin, addr_size) < 0) {
0188afb3
MY
1613 int e = errno;
1614 close(ssd);
1615 errno = e;
1616 err(EXIT_FAILURE, "failed in bind(2)");
1617 }
1618
0e07fc2d
MY
1619 init_addr(sin, true);
1620 if (connect(ssd, sin, addr_size) < 0) {
0188afb3
MY
1621 int e = errno;
1622 close(ssd);
1623 errno = e;
1624 err(EXIT_FAILURE, "failed in connect(2)");
1625 }
1626
1627 fdescs[0] = (struct fdesc) {
1628 .fd = fdescs[0].fd,
1629 .close = close_fdesc,
1630 .data = NULL,
1631 };
1632
1633 return NULL;
1634}
1635
0e07fc2d
MY
1636static void raw_init_addr(struct sockaddr * addr, bool remote_addr)
1637{
1638 struct sockaddr_in *in = (struct sockaddr_in *)addr;
1639 memset(in, 0, sizeof(*in));
1640 in->sin_family = AF_INET;
1641 in->sin_addr.s_addr = htonl(INADDR_LOOPBACK + (remote_addr? 1: 0));
1642}
1643
1644static void *make_raw(const struct factory *factory, struct fdesc fdescs[],
1645 int argc, char ** argv)
1646{
1647 struct sockaddr_in sin;
1648 return make_raw_common(factory, fdescs, argc, argv,
1649 AF_INET,
1650 raw_init_addr, sizeof(sin),
1651 (struct sockaddr *)&sin);
1652}
1653
00a97611
MY
1654static void *make_ping_common(const struct factory *factory, struct fdesc fdescs[],
1655 int argc, char ** argv,
1656 int family, int protocol,
1657 void (*init_addr)(struct sockaddr *, unsigned short),
1658 size_t addr_size,
1659 struct sockaddr *sin)
1660{
1661 struct arg connect_ = decode_arg("connect", factory->params, argc, argv);
1662 bool bconnect = ARG_BOOLEAN(connect_);
1663
1664 struct arg bind_ = decode_arg("bind", factory->params, argc, argv);
1665 bool bbind = ARG_BOOLEAN(bind_);
1666
1667 struct arg id = decode_arg("id", factory->params, argc, argv);
1668 unsigned short iid = (unsigned short)ARG_INTEGER(id);
1669
1670 int sd;
1671
1672 free_arg(&id);
1673 free_arg(&bind_);
1674 free_arg(&connect_);
1675
1676 sd = socket(family, SOCK_DGRAM, protocol);
1677 if (sd < 0)
1678 err(EXIT_FAILURE,
1679 _("failed to make an icmp socket"));
1680
1681 if (sd != fdescs[0].fd) {
1682 if (dup2(sd, fdescs[0].fd) < 0) {
1683 int e = errno;
1684 close(sd);
1685 errno = e;
1686 err(EXIT_FAILURE, "failed to dup %d -> %d", sd, fdescs[0].fd);
1687 }
1688 close(sd);
1689 sd = fdescs[0].fd;
1690 }
1691
1692 if (bbind) {
1693 init_addr(sin, iid);
1694 if (bind(sd, sin, addr_size) < 0) {
1695 int e = errno;
1696 close(sd);
1697 errno = e;
1698 err(EXIT_FAILURE, "failed in bind(2)");
1699 }
1700 }
1701
1702 if (bconnect) {
1703 init_addr(sin, 0);
1704 if (connect(sd, sin, addr_size) < 0) {
1705 int e = errno;
1706 close(sd);
1707 errno = e;
1708 err(EXIT_FAILURE, "failed in connect(2)");
1709 }
1710 }
1711
1712 fdescs[0] = (struct fdesc) {
1713 .fd = fdescs[0].fd,
1714 .close = close_fdesc,
1715 .data = NULL,
1716 };
1717
1718 return NULL;
1719}
1720
1721static void ping_init_addr(struct sockaddr *addr, unsigned short id)
1722{
1723 struct sockaddr_in *in = (struct sockaddr_in *)addr;
1724 memset(in, 0, sizeof(*in));
1725 in->sin_family = AF_INET;
1726 in->sin_port = htons(id);
1727 in->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
1728}
1729
1730static void *make_ping(const struct factory *factory, struct fdesc fdescs[],
1731 int argc, char ** argv)
1732{
1733 struct sockaddr_in in;
1734 return make_ping_common(factory, fdescs, argc, argv,
1735 AF_INET, IPPROTO_ICMP,
1736 ping_init_addr,
1737 sizeof(in),
1738 (struct sockaddr *)&in);
1739}
1740
ca517a11
MY
1741static void tcp6_init_addr(struct sockaddr *addr, unsigned short port)
1742{
1743 struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)addr;
1744 memset(in6, 0, sizeof(*in6));
1745 in6->sin6_family = AF_INET6;
1746 in6->sin6_flowinfo = 0;
1747 in6->sin6_port = htons(port);
1748 in6->sin6_addr = in6addr_loopback;
1749}
1750
1751static void *make_tcp6(const struct factory *factory, struct fdesc fdescs[],
1752 int argc, char ** argv)
1753{
1754 struct sockaddr_in6 sin, cin;
1755 return make_tcp_common(factory, fdescs, argc, argv,
1756 AF_INET6,
1757 tcp6_init_addr, sizeof(sin),
1758 (struct sockaddr *)&sin, (struct sockaddr *)&cin);
1759}
1760
e568c794
MY
1761static void *make_udp6(const struct factory *factory, struct fdesc fdescs[],
1762 int argc, char ** argv)
1763{
1764 struct sockaddr_in6 sin, cin;
1765 return make_udp_common(factory, fdescs, argc, argv,
1766 AF_INET6,
1767 tcp6_init_addr, sizeof(sin),
1768 (struct sockaddr *)&sin, (struct sockaddr *)&cin);
1769}
1770
0e07fc2d
MY
1771static void raw6_init_addr(struct sockaddr *addr, bool remote_addr)
1772{
1773 struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)addr;
1774 memset(in6, 0, sizeof(*in6));
1775 in6->sin6_family = AF_INET6;
1776 in6->sin6_flowinfo = 0;
1777
1778 if (remote_addr) {
1779 /* ::ffff:127.0.0.1 */
1780 in6->sin6_addr.s6_addr16[5] = 0xffff;
1781 in6->sin6_addr.s6_addr32[3] = htonl(INADDR_LOOPBACK);
1782 } else
1783 in6->sin6_addr = in6addr_loopback;
1784}
1785
1786static void *make_raw6(const struct factory *factory, struct fdesc fdescs[],
1787 int argc, char ** argv)
1788{
1789 struct sockaddr_in6 sin;
1790 return make_raw_common(factory, fdescs, argc, argv,
1791 AF_INET6,
1792 raw6_init_addr, sizeof(sin),
1793 (struct sockaddr *)&sin);
1794}
1795
00a97611
MY
1796static void ping6_init_addr(struct sockaddr *addr, unsigned short id)
1797{
1798 struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)addr;
1799 memset(in6, 0, sizeof(*in6));
1800 in6->sin6_family = AF_INET6;
1801 in6->sin6_port = htons(id);
1802 in6->sin6_addr = in6addr_loopback;
1803}
1804
1805static void *make_ping6(const struct factory *factory, struct fdesc fdescs[],
1806 int argc, char ** argv)
1807{
1808 struct sockaddr_in6 in6;
1809 return make_ping_common(factory, fdescs, argc, argv,
1810 AF_INET6, IPPROTO_ICMPV6,
1811 ping6_init_addr,
1812 sizeof(in6),
1813 (struct sockaddr *)&in6);
1814}
0e07fc2d 1815
c5eb81b3
MY
1816static void *make_netns(const struct factory *factory _U_, struct fdesc fdescs[],
1817 int argc _U_, char ** argv _U_)
1818{
1819 int sd = socket(AF_UNIX, SOCK_STREAM, 0);
1820 if (sd < 0)
1821 err(EXIT_FAILURE, "failed in socket()");
1822
1823 int ns = ioctl(sd, SIOCGSKNS);
1824 if (ns < 0)
7b061401
MY
1825 err((errno == ENOSYS? EXIT_ENOSYS: EXIT_FAILURE),
1826 "failed in ioctl(SIOCGSKNS)");
c5eb81b3
MY
1827 close(sd);
1828
1829 if (ns != fdescs[0].fd) {
1830 if (dup2(ns, fdescs[0].fd) < 0) {
1831 int e = errno;
1832 close(ns);
1833 errno = e;
1834 err(EXIT_FAILURE, "failed to dup %d -> %d", ns, fdescs[0].fd);
1835 }
1836 close(ns);
1837 }
1838
1839 fdescs[0] = (struct fdesc){
1840 .fd = fdescs[0].fd,
1841 .close = close_fdesc,
1842 .data = NULL
1843 };
1844
1845 return NULL;
1846}
1847
4763f067
MY
1848static void *make_netlink(const struct factory *factory, struct fdesc fdescs[],
1849 int argc, char ** argv)
1850{
1851 struct arg protocol = decode_arg("protocol", factory->params, argc, argv);
1852 int iprotocol = ARG_INTEGER(protocol);
1853 struct arg groups = decode_arg("groups", factory->params, argc, argv);
1854 unsigned int ugroups = ARG_UINTEGER(groups);
1855 int sd;
1856
1857 free_arg(&protocol);
1858
1859 sd = socket(AF_NETLINK, SOCK_RAW, iprotocol);
1860 if (sd < 0)
1861 err((errno == EPROTONOSUPPORT)? EXIT_EPROTONOSUPPORT: EXIT_FAILURE,
1862 "failed in socket()");
1863
1864 if (sd != fdescs[0].fd) {
1865 if (dup2(sd, fdescs[0].fd) < 0) {
1866 int e = errno;
1867 close(sd);
1868 errno = e;
1869 err(EXIT_FAILURE, "failed to dup %d -> %d", sd, fdescs[0].fd);
1870 }
1871 close(sd);
1872 }
1873
1874 struct sockaddr_nl nl;
1875 memset(&nl, 0, sizeof(nl));
1876 nl.nl_family = AF_NETLINK;
1877 nl.nl_groups = ugroups;
1878 if (bind(sd, (struct sockaddr*)&nl, sizeof(nl)) < 0) {
1879 int e = errno;
1880 close(sd);
1881 errno = e;
1882 err(EXIT_FAILURE, "failed in bind(2)");
1883 }
1884
1885 fdescs[0] = (struct fdesc){
1886 .fd = fdescs[0].fd,
1887 .close = close_fdesc,
1888 .data = NULL
1889 };
1890
1891 return NULL;
1892}
1893
ae9ff1cd 1894#define PARAM_END { .name = NULL, }
9557ed44 1895static const struct factory factories[] = {
fd81d3a5
MY
1896 {
1897 .name = "ro-regular-file",
ae9ff1cd 1898 .desc = "read-only regular file",
fd81d3a5
MY
1899 .priv = false,
1900 .N = 1,
d14e9efd 1901 .EX_N = 0,
fd81d3a5 1902 .make = open_ro_regular_file,
ae9ff1cd
MY
1903 .params = (struct parameter []) {
1904 {
1905 .name = "file",
1906 .type = PTYPE_STRING,
1907 .desc = "file to be opened",
1908 .defv.string = "/etc/passwd",
1909 },
6d201bcb
MY
1910 {
1911 .name = "offset",
1912 .type = PTYPE_INTEGER,
1913 .desc = "seek bytes after open with SEEK_CUR",
1914 .defv.integer = 0,
1915 },
ae9ff1cd
MY
1916 PARAM_END
1917 },
fd81d3a5
MY
1918 },
1919 {
1920 .name = "pipe-no-fork",
1921 .desc = "making pair of fds with pipe(2)",
1922 .priv = false,
1923 .N = 2,
9aa68bf4 1924 .EX_N = 2,
fd81d3a5 1925 .make = make_pipe,
d85c66d8
MY
1926 .params = (struct parameter []) {
1927 {
1928 .name = "nonblock",
1929 .type = PTYPE_STRING,
1930 .desc = "set nonblock flag (\"--\", \"r-\", \"-w\", or \"rw\")",
1931 .defv.string = "--",
1932 },
9aa68bf4
MY
1933 {
1934 .name = "rdup",
1935 .type = PTYPE_INTEGER,
1936 .desc = "file descriptor for duplicating the pipe input",
1937 .defv.integer = -1,
1938 },
1939 {
1940 .name = "wdup",
1941 .type = PTYPE_INTEGER,
1942 .desc = "file descriptor for duplicating the pipe output",
1943 .defv.integer = -1,
1944 },
d85c66d8
MY
1945 PARAM_END
1946 },
fd81d3a5 1947 },
77c70070
MY
1948 {
1949 .name = "directory",
8c670091 1950 .desc = "directory",
77c70070
MY
1951 .priv = false,
1952 .N = 1,
d14e9efd 1953 .EX_N = 0,
8c670091
MY
1954 .make = open_directory,
1955 .params = (struct parameter []) {
1956 {
1957 .name = "dir",
1958 .type = PTYPE_STRING,
1959 .desc = "directory to be opened",
1960 .defv.string = "/",
1961 },
5c265705
MY
1962 {
1963 .name = "dentries",
1964 .type = PTYPE_INTEGER,
1965 .desc = "read the number of dentries after open with readdir(3)",
1966 .defv.integer = 0,
1967 },
8c670091
MY
1968 PARAM_END
1969 },
77c70070 1970 },
7c06963e
MY
1971 {
1972 .name = "rw-character-device",
1973 .desc = "character device with O_RDWR flag",
1974 .priv = false,
1975 .N = 1,
d14e9efd 1976 .EX_N = 0,
7c06963e
MY
1977 .make = open_rw_chrdev,
1978 .params = (struct parameter []) {
1979 {
1980 .name = "chrdev",
1981 .type = PTYPE_STRING,
1982 .desc = "character device node to be opened",
1983 .defv.string = "/dev/zero",
1984 },
1985 PARAM_END
1986 },
1987 },
d827da23
MY
1988 {
1989 .name = "socketpair",
1990 .desc = "AF_UNIX socket pair created with socketpair(2)",
1991 .priv = false,
1992 .N = 2,
d14e9efd 1993 .EX_N = 0,
d827da23
MY
1994 .make = make_socketpair,
1995 .params = (struct parameter []) {
1996 {
1997 .name = "socktype",
1998 .type = PTYPE_STRING,
1999 .desc = "STREAM, DGRAM, or SEQPACKET",
2000 .defv.string = "STREAM",
2001 },
2002 PARAM_END
2003 },
2004 },
21c70c29
MY
2005 {
2006 .name = "symlink",
2007 .desc = "symbolic link itself opened with O_PATH",
2008 .priv = false,
2009 .N = 1,
d14e9efd 2010 .EX_N = 0,
21c70c29
MY
2011 .make = open_with_opath,
2012 .params = (struct parameter []) {
2013 {
2014 .name = "path",
2015 .type = PTYPE_STRING,
2016 .desc = "path to a symbolic link",
2017 .defv.string = "/dev/stdin",
2018 },
2019 PARAM_END
2020 },
2021 },
cb16b318
MY
2022 {
2023 .name = "ro-block-device",
2024 .desc = "block device with O_RDONLY flag",
2025 .priv = true,
2026 .N = 1,
d14e9efd 2027 .EX_N = 0,
cb16b318
MY
2028 .make = open_ro_blkdev,
2029 .params = (struct parameter []) {
2030 {
2031 .name = "blkdev",
2032 .type = PTYPE_STRING,
2033 .desc = "block device node to be opened",
2034 .defv.string = "/dev/nullb0",
2035 },
2036 PARAM_END
2037 },
2038 },
bce240a7
MY
2039 {
2040 .name = "mapped-packet-socket",
2041 .desc = "mmap'ed AF_PACKET socket",
2042 .priv = true,
2043 .N = 1,
2044 .EX_N = 0,
bce240a7
MY
2045 .make = make_mmapped_packet_socket,
2046 .params = (struct parameter []) {
2047 {
2048 .name = "socktype",
2049 .type = PTYPE_STRING,
2050 .desc = "DGRAM or RAW",
2051 .defv.string = "RAW",
2052 },
2053 {
2054 .name = "interface",
2055 .type = PTYPE_STRING,
2056 .desc = "a name of network interface like eth0 or lo",
2057 .defv.string = "lo",
2058 },
2059 PARAM_END
2060 },
2061 },
ba8c749f 2062 {
ba8c749f
MY
2063 .name = "pidfd",
2064 .desc = "pidfd returned from pidfd_open(2)",
2065 .priv = false,
2066 .N = 1,
2067 .EX_N = 0,
ba8c749f
MY
2068 .make = make_pidfd,
2069 .params = (struct parameter []) {
2070 {
2071 .name = "target-pid",
2072 .type = PTYPE_INTEGER,
2073 .desc = "the pid of the target process",
2074 .defv.integer = 1,
2075 },
2076 PARAM_END
2077 },
2078 },
4d51adc5
MY
2079 {
2080 .name = "inotify",
2081 .desc = "inotify fd returned from inotify_init(2)",
2082 .priv = false,
2083 .N = 1,
2084 .EX_N = 0,
4d51adc5
MY
2085 .make = make_inotify_fd,
2086 .params = (struct parameter []) {
2087 PARAM_END
2088 },
2089 },
a234bad9
MY
2090 {
2091 .name = "unix-stream",
2092 .desc = "AF_UNIX+SOCK_STREAM sockets",
2093 .priv = false,
2094 .N = 3,
2095 .EX_N = 0,
2096 .make = make_unix_stream,
2097 .params = (struct parameter []) {
2098 {
2099 .name = "path",
2100 .type = PTYPE_STRING,
2101 .desc = "path for listening-socket bound to",
2102 .defv.string = "/tmp/test_mkfds-unix-stream",
2103 },
2104 {
2105 .name = "backlog",
2106 .type = PTYPE_INTEGER,
2107 .desc = "backlog passed to listen(2)",
2108 .defv.integer = 5,
2109 },
2110 {
2111 .name = "abstract",
2112 .type = PTYPE_BOOLEAN,
2113 .desc = "use PATH as an abstract socket address",
2114 .defv.boolean = false,
2115 },
2116 {
2117 .name = "server-shutdown",
2118 .type = PTYPE_INTEGER,
2119 .desc = "shutdown the accepted socket; 1: R, 2: W, 3: RW",
2120 .defv.integer = 0,
2121 },
2122 {
2123 .name = "client-shutdown",
2124 .type = PTYPE_INTEGER,
2125 .desc = "shutdown the client socket; 1: R, 2: W, 3: RW",
2126 .defv.integer = 0,
2127 },
223063b1
MY
2128 {
2129 .name = "type",
2130 .type = PTYPE_STRING,
2131 .desc = "stream or seqpacket",
2132 .defv.string = "stream",
2133 },
a234bad9
MY
2134 PARAM_END
2135 },
2136 },
2229a4b4
MY
2137 {
2138 .name = "unix-dgram",
2139 .desc = "AF_UNIX+SOCK_DGRAM sockets",
2140 .priv = false,
2141 .N = 2,
2142 .EX_N = 0,
2143 .make = make_unix_dgram,
2144 .params = (struct parameter []) {
2145 {
2146 .name = "path",
2147 .type = PTYPE_STRING,
2148 .desc = "path for unix non-stream bound to",
2149 .defv.string = "/tmp/test_mkfds-unix-dgram",
2150 },
2151 {
2152 .name = "abstract",
2153 .type = PTYPE_BOOLEAN,
2154 .desc = "use PATH as an abstract socket address",
2155 .defv.boolean = false,
2156 },
2157 PARAM_END
2158 },
2159 },
55529a26
MY
2160 {
2161 .name = "unix-in-netns",
2162 .desc = "make a unix socket in a new network namespace",
2163 .priv = true,
2164 .N = 3,
2165 .EX_N = 0,
2166 .make = make_unix_in_new_netns,
2167 .params = (struct parameter []) {
2168 {
2169 .name = "type",
2170 .type = PTYPE_STRING,
2171 .desc = "dgram, stream, or seqpacket",
2172 .defv.string = "stream",
2173 },
2174 {
2175 .name = "path",
2176 .type = PTYPE_STRING,
2177 .desc = "path for unix non-stream bound to",
2178 .defv.string = "/tmp/test_mkfds-unix-in-netns",
2179 },
2180 {
2181 .name = "abstract",
2182 .type = PTYPE_BOOLEAN,
2183 .desc = "use PATH as an abstract socket address",
2184 .defv.boolean = false,
2185 },
2186 PARAM_END
2187 },
2188 },
20d97930
MY
2189 {
2190 .name = "tcp",
2191 .desc = "AF_INET+SOCK_STREAM sockets",
2192 .priv = false,
2193 .N = 3,
2194 .EX_N = 0,
2195 .make = make_tcp,
2196 .params = (struct parameter []) {
2197 {
2198 .name = "server-port",
2199 .type = PTYPE_INTEGER,
2200 .desc = "TCP port the server may listen",
2201 .defv.integer = 12345,
2202 },
2203 {
2204 .name = "client-port",
2205 .type = PTYPE_INTEGER,
2206 .desc = "TCP port the client may bind",
2207 .defv.integer = 23456,
2208 },
2209 PARAM_END
2210 }
2211 },
abefebd7
MY
2212 {
2213 .name = "udp",
2214 .desc = "AF_INET+SOCK_DGRAM sockets",
2215 .priv = false,
2216 .N = 2,
2217 .EX_N = 0,
2218 .make = make_udp,
2219 .params = (struct parameter []) {
6c9e370e
MY
2220 {
2221 .name = "lite",
2222 .type = PTYPE_BOOLEAN,
2223 .desc = "Use UDPLITE instead of UDP",
2224 .defv.boolean = false,
2225 },
abefebd7
MY
2226 {
2227 .name = "server-port",
2228 .type = PTYPE_INTEGER,
8971afef 2229 .desc = "UDP port the server may listen",
abefebd7
MY
2230 .defv.integer = 12345,
2231 },
2232 {
2233 .name = "client-port",
2234 .type = PTYPE_INTEGER,
8971afef 2235 .desc = "UDP port the client may bind",
abefebd7
MY
2236 .defv.integer = 23456,
2237 },
2238 {
2239 .name = "server-do-bind",
2240 .type = PTYPE_BOOLEAN,
2241 .desc = "call bind with the server socket",
2242 .defv.boolean = true,
2243 },
2244 {
2245 .name = "client-do-bind",
2246 .type = PTYPE_BOOLEAN,
2247 .desc = "call bind with the client socket",
2248 .defv.boolean = true,
2249 },
2250 {
2251 .name = "client-do-connect",
2252 .type = PTYPE_BOOLEAN,
2253 .desc = "call connect with the client socket",
2254 .defv.boolean = true,
2255 },
2256 PARAM_END
2257 }
c5eb81b3 2258 },
0188afb3
MY
2259 {
2260 .name = "raw",
2261 .desc = "AF_INET+SOCK_RAW sockets",
2262 .priv = true,
2263 .N = 1,
2264 .EX_N = 0,
2265 .make = make_raw,
2266 .params = (struct parameter []) {
2267 {
2268 .name = "protocol",
2269 .type = PTYPE_INTEGER,
2270 .desc = "protocol passed to socket(AF_INET, SOCK_RAW, protocol)",
2271 .defv.integer = IPPROTO_IPIP,
2272 },
2273 PARAM_END
2274 }
2275
2276 },
00a97611
MY
2277 {
2278 .name = "ping",
2279 .desc = "AF_INET+SOCK_DGRAM+IPPROTO_ICMP sockets",
2280 .priv = false,
2281 .N = 1,
2282 .EX_N = 0,
2283 .make = make_ping,
2284 .params = (struct parameter []) {
2285 {
2286 .name = "connect",
2287 .type = PTYPE_BOOLEAN,
2288 .desc = "call connect(2) with the socket",
2289 .defv.boolean = true,
2290 },
2291 {
2292 .name = "bind",
2293 .type = PTYPE_BOOLEAN,
2294 .desc = "call bind(2) with the socket",
2295 .defv.boolean = true,
2296 },
2297 {
2298 .name = "id",
2299 .type = PTYPE_INTEGER,
2300 .desc = "ICMP echo request id",
2301 .defv.integer = 0,
2302 },
2303 PARAM_END
2304 }
2305 },
ca517a11
MY
2306 {
2307 .name = "tcp6",
2308 .desc = "AF_INET6+SOCK_STREAM sockets",
2309 .priv = false,
2310 .N = 3,
2311 .EX_N = 0,
2312 .make = make_tcp6,
2313 .params = (struct parameter []) {
2314 {
2315 .name = "server-port",
2316 .type = PTYPE_INTEGER,
2317 .desc = "TCP port the server may listen",
2318 .defv.integer = 12345,
2319 },
2320 {
2321 .name = "client-port",
2322 .type = PTYPE_INTEGER,
2323 .desc = "TCP port the client may bind",
2324 .defv.integer = 23456,
2325 },
2326 PARAM_END
2327 }
2328 },
e568c794
MY
2329 {
2330 .name = "udp6",
2331 .desc = "AF_INET6+SOCK_DGRAM sockets",
2332 .priv = false,
2333 .N = 2,
2334 .EX_N = 0,
2335 .make = make_udp6,
2336 .params = (struct parameter []) {
6c9e370e
MY
2337 {
2338 .name = "lite",
2339 .type = PTYPE_BOOLEAN,
2340 .desc = "Use UDPLITE instead of UDP",
2341 .defv.boolean = false,
2342 },
e568c794
MY
2343 {
2344 .name = "server-port",
2345 .type = PTYPE_INTEGER,
2346 .desc = "UDP port the server may listen",
2347 .defv.integer = 12345,
2348 },
2349 {
2350 .name = "client-port",
2351 .type = PTYPE_INTEGER,
2352 .desc = "UDP port the client may bind",
2353 .defv.integer = 23456,
2354 },
2355 {
2356 .name = "server-do-bind",
2357 .type = PTYPE_BOOLEAN,
2358 .desc = "call bind with the server socket",
2359 .defv.boolean = true,
2360 },
2361 {
2362 .name = "client-do-bind",
2363 .type = PTYPE_BOOLEAN,
2364 .desc = "call bind with the client socket",
2365 .defv.boolean = true,
2366 },
2367 {
2368 .name = "client-do-connect",
2369 .type = PTYPE_BOOLEAN,
2370 .desc = "call connect with the client socket",
2371 .defv.boolean = true,
2372 },
2373 PARAM_END
2374 }
2375 },
0e07fc2d
MY
2376 {
2377 .name = "raw6",
2378 .desc = "AF_INET6+SOCK_RAW sockets",
2379 .priv = true,
2380 .N = 1,
2381 .EX_N = 0,
2382 .make = make_raw6,
2383 .params = (struct parameter []) {
2384 {
2385 .name = "protocol",
2386 .type = PTYPE_INTEGER,
2387 .desc = "protocol passed to socket(AF_INET6, SOCK_RAW, protocol)",
2388 .defv.integer = IPPROTO_IPIP,
2389 },
2390 PARAM_END
2391 }
2392
2393 },
00a97611
MY
2394 {
2395 .name = "ping6",
2396 .desc = "AF_INET6+SOCK_DGRAM+IPPROTO_ICMPV6 sockets",
2397 .priv = false,
2398 .N = 1,
2399 .EX_N = 0,
2400 .make = make_ping6,
2401 .params = (struct parameter []) {
2402 {
2403 .name = "connect",
2404 .type = PTYPE_BOOLEAN,
2405 .desc = "call connect(2) with the socket",
2406 .defv.boolean = true,
2407 },
2408 {
2409 .name = "bind",
2410 .type = PTYPE_BOOLEAN,
2411 .desc = "call bind(2) with the socket",
2412 .defv.boolean = true,
2413 },
2414 {
2415 .name = "id",
2416 .type = PTYPE_INTEGER,
2417 .desc = "ICMP echo request id",
2418 .defv.integer = 0,
2419 },
2420 PARAM_END
2421 }
2422 },
c5eb81b3
MY
2423 {
2424 .name = "netns",
2425 .desc = "open a file specifying a netns",
2426 .priv = true,
2427 .N = 1,
2428 .EX_N = 0,
2429 .make = make_netns,
2430 .params = (struct parameter []) {
2431 PARAM_END
2432 }
2433 },
4763f067
MY
2434 {
2435 .name = "netlink",
2436 .desc = "AF_NETLINK sockets",
2437 .priv = false,
2438 .N = 1,
2439 .EX_N = 0,
2440 .make = make_netlink,
2441 .params = (struct parameter []) {
2442 {
2443 .name = "protocol",
2444 .type = PTYPE_INTEGER,
2445 .desc = "protocol passed to socket(AF_NETLINK, SOCK_RAW, protocol)",
2446 .defv.integer = NETLINK_USERSOCK,
2447 },
2448 {
2449 .name = "groups",
2450 .type = PTYPE_UINTEGER,
2451 .desc = "multicast groups of netlink communication (requires CAP_NET_ADMIN)",
2452 .defv.uinteger = 0,
2453 },
2454 PARAM_END
2455 }
2456 },
fd81d3a5
MY
2457};
2458
13b7739a
MY
2459static int count_parameters(const struct factory *factory)
2460{
2461
2462 const struct parameter *p = factory->params;
2463 if (!p)
2464 return 0;
2465 while (p->name)
2466 p++;
2467 return p - factory->params;
2468}
2469
9557ed44 2470static void print_factory(const struct factory *factory)
fd81d3a5 2471{
95ac0ff9 2472 printf("%-20s %4s %5d %6d %s\n",
fd81d3a5
MY
2473 factory->name,
2474 factory->priv? "yes": "no",
2475 factory->N,
13b7739a 2476 count_parameters(factory),
fd81d3a5
MY
2477 factory->desc);
2478}
2479
2480static void list_factories(void)
2481{
95ac0ff9 2482 printf("%-20s PRIV COUNT NPARAM DESCRIPTION\n", "FACTORY");
fd81d3a5
MY
2483 for (size_t i = 0; i < ARRAY_SIZE(factories); i++)
2484 print_factory(factories + i);
2485}
2486
9557ed44 2487static const struct factory *find_factory(const char *name)
fd81d3a5
MY
2488{
2489 for (size_t i = 0; i < ARRAY_SIZE(factories); i++)
2490 if (strcmp(factories[i].name, name) == 0)
2491 return factories + i;
2492 return NULL;
2493}
2494
13b7739a
MY
2495static void list_parameters(const char *factory_name)
2496{
2497 const struct factory *factory = find_factory(factory_name);
2498 const char *fmt = "%-15s %-8s %15s %s\n";
2499
2500 if (!factory)
2501 errx(EXIT_FAILURE, _("no such factory: %s"), factory_name);
2502
2503 if (!factory->params)
2504 return;
2505
2506 printf(fmt, "PARAMETER", "TYPE", "DEFAULT_VALUE", "DESCRIPTION");
2507 for (const struct parameter *p = factory->params; p->name != NULL; p++) {
2508 char *defv = ptype_classes[p->type].sprint(&p->defv);
2509 printf(fmt, p->name, ptype_classes[p->type].name, defv, p->desc);
2510 free(defv);
2511 }
2512}
2513
863a6544
MY
2514static void rename_self(const char *comm)
2515{
2516 if (prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0) < 0)
2517 err(EXIT_FAILURE, _("failed to rename self via prctl: %s"), comm);
2518}
2519
fd81d3a5
MY
2520static void do_nothing(int signum _U_)
2521{
2522}
2523
ba8c749f
MY
2524#ifdef __NR_pidfd_open
2525
2526static int
2527pidfd_open(pid_t pid, unsigned int flags)
2528{
2529 return syscall(__NR_pidfd_open, pid, flags);
2530}
2531#else
2532static int
2533pidfd_open(pid_t pid _U_, unsigned int flags _U_)
2534{
2535 errno = ENOSYS;
2536 return -1;
2537}
2538#endif
2539
caa97697
MY
2540static void wait_event(void)
2541{
2542 fd_set readfds;
2543 sigset_t sigset;
2544 int n = 0;
2545
2546 FD_ZERO(&readfds);
2547 /* Monitor the standard input only when the process
2548 * is in foreground. */
2549 if (tcgetpgrp(STDIN_FILENO) == getpgrp()) {
2550 n = 1;
2551 FD_SET(0, &readfds);
2552 }
2553
2554 sigemptyset(&sigset);
2555
2556 if (pselect(n, &readfds, NULL, NULL, NULL, &sigset) < 0
2557 && errno != EINTR)
2558 errx(EXIT_FAILURE, _("failed in pselect"));
2559}
2560
fd81d3a5
MY
2561int main(int argc, char **argv)
2562{
2563 int c;
9557ed44 2564 const struct factory *factory;
fd81d3a5
MY
2565 struct fdesc fdescs[MAX_N];
2566 bool quiet = false;
2567 bool cont = false;
67aab5a5 2568 void *data;
fd81d3a5 2569
fd81d3a5
MY
2570 static const struct option longopts[] = {
2571 { "list", no_argument, NULL, 'l' },
13b7739a 2572 { "parameters", required_argument, NULL, 'I' },
863a6544 2573 { "comm", required_argument, NULL, 'r' },
fd81d3a5
MY
2574 { "quiet", no_argument, NULL, 'q' },
2575 { "dont-puase", no_argument, NULL, 'c' },
2576 { "help", no_argument, NULL, 'h' },
3e244bab 2577 { NULL, 0, NULL, 0 },
fd81d3a5
MY
2578 };
2579
863a6544 2580 while ((c = getopt_long(argc, argv, "lhqcI:r:", longopts, NULL)) != -1) {
fd81d3a5
MY
2581 switch (c) {
2582 case 'h':
2583 usage(stdout, EXIT_SUCCESS);
2584 case 'l':
2585 list_factories();
2586 exit(EXIT_SUCCESS);
13b7739a
MY
2587 case 'I':
2588 list_parameters(optarg);
2589 exit(EXIT_SUCCESS);
fd81d3a5
MY
2590 case 'q':
2591 quiet = true;
2592 break;
2593 case 'c':
2594 cont = true;
2595 break;
863a6544
MY
2596 case 'r':
2597 rename_self(optarg);
2598 break;
fd81d3a5
MY
2599 default:
2600 usage(stderr, EXIT_FAILURE);
2601 }
2602 }
2603
fd81d3a5
MY
2604 if (optind == argc)
2605 errx(EXIT_FAILURE, _("no file descriptor specification given"));
2606
2607 factory = find_factory(argv[optind]);
2608 if (!factory)
2609 errx(EXIT_FAILURE, _("no such factory: %s"), argv[optind]);
d14e9efd 2610 assert(factory->N + factory->EX_N < MAX_N);
fd81d3a5
MY
2611 optind++;
2612
2613 if ((optind + factory->N) > argc)
2614 errx(EXIT_FAILURE, _("not enough file descriptors given for %s"),
2615 factory->name);
d14e9efd 2616
811ad38e
MY
2617 if (factory->priv && getuid() != 0)
2618 errx(EXIT_FAILURE, "%s factory requires root privilege", factory->name);
2619
01f7300b 2620 for (int i = 0; i < MAX_N; i++) {
d14e9efd 2621 fdescs[i].fd = -1;
01f7300b
MY
2622 fdescs[i].close = NULL;
2623 }
d14e9efd 2624
fd81d3a5
MY
2625 for (int i = 0; i < factory->N; i++) {
2626 char *str = argv[optind + i];
2627 long fd;
adf7af0e 2628 char *ep;
fd81d3a5 2629
adf7af0e
MY
2630 errno = 0;
2631 fd = strtol(str, &ep, 10);
fd81d3a5
MY
2632 if (errno)
2633 err(EXIT_FAILURE, "failed to convert fd number: %s", str);
adf7af0e
MY
2634 if (ep == str)
2635 errx(EXIT_FAILURE, "failed to convert fd number: %s", str);
2636 if (*ep != '\0')
2637 errx(EXIT_FAILURE, _("garbage at the end of number: %s"), str);
fd81d3a5
MY
2638 if (fd < 0)
2639 errx(EXIT_FAILURE, "fd number should not be negative: %s", str);
2640 if (fd < 3)
2641 errx(EXIT_FAILURE, "fd 0, 1, 2 are reserved: %s", str);
2642 fdescs[i].fd = fd;
2643 }
2644 optind += factory->N;
2645
67aab5a5 2646 data = factory->make(factory, fdescs, argc - optind, argv + optind);
fd81d3a5
MY
2647
2648 signal(SIGCONT, do_nothing);
2649
2650 if (!quiet) {
b5eb0333 2651 printf("%d", getpid());
fd81d3a5 2652 putchar('\n');
271bc510
MY
2653 if (factory->report)
2654 factory->report(factory, data, stdout);
fd81d3a5
MY
2655 fflush(stdout);
2656 }
2657
2658 if (!cont)
caa97697 2659 wait_event();
fd81d3a5 2660
d14e9efd 2661 for (int i = 0; i < factory->N + factory->EX_N; i++)
01f7300b 2662 if (fdescs[i].fd >= 0 && fdescs[i].close)
d14e9efd 2663 fdescs[i].close(fdescs[i].fd, fdescs[i].data);
fd81d3a5 2664
67aab5a5
MY
2665 if (factory->free)
2666 factory->free (factory, data);
2667
fd81d3a5
MY
2668 exit(EXIT_SUCCESS);
2669}