// SPDX-License-Identifier: GPL-2.0-only /* * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include // Adapted 2019-12-14 from Documentation/networking/tuntap.txt in Linux. int tap_alloc(char *dev) { struct ifreq ifr; int fd, err; if ((fd = open("/dev/net/tun", O_RDWR)) < 0) return -1; memset(&ifr, 0, sizeof(ifr)); /* Flags: IFF_TUN - TUN device (no Ethernet headers) * IFF_TAP - TAP device * * IFF_NO_PI - Do not provide packet information */ ifr.ifr_flags = IFF_TAP; 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("mktap: usage: mktap [-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; int c; while ((c = getopt(argc, argv, "+Ei:")) != -1) { switch (c) { case 'E': name_env = false; break; case 'i': ifr_name_in = optarg; break; default: ex_usage(); } } if (argc - optind < 2) ex_usage(); // ifr_name is a macro, so add a trailing _. char *ifr_name_ = calloc(IFNAMSIZ, sizeof(char)); if (ifr_name_in) { if (strlen(ifr_name_in) > IFNAMSIZ - 1) { fprintf(stderr, "mktap: 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) { fprintf(stderr, "mktap: %s\n", strerror(errno)); return EX_USAGE; } if (argv[optind][0] == '\0' || target_fd_end[0] != '\0') { fprintf(stderr, "mktap: invalid integer: `%s'\n", argv[optind]); return EX_USAGE; } if (target_fd_l < INT_MIN || target_fd_l > INT_MAX) { fprintf(stderr, "mktap: out of range for file descriptor: `%li'\n", target_fd_l); return EX_USAGE; } int target_fd = (int)target_fd_l; int tap_fd = tap_alloc(ifr_name_); if (tap_fd < 0) { fprintf(stderr, "mktap: failed to open tap device: %s\n", strerror(errno)); return EX_IOERR; } if (dup2(tap_fd, target_fd) == -1) { fprintf(stderr, "mktap: %s\n", strerror(errno)); return EX_IOERR; } if (close(tap_fd) == -1) { fprintf(stderr, "mktap: %s\n", strerror(errno)); return EX_IOERR; } if (name_env) { if (setenv("TUNTAP_NAME", ifr_name_, 1) == -1) { fprintf(stderr, "mktap: %s\n", strerror(errno)); return EX_OSERR; } } execvp(argv[optind + 1], &argv[optind + 1]); fprintf(stderr, "failed to exec: %s\n", strerror(errno)); return EX_OSERR; }