// tun_if.c - Cross-platform TUN interface #include "config_parser.h" #include "tun_if.h" #include "etcp.h" #include "packet_dump.h" #include "../lib/debug_config.h" #include "../lib/u_async.h" #include "../lib/ll_queue.h" #include "../lib/memory_pool.h" #include #include #include #include "../lib/mem.h" #ifdef _WIN32 #include #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 (+1 for prefix byte) uint8_t* packet_data = u_malloc(nread + 1); if (!packet_data) { DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to allocate packet data (size=%zd)", nread + 1); tun->read_errors++; return; } packet_data[0] = 0; // Prefix byte memcpy(packet_data + 1, buffer, nread); struct ll_entry* pkt = queue_entry_new_from_pool(tun->pool); if (!pkt) { DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to allocate struct ll_entry from pool"); u_free(packet_data); tun->read_errors++; return; } // Initialize entry pkt->dgram = packet_data; pkt->len = nread + 1; // Add to output queue (TUN → routing) if (queue_data_put(tun->output_queue, pkt, 0) != 0) { DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to add packet to output queue"); u_free(packet_data); memory_pool_free(tun->pool, pkt); tun->read_errors++; return; } // Update statistics tun->bytes_read += nread + 1; tun->packets_read++; DEBUG_DEBUG(DEBUG_CATEGORY_TUN, "Read %zd bytes from TUN %s", nread + 1, tun->ifname); if (debug_should_output(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_TUN)) { DEBUG_DEBUG(DEBUG_CATEGORY_TUN, "[TUN->] %s", dump_ip_packet_to_buffer(packet_data + 1, nread)); } } #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 ll_entry* pkt = queue_data_get(tun->input_queue); if (!pkt) return; if (pkt->dgram && pkt->len > 1) { if (pkt->len > TUN_MAX_PACKET_SIZE + 1) { DEBUG_WARN(DEBUG_CATEGORY_TUN, "Packet too large: %zu bytes", pkt->len); } else { // Skip prefix byte, write from second byte ssize_t n = tun_platform_write(tun, pkt->dgram + 1, pkt->len - 1); if (n < 0) { DEBUG_ERROR(DEBUG_CATEGORY_TUN, "tun_platform_write failed"); tun->write_errors++; } else { tun->bytes_written += n; tun->packets_written++; if (debug_should_output(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_TUN)) { DEBUG_DEBUG(DEBUG_CATEGORY_TUN, "[->TUN] %s", dump_ip_packet_to_buffer(pkt->dgram + 1, pkt->len - 1)); } } } queue_dgram_free(pkt); } queue_entry_free(pkt); queue_resume_callback(tun->input_queue); } // =================================================================== // 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 = u_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) { snprintf(ip_str, sizeof(ip_str), "%s/32", ip_to_str(&config->global.tun_ip.addr.v4, AF_INET).str); } else { u_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) { u_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 ll_entry)); if (!tun->pool) goto fail; tun->output_queue = queue_new(ua, 0, "TUN output"); tun->input_queue = queue_new(ua, 0, "TUN input"); if (!tun->output_queue || !tun->input_queue) goto fail; queue_set_callback(tun->input_queue, tun_input_queue_callback, tun); 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); } #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); u_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); } #endif if (tun->socket_id && tun->ua) { uasync_remove_socket(tun->ua, tun->socket_id); } struct ll_entry* pkt; while ((pkt = queue_data_get(tun->input_queue)) != NULL) { queue_dgram_free(pkt); queue_entry_free(pkt); } queue_free(tun->input_queue); while ((pkt = queue_data_get(tun->output_queue)) != NULL) { queue_dgram_free(pkt); queue_entry_free(pkt); } queue_free(tun->output_queue); memory_pool_destroy(tun->pool); if (!tun->test_mode) tun_platform_cleanup(tun); u_free(tun); } ssize_t tun_write(struct tun_if* tun, const uint8_t* buf, size_t len) { if (!tun || !buf || len <= 1) return -1; if (tun->test_mode) { // Skip prefix byte for test mode injection return tun_inject_packet(tun, buf + 1, len - 1) == 0 ? (ssize_t)(len - 1) : -1; } // Skip prefix byte, write from second byte ssize_t n = tun_platform_write(tun, buf + 1, len - 1); if (n > 0) { tun->bytes_written += n; tun->packets_written++; if (debug_should_output(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_TUN)) { DEBUG_DEBUG(DEBUG_CATEGORY_TUN, "[->TUN] %s", dump_ip_packet_to_buffer(buf + 1, len - 1)); } } 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; } // =================================================================== // Callback для обработки пакетов из TUN в main thread // Вызывается через uasync_post из read thread (Windows) // =================================================================== void tun_packet_handler(void* arg) { struct tun_packet_data* pd = (struct tun_packet_data*)arg; if (!pd || !pd->tun || !pd->entry) { DEBUG_ERROR(DEBUG_CATEGORY_TUN, "ARG ZERO"); if (pd) u_free(pd); return; } struct tun_if* tun = pd->tun; struct ll_entry* entry = pd->entry; // Освободить временную структуру u_free(pd); if (debug_should_output(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_TUN)) { DEBUG_DEBUG(DEBUG_CATEGORY_TUN, "[TUNh->] %s", dump_ip_packet_to_buffer(entry->dgram, entry->len)); } // Положить в очередь (уже в main thread) int ok = queue_data_put(tun->output_queue, entry, 0); if (ok != 0) { DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Put error"); queue_entry_free(entry); tun->read_errors++; } // Статистика уже обновлена в read thread } 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; // Allocate with +1 for prefix byte uint8_t* data = u_malloc(len + 1); if (!data) { tun->read_errors++; return -1; } data[0] = 0; // Prefix byte memcpy(data + 1, buf, len); struct ll_entry* pkt = queue_entry_new_from_pool(tun->pool); if (!pkt) { u_free(data); tun->read_errors++; return -1; } pkt->dgram = data; pkt->len = len + 1; int ret = queue_data_put(tun->output_queue, pkt, 0); if (ret != 0) { u_free(data); memory_pool_free(tun->pool, pkt); tun->read_errors++; return -1; } tun->bytes_read += len + 1; 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 ll_entry* pkt = queue_data_get(tun->input_queue); if (!pkt) return 0; if (pkt->len > len) { queue_data_put(tun->input_queue, pkt, 0); return -1; } // Add prefix byte at start buf[0] = 0; memcpy(buf + 1, pkt->dgram, pkt->len); ssize_t ret = pkt->len + 1; queue_dgram_free(pkt); queue_entry_free(pkt); tun->bytes_written += ret; tun->packets_written++; if (debug_should_output(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_TUN)) { DEBUG_DEBUG(DEBUG_CATEGORY_TUN, "[TUN->] %s", dump_ip_packet_to_buffer(buf + 1, pkt->len)); } return ret; }