You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
238 lines
5.3 KiB
238 lines
5.3 KiB
// tun_if.c - TUN interface management implementation |
|
#define _POSIX_C_SOURCE 200809L |
|
#include "tun_if.h" |
|
|
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <unistd.h> |
|
#include <fcntl.h> |
|
#include <sys/ioctl.h> |
|
#include <sys/socket.h> |
|
#include <sys/types.h> |
|
#include <sys/stat.h> |
|
#include <net/if.h> |
|
#include <netinet/in.h> |
|
#include <arpa/inet.h> |
|
#include <linux/if.h> |
|
#include <linux/if_tun.h> |
|
#include <errno.h> |
|
|
|
// Create TUN device |
|
static int create_tun_device(char *ifname, size_t ifname_len) { |
|
struct ifreq ifr; |
|
int fd; |
|
|
|
fd = open("/dev/net/tun", O_RDWR); |
|
if (fd < 0) { |
|
perror("open /dev/net/tun"); |
|
return -1; |
|
} |
|
|
|
memset(&ifr, 0, sizeof(ifr)); |
|
ifr.ifr_flags = IFF_TUN | IFF_NO_PI; |
|
|
|
if (ifname && ifname_len > 0 && ifname[0] != '\0') { |
|
strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1); |
|
} |
|
|
|
if (ioctl(fd, TUNSETIFF, &ifr) < 0) { |
|
perror("ioctl TUNSETIFF"); |
|
close(fd); |
|
return -1; |
|
} |
|
|
|
if (ifname && ifname_len > 0) { |
|
strncpy(ifname, ifr.ifr_name, ifname_len - 1); |
|
ifname[ifname_len - 1] = '\0'; |
|
} |
|
|
|
return fd; |
|
} |
|
|
|
// Run system command |
|
static int run_command(const char *cmd) { |
|
int ret = system(cmd); |
|
if (ret == -1) { |
|
perror("system"); |
|
return -1; |
|
} |
|
if (WIFEXITED(ret) && WEXITSTATUS(ret) != 0) { |
|
fprintf(stderr, "Command failed: %s\n", cmd); |
|
return -1; |
|
} |
|
return 0; |
|
} |
|
|
|
// Parse IP address and mask |
|
static int parse_ip_mask(const char *ip_addr, char *ip, size_t ip_len, int *mask) { |
|
if (!ip_addr || !ip || !mask) return -1; |
|
|
|
char *slash = strchr(ip_addr, '/'); |
|
if (!slash) { |
|
// Default mask /32 |
|
*mask = 32; |
|
strncpy(ip, ip_addr, ip_len - 1); |
|
ip[ip_len - 1] = '\0'; |
|
} else { |
|
size_t ip_size = slash - ip_addr; |
|
if (ip_size >= ip_len) return -1; |
|
strncpy(ip, ip_addr, ip_size); |
|
ip[ip_size] = '\0'; |
|
|
|
char *endptr; |
|
long mask_val = strtol(slash + 1, &endptr, 10); |
|
if (*endptr != '\0' || mask_val < 0 || mask_val > 32) return -1; |
|
*mask = (int)mask_val; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
int tun_create(tun_config_t *config) { |
|
if (!config) { |
|
errno = EINVAL; |
|
return -1; |
|
} |
|
|
|
// Create TUN device |
|
int fd = create_tun_device(config->ifname, sizeof(config->ifname)); |
|
if (fd < 0) { |
|
return -1; |
|
} |
|
|
|
config->fd = fd; |
|
|
|
// Configure IP if specified |
|
if (config->ip_addr[0] != '\0') { |
|
if (tun_set_ip(config->ifname, config->ip_addr) < 0) { |
|
close(fd); |
|
return -1; |
|
} |
|
} |
|
|
|
// Set MTU if specified |
|
if (config->mtu > 0) { |
|
if (tun_set_mtu(config->ifname, config->mtu) < 0) { |
|
// Non-fatal error, just warn |
|
fprintf(stderr, "Warning: failed to set MTU on %s\n", config->ifname); |
|
} |
|
} |
|
|
|
// Bring interface up |
|
if (tun_set_up(config->ifname) < 0) { |
|
close(fd); |
|
return -1; |
|
} |
|
|
|
config->is_up = 1; |
|
return 0; |
|
} |
|
|
|
int tun_set_ip(const char *ifname, const char *ip_addr) { |
|
if (!ifname || !ip_addr) { |
|
errno = EINVAL; |
|
return -1; |
|
} |
|
|
|
char ip[64]; |
|
int mask; |
|
if (parse_ip_mask(ip_addr, ip, sizeof(ip), &mask) < 0) { |
|
fprintf(stderr, "Invalid IP address format: %s\n", ip_addr); |
|
errno = EINVAL; |
|
return -1; |
|
} |
|
|
|
char cmd[256]; |
|
snprintf(cmd, sizeof(cmd), "ip addr add %s dev %s", ip_addr, ifname); |
|
if (run_command(cmd) < 0) { |
|
return -1; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
int tun_set_up(const char *ifname) { |
|
if (!ifname) { |
|
errno = EINVAL; |
|
return -1; |
|
} |
|
|
|
char cmd[256]; |
|
snprintf(cmd, sizeof(cmd), "ip link set %s up", ifname); |
|
if (run_command(cmd) < 0) { |
|
return -1; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
int tun_set_mtu(const char *ifname, int mtu) { |
|
if (!ifname || mtu <= 0) { |
|
errno = EINVAL; |
|
return -1; |
|
} |
|
|
|
char cmd[256]; |
|
snprintf(cmd, sizeof(cmd), "ip link set %s mtu %d", ifname, mtu); |
|
if (run_command(cmd) < 0) { |
|
return -1; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
ssize_t tun_read(int fd, uint8_t *buffer, size_t size) { |
|
if (fd < 0 || !buffer || size == 0) { |
|
errno = EINVAL; |
|
return -1; |
|
} |
|
|
|
ssize_t nread = read(fd, buffer, size); |
|
if (nread < 0) { |
|
perror("tun_read"); |
|
} |
|
|
|
return nread; |
|
} |
|
|
|
ssize_t tun_write(int fd, const uint8_t *buffer, size_t size) { |
|
if (fd < 0 || !buffer || size == 0) { |
|
errno = EINVAL; |
|
return -1; |
|
} |
|
|
|
ssize_t nwritten = write(fd, buffer, size); |
|
if (nwritten < 0) { |
|
perror("tun_write"); |
|
} |
|
|
|
return nwritten; |
|
} |
|
|
|
void tun_close(tun_config_t *config) { |
|
if (!config) return; |
|
|
|
if (config->fd >= 0) { |
|
close(config->fd); |
|
config->fd = -1; |
|
} |
|
|
|
config->is_up = 0; |
|
} |
|
|
|
int tun_get_config(const char *ifname, tun_config_t *config) { |
|
if (!ifname || !config) { |
|
errno = EINVAL; |
|
return -1; |
|
} |
|
|
|
// TODO: Implement reading current interface configuration |
|
// This would require parsing ip addr show output |
|
|
|
memset(config, 0, sizeof(*config)); |
|
strncpy(config->ifname, ifname, sizeof(config->ifname) - 1); |
|
config->fd = -1; |
|
|
|
return 0; |
|
} |