// 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 /* * 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) { fprintf(stderr, "mktuntap: %s\n", strerror(errno)); 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) { fprintf(stderr, "mktuntap: failed to open tuntap device: %s\n", strerror(errno)); return EX_IOERR; } if (tuntap_fd != target_fd) { if (dup2(tuntap_fd, target_fd) == -1) { fprintf(stderr, "mktuntap: %s\n", strerror(errno)); return EX_IOERR; } if (close(tuntap_fd) == -1) { fprintf(stderr, "mktuntap: %s\n", strerror(errno)); return EX_IOERR; } } if (name_env) { if (setenv("TUNTAP_NAME", ifr_name_, 1) == -1) { fprintf(stderr, "mktuntap: %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; }