// lwIP stack provider glue. This file bridges the kernel network-device model into lwIP. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define LWIP_MAX_NETIFS 8u #define LWIP_PROVIDER_SOCKET_IO_MAX_BYTES (1024u * 1024u) #define LWIP_PROVIDER_SOCKET_UDP_MAX_BYTES 65507u #define LWIP_PROVIDER_SOCKET_RAW_MAX_BYTES 65535u #define LWIP_PROVIDER_CANCEL_POLL_MS 25u #define LWIP_SETTINGS_DEFAULT_SECTION "network.default" #define LWIP_SETTINGS_SECTION_PREFIX "network." #define LWIP_SETTINGS_VALUE_MAX 64u typedef struct LWIP_NETIF_CONFIG { uint8_t UseDhcp; uint8_t HasDns4; uint8_t EnableIpv6; uint8_t Ipv6Autoconfig; uint8_t HasStaticIpv6; uint8_t HasDns6; char Section[64]; ip4_addr_t Address; ip4_addr_t Netmask; ip4_addr_t Gateway; ip4_addr_t Dns4; ip6_addr_t Address6; ip6_addr_t Dns6; } LWIP_NETIF_CONFIG; typedef struct LWIP_BINDING { NET_DEVICE* Device; struct netif Netif; LWIP_NETIF_CONFIG Config; uint8_t Attached; } LWIP_BINDING; typedef struct LWIP_ATTACH_CTX { LWIP_BINDING* Binding; SEMAPHORE* Done; err_t Result; } LWIP_ATTACH_CTX; typedef struct LWIP_PROVIDER_SOCKET { SEMAPHORE* Lock; struct netconn* Conn; struct netbuf* ReceiveBuffer; size_t ReceiveOffset; uint32_t Domain; uint32_t Type; uint32_t Protocol; uint8_t Bound; uint8_t Listening; uint8_t Connected; uint8_t ShutdownRead; uint8_t ShutdownWrite; uint8_t NonBlocking; uint32_t ReceiveTimeoutMs; uint32_t SendTimeoutMs; volatile int32_t PollReadEvents; volatile int32_t PollWriteEvents; volatile int32_t PollErrorEvents; } LWIP_PROVIDER_SOCKET; static LWIP_BINDING* gBindings[LWIP_MAX_NETIFS]; static volatile int gLwipInitialized = 0; static KSTATUS LwipProviderAttachDevice(NET_DEVICE* device); static KSTATUS LwipProviderReceiveFrame(NET_DEVICE* device, const void* frame, size_t length); static KSTATUS LwipProviderSocketCreate(uint32_t domain, uint32_t type, uint32_t protocol, void** outSocket); static void LwipProviderSocketClose(void* socket); static KSTATUS LwipProviderBind(void* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize); static KSTATUS LwipProviderListen(void* socket, uint32_t backlog); static KSTATUS LwipProviderAccept(void* socket, void** acceptedOut, SYS_SOCKET_ADDRESS_K* remoteOut, volatile uint8_t* cancelRequested); static KSTATUS LwipProviderConnect(void* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize, volatile uint8_t* cancelRequested); static KSTATUS LwipProviderSend(void* socket, const void* buffer, size_t length, uint32_t flags, size_t* sentOut); static KSTATUS LwipProviderReceive(void* socket, void* buffer, size_t length, uint32_t flags, size_t* receivedOut, volatile uint8_t* cancelRequested); static KSTATUS LwipProviderSendTo(void* socket, const void* buffer, size_t length, uint32_t flags, const SYS_SOCKET_ADDRESS_K* remote, size_t remoteSize, size_t* sentOut); static KSTATUS LwipProviderReceiveFrom(void* socket, void* buffer, size_t length, uint32_t flags, SYS_SOCKET_ADDRESS_K* remoteOut, size_t* receivedOut, volatile uint8_t* cancelRequested); static KSTATUS LwipProviderShutdown(void* socket, uint32_t how); static KSTATUS LwipProviderSetOption(void* socket, uint32_t option, const void* value, size_t valueSize); static KSTATUS LwipProviderGetOption(void* socket, uint32_t option, void* value, size_t valueSize, size_t* actualSizeOut); static KSTATUS LwipProviderPoll(void* socket, uint32_t events, uint32_t* returnedEventsOut); static KSTATUS LwipProviderResolveHost(const char* name, uint32_t family, SYS_SOCKET_ADDRESS_K* addressOut); static KSTATUS LwipDispatchInternalDeviceControl(DEVICE_OBJECT* device, IRP* irp); static DRIVER_OBJECT gLwipDriver; static DEVICE_OBJECT* gLwipDevice; static NET_STACK_PROVIDER gLwipProvider = { NET_STACK_PROVIDER_VERSION, "lwIP", NULL, LwipProviderAttachDevice, LwipProviderReceiveFrame, }; // Implements the err To Status operation. static KSTATUS LwipErrToStatus(err_t err) { switch (err) { case ERR_OK: return STATUS_SUCCESS; case ERR_MEM: return STATUS_NO_MEMORY; case ERR_BUF: return STATUS_BUFFER_TOO_SMALL; case ERR_TIMEOUT: return STATUS_TIMEOUT; case ERR_WOULDBLOCK:return STATUS_WOULD_BLOCK; case ERR_RTE: return STATUS_NOT_FOUND; case ERR_VAL: return STATUS_INVALID_PARAMETER; case ERR_ARG: return STATUS_INVALID_PARAMETER; case ERR_USE: return STATUS_ALREADY_EXISTS; case ERR_ISCONN: return STATUS_ALREADY_EXISTS; case ERR_CONN: return STATUS_OBJECT_DEAD; case ERR_CLSD: return STATUS_OBJECT_DEAD; case ERR_ABRT: return STATUS_OBJECT_DEAD; case ERR_RST: return STATUS_OBJECT_DEAD; default: return STATUS_FAILURE; } } // Implements the ascii Is Digit operation. static int LwipAsciiIsDigit(char c) { return c >= '0' && c <= '9'; } // Parses ipv4 Part. static int LwipParseIpv4Part(const char** cursor, uint8_t* out) { const char* p = *cursor; uint32_t value = 0; uint32_t digits = 0; while (LwipAsciiIsDigit(*p)) { value = value * 10u + (uint32_t)(*p - '0'); if (value > 255u) return 0; p++; digits++; } if (digits == 0) return 0; *out = (uint8_t)value; *cursor = p; return 1; } // Parses ipv4. static int LwipParseIpv4(const char* text, ip4_addr_t* out) { if (!text || !out) return 0; const char* p = text; uint8_t a = 0, b = 0, c = 0, d = 0; if (!LwipParseIpv4Part(&p, &a) || *p++ != '.') return 0; if (!LwipParseIpv4Part(&p, &b) || *p++ != '.') return 0; if (!LwipParseIpv4Part(&p, &c) || *p++ != '.') return 0; if (!LwipParseIpv4Part(&p, &d) || *p != '\0') return 0; IP4_ADDR(out, a, b, c, d); return 1; } static KSTATUS LwipGetSettingIpv4(const char* section, const char* key, ip4_addr_t* out, int required) { char value[LWIP_SETTINGS_VALUE_MAX]; KSTATUS st = KeGetSettingString(section, key, value, sizeof(value)); if (st == STATUS_NOT_FOUND && !required) return STATUS_NOT_FOUND; if (!KSUCCESS(st)) return st; if (!LwipParseIpv4(value, out)) { DbgPrint("lwIP: invalid IPv4 setting [%s] %s=%s\n", section, key, value); return STATUS_INVALID_PARAMETER; } return STATUS_SUCCESS; } static KSTATUS LwipGetSettingIpv6(const char* section, const char* key, ip6_addr_t* out, int required) { char value[LWIP_SETTINGS_VALUE_MAX]; KSTATUS st = KeGetSettingString(section, key, value, sizeof(value)); if (st == STATUS_NOT_FOUND && !required) return STATUS_NOT_FOUND; if (!KSUCCESS(st)) return st; if (!ip6addr_aton(value, out)) { DbgPrint("lwIP: invalid IPv6 setting [%s] %s=%s\n", section, key, value); return STATUS_INVALID_PARAMETER; } return STATUS_SUCCESS; } static KSTATUS LwipLoadIpv6Config(const char* section, LWIP_NETIF_CONFIG* cfg) { char mode[LWIP_SETTINGS_VALUE_MAX]; KSTATUS st = KeGetSettingString(section, "ipv6", mode, sizeof(mode)); if (st == STATUS_NOT_FOUND) { cfg->EnableIpv6 = 1; cfg->Ipv6Autoconfig = 1; } else if (!KSUCCESS(st)) { return st; } else if (strcmp(mode, "off") == 0 || strcmp(mode, "disabled") == 0) { cfg->EnableIpv6 = 0; } else if (strcmp(mode, "auto") == 0) { cfg->EnableIpv6 = 1; cfg->Ipv6Autoconfig = 1; } else if (strcmp(mode, "static") == 0) { cfg->EnableIpv6 = 1; cfg->HasStaticIpv6 = 1; st = LwipGetSettingIpv6(section, "address6", &cfg->Address6, 1); if (!KSUCCESS(st)) return st; } else { DbgPrint("lwIP: invalid IPv6 mode [%s] ipv6=%s\n", section, mode); return STATUS_INVALID_PARAMETER; } if (!cfg->EnableIpv6) return STATUS_SUCCESS; st = LwipGetSettingIpv6(section, "dns6", &cfg->Dns6, 0); if (KSUCCESS(st)) cfg->HasDns6 = 1; else if (st != STATUS_NOT_FOUND) return st; return STATUS_SUCCESS; } // Copies section Name. static KSTATUS LwipCopySectionName(char* out, size_t outSize, const char* prefix, const char* name) { if (!out || !outSize || !prefix || !name) return STATUS_INVALID_PARAMETER; size_t prefixLen = strlen(prefix); size_t nameLen = strlen(name); if (prefixLen + nameLen + 1 > outSize) return STATUS_BUFFER_TOO_SMALL; memcpy(out, prefix, prefixLen); memcpy(out + prefixLen, name, nameLen + 1); return STATUS_SUCCESS; } // Loads config From Section. static KSTATUS LwipLoadConfigFromSection(const char* section, LWIP_NETIF_CONFIG* cfg) { if (!section || !cfg) return STATUS_INVALID_PARAMETER; char mode[LWIP_SETTINGS_VALUE_MAX]; KSTATUS st = KeGetSettingString(section, "mode", mode, sizeof(mode)); if (!KSUCCESS(st)) return st; memset(cfg, 0, sizeof(*cfg)); size_t sectionLen = strlen(section); if (sectionLen >= sizeof(cfg->Section)) return STATUS_BUFFER_TOO_SMALL; memcpy(cfg->Section, section, sectionLen + 1); if (strcmp(mode, "static") == 0) { cfg->UseDhcp = 0; st = LwipGetSettingIpv4(section, "address", &cfg->Address, 1); if (!KSUCCESS(st)) return st; st = LwipGetSettingIpv4(section, "netmask", &cfg->Netmask, 1); if (!KSUCCESS(st)) return st; st = LwipGetSettingIpv4(section, "gateway", &cfg->Gateway, 1); if (!KSUCCESS(st)) return st; st = LwipGetSettingIpv4(section, "dns", &cfg->Dns4, 1); if (!KSUCCESS(st)) return st; cfg->HasDns4 = 1; return LwipLoadIpv6Config(section, cfg); } if (strcmp(mode, "dhcp") == 0) { cfg->UseDhcp = 1; st = LwipGetSettingIpv4(section, "dns", &cfg->Dns4, 0); if (KSUCCESS(st)) cfg->HasDns4 = 1; else if (st != STATUS_NOT_FOUND) return st; return LwipLoadIpv6Config(section, cfg); } DbgPrint("lwIP: invalid network mode [%s] mode=%s\n", section, mode); return STATUS_INVALID_PARAMETER; } // Loads netif Config. static KSTATUS LwipLoadNetifConfig(const NET_DEVICE* device, LWIP_NETIF_CONFIG* cfg) { if (!device || !cfg) return STATUS_INVALID_PARAMETER; char section[64]; KSTATUS st = LwipCopySectionName(section, sizeof(section), LWIP_SETTINGS_SECTION_PREFIX, device->Name); if (!KSUCCESS(st)) return st; st = LwipLoadConfigFromSection(section, cfg); if (KSUCCESS(st)) return STATUS_SUCCESS; if (st != STATUS_NOT_FOUND) { DbgPrint("lwIP: invalid settings for [%s] (0x%x)\n", section, st); return st; } st = LwipLoadConfigFromSection(LWIP_SETTINGS_DEFAULT_SECTION, cfg); if (!KSUCCESS(st)) { DbgPrint("lwIP: missing or invalid settings for %s; expected [%s] or [%s] (0x%x)\n", device->Name, section, LWIP_SETTINGS_DEFAULT_SECTION, st); return st; } return STATUS_SUCCESS; } // Implements the socket Lock operation. static KSTATUS LwipSocketLock(LWIP_PROVIDER_SOCKET* s) { if (!s || !s->Lock) return STATUS_INVALID_PARAMETER; return KeWaitSemaphore(s->Lock); } // Implements the socket Unlock operation. static void LwipSocketUnlock(LWIP_PROVIDER_SOCKET* s) { if (s && s->Lock) KeSignalSemaphore(s->Lock); } // Implements the socket Callback operation. static void LwipSocketCallback(struct netconn* conn, enum netconn_evt evt, u16_t len) { (void)len; if (!conn) return; void* callbackArg = netconn_get_callback_arg(conn); uintptr_t rawArg = (uintptr_t)callbackArg; if (!callbackArg) return; // Accepted netconns inherit the listener callback before the wrapper can // replace lwIP's internal negative socket sentinel with our socket object. if (rawArg >= 0x80000000ull && rawArg <= 0xffffffffull) return; LWIP_PROVIDER_SOCKET* socket = (LWIP_PROVIDER_SOCKET*)callbackArg; switch (evt) { case NETCONN_EVT_RCVPLUS: socket->PollReadEvents++; break; case NETCONN_EVT_RCVMINUS: if (socket->PollReadEvents > 0) socket->PollReadEvents--; break; case NETCONN_EVT_SENDPLUS: socket->PollWriteEvents++; break; case NETCONN_EVT_SENDMINUS: if (socket->PollWriteEvents > 0) socket->PollWriteEvents--; break; case NETCONN_EVT_ERROR: socket->PollErrorEvents++; break; default: break; } } static KSTATUS LwipSocketAllocateWithConn(uint32_t domain, uint32_t type, uint32_t protocol, struct netconn* conn, LWIP_PROVIDER_SOCKET** out) { if (!conn || !out) return STATUS_INVALID_PARAMETER; *out = NULL; LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)MmAllocatePool(sizeof(*s)); if (!s) return STATUS_NO_MEMORY; memset(s, 0, sizeof(*s)); KSTATUS st = KeSemaphoreCreate(1, 1, &s->Lock); if (!KSUCCESS(st)) { MmFreePool(s); return st; } s->Conn = conn; s->Domain = domain; s->Type = type; s->Protocol = protocol; netconn_set_callback_arg(conn, s); conn->callback = LwipSocketCallback; netconn_set_recvtimeout(conn, 0); netconn_set_sendtimeout(conn, 0); *out = s; return STATUS_SUCCESS; } // Returns whether the IRP cancel token has been set by SysCancelIo. static int LwipSocketCancelRequested(volatile uint8_t* cancelRequested) { return cancelRequested && *cancelRequested; } // Computes the bounded wait slice used by cancel-aware receive and accept loops. static uint32_t LwipSocketCancelWaitSlice(uint32_t remainingMs) { if (remainingMs != 0 && remainingMs < LWIP_PROVIDER_CANCEL_POLL_MS) return remainingMs; return LWIP_PROVIDER_CANCEL_POLL_MS; } // Waits for a receive-style netconn mailbox while periodically observing cancellation. static KSTATUS LwipSocketWaitRecvNetbuf(LWIP_PROVIDER_SOCKET* s, struct netbuf** bufferOut, volatile uint8_t* cancelRequested, err_t* errOut) { if (!s || !s->Conn || !bufferOut || !errOut) return STATUS_INVALID_PARAMETER; *bufferOut = NULL; *errOut = ERR_OK; if (!cancelRequested || s->NonBlocking) { *errOut = netconn_recv(s->Conn, bufferOut); return STATUS_SUCCESS; } uint32_t originalTimeout = s->ReceiveTimeoutMs; uint32_t elapsed = 0; for (;;) { if (LwipSocketCancelRequested(cancelRequested)) { netconn_set_recvtimeout(s->Conn, originalTimeout); return STATUS_CANCELLED; } uint32_t remaining = 0; if (originalTimeout != 0) { if (elapsed >= originalTimeout) { netconn_set_recvtimeout(s->Conn, originalTimeout); *errOut = ERR_TIMEOUT; return STATUS_SUCCESS; } remaining = originalTimeout - elapsed; } uint32_t slice = LwipSocketCancelWaitSlice(remaining); netconn_set_recvtimeout(s->Conn, slice); err_t err = netconn_recv(s->Conn, bufferOut); if (err != ERR_TIMEOUT) { netconn_set_recvtimeout(s->Conn, originalTimeout); *errOut = err; return STATUS_SUCCESS; } elapsed += slice; } } // Waits for accept while preserving the caller's receive timeout model. static KSTATUS LwipSocketWaitAccept(LWIP_PROVIDER_SOCKET* s, struct netconn** acceptedOut, volatile uint8_t* cancelRequested, err_t* errOut) { if (!s || !s->Conn || !acceptedOut || !errOut) return STATUS_INVALID_PARAMETER; *acceptedOut = NULL; *errOut = ERR_OK; if (!cancelRequested || s->NonBlocking) { *errOut = netconn_accept(s->Conn, acceptedOut); return STATUS_SUCCESS; } uint32_t originalTimeout = s->ReceiveTimeoutMs; uint32_t elapsed = 0; for (;;) { if (LwipSocketCancelRequested(cancelRequested)) { netconn_set_recvtimeout(s->Conn, originalTimeout); return STATUS_CANCELLED; } uint32_t remaining = 0; if (originalTimeout != 0) { if (elapsed >= originalTimeout) { netconn_set_recvtimeout(s->Conn, originalTimeout); *errOut = ERR_TIMEOUT; return STATUS_SUCCESS; } remaining = originalTimeout - elapsed; } uint32_t slice = LwipSocketCancelWaitSlice(remaining); netconn_set_recvtimeout(s->Conn, slice); err_t err = netconn_accept(s->Conn, acceptedOut); if (err != ERR_TIMEOUT) { netconn_set_recvtimeout(s->Conn, originalTimeout); *errOut = err; return STATUS_SUCCESS; } elapsed += slice; } } static int LwipSocketAddressValid(const SYS_SOCKET_ADDRESS_K* address, size_t addressSize) { if (!address || addressSize < sizeof(SYS_SOCKET_ADDRESS_K)) return 0; if (address->Size < sizeof(SYS_SOCKET_ADDRESS_K)) return 0; return address->Family == SYS_AF_INET || address->Family == SYS_AF_INET6; } static int LwipSocketFamilyMatches(const LWIP_PROVIDER_SOCKET* s, const SYS_SOCKET_ADDRESS_K* address) { if (!s || !address) return 0; if (s->Domain == SYS_AF_INET) return address->Family == SYS_AF_INET; if (s->Domain == SYS_AF_INET6) return address->Family == SYS_AF_INET6; return 0; } // Implements the socket Make Ip operation. static KSTATUS LwipSocketMakeIp(const SYS_SOCKET_ADDRESS_K* address, ip_addr_t* out) { if (!address || !out) return STATUS_INVALID_PARAMETER; if (address->Family == SYS_AF_INET) { IP_ADDR4(out, address->Address[0], address->Address[1], address->Address[2], address->Address[3]); return STATUS_SUCCESS; } if (address->Family == SYS_AF_INET6) { uint32_t a0 = ((uint32_t)address->Address[0] << 24) | ((uint32_t)address->Address[1] << 16) | ((uint32_t)address->Address[2] << 8) | (uint32_t)address->Address[3]; uint32_t a1 = ((uint32_t)address->Address[4] << 24) | ((uint32_t)address->Address[5] << 16) | ((uint32_t)address->Address[6] << 8) | (uint32_t)address->Address[7]; uint32_t a2 = ((uint32_t)address->Address[8] << 24) | ((uint32_t)address->Address[9] << 16) | ((uint32_t)address->Address[10] << 8) | (uint32_t)address->Address[11]; uint32_t a3 = ((uint32_t)address->Address[12] << 24) | ((uint32_t)address->Address[13] << 16) | ((uint32_t)address->Address[14] << 8) | (uint32_t)address->Address[15]; IP_ADDR6_HOST(out, a0, a1, a2, a3); ip6_addr_set_zone(ip_2_ip6(out), (u8_t)address->ScopeId); return STATUS_SUCCESS; } return STATUS_NOT_SUPPORTED; } // Implements the socket Fill Address operation. static void LwipSocketFillAddress(SYS_SOCKET_ADDRESS_K* out, const ip_addr_t* ip, u16_t port) { if (!out) return; memset(out, 0, sizeof(*out)); out->Size = sizeof(*out); out->Port = port; if (!ip) return; if (IP_IS_V6(ip)) { out->Family = SYS_AF_INET6; out->ScopeId = ip6_addr_zone(ip_2_ip6(ip)); for (size_t i = 0; i < 4; ++i) { uint32_t word = lwip_htonl(ip_2_ip6(ip)->addr[i]); out->Address[i * 4 + 0] = (uint8_t)(word >> 24); out->Address[i * 4 + 1] = (uint8_t)(word >> 16); out->Address[i * 4 + 2] = (uint8_t)(word >> 8); out->Address[i * 4 + 3] = (uint8_t)word; } return; } out->Family = SYS_AF_INET; out->Address[0] = (uint8_t)ip4_addr1(ip_2_ip4(ip)); out->Address[1] = (uint8_t)ip4_addr2(ip_2_ip4(ip)); out->Address[2] = (uint8_t)ip4_addr3(ip_2_ip4(ip)); out->Address[3] = (uint8_t)ip4_addr4(ip_2_ip4(ip)); } // Implements the low Level Output operation. static err_t LwipLowLevelOutput(struct netif* netif, struct pbuf* p) { LWIP_BINDING* b = (LWIP_BINDING*)netif->state; if (!b || !b->Device || !p || p->tot_len < NET_ETHERNET_HEADER_LENGTH) return ERR_ARG; if (p->tot_len > NET_ETHERNET_MAX_FRAME) return ERR_BUF; uint8_t* frame = (uint8_t*)MmAllocatePool(p->tot_len); if (!frame) return ERR_MEM; size_t off = 0; for (struct pbuf* q = p; q != NULL; q = q->next) { if (off + q->len > p->tot_len) { MmFreePool(frame); return ERR_BUF; } memcpy(frame + off, q->payload, q->len); off += q->len; } KSTATUS st = NetSendFrame(b->Device->Index, frame, p->tot_len); MmFreePool(frame); return KSUCCESS(st) ? ERR_OK : ERR_IF; } // Implements the netif Init operation. static err_t LwipNetifInit(struct netif* netif) { LWIP_BINDING* b = (LWIP_BINDING*)netif->state; if (!b || !b->Device) return ERR_ARG; NET_DEVICE* device = b->Device; netif->name[0] = 'e'; netif->name[1] = 'n'; netif->output = etharp_output; #if LWIP_IPV6 netif->output_ip6 = ethip6_output; #endif netif->linkoutput = LwipLowLevelOutput; netif->mtu = (u16_t)(device->Mtu ? device->Mtu : NET_MTU_ETHERNET); netif->hwaddr_len = NET_ETHERNET_ADDRESS_LENGTH; memcpy(netif->hwaddr, device->HardwareAddress, NET_ETHERNET_ADDRESS_LENGTH); netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET; #if LWIP_IPV6_MLD netif->flags |= NETIF_FLAG_MLD6; #endif return ERR_OK; } // Attaches callback. static void LwipAttachCallback(void* arg) { LWIP_ATTACH_CTX* ctx = (LWIP_ATTACH_CTX*)arg; LWIP_BINDING* b = ctx->Binding; NET_DEVICE* device = b->Device; ip4_addr_t ipaddr; ip4_addr_t netmask; ip4_addr_t gw; if (b->Config.UseDhcp) { ip4_addr_set_zero(&ipaddr); ip4_addr_set_zero(&netmask); ip4_addr_set_zero(&gw); } else { ipaddr = b->Config.Address; netmask = b->Config.Netmask; gw = b->Config.Gateway; } struct netif* n = netif_add(&b->Netif, &ipaddr, &netmask, &gw, b, LwipNetifInit, tcpip_input); if (!n) { ctx->Result = ERR_IF; if (ctx->Done) KeSignalSemaphore(ctx->Done); return; } netif_set_link_up(n); netif_set_up(n); if (netif_default == NULL) netif_set_default(n); if (b->Config.HasDns4) { ip_addr_t dns; ip_addr_copy_from_ip4(dns, b->Config.Dns4); dns_setserver(0, &dns); } #if LWIP_IPV6 if (b->Config.EnableIpv6) { netif_create_ip6_linklocal_address(n, 1); if (b->Config.Ipv6Autoconfig) n->ip6_autoconfig_enabled = 1; if (b->Config.HasStaticIpv6) { netif_ip6_addr_set(n, 1, &b->Config.Address6); netif_ip6_addr_set_state(n, 1, IP6_ADDR_PREFERRED); } if (b->Config.HasDns6) { ip_addr_t dns6; ip_addr_copy_from_ip6(dns6, b->Config.Dns6); dns_setserver(1, &dns6); } } #endif if (b->Config.UseDhcp) { #if LWIP_DHCP err_t dhcpErr = dhcp_start(n); if (dhcpErr != ERR_OK) { ctx->Result = dhcpErr; if (ctx->Done) KeSignalSemaphore(ctx->Done); return; } #else ctx->Result = ERR_IF; if (ctx->Done) KeSignalSemaphore(ctx->Done); return; #endif } b->Attached = 1; ctx->Result = ERR_OK; if (ctx->Done) KeSignalSemaphore(ctx->Done); } // Initializes done. static void LwipInitDone(void* arg) { SEMAPHORE* sem = (SEMAPHORE*)arg; KeSignalSemaphore(sem); } // Implements the stack Initialize operation. static KSTATUS LwipStackInitialize(const HAL_API* hal) { if (gLwipInitialized) return STATUS_SUCCESS; if (!hal || !hal->Timer.QueryTicks) return STATUS_INVALID_PARAMETER; LwipPortSetTimerQuery(hal->Timer.QueryTicks); SEMAPHORE* done = 0; KSTATUS st = KeSemaphoreCreate(0, 1, &done); if (!KSUCCESS(st)) return st; tcpip_init(LwipInitDone, done); st = KeWaitSemaphore(done); ObDereferenceObject(&done->Header); if (!KSUCCESS(st)) return st; gLwipInitialized = 1; DbgPrint("lwIP: tcpip thread initialized\n"); return STATUS_SUCCESS; } // Implements the provider Attach Device operation. static KSTATUS LwipProviderAttachDevice(NET_DEVICE* device) { if (!gLwipInitialized) return STATUS_NOT_SUPPORTED; if (!device || device->Index >= LWIP_MAX_NETIFS) return STATUS_INVALID_PARAMETER; if (device->HardwareAddressLength != NET_ETHERNET_ADDRESS_LENGTH) return STATUS_NOT_SUPPORTED; if (gBindings[device->Index]) return STATUS_ALREADY_EXISTS; LWIP_BINDING* b = (LWIP_BINDING*)MmAllocatePool(sizeof(*b)); if (!b) return STATUS_NO_MEMORY; memset(b, 0, sizeof(*b)); b->Device = device; KSTATUS st = LwipLoadNetifConfig(device, &b->Config); if (!KSUCCESS(st)) { MmFreePool(b); return st; } SEMAPHORE* done = 0; st = KeSemaphoreCreate(0, 1, &done); if (!KSUCCESS(st)) { MmFreePool(b); return st; } LWIP_ATTACH_CTX ctx; ctx.Binding = b; ctx.Done = done; ctx.Result = ERR_IF; err_t err = tcpip_callback(LwipAttachCallback, &ctx); if (err == ERR_OK) st = KeWaitSemaphore(done); else st = STATUS_FAILURE; ObDereferenceObject(&done->Header); if (!KSUCCESS(st) || ctx.Result != ERR_OK) { MmFreePool(b); return KSUCCESS(st) ? STATUS_FAILURE : st; } gBindings[device->Index] = b; DbgPrint("lwIP: attached %s as netif %c%c%u using [%s] mode=%s ipv6=%s\n", device->Name, b->Netif.name[0], b->Netif.name[1], b->Netif.num, b->Config.Section, b->Config.UseDhcp ? "dhcp" : "static", b->Config.EnableIpv6 ? (b->Config.HasStaticIpv6 ? "static" : "auto") : "off"); return STATUS_SUCCESS; } // Implements the provider Receive Frame operation. static KSTATUS LwipProviderReceiveFrame(NET_DEVICE* device, const void* frame, size_t length) { if (!gLwipInitialized) return STATUS_NOT_SUPPORTED; if (!device || device->Index >= LWIP_MAX_NETIFS || !frame) return STATUS_INVALID_PARAMETER; LWIP_BINDING* b = gBindings[device->Index]; if (!b || !b->Attached) return STATUS_NOT_FOUND; if (length < NET_ETHERNET_HEADER_LENGTH || length > NET_ETHERNET_MAX_FRAME) return STATUS_INVALID_PARAMETER; struct pbuf* p = pbuf_alloc(PBUF_RAW, (u16_t)length, PBUF_POOL); if (!p) p = pbuf_alloc(PBUF_RAW, (u16_t)length, PBUF_RAM); if (!p) return STATUS_NO_MEMORY; if (pbuf_take(p, frame, (u16_t)length) != ERR_OK) { pbuf_free(p); return STATUS_BUFFER_TOO_SMALL; } err_t err = b->Netif.input(p, &b->Netif); if (err != ERR_OK) { pbuf_free(p); return STATUS_FAILURE; } return STATUS_SUCCESS; } // Implements the provider Socket Create operation. static KSTATUS LwipProviderSocketCreate(uint32_t domain, uint32_t type, uint32_t protocol, void** outSocket) { if (!outSocket) return STATUS_INVALID_PARAMETER; *outSocket = NULL; if (!gLwipInitialized) return STATUS_NOT_SUPPORTED; if (domain != SYS_AF_INET && domain != SYS_AF_INET6) return STATUS_NOT_SUPPORTED; int ipv6 = domain == SYS_AF_INET6; enum netconn_type netconnType; u8_t rawProtocol = 0; if (type == SYS_SOCK_STREAM) { if (protocol != 0 && protocol != SYS_IPPROTO_TCP) return STATUS_NOT_SUPPORTED; netconnType = ipv6 ? NETCONN_TCP_IPV6 : NETCONN_TCP; protocol = SYS_IPPROTO_TCP; } else if (type == SYS_SOCK_DGRAM) { if (protocol != 0 && protocol != SYS_IPPROTO_UDP) return STATUS_NOT_SUPPORTED; netconnType = ipv6 ? NETCONN_UDP_IPV6 : NETCONN_UDP; protocol = SYS_IPPROTO_UDP; } else if (type == SYS_SOCK_RAW) { if ((!ipv6 && protocol != SYS_IPPROTO_ICMP) || (ipv6 && protocol != SYS_IPPROTO_ICMPV6)) return STATUS_NOT_SUPPORTED; netconnType = ipv6 ? NETCONN_RAW_IPV6 : NETCONN_RAW; rawProtocol = ipv6 ? IP6_NEXTH_ICMP6 : IP_PROTO_ICMP; } else { return STATUS_NOT_SUPPORTED; } struct netconn* conn = netconn_new_with_proto_and_callback(netconnType, rawProtocol, NULL); if (!conn) return STATUS_NO_MEMORY; LWIP_PROVIDER_SOCKET* s = NULL; KSTATUS st = LwipSocketAllocateWithConn(domain, type, protocol, conn, &s); if (!KSUCCESS(st)) { netconn_delete(conn); return st; } *outSocket = s; return STATUS_SUCCESS; } // Implements the provider Socket Close operation. static void LwipProviderSocketClose(void* socket) { LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket; if (!s) return; if (s->ReceiveBuffer) { netbuf_delete(s->ReceiveBuffer); s->ReceiveBuffer = NULL; s->ReceiveOffset = 0; } if (s->Conn) { netconn_set_callback_arg(s->Conn, NULL); (void)netconn_close(s->Conn); netconn_delete(s->Conn); s->Conn = NULL; } if (s->Lock) { ObDereferenceObject(&s->Lock->Header); s->Lock = NULL; } MmFreePool(s); } // Implements the provider Bind operation. static KSTATUS LwipProviderBind(void* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize) { LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket; if (!s || !address) return STATUS_INVALID_PARAMETER; if (!LwipSocketAddressValid(address, addressSize)) return STATUS_BUFFER_TOO_SMALL; if ((s->Type != SYS_SOCK_STREAM && s->Type != SYS_SOCK_DGRAM) || !s->Conn) return STATUS_INVALID_PARAMETER; if (!LwipSocketFamilyMatches(s, address)) return STATUS_NOT_SUPPORTED; KSTATUS st = LwipSocketLock(s); if (!KSUCCESS(st)) return st; if (s->Connected || s->Listening || s->ShutdownRead || s->ShutdownWrite) { LwipSocketUnlock(s); return STATUS_INVALID_PARAMETER; } ip_addr_t ip; st = LwipSocketMakeIp(address, &ip); if (!KSUCCESS(st)) { LwipSocketUnlock(s); return st; } err_t err = netconn_bind(s->Conn, &ip, address->Port); if (err == ERR_OK) s->Bound = 1; LwipSocketUnlock(s); return LwipErrToStatus(err); } // Implements the provider Listen operation. static KSTATUS LwipProviderListen(void* socket, uint32_t backlog) { LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket; if (!s) return STATUS_INVALID_PARAMETER; if (s->Type != SYS_SOCK_STREAM || !s->Conn) return STATUS_INVALID_PARAMETER; if (backlog > 255u) backlog = 255u; KSTATUS st = LwipSocketLock(s); if (!KSUCCESS(st)) return st; if (s->Connected || s->ShutdownRead || s->ShutdownWrite) { LwipSocketUnlock(s); return STATUS_INVALID_PARAMETER; } err_t err = netconn_listen_with_backlog(s->Conn, (u8_t)backlog); if (err == ERR_OK) { s->Bound = 1; s->Listening = 1; } LwipSocketUnlock(s); return LwipErrToStatus(err); } // Implements the provider Accept operation. static KSTATUS LwipProviderAccept(void* socket, void** acceptedOut, SYS_SOCKET_ADDRESS_K* remoteOut, volatile uint8_t* cancelRequested) { LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket; if (!s || !acceptedOut) return STATUS_INVALID_PARAMETER; *acceptedOut = NULL; if (remoteOut) memset(remoteOut, 0, sizeof(*remoteOut)); if (s->Type != SYS_SOCK_STREAM || !s->Conn) return STATUS_INVALID_PARAMETER; KSTATUS st = LwipSocketLock(s); if (!KSUCCESS(st)) return st; if (!s->Listening || s->ShutdownRead || s->ShutdownWrite) { LwipSocketUnlock(s); return STATUS_INVALID_PARAMETER; } struct netconn* acceptedConn = NULL; err_t err = ERR_OK; st = LwipSocketWaitAccept(s, &acceptedConn, cancelRequested, &err); LwipSocketUnlock(s); if (!KSUCCESS(st)) return st; if (err != ERR_OK) return LwipErrToStatus(err); if (!acceptedConn) return STATUS_FAILURE; LWIP_PROVIDER_SOCKET* accepted = NULL; st = LwipSocketAllocateWithConn(s->Domain, s->Type, s->Protocol, acceptedConn, &accepted); if (!KSUCCESS(st)) { (void)netconn_close(acceptedConn); netconn_delete(acceptedConn); return st; } accepted->Connected = 1; if (remoteOut) { ip_addr_t peerIp; u16_t peerPort = 0; err = netconn_peer(acceptedConn, &peerIp, &peerPort); if (err == ERR_OK) { LwipSocketFillAddress(remoteOut, &peerIp, peerPort); } else { memset(remoteOut, 0, sizeof(*remoteOut)); remoteOut->Size = sizeof(*remoteOut); remoteOut->Family = s->Domain; } } *acceptedOut = accepted; return STATUS_SUCCESS; } // Implements the provider Connect operation. static KSTATUS LwipProviderConnect(void* socket, const SYS_SOCKET_ADDRESS_K* address, size_t addressSize, volatile uint8_t* cancelRequested) { LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket; if (!s || !address) return STATUS_INVALID_PARAMETER; if (!LwipSocketAddressValid(address, addressSize)) return STATUS_BUFFER_TOO_SMALL; if ((s->Type != SYS_SOCK_STREAM && s->Type != SYS_SOCK_DGRAM) || !s->Conn) return STATUS_INVALID_PARAMETER; if (!LwipSocketFamilyMatches(s, address)) return STATUS_NOT_SUPPORTED; KSTATUS st = LwipSocketLock(s); if (!KSUCCESS(st)) return st; if (s->Listening) { LwipSocketUnlock(s); return STATUS_INVALID_PARAMETER; } if (s->ShutdownRead || s->ShutdownWrite) { LwipSocketUnlock(s); return STATUS_OBJECT_DEAD; } ip_addr_t ip; st = LwipSocketMakeIp(address, &ip); if (!KSUCCESS(st)) { LwipSocketUnlock(s); return st; } if (!cancelRequested || s->Type != SYS_SOCK_STREAM) { err_t err = netconn_connect(s->Conn, &ip, address->Port); if (err == ERR_OK) s->Connected = 1; LwipSocketUnlock(s); return LwipErrToStatus(err); } u8_t wasNonBlocking = netconn_is_nonblocking(s->Conn) ? 1 : 0; netconn_set_nonblocking(s->Conn, 1); err_t err = netconn_connect(s->Conn, &ip, address->Port); if (err == ERR_OK) { netconn_set_nonblocking(s->Conn, wasNonBlocking || s->NonBlocking); s->Connected = 1; LwipSocketUnlock(s); return STATUS_SUCCESS; } if (err != ERR_INPROGRESS) { netconn_set_nonblocking(s->Conn, wasNonBlocking || s->NonBlocking); LwipSocketUnlock(s); return LwipErrToStatus(err); } for (;;) { if (LwipSocketCancelRequested(cancelRequested)) { (void)netconn_close(s->Conn); s->ShutdownRead = 1; s->ShutdownWrite = 1; netconn_set_nonblocking(s->Conn, wasNonBlocking || s->NonBlocking); LwipSocketUnlock(s); return STATUS_CANCELLED; } if (s->Conn->state != NETCONN_CONNECT) { err = netconn_err(s->Conn); netconn_set_nonblocking(s->Conn, wasNonBlocking || s->NonBlocking); if (err == ERR_OK) { s->Connected = 1; LwipSocketUnlock(s); return STATUS_SUCCESS; } LwipSocketUnlock(s); return LwipErrToStatus(err); } LwipSocketUnlock(s); (void)KeDelayThread(LWIP_PROVIDER_CANCEL_POLL_MS); st = LwipSocketLock(s); if (!KSUCCESS(st)) return st; } } // Implements the provider Send operation. static KSTATUS LwipProviderSend(void* socket, const void* buffer, size_t length, uint32_t flags, size_t* sentOut) { LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket; if (sentOut) *sentOut = 0; if (!s || (!buffer && length)) return STATUS_INVALID_PARAMETER; if (flags != 0) return STATUS_NOT_SUPPORTED; if (length > LWIP_PROVIDER_SOCKET_IO_MAX_BYTES) return STATUS_INVALID_PARAMETER; if (length == 0) return STATUS_SUCCESS; if (s->Type != SYS_SOCK_STREAM || !s->Conn) return STATUS_INVALID_PARAMETER; KSTATUS st = LwipSocketLock(s); if (!KSUCCESS(st)) return st; if (s->Listening) { LwipSocketUnlock(s); return STATUS_INVALID_PARAMETER; } if (s->ShutdownWrite) { LwipSocketUnlock(s); return STATUS_OBJECT_DEAD; } size_t written = 0; err_t err = netconn_write_partly(s->Conn, buffer, length, NETCONN_COPY, &written); if (err == ERR_OK && sentOut) *sentOut = written; LwipSocketUnlock(s); return LwipErrToStatus(err); } static KSTATUS LwipReceiveFromBuffered(LWIP_PROVIDER_SOCKET* s, uint8_t* out, size_t capacity, size_t* copiedOut) { size_t copied = copiedOut ? *copiedOut : 0; while (s->ReceiveBuffer && copied < capacity) { struct netbuf* nb = s->ReceiveBuffer; void* data = NULL; u16_t segLen = 0; err_t err = netbuf_data(nb, &data, &segLen); if (err != ERR_OK || !data) { netbuf_delete(nb); s->ReceiveBuffer = NULL; s->ReceiveOffset = 0; return LwipErrToStatus(err); } if (s->ReceiveOffset >= segLen) { s->ReceiveOffset = 0; if (netbuf_next(nb) < 0) { netbuf_delete(nb); s->ReceiveBuffer = NULL; } continue; } size_t available = (size_t)segLen - s->ReceiveOffset; size_t want = capacity - copied; size_t take = (available < want) ? available : want; memcpy(out + copied, (const uint8_t*)data + s->ReceiveOffset, take); copied += take; s->ReceiveOffset += take; if (s->ReceiveOffset >= segLen) { s->ReceiveOffset = 0; if (netbuf_next(nb) < 0) { netbuf_delete(nb); s->ReceiveBuffer = NULL; } } } if (copiedOut) *copiedOut = copied; return STATUS_SUCCESS; } // Implements the provider Receive operation. static KSTATUS LwipProviderReceive(void* socket, void* buffer, size_t length, uint32_t flags, size_t* receivedOut, volatile uint8_t* cancelRequested) { LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket; if (receivedOut) *receivedOut = 0; if (!s || (!buffer && length)) return STATUS_INVALID_PARAMETER; if (flags != 0) return STATUS_NOT_SUPPORTED; if (length > LWIP_PROVIDER_SOCKET_IO_MAX_BYTES) return STATUS_INVALID_PARAMETER; if (length == 0) return STATUS_SUCCESS; if (s->Type != SYS_SOCK_STREAM || !s->Conn) return STATUS_INVALID_PARAMETER; KSTATUS st = LwipSocketLock(s); if (!KSUCCESS(st)) return st; if (s->Listening) { LwipSocketUnlock(s); return STATUS_INVALID_PARAMETER; } if (s->ShutdownRead) { LwipSocketUnlock(s); return STATUS_OBJECT_DEAD; } size_t copied = 0; st = LwipReceiveFromBuffered(s, (uint8_t*)buffer, length, &copied); if (!KSUCCESS(st)) { LwipSocketUnlock(s); return st; } if (copied == 0 && !s->ReceiveBuffer) { struct netbuf* nb = NULL; err_t err = ERR_OK; st = LwipSocketWaitRecvNetbuf(s, &nb, cancelRequested, &err); if (!KSUCCESS(st)) { LwipSocketUnlock(s); return st; } if (err == ERR_CLSD) { s->ShutdownRead = 1; if (receivedOut) *receivedOut = 0; LwipSocketUnlock(s); return STATUS_SUCCESS; } if (err != ERR_OK) { LwipSocketUnlock(s); return LwipErrToStatus(err); } s->ReceiveBuffer = nb; s->ReceiveOffset = 0; st = LwipReceiveFromBuffered(s, (uint8_t*)buffer, length, &copied); if (!KSUCCESS(st)) { LwipSocketUnlock(s); return st; } } if (receivedOut) *receivedOut = copied; LwipSocketUnlock(s); return STATUS_SUCCESS; } static KSTATUS LwipProviderSendTo(void* socket, const void* buffer, size_t length, uint32_t flags, const SYS_SOCKET_ADDRESS_K* remote, size_t remoteSize, size_t* sentOut) { LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket; if (sentOut) *sentOut = 0; if (!s || (!buffer && length) || !remote) return STATUS_INVALID_PARAMETER; if (!LwipSocketAddressValid(remote, remoteSize)) return STATUS_BUFFER_TOO_SMALL; if (flags != 0) return STATUS_NOT_SUPPORTED; if (s->Type == SYS_SOCK_DGRAM && length > LWIP_PROVIDER_SOCKET_UDP_MAX_BYTES) return STATUS_INVALID_PARAMETER; if (s->Type == SYS_SOCK_RAW && length > LWIP_PROVIDER_SOCKET_RAW_MAX_BYTES) return STATUS_INVALID_PARAMETER; if (!LwipSocketFamilyMatches(s, remote)) return STATUS_NOT_SUPPORTED; if (length == 0) return STATUS_SUCCESS; if ((s->Type != SYS_SOCK_DGRAM && s->Type != SYS_SOCK_RAW) || !s->Conn) return STATUS_INVALID_PARAMETER; KSTATUS st = LwipSocketLock(s); if (!KSUCCESS(st)) return st; if (s->ShutdownWrite) { LwipSocketUnlock(s); return STATUS_OBJECT_DEAD; } struct netbuf* nb = netbuf_new(); if (!nb) { LwipSocketUnlock(s); return STATUS_NO_MEMORY; } err_t err = netbuf_ref(nb, buffer, (u16_t)length); if (err == ERR_OK) { ip_addr_t ip; KSTATUS makeStatus = LwipSocketMakeIp(remote, &ip); if (KSUCCESS(makeStatus)) err = netconn_sendto(s->Conn, nb, &ip, remote->Port); else err = ERR_ARG; } netbuf_delete(nb); if (err == ERR_OK && sentOut) *sentOut = length; LwipSocketUnlock(s); return LwipErrToStatus(err); } static KSTATUS LwipProviderReceiveFrom(void* socket, void* buffer, size_t length, uint32_t flags, SYS_SOCKET_ADDRESS_K* remoteOut, size_t* receivedOut, volatile uint8_t* cancelRequested) { LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket; if (receivedOut) *receivedOut = 0; if (!s || (!buffer && length)) return STATUS_INVALID_PARAMETER; if (flags != 0) return STATUS_NOT_SUPPORTED; if (s->Type == SYS_SOCK_DGRAM && length > LWIP_PROVIDER_SOCKET_UDP_MAX_BYTES) return STATUS_INVALID_PARAMETER; if (s->Type == SYS_SOCK_RAW && length > LWIP_PROVIDER_SOCKET_RAW_MAX_BYTES) return STATUS_INVALID_PARAMETER; if (length == 0) return STATUS_SUCCESS; if ((s->Type != SYS_SOCK_DGRAM && s->Type != SYS_SOCK_RAW) || !s->Conn) return STATUS_INVALID_PARAMETER; KSTATUS st = LwipSocketLock(s); if (!KSUCCESS(st)) return st; if (s->ShutdownRead) { LwipSocketUnlock(s); return STATUS_OBJECT_DEAD; } struct netbuf* nb = NULL; err_t err = ERR_OK; st = LwipSocketWaitRecvNetbuf(s, &nb, cancelRequested, &err); if (!KSUCCESS(st)) { LwipSocketUnlock(s); return st; } if (err != ERR_OK) { LwipSocketUnlock(s); return LwipErrToStatus(err); } if (!nb) { LwipSocketUnlock(s); return STATUS_FAILURE; } size_t copied = 0; do { void* data = NULL; u16_t segLen = 0; err = netbuf_data(nb, &data, &segLen); if (err != ERR_OK || !data) break; size_t remaining = length - copied; size_t take = ((size_t)segLen < remaining) ? (size_t)segLen : remaining; if (take) { memcpy((uint8_t*)buffer + copied, data, take); copied += take; } } while (copied < length && netbuf_next(nb) >= 0); if (remoteOut) { const ip_addr_t* from = netbuf_fromaddr(nb); if (from) { LwipSocketFillAddress(remoteOut, from, netbuf_fromport(nb)); } else { memset(remoteOut, 0, sizeof(*remoteOut)); remoteOut->Size = sizeof(*remoteOut); remoteOut->Family = s->Domain; } } netbuf_delete(nb); if (receivedOut) *receivedOut = copied; LwipSocketUnlock(s); return LwipErrToStatus(err); } // Implements the provider Set Option operation. static KSTATUS LwipProviderSetOption(void* socket, uint32_t option, const void* value, size_t valueSize) { LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket; if (!s || !value) return STATUS_INVALID_PARAMETER; if (valueSize != sizeof(uint32_t)) return STATUS_BUFFER_TOO_SMALL; if (!s->Conn) return STATUS_INVALID_PARAMETER; uint32_t v = 0; memcpy(&v, value, sizeof(v)); KSTATUS st = LwipSocketLock(s); if (!KSUCCESS(st)) return st; switch (option) { case SYS_SOCKET_OPT_RECV_TIMEOUT_MS: s->ReceiveTimeoutMs = v; netconn_set_recvtimeout(s->Conn, v); break; case SYS_SOCKET_OPT_SEND_TIMEOUT_MS: s->SendTimeoutMs = v; netconn_set_sendtimeout(s->Conn, v); break; case SYS_SOCKET_OPT_NONBLOCKING: s->NonBlocking = v ? 1 : 0; netconn_set_nonblocking(s->Conn, s->NonBlocking); break; default: LwipSocketUnlock(s); return STATUS_NOT_SUPPORTED; } LwipSocketUnlock(s); return STATUS_SUCCESS; } // Implements the provider Get Option operation. static KSTATUS LwipProviderGetOption(void* socket, uint32_t option, void* value, size_t valueSize, size_t* actualSizeOut) { LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket; if (actualSizeOut) *actualSizeOut = sizeof(uint32_t); if (!s || !value) return STATUS_INVALID_PARAMETER; if (valueSize < sizeof(uint32_t)) return STATUS_BUFFER_TOO_SMALL; if (!s->Conn) return STATUS_INVALID_PARAMETER; KSTATUS st = LwipSocketLock(s); if (!KSUCCESS(st)) return st; uint32_t v = 0; switch (option) { case SYS_SOCKET_OPT_RECV_TIMEOUT_MS: v = s->ReceiveTimeoutMs; break; case SYS_SOCKET_OPT_SEND_TIMEOUT_MS: v = s->SendTimeoutMs; break; case SYS_SOCKET_OPT_NONBLOCKING: v = s->NonBlocking ? 1u : 0u; break; default: LwipSocketUnlock(s); return STATUS_NOT_SUPPORTED; } memcpy(value, &v, sizeof(v)); LwipSocketUnlock(s); return STATUS_SUCCESS; } // Implements the provider Poll operation. static KSTATUS LwipProviderPoll(void* socket, uint32_t events, uint32_t* returnedEventsOut) { LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket; if (!s || !returnedEventsOut) return STATUS_INVALID_PARAMETER; uint32_t revents = 0; KSTATUS st = LwipSocketLock(s); if (!KSUCCESS(st)) return st; if (!s->Conn) { *returnedEventsOut = SYS_POLL_INVALID; LwipSocketUnlock(s); return STATUS_SUCCESS; } err_t lastErr = netconn_err(s->Conn); if (s->PollErrorEvents > 0 || (lastErr != ERR_OK && lastErr != ERR_INPROGRESS)) revents |= SYS_POLL_ERROR; if ((events & SYS_POLL_READ) != 0) { if (s->ReceiveBuffer || s->PollReadEvents > 0 || s->Conn->recv_avail > 0 || s->ShutdownRead) revents |= SYS_POLL_READ; } if ((events & SYS_POLL_WRITE) != 0) { if (!s->Listening && !s->ShutdownWrite && !s->PollErrorEvents) revents |= SYS_POLL_WRITE; } if (s->ShutdownRead || s->ShutdownWrite) revents |= SYS_POLL_HANGUP; *returnedEventsOut = revents; LwipSocketUnlock(s); return STATUS_SUCCESS; } // Implements the provider Shutdown operation. static KSTATUS LwipProviderShutdown(void* socket, uint32_t how) { LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket; if (!s) return STATUS_INVALID_PARAMETER; if (how != SYS_SHUTDOWN_READ && how != SYS_SHUTDOWN_WRITE && how != SYS_SHUTDOWN_BOTH) return STATUS_INVALID_PARAMETER; if (!s->Conn) return STATUS_INVALID_PARAMETER; KSTATUS st = LwipSocketLock(s); if (!KSUCCESS(st)) return st; u8_t shutRx = (how == SYS_SHUTDOWN_READ || how == SYS_SHUTDOWN_BOTH) ? 1 : 0; u8_t shutTx = (how == SYS_SHUTDOWN_WRITE || how == SYS_SHUTDOWN_BOTH) ? 1 : 0; err_t err = netconn_shutdown(s->Conn, shutRx, shutTx); if (err == ERR_OK) { if (shutRx) s->ShutdownRead = 1; if (shutTx) s->ShutdownWrite = 1; } LwipSocketUnlock(s); return LwipErrToStatus(err); } // Implements the provider Resolve Host operation. static KSTATUS LwipProviderResolveHost(const char* name, uint32_t family, SYS_SOCKET_ADDRESS_K* addressOut) { if (!name || !addressOut) return STATUS_INVALID_PARAMETER; if (!gLwipInitialized) return STATUS_NOT_SUPPORTED; u8_t dnsType; if (family == SYS_AF_INET) dnsType = NETCONN_DNS_IPV4; else if (family == SYS_AF_INET6) dnsType = NETCONN_DNS_IPV6; else return STATUS_NOT_SUPPORTED; ip_addr_t resolved; err_t err = netconn_gethostbyname_addrtype(name, &resolved, dnsType); if (err != ERR_OK) return LwipErrToStatus(err); if ((family == SYS_AF_INET && !IP_IS_V4(&resolved)) || (family == SYS_AF_INET6 && !IP_IS_V6(&resolved))) return STATUS_NOT_FOUND; LwipSocketFillAddress(addressOut, &resolved, 0); return STATUS_SUCCESS; } // Dispatches a provider-private socket request carried by an IRP. static KSTATUS LwipDispatchSocketRequest(NET_SOCKET_IRP_REQUEST* request) { if (!request || request->Size != sizeof(*request)) return STATUS_INVALID_PARAMETER; switch (request->Operation) { case NetSocketIrpCreate: return LwipProviderSocketCreate(request->u.Create.Domain, request->u.Create.Type, request->u.Create.Protocol, request->u.Create.ProviderSocketOut); case NetSocketIrpClose: LwipProviderSocketClose(request->u.Close.Socket); return STATUS_SUCCESS; case NetSocketIrpBind: return LwipProviderBind(request->u.Bind.Socket, request->u.Bind.Address, request->u.Bind.AddressSize); case NetSocketIrpListen: return LwipProviderListen(request->u.Listen.Socket, request->u.Listen.Backlog); case NetSocketIrpAccept: return LwipProviderAccept(request->u.Accept.Socket, request->u.Accept.AcceptedOut, request->u.Accept.RemoteOut, request->CancelRequested); case NetSocketIrpConnect: return LwipProviderConnect(request->u.Connect.Socket, request->u.Connect.Address, request->u.Connect.AddressSize, request->CancelRequested); case NetSocketIrpSend: return LwipProviderSend(request->u.Send.Socket, request->u.Send.Buffer, request->u.Send.Length, request->u.Send.Flags, request->u.Send.SentOut); case NetSocketIrpReceive: return LwipProviderReceive(request->u.Receive.Socket, request->u.Receive.Buffer, request->u.Receive.Length, request->u.Receive.Flags, request->u.Receive.ReceivedOut, request->CancelRequested); case NetSocketIrpSendTo: return LwipProviderSendTo(request->u.SendTo.Socket, request->u.SendTo.Buffer, request->u.SendTo.Length, request->u.SendTo.Flags, request->u.SendTo.Remote, request->u.SendTo.RemoteSize, request->u.SendTo.SentOut); case NetSocketIrpReceiveFrom: return LwipProviderReceiveFrom(request->u.ReceiveFrom.Socket, request->u.ReceiveFrom.Buffer, request->u.ReceiveFrom.Length, request->u.ReceiveFrom.Flags, request->u.ReceiveFrom.RemoteOut, request->u.ReceiveFrom.ReceivedOut, request->CancelRequested); case NetSocketIrpShutdown: return LwipProviderShutdown(request->u.Shutdown.Socket, request->u.Shutdown.How); case NetSocketIrpSetOption: return LwipProviderSetOption(request->u.SetOption.Socket, request->u.SetOption.Option, request->u.SetOption.Value, request->u.SetOption.ValueSize); case NetSocketIrpGetOption: return LwipProviderGetOption(request->u.GetOption.Socket, request->u.GetOption.Option, request->u.GetOption.Value, request->u.GetOption.ValueSize, request->u.GetOption.ActualSizeOut); case NetSocketIrpPoll: return LwipProviderPoll(request->u.Poll.Socket, request->u.Poll.Events, request->u.Poll.ReturnedEventsOut); case NetSocketIrpResolveHost: return LwipProviderResolveHost(request->u.ResolveHost.Name, request->u.ResolveHost.Family, request->u.ResolveHost.AddressOut); default: return STATUS_INVALID_PARAMETER; } } // Dispatches socket internal device controls for the lwIP stack device. static KSTATUS LwipDispatchInternalDeviceControl(DEVICE_OBJECT* device, IRP* irp) { (void)device; IO_STACK_LOCATION* ios = IoGetCurrentIrpStackLocation(irp); if (!ios) return STATUS_INVALID_PARAMETER; if (ios->Parameters.DeviceIoControl.IoControlCode != NET_IOCTL_SOCKET) return STATUS_NOT_SUPPORTED; if (ios->Parameters.DeviceIoControl.InputLength < sizeof(NET_SOCKET_IRP_REQUEST)) return STATUS_BUFFER_TOO_SMALL; NET_SOCKET_IRP_REQUEST* request = (NET_SOCKET_IRP_REQUEST*)ios->Parameters.DeviceIoControl.InputBuffer; return LwipDispatchSocketRequest(request); } // Implements the driver Entry operation. KSTATUS DriverEntry(const HAL_API* hal) { DbgPrint("lwIP: driver loading...\n"); LWIP_NETIF_CONFIG defaultConfig; KSTATUS st = LwipLoadConfigFromSection(LWIP_SETTINGS_DEFAULT_SECTION, &defaultConfig); if (!KSUCCESS(st)) { DbgPrint("lwIP: required settings [%s] missing or invalid in %s (0x%x)\n", LWIP_SETTINGS_DEFAULT_SECTION, KE_SETTINGS_PATH, st); return st; } st = LwipStackInitialize(hal); if (!KSUCCESS(st)) { DbgPrint("lwIP: initialization failed: 0x%x\n", st); return st; } IoInitializeDriverObject(&gLwipDriver, SYS_SV_LITERAL("lwip-stack")); gLwipDriver.MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = LwipDispatchInternalDeviceControl; st = IoRegisterDriverObject(&gLwipDriver); if (!KSUCCESS(st)) { DbgPrint("lwIP: driver object registration failed: 0x%x\n", st); return st; } st = IoCreateDevice(&gLwipDriver, 0, &gLwipDevice); if (!KSUCCESS(st)) { DbgPrint("lwIP: stack device creation failed: 0x%x\n", st); return st; } (void)IoSetDeviceName(gLwipDevice, SYS_SV_LITERAL("lwip-stack")); gLwipProvider.StackDevice = gLwipDevice; st = NetRegisterStackProvider(&gLwipProvider); if (!KSUCCESS(st)) { DbgPrint("lwIP: provider registration failed: 0x%x\n", st); return st; } DbgPrint("lwIP: driver initialized\n"); return STATUS_SUCCESS; }