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

// 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;
}