diff options
Diffstat (limited to 'src/tun.c')
| -rw-r--r-- | src/tun.c | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/src/tun.c b/src/tun.c new file mode 100644 index 0000000..8658893 --- /dev/null +++ b/src/tun.c @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ + +#include "tun.h" + +#include <arpa/inet.h> +#include <errno.h> +#include <fcntl.h> +#include <linux/if.h> +#include <linux/if_tun.h> +#include <linux/sockios.h> +#include <netinet/in.h> +#include <stdio.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <unistd.h> + +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; +} |
