// lwip_stack.c - lwIP network stack provider driver. #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_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 HasDns; char Section[64]; ip4_addr_t Address; ip4_addr_t Netmask; ip4_addr_t Gateway; ip4_addr_t Dns; } 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; uint8_t Reserved0; 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_IPV4_K* address, size_t addressSize); static KSTATUS LwipProviderListen(void* socket, uint32_t backlog); static KSTATUS LwipProviderAccept(void* socket, void** acceptedOut, SYS_SOCKET_ADDRESS_IPV4_K* remoteOut); static KSTATUS LwipProviderConnect(void* socket, const SYS_SOCKET_ADDRESS_IPV4_K* address, size_t addressSize); 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); static KSTATUS LwipProviderSendTo(void* socket, const void* buffer, size_t length, uint32_t flags, const SYS_SOCKET_ADDRESS_IPV4_K* remote, size_t remoteSize, size_t* sentOut); static KSTATUS LwipProviderReceiveFrom(void* socket, void* buffer, size_t length, uint32_t flags, SYS_SOCKET_ADDRESS_IPV4_K* remoteOut, size_t* receivedOut); 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 LwipProviderResolveHostIpv4(const char* name, SYS_SOCKET_ADDRESS_IPV4_K* addressOut); static const NET_STACK_PROVIDER gLwipProvider = { NET_STACK_PROVIDER_VERSION, "lwIP", LwipProviderAttachDevice, LwipProviderReceiveFrame, LwipProviderSocketCreate, LwipProviderSocketClose, LwipProviderBind, LwipProviderListen, LwipProviderAccept, LwipProviderConnect, LwipProviderSend, LwipProviderReceive, LwipProviderSendTo, LwipProviderReceiveFrom, LwipProviderShutdown, LwipProviderSetOption, LwipProviderGetOption, LwipProviderPoll, LwipProviderResolveHostIpv4, }; 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_TIMEOUT; 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; } } static int LwipAsciiIsDigit(char c) { return c >= '0' && c <= '9'; } 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; } 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 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; } 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->Dns, 1); if (!KSUCCESS(st)) return st; cfg->HasDns = 1; return STATUS_SUCCESS; } if (strcmp(mode, "dhcp") == 0) { cfg->UseDhcp = 1; st = LwipGetSettingIpv4(section, "dns", &cfg->Dns, 0); if (KSUCCESS(st)) cfg->HasDns = 1; else if (st != STATUS_NOT_FOUND) return st; return STATUS_SUCCESS; } DbgPrint("lwIP: invalid network mode [%s] mode=%s\n", section, mode); return STATUS_INVALID_PARAMETER; } 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; } static KSTATUS LwipSocketLock(LWIP_PROVIDER_SOCKET* s) { if (!s || !s->Lock) return STATUS_INVALID_PARAMETER; return KeWaitSemaphore(s->Lock); } static void LwipSocketUnlock(LWIP_PROVIDER_SOCKET* s) { if (s && s->Lock) KeSignalSemaphore(s->Lock); } static void LwipSocketCallback(struct netconn* conn, enum netconn_evt evt, u16_t len) { (void)len; if (!conn) return; LWIP_PROVIDER_SOCKET* socket = (LWIP_PROVIDER_SOCKET*)netconn_get_callback_arg(conn); if (!socket) return; 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); netconn_set_recvtimeout(conn, 0); netconn_set_sendtimeout(conn, 0); *out = s; return STATUS_SUCCESS; } static void LwipSocketMakeIp(const SYS_SOCKET_ADDRESS_IPV4_K* address, ip_addr_t* out) { IP_ADDR4(out, address->Address[0], address->Address[1], address->Address[2], address->Address[3]); } static void LwipSocketFillAddress(SYS_SOCKET_ADDRESS_IPV4_K* out, const ip_addr_t* ip, u16_t port) { if (!out) return; memset(out, 0, sizeof(*out)); out->Family = SYS_AF_INET; out->Port = port; 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)); } 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; } 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; 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; return ERR_OK; } 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.HasDns) { ip_addr_t dns; ip_addr_copy_from_ip4(dns, b->Config.Dns); dns_setserver(0, &dns); } 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); } static void LwipInitDone(void* arg) { SEMAPHORE* sem = (SEMAPHORE*)arg; KeSignalSemaphore(sem); } 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; } 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\n", device->Name, b->Netif.name[0], b->Netif.name[1], b->Netif.num, b->Config.Section, b->Config.UseDhcp ? "dhcp" : "static"); return STATUS_SUCCESS; } 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; } 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) return STATUS_NOT_SUPPORTED; 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 = NETCONN_TCP; protocol = SYS_IPPROTO_TCP; } else if (type == SYS_SOCK_DGRAM) { if (protocol != 0 && protocol != SYS_IPPROTO_UDP) return STATUS_NOT_SUPPORTED; netconnType = NETCONN_UDP; protocol = SYS_IPPROTO_UDP; } else if (type == SYS_SOCK_RAW) { if (protocol != SYS_IPPROTO_ICMP) return STATUS_NOT_SUPPORTED; netconnType = NETCONN_RAW; rawProtocol = IP_PROTO_ICMP; } else { return STATUS_NOT_SUPPORTED; } struct netconn* conn = netconn_new_with_proto_and_callback(netconnType, rawProtocol, LwipSocketCallback); 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; } 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); } static KSTATUS LwipProviderBind(void* socket, const SYS_SOCKET_ADDRESS_IPV4_K* address, size_t addressSize) { LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket; if (!s || !address) return STATUS_INVALID_PARAMETER; if (addressSize < sizeof(SYS_SOCKET_ADDRESS_IPV4_K)) return STATUS_BUFFER_TOO_SMALL; if ((s->Type != SYS_SOCK_STREAM && s->Type != SYS_SOCK_DGRAM) || !s->Conn) return STATUS_INVALID_PARAMETER; if (address->Family != SYS_AF_INET) 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; LwipSocketMakeIp(address, &ip); err_t err = netconn_bind(s->Conn, &ip, address->Port); if (err == ERR_OK) s->Bound = 1; LwipSocketUnlock(s); return LwipErrToStatus(err); } 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); } static KSTATUS LwipProviderAccept(void* socket, void** acceptedOut, SYS_SOCKET_ADDRESS_IPV4_K* remoteOut) { 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 = netconn_accept(s->Conn, &acceptedConn); LwipSocketUnlock(s); if (err != ERR_OK) return LwipErrToStatus(err); if (!acceptedConn) return STATUS_FAILURE; acceptedConn->callback = LwipSocketCallback; 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 && IP_IS_V4(&peerIp)) { LwipSocketFillAddress(remoteOut, &peerIp, peerPort); } else { memset(remoteOut, 0, sizeof(*remoteOut)); remoteOut->Family = SYS_AF_INET; } } *acceptedOut = accepted; return STATUS_SUCCESS; } static KSTATUS LwipProviderConnect(void* socket, const SYS_SOCKET_ADDRESS_IPV4_K* address, size_t addressSize) { LWIP_PROVIDER_SOCKET* s = (LWIP_PROVIDER_SOCKET*)socket; if (!s || !address) return STATUS_INVALID_PARAMETER; if (addressSize < sizeof(SYS_SOCKET_ADDRESS_IPV4_K)) return STATUS_BUFFER_TOO_SMALL; if ((s->Type != SYS_SOCK_STREAM && s->Type != SYS_SOCK_DGRAM) || !s->Conn) return STATUS_INVALID_PARAMETER; if (address->Family != SYS_AF_INET) 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; LwipSocketMakeIp(address, &ip); err_t err = netconn_connect(s->Conn, &ip, address->Port); if (err == ERR_OK) s->Connected = 1; LwipSocketUnlock(s); return LwipErrToStatus(err); } 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; } static KSTATUS LwipProviderReceive(void* socket, void* buffer, size_t length, uint32_t flags, size_t* receivedOut) { 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 = netconn_recv(s->Conn, &nb); 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_IPV4_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 (remoteSize < sizeof(SYS_SOCKET_ADDRESS_IPV4_K)) 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 (remote->Family != SYS_AF_INET) 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; LwipSocketMakeIp(remote, &ip); err = netconn_sendto(s->Conn, nb, &ip, remote->Port); } 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_IPV4_K* remoteOut, size_t* receivedOut) { 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 = netconn_recv(s->Conn, &nb); 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 && IP_IS_V4(from)) { LwipSocketFillAddress(remoteOut, from, netbuf_fromport(nb)); } else { memset(remoteOut, 0, sizeof(*remoteOut)); remoteOut->Family = SYS_AF_INET; } } netbuf_delete(nb); if (receivedOut) *receivedOut = copied; LwipSocketUnlock(s); return LwipErrToStatus(err); } 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; } 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; } 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; } 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); } static KSTATUS LwipProviderResolveHostIpv4(const char* name, SYS_SOCKET_ADDRESS_IPV4_K* addressOut) { if (!name || !addressOut) return STATUS_INVALID_PARAMETER; if (!gLwipInitialized) return STATUS_NOT_SUPPORTED; ip_addr_t resolved; err_t err = netconn_gethostbyname_addrtype(name, &resolved, NETCONN_DNS_IPV4); if (err != ERR_OK) return LwipErrToStatus(err); if (!IP_IS_V4(&resolved)) return STATUS_NOT_FOUND; memset(addressOut, 0, sizeof(*addressOut)); addressOut->Family = SYS_AF_INET; addressOut->Port = 0; addressOut->Address[0] = (uint8_t)ip4_addr1(ip_2_ip4(&resolved)); addressOut->Address[1] = (uint8_t)ip4_addr2(ip_2_ip4(&resolved)); addressOut->Address[2] = (uint8_t)ip4_addr3(ip_2_ip4(&resolved)); addressOut->Address[3] = (uint8_t)ip4_addr4(ip_2_ip4(&resolved)); return STATUS_SUCCESS; } 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; } 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; }