- Fixed incorrect init_connections() call in test_etcp_simple_traffic.c (was calling server_instance instead of client_instance) - Fixed double free in timeout_heap_pop() when handling deleted elements - Enhanced NULL pointer safety in uasync_print_resources() by removing complex heap manipulation - Added debug logging to timeout_heap_pop() for better error tracking Test results: test_etcp_simple_traffic now passes without double free errors
340 lines
9.4 KiB
C
Executable File
340 lines
9.4 KiB
C
Executable File
// tun_if.c - TUN interface management implementation
|
|
#define _POSIX_C_SOURCE 200809L
|
|
#include "tun_if.h"
|
|
#include "../lib/debug_config.h"
|
|
#include "routing.h"
|
|
#include "../lib/u_async.h"
|
|
#include "etcp_connections.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) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to open /dev/net/tun: %s", strerror(errno));
|
|
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) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to configure TUN device: %s", strerror(errno));
|
|
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) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Command failed: %s", 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(struct tun_config *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) {
|
|
DEBUG_WARN(DEBUG_CATEGORY_TUN, "Failed to set MTU %d on %s: %s", config->mtu, config->ifname, strerror(errno));
|
|
}
|
|
}
|
|
|
|
// 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) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Invalid IP address format: %s", 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) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to write to TUN device fd=%d: %s", fd, strerror(errno));
|
|
}
|
|
|
|
return nwritten;
|
|
}
|
|
|
|
void tun_close(struct tun_config *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, struct tun_config *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;
|
|
}
|
|
|
|
// Extract destination IPv4 address from packet
|
|
static uint32_t tun_get_dest_ip(const uint8_t *packet, size_t len) {
|
|
if (len < 20) return 0; // Minimum IPv4 header size
|
|
// Check IP version (first nibble)
|
|
uint8_t version = (packet[0] >> 4) & 0x0F;
|
|
if (version != 4) return 0;
|
|
// Destination IP is at offset 16
|
|
uint32_t dest_ip;
|
|
memcpy(&dest_ip, packet + 16, 4);
|
|
return dest_ip;
|
|
}
|
|
|
|
// Extract destination IPv4 address from packet
|
|
static uint32_t get_dest_ip(const uint8_t *packet, size_t len) {
|
|
if (len < 20) return 0; // Minimum IPv4 header size
|
|
// Check IP version (first nibble)
|
|
uint8_t version = (packet[0] >> 4) & 0x0F;
|
|
if (version != 4) return 0;
|
|
// Destination IP is at offset 16
|
|
uint32_t dest_ip;
|
|
memcpy(&dest_ip, packet + 16, 4);
|
|
return dest_ip;
|
|
}
|
|
|
|
|
|
// Callback for TUN device read events
|
|
void tun_read_callback(int fd, void* user_arg) {
|
|
struct UTUN_INSTANCE *instance = (struct UTUN_INSTANCE*)user_arg;
|
|
uint8_t buffer[MAX_PACKET_SIZE];
|
|
|
|
// Read from TUN device
|
|
ssize_t nread = tun_read(fd, buffer, sizeof(buffer));
|
|
if (nread < 0) {
|
|
if (errno == EINTR) return;
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to read from TUN device %s: %s", instance->tun.ifname, strerror(errno));
|
|
return;
|
|
}
|
|
|
|
if (nread > 0) {
|
|
// Route packet based on destination IP
|
|
uint32_t dest_ip = get_dest_ip(buffer, nread);
|
|
struct route_entry route;
|
|
|
|
if (routing_table_lookup(instance->routing_table, dest_ip, &route)) {
|
|
// Found route, send to next hop connection
|
|
if (route.next_hop) {
|
|
struct sockaddr_in dest_addr;
|
|
memset(&dest_addr, 0, sizeof(dest_addr));
|
|
dest_addr.sin_family = AF_INET;
|
|
dest_addr.sin_addr.s_addr = route.next_hop_ip;
|
|
// todo: отправлять в модуль роутинга все входящие пакеты. когда этот модуль будет. модуль роутинга это не роутинг таблица (routing.h)
|
|
// if (etcp_connections_send(route.next_hop, buffer, nread, (struct sockaddr*)&dest_addr, sizeof(dest_addr)) < 0) {
|
|
// DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to send packet via route");
|
|
// }
|
|
} else {
|
|
// Local route - no forwarding needed
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_ROUTING, "Local packet, no forwarding");
|
|
}
|
|
} else {
|
|
// No route found, drop packet
|
|
char ip_str[16];
|
|
ip_to_string(dest_ip, ip_str);
|
|
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "No route for destination IP %s", ip_str);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Register sockets with uasync
|
|
int utun_instance_register_sockets(struct UTUN_INSTANCE *instance) {
|
|
if (!instance || !instance->ua || instance->tun.fd < 0) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Invalid instance or TUN fd");
|
|
return -1;
|
|
}
|
|
|
|
// Register TUN file descriptor
|
|
instance->tun_socket_id = uasync_add_socket(instance->ua, instance->tun.fd,
|
|
tun_read_callback, NULL, NULL, instance);
|
|
if (!instance->tun_socket_id) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to register TUN socket");
|
|
return -1;
|
|
}
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Registered TUN socket (fd=%d)", instance->tun.fd);
|
|
return 0;
|
|
}
|
|
|
|
// Unregister sockets
|
|
void utun_instance_unregister_sockets(struct UTUN_INSTANCE *instance) {
|
|
if (!instance || !instance->ua) return;
|
|
|
|
if (instance->tun_socket_id) {
|
|
uasync_remove_socket(instance->ua, instance->tun_socket_id);
|
|
instance->tun_socket_id = NULL;
|
|
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Unregistered TUN socket: %s", instance->tun.ifname);
|
|
}
|
|
}
|
|
|