// SPDX-FileCopyrightText: 2019 Alyssa Ross // SPDX-License-Identifier: GPL-2.0-only #include #include #include #include #include #include #include #include #include #include #include #include /* * ifr_flags, ifr_name, etc. are macros, so throughout this program * variable names are suffixed with underscores where not doing so * would conflict with a macro. */ // Adapted 2019-12-14 from Documentation/networking/tuntuntap.txt in Linux. int tuntap_alloc(char *dev, int open_flags, short ifr_flags_) { struct ifreq ifr; int fd, err; if ((fd = open("/dev/net/tun", O_RDWR | open_flags)) < 0) return -1; memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = ifr_flags_; if (*dev) strncpy(ifr.ifr_name, dev, IFNAMSIZ); if ((err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0) { close(fd); return err; } strcpy(dev, ifr.ifr_name); return fd; } void ex_usage() { fputs("mktuntap: usage: mktuntap (-n | -p) [-v] [-B] [-P] [-E] [-i ifr_name] fd prog...\n", stderr); exit(EX_USAGE); } int main(int argc, char **argv) { char *ifr_name_in = NULL; bool name_env = true; bool tap = false, tun = false; bool vnet_hdr = false, non_block = false, no_pi = true; int c; while ((c = getopt(argc, argv, "+npvBPEi:")) != -1) { switch (c) { case 'n': tun = true; break; case 'p': tap = true; break; case 'v': vnet_hdr = true; break; case 'B': non_block = true; break; case 'P': no_pi = false; break; case 'E': name_env = false; break; case 'i': ifr_name_in = optarg; break; default: ex_usage(); } } if (argc - optind < 2 || !(tap ^ tun)) ex_usage(); char *ifr_name_ = calloc(IFNAMSIZ, sizeof(char)); if (ifr_name_in) { if (strlen(ifr_name_in) > IFNAMSIZ - 1) { fprintf(stderr, "mktuntap: ifr_name too long (max %i)\n", IFNAMSIZ - 1); return EX_USAGE; } strcpy(ifr_name_, ifr_name_in); } errno = 0; char *target_fd_end; long int target_fd_l = strtol(argv[optind], &target_fd_end, 10); if (errno) { perror("mktuntap"); return EX_USAGE; } if (argv[optind][0] == '\0' || target_fd_end[0] != '\0') { fprintf(stderr, "mktuntap: invalid integer: `%s'\n", argv[optind]); return EX_USAGE; } if (target_fd_l < INT_MIN || target_fd_l > INT_MAX) { fprintf(stderr, "mktuntap: out of range for file descriptor: `%li'\n", target_fd_l); return EX_USAGE; } int target_fd = (int)target_fd_l; short open_flags = 0; if (non_block) open_flags |= O_NONBLOCK; short ifr_flags_ = tap ? IFF_TAP : IFF_TUN; if (no_pi) ifr_flags_ |= IFF_NO_PI; if (vnet_hdr) ifr_flags_ |= IFF_VNET_HDR; int tuntap_fd = tuntap_alloc(ifr_name_, open_flags, ifr_flags_); if (tuntap_fd < 0) { perror("mktuntap: failed to open tuntap device"); return EX_IOERR; } if (tuntap_fd != target_fd) { if (dup2(tuntap_fd, target_fd) == -1) { perror("mktuntap"); return EX_IOERR; } if (close(tuntap_fd) == -1) { perror("mktuntap"); return EX_IOERR; } } if (name_env) { if (setenv("TUNTAP_NAME", ifr_name_, 1) == -1) { perror("mktunap"); return EX_OSERR; } } execvp(argv[optind + 1], &argv[optind + 1]); perror("failed to exec"); return EX_OSERR; }