Files
nt-lite/code/drivers/net/lwip/lwip_stack.c
T

1479 lines
45 KiB
C

// lwIP stack provider glue. This file bridges the kernel network-device model into lwIP.
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <ntlite/ddi/hal_api.h>
#include <ntlite/status.h>
#include <kernel/ke_debug.h>
#include <kernel/ke_sched.h>
#include <kernel/ke_settings.h>
#include <kernel/ke_semaphore.h>
#include <kernel/ob_object.h>
#include <kernel/mm_virt.h>
#include <ntlite/ddi/net_device.h>
#include <ntlite/ddi/net_stack.h>
#include <lwip/init.h>
#include <lwip/tcpip.h>
#include <lwip/sys.h>
#include <lwip/netif.h>
#include <lwip/pbuf.h>
#include <lwip/etharp.h>
#include <lwip/ethip6.h>
#include <lwip/dhcp.h>
#include <lwip/dns.h>
#include <lwip/ip4_addr.h>
#include <lwip/ip6_addr.h>
#include <lwip/ip_addr.h>
#include <lwip/api.h>
#include <lwip/err.h>
#include <lwip/prot/ip.h>
#include <lwip/prot/ip6.h>
#include <netif/ethernet.h>
#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 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);
static KSTATUS LwipProviderConnect(void* socket, const SYS_SOCKET_ADDRESS_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_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);
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 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,
LwipProviderResolveHost,
};
// 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;
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 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, 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;
}
// 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) {
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) {
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) {
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;
}
err_t err = netconn_connect(s->Conn, &ip, address->Port);
if (err == ERR_OK)
s->Connected = 1;
LwipSocketUnlock(s);
return LwipErrToStatus(err);
}
// 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) {
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_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) {
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) {
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;
}
// 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;
}
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;
}