summaryrefslogtreecommitdiff
path: root/src/tun.c
diff options
context:
space:
mode:
authorPancakeTAS <pancake@mgnet.work>2026-06-13 22:33:14 +0200
committerPancakeTAS <pancake@mgnet.work>2026-06-14 16:27:32 +0200
commite6308f6e28fe40312d9a99b8c3ce97a37c496b70 (patch)
tree3724b13fd8a0cab38a9f63d9aca8397d37d6fa5d /src/tun.c
parentperf(mpu): Remove zero-copy items (diff)
feat: Implement TUN modulemaster
Diffstat (limited to 'src/tun.c')
-rw-r--r--src/tun.c104
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;
+}