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.
 
 
 
 
 
 

325 lines
9.8 KiB

// tun_if.c - Cross-platform TUN interface
#include "config_parser.h"
#include "tun_if.h"
#include "etcp.h"
#include "../lib/debug_config.h"
#include "../lib/u_async.h"
#include "../lib/ll_queue.h"
#include "../lib/memory_pool.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include <windows.h>
#endif
// ===================================================================
// Linux-only: TUN read callback (uasync) — определена ПЕРЕД tun_init
// ===================================================================
#ifndef _WIN32
// Internal read callback - called by uasync when data available on TUN
static void tun_read_callback(int fd, void* user_arg)
{
(void)fd;
struct tun_if* tun = (struct tun_if*)user_arg;
uint8_t buffer[TUN_MAX_PACKET_SIZE];
// Read from TUN device
ssize_t nread = tun_platform_read(tun, buffer, sizeof(buffer));
if (nread < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to read from TUN device %s", tun->ifname);
tun->read_errors++;
return;
}
if (nread == 0) return;
// Allocate packet data
uint8_t* packet_data = malloc(nread);
if (!packet_data) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to allocate packet data (size=%zd)", nread);
tun->read_errors++;
return;
}
memcpy(packet_data, buffer, nread);
// Allocate ETCP_FRAGMENT from pool
struct ETCP_FRAGMENT* pkt = (struct ETCP_FRAGMENT*)queue_entry_new_from_pool(tun->pool);
if (!pkt) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to allocate ETCP_FRAGMENT from pool");
free(packet_data);
tun->read_errors++;
return;
}
// Initialize fragment
pkt->seq = 0;
pkt->timestamp = 0;
pkt->ll.dgram = packet_data;
pkt->ll.len = nread;
// Add to output queue (TUN → routing)
if (queue_data_put(tun->output_queue, (struct ll_entry*)pkt, 0) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to add packet to output queue");
free(packet_data);
memory_pool_free(tun->pool, pkt);
tun->read_errors++;
return;
}
// Update statistics
tun->bytes_read += nread;
tun->packets_read++;
DEBUG_DEBUG(DEBUG_CATEGORY_TUN, "Read %zd bytes from TUN %s", nread, tun->ifname);
}
#endif
// ===================================================================
// Callback: routing → TUN (вызывается из main thread)
// ===================================================================
static void tun_input_queue_callback(struct ll_queue* q, void* arg)
{
(void)q;
struct tun_if* tun = (struct tun_if*)arg;
struct ETCP_FRAGMENT* pkt;
while ((pkt = (struct ETCP_FRAGMENT*)queue_data_get(tun->input_queue)) != NULL) {
if (pkt->ll.dgram && pkt->ll.len > 0) {
if (pkt->ll.len > TUN_MAX_PACKET_SIZE) {
DEBUG_WARN(DEBUG_CATEGORY_TUN, "Packet too large: %zu bytes", pkt->ll.len);
} else {
ssize_t n = tun_platform_write(tun, pkt->ll.dgram, pkt->ll.len);
if (n < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "tun_platform_write failed");
tun->write_errors++;
} else {
tun->bytes_written += n;
tun->packets_written++;
}
}
free(pkt->ll.dgram);
}
memory_pool_free(tun->pool, pkt);
}
}
// ===================================================================
// Init
// ===================================================================
struct tun_if* tun_init(struct UASYNC* ua, struct utun_config* config)
{
if (!ua || !config) return NULL;
int test_mode = config->global.tun_test_mode;
struct tun_if* tun = calloc(1, sizeof(struct tun_if));
if (!tun) return NULL;
tun->ua = ua;
tun->test_mode = test_mode;
tun->fd = -1;
const char* name = config->global.tun_ifname;
if (name && name[0]) strncpy(tun->ifname, name, sizeof(tun->ifname)-1);
char ip_str[64];
if (config->global.tun_ip.family == AF_INET) {
char buf[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &config->global.tun_ip.addr.v4, buf, sizeof(buf));
snprintf(ip_str, sizeof(ip_str), "%s/32", buf);
} else {
free(tun);
return NULL;
}
int mtu = config->global.mtu > 0 ? config->global.mtu : TUN_MTU_DEFAULT;
if (!test_mode) {
if (tun_platform_init(tun, tun->ifname, ip_str, mtu) != 0) {
free(tun);
return NULL;
}
} else if (tun->ifname[0] == '\0') {
strncpy(tun->ifname, "tun_test", sizeof(tun->ifname)-1);
}
tun->pool = memory_pool_init(sizeof(struct ETCP_FRAGMENT));
if (!tun->pool) goto fail;
tun->output_queue = queue_new(ua, 0);
tun->input_queue = queue_new(ua, 0);
if (!tun->output_queue || !tun->input_queue) goto fail;
queue_set_callback(tun->input_queue, tun_input_queue_callback, tun);
#ifdef _WIN32
InitializeCriticalSection(&tun->output_queue_lock);
#endif
if (!test_mode) {
int poll_fd = tun_platform_get_poll_fd(tun);
if (poll_fd >= 0) {
#ifndef _WIN32
// ==================== LINUX ====================
tun->socket_id = uasync_add_socket(ua, poll_fd, tun_read_callback, NULL, NULL, tun);
if (!tun->socket_id) goto fail;
#endif
} else {
#ifdef _WIN32
// ==================== WINDOWS ====================
tun->running = 1;
tun->stop_event = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!tun->stop_event) goto fail;
tun->read_thread = CreateThread(NULL, 0, tun_read_thread_proc, tun, 0, NULL);
if (!tun->read_thread) {
CloseHandle(tun->stop_event);
goto fail;
}
#endif
}
}
DEBUG_INFO(DEBUG_CATEGORY_TUN, "TUN %s initialized (%s mode)", tun->ifname,
test_mode ? "TEST" : "REAL");
return tun;
fail:
#ifdef _WIN32
if (tun->stop_event) CloseHandle(tun->stop_event);
if (tun->read_thread) {
tun->running = 0;
WaitForSingleObject(tun->read_thread, INFINITE);
CloseHandle(tun->read_thread);
}
DeleteCriticalSection(&tun->output_queue_lock);
#endif
if (tun->output_queue) queue_free(tun->output_queue);
if (tun->input_queue) queue_free(tun->input_queue);
if (tun->pool) memory_pool_destroy(tun->pool);
if (!test_mode) tun_platform_cleanup(tun);
free(tun);
return NULL;
}
// ===================================================================
// Остальные функции (без изменений)
// ===================================================================
void tun_close(struct tun_if* tun)
{
if (!tun) return;
#ifdef _WIN32
if (tun->read_thread) {
tun->running = 0;
SetEvent(tun->stop_event);
WaitForSingleObject(tun->read_thread, INFINITE);
CloseHandle(tun->read_thread);
CloseHandle(tun->stop_event);
DeleteCriticalSection(&tun->output_queue_lock);
}
#endif
if (tun->socket_id && tun->ua) {
uasync_remove_socket(tun->ua, tun->socket_id);
}
struct ETCP_FRAGMENT* pkt;
while ((pkt = (struct ETCP_FRAGMENT*)queue_data_get(tun->input_queue)) != NULL) {
if (pkt->ll.dgram) free(pkt->ll.dgram);
memory_pool_free(tun->pool, pkt);
}
queue_free(tun->input_queue);
while ((pkt = (struct ETCP_FRAGMENT*)queue_data_get(tun->output_queue)) != NULL) {
if (pkt->ll.dgram) free(pkt->ll.dgram);
memory_pool_free(tun->pool, pkt);
}
queue_free(tun->output_queue);
memory_pool_destroy(tun->pool);
if (!tun->test_mode) tun_platform_cleanup(tun);
free(tun);
}
ssize_t tun_write(struct tun_if* tun, const uint8_t* buf, size_t len)
{
if (!tun || !buf || len == 0) return -1;
if (tun->test_mode) {
return tun_inject_packet(tun, buf, len) == 0 ? (ssize_t)len : -1;
}
ssize_t n = tun_platform_write(tun, buf, len);
if (n > 0) {
tun->bytes_written += n;
tun->packets_written++;
} else {
tun->write_errors++;
}
return n;
}
struct ll_queue* tun_get_input_queue(struct tun_if* tun) { return tun ? tun->input_queue : NULL; }
struct ll_queue* tun_get_output_queue(struct tun_if* tun) { return tun ? tun->output_queue : NULL; }
int tun_is_test_mode(struct tun_if* tun) { return tun ? tun->test_mode : -1; }
int tun_inject_packet(struct tun_if* tun, const uint8_t* buf, size_t len)
{
if (!tun || !buf || len == 0 || len > TUN_MAX_PACKET_SIZE) return -1;
uint8_t* data = malloc(len);
if (!data) { tun->read_errors++; return -1; }
memcpy(data, buf, len);
struct ETCP_FRAGMENT* pkt = (struct ETCP_FRAGMENT*)queue_entry_new_from_pool(tun->pool);
if (!pkt) { free(data); tun->read_errors++; return -1; }
pkt->seq = 0;
pkt->timestamp = 0;
pkt->ll.dgram = data;
pkt->ll.len = len;
#ifdef _WIN32
EnterCriticalSection(&tun->output_queue_lock);
#endif
int ret = queue_data_put(tun->output_queue, (struct ll_entry*)pkt, 0);
#ifdef _WIN32
LeaveCriticalSection(&tun->output_queue_lock);
#endif
if (ret != 0) {
free(data);
memory_pool_free(tun->pool, pkt);
tun->read_errors++;
return -1;
}
tun->bytes_read += len;
tun->packets_read++;
return 0;
}
ssize_t tun_read_packet(struct tun_if* tun, uint8_t* buf, size_t len)
{
if (!tun || !buf || len == 0) return -1;
struct ETCP_FRAGMENT* pkt = (struct ETCP_FRAGMENT*)queue_data_get(tun->input_queue);
if (!pkt) return 0;
if (pkt->ll.len > len) {
queue_data_put(tun->input_queue, (struct ll_entry*)pkt, 0);
return -1;
}
memcpy(buf, pkt->ll.dgram, pkt->ll.len);
ssize_t ret = pkt->ll.len;
free(pkt->ll.dgram);
memory_pool_free(tun->pool, pkt);
tun->bytes_written += ret;
tun->packets_written++;
return ret;
}