// tun_windows.c - Wintun implementation #include "../lib/debug_config.h" #include "../lib/u_async.h" #include "tun_if.h" #include "wintun.h" #include "ll_queue.h" // ← добавлено #include "etcp.h" // ← добавлено (struct ETCP_FRAGMENT) #include #include #include #include // Wintun function pointers static HMODULE wintun_dll = NULL; static WINTUN_CREATE_ADAPTER_FUNC *WintunCreateAdapter = NULL; static WINTUN_CLOSE_ADAPTER_FUNC *WintunCloseAdapter = NULL; static WINTUN_OPEN_ADAPTER_FUNC *WintunOpenAdapter = NULL; static WINTUN_GET_ADAPTER_LUID_FUNC *WintunGetAdapterLUID = NULL; static WINTUN_START_SESSION_FUNC *WintunStartSession = NULL; static WINTUN_END_SESSION_FUNC *WintunEndSession = NULL; static WINTUN_GET_READ_WAIT_EVENT_FUNC *WintunGetReadWaitEvent = NULL; static WINTUN_RECEIVE_PACKET_FUNC *WintunReceivePacket = NULL; static WINTUN_RELEASE_RECEIVE_PACKET_FUNC *WintunReleaseReceivePacket = NULL; static WINTUN_ALLOCATE_SEND_PACKET_FUNC *WintunAllocateSendPacket = NULL; static WINTUN_SEND_PACKET_FUNC *WintunSendPacket = NULL; static int wintun_load_dll(void) { if (wintun_dll) return 0; wchar_t path[MAX_PATH]; if (GetModuleFileNameW(NULL, path, MAX_PATH)) { for (int i = (int)wcslen(path)-1; i >= 0; i--) { if (path[i] == L'\\' || path[i] == L'/') { path[i+1] = L'\0'; wcscat(path, L"wintun.dll"); wintun_dll = LoadLibraryW(path); break; } } } if (!wintun_dll) wintun_dll = LoadLibraryA("wintun.dll"); if (!wintun_dll) { DEBUG_ERROR(DEBUG_CATEGORY_TUN, "wintun.dll not found"); return -1; } WintunCreateAdapter = (WINTUN_CREATE_ADAPTER_FUNC*)GetProcAddress(wintun_dll, "WintunCreateAdapter"); WintunCloseAdapter = (WINTUN_CLOSE_ADAPTER_FUNC*)GetProcAddress(wintun_dll, "WintunCloseAdapter"); WintunOpenAdapter = (WINTUN_OPEN_ADAPTER_FUNC*)GetProcAddress(wintun_dll, "WintunOpenAdapter"); WintunGetAdapterLUID = (WINTUN_GET_ADAPTER_LUID_FUNC*)GetProcAddress(wintun_dll, "WintunGetAdapterLUID"); WintunStartSession = (WINTUN_START_SESSION_FUNC*)GetProcAddress(wintun_dll, "WintunStartSession"); WintunEndSession = (WINTUN_END_SESSION_FUNC*)GetProcAddress(wintun_dll, "WintunEndSession"); WintunGetReadWaitEvent = (WINTUN_GET_READ_WAIT_EVENT_FUNC*)GetProcAddress(wintun_dll, "WintunGetReadWaitEvent"); WintunReceivePacket = (WINTUN_RECEIVE_PACKET_FUNC*)GetProcAddress(wintun_dll, "WintunReceivePacket"); WintunReleaseReceivePacket = (WINTUN_RELEASE_RECEIVE_PACKET_FUNC*)GetProcAddress(wintun_dll, "WintunReleaseReceivePacket"); WintunAllocateSendPacket = (WINTUN_ALLOCATE_SEND_PACKET_FUNC*)GetProcAddress(wintun_dll, "WintunAllocateSendPacket"); WintunSendPacket = (WINTUN_SEND_PACKET_FUNC*)GetProcAddress(wintun_dll, "WintunSendPacket"); if (!WintunCreateAdapter || !WintunCloseAdapter || !WintunOpenAdapter || !WintunGetAdapterLUID || !WintunStartSession || !WintunEndSession || !WintunGetReadWaitEvent || !WintunReceivePacket || !WintunReleaseReceivePacket || !WintunAllocateSendPacket || !WintunSendPacket) { DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to resolve Wintun functions"); FreeLibrary(wintun_dll); wintun_dll = NULL; return -1; } return 0; } // =================================================================== // Helper: set IP + MTU + bring interface up // =================================================================== static int wintun_configure_interface(WINTUN_ADAPTER_HANDLE adapter, const char* ip_str, int mtu) { NET_LUID luid; WintunGetAdapterLUID(adapter, &luid); char ip_only[64] = {0}; int prefix = 32; if (ip_str) { strncpy(ip_only, ip_str, sizeof(ip_only)-1); char* slash = strchr(ip_only, '/'); if (slash) { *slash = '\0'; prefix = atoi(slash + 1); if (prefix < 0 || prefix > 32) prefix = 32; } } struct in_addr addr; if (inet_pton(AF_INET, ip_only, &addr) != 1) { DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Invalid IP: %s", ip_str); return -1; } // IP address MIB_UNICASTIPADDRESS_ROW row = {0}; InitializeUnicastIpAddressEntry(&row); row.Address.si_family = AF_INET; row.Address.Ipv4.sin_family = AF_INET; row.Address.Ipv4.sin_addr.s_addr = addr.s_addr; row.InterfaceLuid = luid; row.OnLinkPrefixLength = (UINT8)prefix; row.DadState = IpDadStatePreferred; row.SuffixOrigin = IpSuffixOriginManual; row.PrefixOrigin = IpPrefixOriginManual; DWORD err = CreateUnicastIpAddressEntry(&row); if (err != NO_ERROR && err != ERROR_OBJECT_ALREADY_EXISTS) { DEBUG_ERROR(DEBUG_CATEGORY_TUN, "CreateUnicastIpAddressEntry failed: %lu", err); return -1; } // MTU if (mtu > 0) { MIB_IPINTERFACE_ROW ifRow = {0}; InitializeIpInterfaceEntry(&ifRow); ifRow.Family = AF_INET; ifRow.InterfaceLuid = luid; if (GetIpInterfaceEntry(&ifRow) == NO_ERROR) { ifRow.NlMtu = mtu; SetIpInterfaceEntry(&ifRow); } } // Bring UP NET_IFINDEX idx = 0; if (ConvertInterfaceLuidToIndex(&luid, &idx) == NO_ERROR) { MIB_IFROW ifr = {0}; ifr.dwIndex = idx; if (GetIfEntry(&ifr) == NO_ERROR) { ifr.dwAdminStatus = MIB_IF_ADMIN_STATUS_UP; SetIfEntry(&ifr); } } return 0; } // =================================================================== // Platform functions // =================================================================== int tun_platform_init(struct tun_if* tun, const char* ifname, const char* ip_str, int mtu) { if (wintun_load_dll() != 0) return -1; wchar_t wname[128] = L"utun"; if (ifname && ifname[0]) MultiByteToWideChar(CP_UTF8, 0, ifname, -1, wname, 128); WINTUN_ADAPTER_HANDLE adapter = WintunCreateAdapter(wname, L"utun", NULL); if (!adapter) { DEBUG_ERROR(DEBUG_CATEGORY_TUN, "WintunCreateAdapter failed: %lu", GetLastError()); return -1; } WINTUN_SESSION_HANDLE session = WintunStartSession(adapter, 0x400000); // 4 MiB ring if (!session) { DEBUG_ERROR(DEBUG_CATEGORY_TUN, "WintunStartSession failed: %lu", GetLastError()); WintunCloseAdapter(adapter); return -1; } if (wintun_configure_interface(adapter, ip_str, mtu) != 0) { WintunEndSession(session); WintunCloseAdapter(adapter); return -1; } tun->platform_handle = session; tun->adapter_handle = adapter; strncpy(tun->ifname, ifname && ifname[0] ? ifname : "utun", sizeof(tun->ifname)-1); return 0; } void tun_platform_cleanup(struct tun_if* tun) { if (tun->platform_handle) { WintunEndSession((WINTUN_SESSION_HANDLE)tun->platform_handle); tun->platform_handle = NULL; } if (tun->adapter_handle) { WintunCloseAdapter((WINTUN_ADAPTER_HANDLE)tun->adapter_handle); tun->adapter_handle = NULL; } } ssize_t tun_platform_read(struct tun_if* tun, uint8_t* buf, size_t len) { if (!tun->platform_handle) return -1; DWORD size; BYTE* pkt = WintunReceivePacket((WINTUN_SESSION_HANDLE)tun->platform_handle, &size); if (!pkt) { if (GetLastError() == ERROR_NO_MORE_ITEMS) return 0; return -1; } if (size > len) size = (DWORD)len; memcpy(buf, pkt, size); WintunReleaseReceivePacket((WINTUN_SESSION_HANDLE)tun->platform_handle, pkt); return (ssize_t)size; } ssize_t tun_platform_write(struct tun_if* tun, const uint8_t* buf, size_t len) { if (!tun->platform_handle) return -1; BYTE* pkt = WintunAllocateSendPacket((WINTUN_SESSION_HANDLE)tun->platform_handle, (DWORD)len); if (!pkt) return -1; memcpy(pkt, buf, len); WintunSendPacket((WINTUN_SESSION_HANDLE)tun->platform_handle, pkt); return (ssize_t)len; } int tun_platform_get_poll_fd(struct tun_if* tun) { (void)tun; return -1; } // =================================================================== // Windows-only notify // =================================================================== static void tun_output_notify(void* arg) { struct tun_if* tun = (struct tun_if*)arg; if (tun) { DEBUG_DEBUG(DEBUG_CATEGORY_TUN, "TUN read notify → output_queue (%s)", tun->ifname); } } // =================================================================== // Read thread // =================================================================== DWORD WINAPI tun_read_thread_proc(LPVOID arg) { struct tun_if* tun = (struct tun_if*)arg; HANDLE event = WintunGetReadWaitEvent((WINTUN_SESSION_HANDLE)tun->platform_handle); if (!event) return 1; HANDLE handles[2] = {event, tun->stop_event}; while (tun->running) { if (WaitForMultipleObjects(2, handles, FALSE, INFINITE) != WAIT_OBJECT_0) break; uint8_t buf[TUN_MAX_PACKET_SIZE]; int got_packet = 0; ssize_t n; while ((n = tun_platform_read(tun, buf, sizeof(buf))) > 0) { got_packet = 1; uint8_t* data = malloc(n); if (!data) { tun->read_errors++; continue; } memcpy(data, buf, n); struct ETCP_FRAGMENT* pkt = (struct ETCP_FRAGMENT*)queue_entry_new_from_pool(tun->pool); if (!pkt) { free(data); tun->read_errors++; continue; } pkt->ll.dgram = data; pkt->ll.len = n; EnterCriticalSection(&tun->output_queue_lock); int ok = queue_data_put(tun->output_queue, (struct ll_entry*)pkt, 0); LeaveCriticalSection(&tun->output_queue_lock); if (ok != 0) { free(data); memory_pool_free(tun->pool, pkt); tun->read_errors++; } else { tun->bytes_read += n; tun->packets_read++; } } if (got_packet) { uasync_post(tun->ua, tun_output_notify, tun); } } return 0; }