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.
287 lines
10 KiB
287 lines
10 KiB
// 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 <windows.h> |
|
#include <iphlpapi.h> |
|
#include <ws2tcpip.h> |
|
#include <stdio.h> |
|
|
|
// 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; |
|
}
|
|
|