/* SPDX-License-Identifier: GPL-3.0-or-later */ #include "tun.h" #include #include #include #include #include #include #include #include #include #include #include #include static int parse_cidr(const char* cidr, struct in_addr* addr, struct in_addr* netmask) { char ip[INET_ADDRSTRLEN]; int prefix = 0; if (sscanf(cidr, "%[^/]/%d", ip, &prefix) != 2 || prefix < 0 || prefix > 32) { return -1; } *netmask = (struct in_addr) { .s_addr = htonl(0xFFFFFFFF << (32 - prefix)), }; if (!inet_aton(ip, addr)) { return -1; } return 0; } int tun_alloc(const char* ifname, const char* cidr, int mtu) { struct in_addr addr, netmask; if (parse_cidr(cidr, &addr, &netmask) < 0) { errno = EINVAL; goto err; } struct ifreq ifr = {0}; strncpy(ifr.ifr_name, ifname, IFNAMSIZ); // Create the tun device int fd = open("/dev/net/tun", O_RDWR); if (fd < 0) { goto err; } ifr.ifr_flags = (short) (IFF_TUN | IFF_NO_PI | IFF_TUN_EXCL); if (ioctl(fd, TUNSETIFF, &ifr) < 0) { goto err_close_fd; } // Configure IP address, netmask and MTU int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { goto err_close_fd; } struct sockaddr_in* sin = (struct sockaddr_in*) &ifr.ifr_data; *sin = (struct sockaddr_in) { .sin_family = AF_INET, .sin_addr = addr, }; if (ioctl(sockfd, SIOCSIFADDR, &ifr) < 0) { goto err_close_sockfd; } *sin = (struct sockaddr_in) { .sin_family = AF_INET, .sin_addr = netmask, }; if (ioctl(sockfd, SIOCSIFNETMASK, &ifr) < 0) { goto err_close_sockfd; } ifr.ifr_mtu = mtu; if (ioctl(sockfd, SIOCSIFMTU, &ifr) < 0) { goto err_close_sockfd; } // Bring interface up ifr.ifr_flags = 0; if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) { goto err_close_sockfd; } ifr.ifr_flags |= IFF_UP | IFF_RUNNING; if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) { goto err_close_sockfd; } return fd; err_close_sockfd: close(sockfd); err_close_fd: close(fd); err: return -1; }