[TCPIP] Rearrange LwIP glue code.

Reduce unnecessary stuff in LwIP itself.
This commit is contained in:
Victor Perevertkin 2023-01-31 03:07:51 +03:00
parent 1734f29721
commit a0a19c60d0
13 changed files with 35 additions and 59 deletions

View file

@ -37,7 +37,3 @@ sys_arch_protect(sys_prot_t *lev);
void
sys_arch_unprotect(sys_prot_t lev);
void
sys_shutdown(void);

View file

@ -1,124 +0,0 @@
#ifndef _ROS_IP_H_
#define _ROS_IP_H_
#include "lwip/tcp.h"
#include "lwip/pbuf.h"
#include "lwip/ip_addr.h"
#include "tcpip.h"
#ifndef LWIP_TAG
#define LWIP_TAG 'PIwl'
#define LWIP_MESSAGE_TAG 'sMwl'
#define LWIP_QUEUE_TAG 'uQwl'
#endif
typedef struct tcp_pcb* PTCP_PCB;
typedef struct _QUEUE_ENTRY
{
struct pbuf *p;
ULONG Offset;
LIST_ENTRY ListEntry;
} QUEUE_ENTRY, *PQUEUE_ENTRY;
struct lwip_callback_msg
{
/* Synchronization */
KEVENT Event;
/* Input */
union {
struct {
PVOID Arg;
} Socket;
struct {
struct tcp_pcb* pcb;
} FreeSocket;
struct {
PCONNECTION_ENDPOINT Connection;
struct ip_addr *IpAddress;
u16_t Port;
} Bind;
struct {
PCONNECTION_ENDPOINT Connection;
u8_t Backlog;
} Listen;
struct {
PCONNECTION_ENDPOINT Connection;
void *Data;
u16_t DataLength;
} Send;
struct {
PCONNECTION_ENDPOINT Connection;
struct ip_addr *IpAddress;
u16_t Port;
} Connect;
struct {
PCONNECTION_ENDPOINT Connection;
int shut_rx;
int shut_tx;
} Shutdown;
struct {
PCONNECTION_ENDPOINT Connection;
int Callback;
} Close;
} Input;
/* Output */
union {
struct {
struct tcp_pcb *NewPcb;
} Socket;
struct {
err_t Error;
} Bind;
struct {
struct tcp_pcb *NewPcb;
} Listen;
struct {
err_t Error;
u32_t Information;
} Send;
struct {
err_t Error;
} Connect;
struct {
err_t Error;
} Shutdown;
struct {
err_t Error;
} Close;
} Output;
};
NTSTATUS LibTCPGetDataFromConnectionQueue(PCONNECTION_ENDPOINT Connection, PUCHAR RecvBuffer, UINT RecvLen, UINT *Received);
/* External TCP event handlers */
extern void TCPConnectEventHandler(void *arg, const err_t err);
extern void TCPAcceptEventHandler(void *arg, PTCP_PCB newpcb);
extern void TCPSendEventHandler(void *arg, const u16_t space);
extern void TCPFinEventHandler(void *arg, const err_t err);
extern void TCPRecvEventHandler(void *arg);
/* TCP functions */
PTCP_PCB LibTCPSocket(void *arg);
VOID LibTCPFreeSocket(PTCP_PCB pcb);
err_t LibTCPBind(PCONNECTION_ENDPOINT Connection, struct ip_addr *const ipaddr, const u16_t port);
PTCP_PCB LibTCPListen(PCONNECTION_ENDPOINT Connection, const u8_t backlog);
err_t LibTCPSend(PCONNECTION_ENDPOINT Connection, void *const dataptr, const u16_t len, u32_t *sent, const int safe);
err_t LibTCPConnect(PCONNECTION_ENDPOINT Connection, struct ip_addr *const ipaddr, const u16_t port);
err_t LibTCPShutdown(PCONNECTION_ENDPOINT Connection, const int shut_rx, const int shut_tx);
err_t LibTCPClose(PCONNECTION_ENDPOINT Connection, const int safe, const int callback);
err_t LibTCPGetPeerName(PTCP_PCB pcb, struct ip_addr *const ipaddr, u16_t *const port);
err_t LibTCPGetHostName(PTCP_PCB pcb, struct ip_addr *const ipaddr, u16_t *const port);
void LibTCPAccept(PTCP_PCB pcb, struct tcp_pcb *listen_pcb, void *arg);
void LibTCPSetNoDelay(PTCP_PCB pcb, BOOLEAN Set);
void LibTCPGetSocketStatus(PTCP_PCB pcb, PULONG State);
/* IP functions */
void LibIPInsertPacket(void *ifarg, const void *const data, const u32_t size);
void LibIPInitialize(void);
void LibIPShutdown(void);
#endif

View file

@ -1,46 +0,0 @@
#include "lwip/sys.h"
#include "lwip/netif.h"
#include "lwip/tcpip.h"
#include "rosip.h"
#include <debug.h>
typedef struct netif* PNETIF;
void
LibIPInsertPacket(void *ifarg,
const void *const data,
const u32_t size)
{
struct pbuf *p;
ASSERT(ifarg);
ASSERT(data);
ASSERT(size > 0);
p = pbuf_alloc(PBUF_RAW, size, PBUF_RAM);
if (p)
{
ASSERT(p->tot_len == p->len);
ASSERT(p->len == size);
RtlCopyMemory(p->payload, data, p->len);
((PNETIF)ifarg)->input(p, (PNETIF)ifarg);
}
}
void
LibIPInitialize(void)
{
/* This completes asynchronously */
tcpip_init(NULL, NULL);
}
void
LibIPShutdown(void)
{
/* This is synchronous */
sys_shutdown();
}

View file

@ -1,66 +0,0 @@
#include "lwip/opt.h"
#include "lwip/def.h"
#include "lwip/mem.h"
#ifndef LWIP_TAG
#define LWIP_TAG 'PIwl'
#endif
void *
malloc(mem_size_t size)
{
return ExAllocatePoolWithTag(NonPagedPool, size, LWIP_TAG);
}
void *
calloc(mem_size_t count, mem_size_t size)
{
void *mem = malloc(count * size);
if (!mem) return NULL;
RtlZeroMemory(mem, count * size);
return mem;
}
void
free(void *mem)
{
ExFreePoolWithTag(mem, LWIP_TAG);
}
/* This is only used to trim in lwIP */
void *
realloc(void *mem, size_t size)
{
void* new_mem;
/* realloc() with a NULL mem pointer acts like a call to malloc() */
if (mem == NULL) {
return malloc(size);
}
/* realloc() with a size 0 acts like a call to free() */
if (size == 0) {
free(mem);
return NULL;
}
/* Allocate the new buffer first */
new_mem = malloc(size);
if (new_mem == NULL) {
/* The old buffer is still intact */
return NULL;
}
/* Copy the data over */
RtlCopyMemory(new_mem, mem, size);
/* Deallocate the old buffer */
free(mem);
/* Return the newly allocated block */
return new_mem;
}

View file

@ -1,888 +0,0 @@
#include "lwip/sys.h"
#include "lwip/netif.h"
#include "lwip/tcpip.h"
#include "rosip.h"
#include <debug.h>
static const char * const tcp_state_str[] = {
"CLOSED",
"LISTEN",
"SYN_SENT",
"SYN_RCVD",
"ESTABLISHED",
"FIN_WAIT_1",
"FIN_WAIT_2",
"CLOSE_WAIT",
"CLOSING",
"LAST_ACK",
"TIME_WAIT"
};
/* The way that lwIP does multi-threading is really not ideal for our purposes but
* we best go along with it unless we want another unstable TCP library. lwIP uses
* a thread called the "tcpip thread" which is the only one allowed to call raw API
* functions. Since this is the case, for each of our LibTCP* functions, we queue a request
* for a callback to "tcpip thread" which calls our LibTCP*Callback functions. Yes, this is
* a lot of unnecessary thread swapping and it could definitely be faster, but I don't want
* to going messing around in lwIP because I have no desire to create another mess like oskittcp */
extern KEVENT TerminationEvent;
extern NPAGED_LOOKASIDE_LIST MessageLookasideList;
extern NPAGED_LOOKASIDE_LIST QueueEntryLookasideList;
/* Required for ERR_T to NTSTATUS translation in receive error handling */
NTSTATUS TCPTranslateError(const err_t err);
void
LibTCPDumpPcb(PVOID SocketContext)
{
struct tcp_pcb *pcb = (struct tcp_pcb*)SocketContext;
unsigned int addr = ntohl(pcb->remote_ip.addr);
DbgPrint("\tState: %s\n", tcp_state_str[pcb->state]);
DbgPrint("\tRemote: (%d.%d.%d.%d, %d)\n",
(addr >> 24) & 0xFF,
(addr >> 16) & 0xFF,
(addr >> 8) & 0xFF,
addr & 0xFF,
pcb->remote_port);
}
static
void
LibTCPEmptyQueue(PCONNECTION_ENDPOINT Connection)
{
PLIST_ENTRY Entry;
PQUEUE_ENTRY qp = NULL;
ReferenceObject(Connection);
while (!IsListEmpty(&Connection->PacketQueue))
{
Entry = RemoveHeadList(&Connection->PacketQueue);
qp = CONTAINING_RECORD(Entry, QUEUE_ENTRY, ListEntry);
/* We're in the tcpip thread here so this is safe */
pbuf_free(qp->p);
ExFreeToNPagedLookasideList(&QueueEntryLookasideList, qp);
}
DereferenceObject(Connection);
}
void LibTCPEnqueuePacket(PCONNECTION_ENDPOINT Connection, struct pbuf *p)
{
PQUEUE_ENTRY qp;
qp = (PQUEUE_ENTRY)ExAllocateFromNPagedLookasideList(&QueueEntryLookasideList);
qp->p = p;
qp->Offset = 0;
LockObject(Connection);
InsertTailList(&Connection->PacketQueue, &qp->ListEntry);
UnlockObject(Connection);
}
PQUEUE_ENTRY LibTCPDequeuePacket(PCONNECTION_ENDPOINT Connection)
{
PLIST_ENTRY Entry;
PQUEUE_ENTRY qp = NULL;
if (IsListEmpty(&Connection->PacketQueue)) return NULL;
Entry = RemoveHeadList(&Connection->PacketQueue);
qp = CONTAINING_RECORD(Entry, QUEUE_ENTRY, ListEntry);
return qp;
}
NTSTATUS LibTCPGetDataFromConnectionQueue(PCONNECTION_ENDPOINT Connection, PUCHAR RecvBuffer, UINT RecvLen, UINT *Received)
{
PQUEUE_ENTRY qp;
struct pbuf* p;
NTSTATUS Status;
UINT ReadLength, PayloadLength, Offset, Copied;
(*Received) = 0;
LockObject(Connection);
if (!IsListEmpty(&Connection->PacketQueue))
{
while ((qp = LibTCPDequeuePacket(Connection)) != NULL)
{
p = qp->p;
/* Calculate the payload length first */
PayloadLength = p->tot_len;
PayloadLength -= qp->Offset;
Offset = qp->Offset;
/* Check if we're reading the whole buffer */
ReadLength = MIN(PayloadLength, RecvLen);
ASSERT(ReadLength != 0);
if (ReadLength != PayloadLength)
{
/* Save this one for later */
qp->Offset += ReadLength;
InsertHeadList(&Connection->PacketQueue, &qp->ListEntry);
qp = NULL;
}
Copied = pbuf_copy_partial(p, RecvBuffer, ReadLength, Offset);
ASSERT(Copied == ReadLength);
/* Update trackers */
RecvLen -= ReadLength;
RecvBuffer += ReadLength;
(*Received) += ReadLength;
if (qp != NULL)
{
/* Use this special pbuf free callback function because we're outside tcpip thread */
pbuf_free_callback(qp->p);
ExFreeToNPagedLookasideList(&QueueEntryLookasideList, qp);
}
else
{
/* If we get here, it means we've filled the buffer */
ASSERT(RecvLen == 0);
}
ASSERT((*Received) != 0);
Status = STATUS_SUCCESS;
if (!RecvLen)
break;
}
}
else
{
if (Connection->ReceiveShutdown)
Status = Connection->ReceiveShutdownStatus;
else
Status = STATUS_PENDING;
}
UnlockObject(Connection);
return Status;
}
static
BOOLEAN
WaitForEventSafely(PRKEVENT Event)
{
PVOID WaitObjects[] = {Event, &TerminationEvent};
if (KeWaitForMultipleObjects(2,
WaitObjects,
WaitAny,
Executive,
KernelMode,
FALSE,
NULL,
NULL) == STATUS_WAIT_0)
{
/* Signalled by the caller's event */
return TRUE;
}
else /* if KeWaitForMultipleObjects() == STATUS_WAIT_1 */
{
/* Signalled by our termination event */
return FALSE;
}
}
static
err_t
InternalSendEventHandler(void *arg, PTCP_PCB pcb, const u16_t space)
{
/* Make sure the socket didn't get closed */
if (!arg) return ERR_OK;
TCPSendEventHandler(arg, space);
return ERR_OK;
}
static
err_t
InternalRecvEventHandler(void *arg, PTCP_PCB pcb, struct pbuf *p, const err_t err)
{
PCONNECTION_ENDPOINT Connection = arg;
/* Make sure the socket didn't get closed */
if (!arg)
{
if (p)
pbuf_free(p);
return ERR_OK;
}
if (p)
{
LibTCPEnqueuePacket(Connection, p);
tcp_recved(pcb, p->tot_len);
TCPRecvEventHandler(arg);
}
else if (err == ERR_OK)
{
/* Complete pending reads with 0 bytes to indicate a graceful closure,
* but note that send is still possible in this state so we don't close the
* whole socket here (by calling tcp_close()) as that would violate TCP specs
*/
Connection->ReceiveShutdown = TRUE;
Connection->ReceiveShutdownStatus = STATUS_SUCCESS;
/* If we already did a send shutdown, we're in TIME_WAIT so we can't use this PCB anymore */
if (Connection->SendShutdown)
{
Connection->SocketContext = NULL;
tcp_arg(pcb, NULL);
}
/* Indicate the graceful close event */
TCPRecvEventHandler(arg);
/* If the PCB is gone, clean up the connection */
if (Connection->SendShutdown)
{
TCPFinEventHandler(Connection, ERR_CLSD);
}
}
return ERR_OK;
}
/* This function MUST return an error value that is not ERR_ABRT or ERR_OK if the connection
* is not accepted to avoid leaking the new PCB */
static
err_t
InternalAcceptEventHandler(void *arg, PTCP_PCB newpcb, const err_t err)
{
/* Make sure the socket didn't get closed */
if (!arg)
return ERR_CLSD;
TCPAcceptEventHandler(arg, newpcb);
/* Set in LibTCPAccept (called from TCPAcceptEventHandler) */
if (newpcb->callback_arg)
return ERR_OK;
else
return ERR_CLSD;
}
static
err_t
InternalConnectEventHandler(void *arg, PTCP_PCB pcb, const err_t err)
{
/* Make sure the socket didn't get closed */
if (!arg)
return ERR_OK;
TCPConnectEventHandler(arg, err);
return ERR_OK;
}
static
void
InternalErrorEventHandler(void *arg, const err_t err)
{
PCONNECTION_ENDPOINT Connection = arg;
/* Make sure the socket didn't get closed */
if (!arg || Connection->SocketContext == NULL) return;
/* The PCB is dead now */
Connection->SocketContext = NULL;
/* Give them one shot to receive the remaining data */
Connection->ReceiveShutdown = TRUE;
Connection->ReceiveShutdownStatus = TCPTranslateError(err);
TCPRecvEventHandler(Connection);
/* Terminate the connection */
TCPFinEventHandler(Connection, err);
}
static
void
LibTCPSocketCallback(void *arg)
{
struct lwip_callback_msg *msg = arg;
ASSERT(msg);
msg->Output.Socket.NewPcb = tcp_new();
if (msg->Output.Socket.NewPcb)
{
tcp_arg(msg->Output.Socket.NewPcb, msg->Input.Socket.Arg);
tcp_err(msg->Output.Socket.NewPcb, InternalErrorEventHandler);
}
KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE);
}
struct tcp_pcb *
LibTCPSocket(void *arg)
{
struct lwip_callback_msg *msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList);
struct tcp_pcb *ret;
if (msg)
{
KeInitializeEvent(&msg->Event, NotificationEvent, FALSE);
msg->Input.Socket.Arg = arg;
tcpip_callback_with_block(LibTCPSocketCallback, msg, 1);
if (WaitForEventSafely(&msg->Event))
ret = msg->Output.Socket.NewPcb;
else
ret = NULL;
ExFreeToNPagedLookasideList(&MessageLookasideList, msg);
return ret;
}
return NULL;
}
static
void
LibTCPFreeSocketCallback(void *arg)
{
struct lwip_callback_msg *msg = arg;
ASSERT(msg);
/* Calling tcp_close will free it */
tcp_close(msg->Input.FreeSocket.pcb);
KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE);
}
void LibTCPFreeSocket(PTCP_PCB pcb)
{
struct lwip_callback_msg msg;
KeInitializeEvent(&msg.Event, NotificationEvent, FALSE);
msg.Input.FreeSocket.pcb = pcb;
tcpip_callback_with_block(LibTCPFreeSocketCallback, &msg, 1);
WaitForEventSafely(&msg.Event);
}
static
void
LibTCPBindCallback(void *arg)
{
struct lwip_callback_msg *msg = arg;
PTCP_PCB pcb = msg->Input.Bind.Connection->SocketContext;
ASSERT(msg);
if (!msg->Input.Bind.Connection->SocketContext)
{
msg->Output.Bind.Error = ERR_CLSD;
goto done;
}
/* We're guaranteed that the local address is valid to bind at this point */
pcb->so_options |= SOF_REUSEADDR;
msg->Output.Bind.Error = tcp_bind(pcb,
msg->Input.Bind.IpAddress,
ntohs(msg->Input.Bind.Port));
done:
KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE);
}
err_t
LibTCPBind(PCONNECTION_ENDPOINT Connection, struct ip_addr *const ipaddr, const u16_t port)
{
struct lwip_callback_msg *msg;
err_t ret;
msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList);
if (msg)
{
KeInitializeEvent(&msg->Event, NotificationEvent, FALSE);
msg->Input.Bind.Connection = Connection;
msg->Input.Bind.IpAddress = ipaddr;
msg->Input.Bind.Port = port;
tcpip_callback_with_block(LibTCPBindCallback, msg, 1);
if (WaitForEventSafely(&msg->Event))
ret = msg->Output.Bind.Error;
else
ret = ERR_CLSD;
ExFreeToNPagedLookasideList(&MessageLookasideList, msg);
return ret;
}
return ERR_MEM;
}
static
void
LibTCPListenCallback(void *arg)
{
struct lwip_callback_msg *msg = arg;
ASSERT(msg);
if (!msg->Input.Listen.Connection->SocketContext)
{
msg->Output.Listen.NewPcb = NULL;
goto done;
}
msg->Output.Listen.NewPcb = tcp_listen_with_backlog((PTCP_PCB)msg->Input.Listen.Connection->SocketContext, msg->Input.Listen.Backlog);
if (msg->Output.Listen.NewPcb)
{
tcp_accept(msg->Output.Listen.NewPcb, InternalAcceptEventHandler);
}
done:
KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE);
}
PTCP_PCB
LibTCPListen(PCONNECTION_ENDPOINT Connection, const u8_t backlog)
{
struct lwip_callback_msg *msg;
PTCP_PCB ret;
msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList);
if (msg)
{
KeInitializeEvent(&msg->Event, NotificationEvent, FALSE);
msg->Input.Listen.Connection = Connection;
msg->Input.Listen.Backlog = backlog;
tcpip_callback_with_block(LibTCPListenCallback, msg, 1);
if (WaitForEventSafely(&msg->Event))
ret = msg->Output.Listen.NewPcb;
else
ret = NULL;
ExFreeToNPagedLookasideList(&MessageLookasideList, msg);
return ret;
}
return NULL;
}
static
void
LibTCPSendCallback(void *arg)
{
struct lwip_callback_msg *msg = arg;
PTCP_PCB pcb = msg->Input.Send.Connection->SocketContext;
ULONG SendLength;
UCHAR SendFlags;
ASSERT(msg);
if (!msg->Input.Send.Connection->SocketContext)
{
msg->Output.Send.Error = ERR_CLSD;
goto done;
}
if (msg->Input.Send.Connection->SendShutdown)
{
msg->Output.Send.Error = ERR_CLSD;
goto done;
}
SendFlags = TCP_WRITE_FLAG_COPY;
SendLength = msg->Input.Send.DataLength;
if (tcp_sndbuf(pcb) == 0)
{
/* No buffer space so return pending */
msg->Output.Send.Error = ERR_INPROGRESS;
goto done;
}
else if (tcp_sndbuf(pcb) < SendLength)
{
/* We've got some room so let's send what we can */
SendLength = tcp_sndbuf(pcb);
/* Don't set the push flag */
SendFlags |= TCP_WRITE_FLAG_MORE;
}
msg->Output.Send.Error = tcp_write(pcb,
msg->Input.Send.Data,
SendLength,
SendFlags);
if (msg->Output.Send.Error == ERR_OK)
{
/* Queued successfully so try to send it */
tcp_output((PTCP_PCB)msg->Input.Send.Connection->SocketContext);
msg->Output.Send.Information = SendLength;
}
else if (msg->Output.Send.Error == ERR_MEM)
{
/* The queue is too long */
msg->Output.Send.Error = ERR_INPROGRESS;
}
done:
KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE);
}
err_t
LibTCPSend(PCONNECTION_ENDPOINT Connection, void *const dataptr, const u16_t len, u32_t *sent, const int safe)
{
err_t ret;
struct lwip_callback_msg *msg;
msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList);
if (msg)
{
KeInitializeEvent(&msg->Event, NotificationEvent, FALSE);
msg->Input.Send.Connection = Connection;
msg->Input.Send.Data = dataptr;
msg->Input.Send.DataLength = len;
if (safe)
LibTCPSendCallback(msg);
else
tcpip_callback_with_block(LibTCPSendCallback, msg, 1);
if (WaitForEventSafely(&msg->Event))
ret = msg->Output.Send.Error;
else
ret = ERR_CLSD;
if (ret == ERR_OK)
*sent = msg->Output.Send.Information;
else
*sent = 0;
ExFreeToNPagedLookasideList(&MessageLookasideList, msg);
return ret;
}
return ERR_MEM;
}
static
void
LibTCPConnectCallback(void *arg)
{
struct lwip_callback_msg *msg = arg;
err_t Error;
ASSERT(arg);
if (!msg->Input.Connect.Connection->SocketContext)
{
msg->Output.Connect.Error = ERR_CLSD;
goto done;
}
tcp_recv((PTCP_PCB)msg->Input.Connect.Connection->SocketContext, InternalRecvEventHandler);
tcp_sent((PTCP_PCB)msg->Input.Connect.Connection->SocketContext, InternalSendEventHandler);
Error = tcp_connect((PTCP_PCB)msg->Input.Connect.Connection->SocketContext,
msg->Input.Connect.IpAddress, ntohs(msg->Input.Connect.Port),
InternalConnectEventHandler);
msg->Output.Connect.Error = Error == ERR_OK ? ERR_INPROGRESS : Error;
done:
KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE);
}
err_t
LibTCPConnect(PCONNECTION_ENDPOINT Connection, struct ip_addr *const ipaddr, const u16_t port)
{
struct lwip_callback_msg *msg;
err_t ret;
msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList);
if (msg)
{
KeInitializeEvent(&msg->Event, NotificationEvent, FALSE);
msg->Input.Connect.Connection = Connection;
msg->Input.Connect.IpAddress = ipaddr;
msg->Input.Connect.Port = port;
tcpip_callback_with_block(LibTCPConnectCallback, msg, 1);
if (WaitForEventSafely(&msg->Event))
{
ret = msg->Output.Connect.Error;
}
else
ret = ERR_CLSD;
ExFreeToNPagedLookasideList(&MessageLookasideList, msg);
return ret;
}
return ERR_MEM;
}
static
void
LibTCPShutdownCallback(void *arg)
{
struct lwip_callback_msg *msg = arg;
PTCP_PCB pcb = msg->Input.Shutdown.Connection->SocketContext;
if (!msg->Input.Shutdown.Connection->SocketContext)
{
msg->Output.Shutdown.Error = ERR_CLSD;
goto done;
}
/* LwIP makes the (questionable) assumption that SHUTDOWN_RDWR is equivalent to tcp_close().
* This assumption holds even if the shutdown calls are done separately (even through multiple
* WinSock shutdown() calls). This assumption means that lwIP has the right to deallocate our
* PCB without telling us if we shutdown TX and RX. To avoid these problems, we'll clear the
* socket context if we have called shutdown for TX and RX.
*/
if (msg->Input.Shutdown.shut_rx != msg->Input.Shutdown.shut_tx) {
if (msg->Input.Shutdown.shut_rx) {
msg->Output.Shutdown.Error = tcp_shutdown(pcb, TRUE, FALSE);
}
if (msg->Input.Shutdown.shut_tx) {
msg->Output.Shutdown.Error = tcp_shutdown(pcb, FALSE, TRUE);
}
}
else if (msg->Input.Shutdown.shut_rx) {
/* We received both RX and TX requests, which seems to mean closing connection from TDI.
* So call tcp_close, otherwise we risk to be put in TCP_WAIT_* states, which makes further
* attempts to close the socket to fail in this state.
*/
msg->Output.Shutdown.Error = tcp_close(pcb);
}
else {
/* This case shouldn't happen */
DbgPrint("Requested socket shutdown(0, 0) !\n");
}
if (!msg->Output.Shutdown.Error)
{
if (msg->Input.Shutdown.shut_rx)
{
msg->Input.Shutdown.Connection->ReceiveShutdown = TRUE;
msg->Input.Shutdown.Connection->ReceiveShutdownStatus = STATUS_FILE_CLOSED;
}
if (msg->Input.Shutdown.shut_tx)
msg->Input.Shutdown.Connection->SendShutdown = TRUE;
if (msg->Input.Shutdown.Connection->ReceiveShutdown &&
msg->Input.Shutdown.Connection->SendShutdown)
{
/* The PCB is not ours anymore */
msg->Input.Shutdown.Connection->SocketContext = NULL;
tcp_arg(pcb, NULL);
TCPFinEventHandler(msg->Input.Shutdown.Connection, ERR_CLSD);
}
}
done:
KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE);
}
err_t
LibTCPShutdown(PCONNECTION_ENDPOINT Connection, const int shut_rx, const int shut_tx)
{
struct lwip_callback_msg *msg;
err_t ret;
msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList);
if (msg)
{
KeInitializeEvent(&msg->Event, NotificationEvent, FALSE);
msg->Input.Shutdown.Connection = Connection;
msg->Input.Shutdown.shut_rx = shut_rx;
msg->Input.Shutdown.shut_tx = shut_tx;
tcpip_callback_with_block(LibTCPShutdownCallback, msg, 1);
if (WaitForEventSafely(&msg->Event))
ret = msg->Output.Shutdown.Error;
else
ret = ERR_CLSD;
ExFreeToNPagedLookasideList(&MessageLookasideList, msg);
return ret;
}
return ERR_MEM;
}
static
void
LibTCPCloseCallback(void *arg)
{
struct lwip_callback_msg *msg = arg;
PTCP_PCB pcb = msg->Input.Close.Connection->SocketContext;
/* Empty the queue even if we're already "closed" */
LibTCPEmptyQueue(msg->Input.Close.Connection);
/* Check if we've already been closed */
if (msg->Input.Close.Connection->Closing)
{
msg->Output.Close.Error = ERR_OK;
goto done;
}
/* Enter "closing" mode if we're doing a normal close */
if (msg->Input.Close.Callback)
msg->Input.Close.Connection->Closing = TRUE;
/* Check if the PCB was already "closed" but the client doesn't know it yet */
if (!msg->Input.Close.Connection->SocketContext)
{
msg->Output.Close.Error = ERR_OK;
goto done;
}
/* Clear the PCB pointer and stop callbacks */
msg->Input.Close.Connection->SocketContext = NULL;
tcp_arg(pcb, NULL);
/* This may generate additional callbacks but we don't care,
* because they're too inconsistent to rely on */
msg->Output.Close.Error = tcp_close(pcb);
if (msg->Output.Close.Error)
{
/* Restore the PCB pointer */
msg->Input.Close.Connection->SocketContext = pcb;
msg->Input.Close.Connection->Closing = FALSE;
}
else if (msg->Input.Close.Callback)
{
TCPFinEventHandler(msg->Input.Close.Connection, ERR_CLSD);
}
done:
KeSetEvent(&msg->Event, IO_NO_INCREMENT, FALSE);
}
err_t
LibTCPClose(PCONNECTION_ENDPOINT Connection, const int safe, const int callback)
{
err_t ret;
struct lwip_callback_msg *msg;
msg = ExAllocateFromNPagedLookasideList(&MessageLookasideList);
if (msg)
{
KeInitializeEvent(&msg->Event, NotificationEvent, FALSE);
msg->Input.Close.Connection = Connection;
msg->Input.Close.Callback = callback;
if (safe)
LibTCPCloseCallback(msg);
else
tcpip_callback_with_block(LibTCPCloseCallback, msg, 1);
if (WaitForEventSafely(&msg->Event))
ret = msg->Output.Close.Error;
else
ret = ERR_CLSD;
ExFreeToNPagedLookasideList(&MessageLookasideList, msg);
return ret;
}
return ERR_MEM;
}
void
LibTCPAccept(PTCP_PCB pcb, struct tcp_pcb *listen_pcb, void *arg)
{
ASSERT(arg);
tcp_arg(pcb, NULL);
tcp_recv(pcb, InternalRecvEventHandler);
tcp_sent(pcb, InternalSendEventHandler);
tcp_err(pcb, InternalErrorEventHandler);
tcp_arg(pcb, arg);
tcp_accepted(listen_pcb);
}
err_t
LibTCPGetHostName(PTCP_PCB pcb, struct ip_addr *const ipaddr, u16_t *const port)
{
if (!pcb)
return ERR_CLSD;
*ipaddr = pcb->local_ip;
*port = pcb->local_port;
return ERR_OK;
}
err_t
LibTCPGetPeerName(PTCP_PCB pcb, struct ip_addr * const ipaddr, u16_t * const port)
{
if (!pcb)
return ERR_CLSD;
*ipaddr = pcb->remote_ip;
*port = pcb->remote_port;
return ERR_OK;
}
void
LibTCPSetNoDelay(
PTCP_PCB pcb,
BOOLEAN Set)
{
if (Set)
pcb->flags |= TF_NODELAY;
else
pcb->flags &= ~TF_NODELAY;
}
void
LibTCPGetSocketStatus(
PTCP_PCB pcb,
PULONG State)
{
/* Translate state from enum tcp_state -> MIB_TCP_STATE */
*State = pcb->state + 1;
}

View file

@ -1,364 +0,0 @@
#include "lwip/sys.h"
#include "lwip/tcp.h"
#include "lwip/pbuf.h"
#include "lwip/err.h"
#include "rosip.h"
#include <debug.h>
static LIST_ENTRY ThreadListHead;
static KSPIN_LOCK ThreadListLock;
KEVENT TerminationEvent;
NPAGED_LOOKASIDE_LIST MessageLookasideList;
NPAGED_LOOKASIDE_LIST QueueEntryLookasideList;
static LARGE_INTEGER StartTime;
typedef struct _thread_t
{
HANDLE Handle;
void (* ThreadFunction)(void *arg);
void *ThreadContext;
LIST_ENTRY ListEntry;
} *thread_t;
u32_t sys_now(void)
{
LARGE_INTEGER CurrentTime;
KeQuerySystemTime(&CurrentTime);
return (CurrentTime.QuadPart - StartTime.QuadPart) / 10000;
}
void
sys_arch_protect(sys_prot_t *lev)
{
/* Preempt the dispatcher */
KeRaiseIrql(DISPATCH_LEVEL, lev);
}
void
sys_arch_unprotect(sys_prot_t lev)
{
KeLowerIrql(lev);
}
err_t
sys_sem_new(sys_sem_t *sem, u8_t count)
{
ASSERT(count == 0 || count == 1);
/* It seems lwIP uses the semaphore implementation as either a completion event or a lock
* so I optimize for this case by using a synchronization event and setting its initial state
* to signalled for a lock and non-signalled for a completion event */
KeInitializeEvent(&sem->Event, SynchronizationEvent, count);
sem->Valid = 1;
return ERR_OK;
}
int sys_sem_valid(sys_sem_t *sem)
{
return sem->Valid;
}
void sys_sem_set_invalid(sys_sem_t *sem)
{
sem->Valid = 0;
}
void
sys_sem_free(sys_sem_t* sem)
{
/* No op (allocated in stack) */
sys_sem_set_invalid(sem);
}
void
sys_sem_signal(sys_sem_t* sem)
{
KeSetEvent(&sem->Event, IO_NO_INCREMENT, FALSE);
}
u32_t
sys_arch_sem_wait(sys_sem_t* sem, u32_t timeout)
{
LARGE_INTEGER LargeTimeout, PreWaitTime, PostWaitTime;
UINT64 TimeDiff;
NTSTATUS Status;
PVOID WaitObjects[] = {&sem->Event, &TerminationEvent};
LargeTimeout.QuadPart = Int32x32To64(timeout, -10000);
KeQuerySystemTime(&PreWaitTime);
Status = KeWaitForMultipleObjects(2,
WaitObjects,
WaitAny,
Executive,
KernelMode,
FALSE,
timeout != 0 ? &LargeTimeout : NULL,
NULL);
if (Status == STATUS_WAIT_0)
{
KeQuerySystemTime(&PostWaitTime);
TimeDiff = PostWaitTime.QuadPart - PreWaitTime.QuadPart;
TimeDiff /= 10000;
return TimeDiff;
}
else if (Status == STATUS_WAIT_1)
{
/* DON'T remove ourselves from the thread list! */
PsTerminateSystemThread(STATUS_SUCCESS);
/* We should never get here! */
ASSERT(FALSE);
return 0;
}
return SYS_ARCH_TIMEOUT;
}
err_t
sys_mbox_new(sys_mbox_t *mbox, int size)
{
KeInitializeSpinLock(&mbox->Lock);
InitializeListHead(&mbox->ListHead);
KeInitializeEvent(&mbox->Event, NotificationEvent, FALSE);
mbox->Valid = 1;
return ERR_OK;
}
int sys_mbox_valid(sys_mbox_t *mbox)
{
return mbox->Valid;
}
void sys_mbox_set_invalid(sys_mbox_t *mbox)
{
mbox->Valid = 0;
}
void
sys_mbox_free(sys_mbox_t *mbox)
{
ASSERT(IsListEmpty(&mbox->ListHead));
sys_mbox_set_invalid(mbox);
}
void
sys_mbox_post(sys_mbox_t *mbox, void *msg)
{
PLWIP_MESSAGE_CONTAINER Container;
Container = ExAllocatePool(NonPagedPool, sizeof(*Container));
ASSERT(Container);
Container->Message = msg;
ExInterlockedInsertTailList(&mbox->ListHead,
&Container->ListEntry,
&mbox->Lock);
KeSetEvent(&mbox->Event, IO_NO_INCREMENT, FALSE);
}
u32_t
sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout)
{
LARGE_INTEGER LargeTimeout, PreWaitTime, PostWaitTime;
UINT64 TimeDiff;
NTSTATUS Status;
PVOID Message;
PLWIP_MESSAGE_CONTAINER Container;
PLIST_ENTRY Entry;
KIRQL OldIrql;
PVOID WaitObjects[] = {&mbox->Event, &TerminationEvent};
LargeTimeout.QuadPart = Int32x32To64(timeout, -10000);
KeQuerySystemTime(&PreWaitTime);
Status = KeWaitForMultipleObjects(2,
WaitObjects,
WaitAny,
Executive,
KernelMode,
FALSE,
timeout != 0 ? &LargeTimeout : NULL,
NULL);
if (Status == STATUS_WAIT_0)
{
KeAcquireSpinLock(&mbox->Lock, &OldIrql);
Entry = RemoveHeadList(&mbox->ListHead);
ASSERT(Entry);
if (IsListEmpty(&mbox->ListHead))
KeClearEvent(&mbox->Event);
KeReleaseSpinLock(&mbox->Lock, OldIrql);
Container = CONTAINING_RECORD(Entry, LWIP_MESSAGE_CONTAINER, ListEntry);
Message = Container->Message;
ExFreePool(Container);
if (msg)
*msg = Message;
KeQuerySystemTime(&PostWaitTime);
TimeDiff = PostWaitTime.QuadPart - PreWaitTime.QuadPart;
TimeDiff /= 10000;
return TimeDiff;
}
else if (Status == STATUS_WAIT_1)
{
/* DON'T remove ourselves from the thread list! */
PsTerminateSystemThread(STATUS_SUCCESS);
/* We should never get here! */
ASSERT(FALSE);
return 0;
}
return SYS_ARCH_TIMEOUT;
}
u32_t
sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
{
if (sys_arch_mbox_fetch(mbox, msg, 1) != SYS_ARCH_TIMEOUT)
return 0;
else
return SYS_MBOX_EMPTY;
}
err_t
sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
{
sys_mbox_post(mbox, msg);
return ERR_OK;
}
VOID
NTAPI
LwipThreadMain(PVOID Context)
{
thread_t Container = (thread_t)Context;
KIRQL OldIrql;
ExInterlockedInsertHeadList(&ThreadListHead, &Container->ListEntry, &ThreadListLock);
Container->ThreadFunction(Container->ThreadContext);
KeAcquireSpinLock(&ThreadListLock, &OldIrql);
RemoveEntryList(&Container->ListEntry);
KeReleaseSpinLock(&ThreadListLock, OldIrql);
ExFreePool(Container);
PsTerminateSystemThread(STATUS_SUCCESS);
}
sys_thread_t
sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize, int prio)
{
thread_t Container;
NTSTATUS Status;
Container = ExAllocatePool(NonPagedPool, sizeof(*Container));
if (!Container)
return 0;
Container->ThreadFunction = thread;
Container->ThreadContext = arg;
Status = PsCreateSystemThread(&Container->Handle,
THREAD_ALL_ACCESS,
NULL,
NULL,
NULL,
LwipThreadMain,
Container);
if (!NT_SUCCESS(Status))
{
ExFreePool(Container);
return 0;
}
return 0;
}
void
sys_init(void)
{
KeInitializeSpinLock(&ThreadListLock);
InitializeListHead(&ThreadListHead);
KeQuerySystemTime(&StartTime);
KeInitializeEvent(&TerminationEvent, NotificationEvent, FALSE);
ExInitializeNPagedLookasideList(&MessageLookasideList,
NULL,
NULL,
0,
sizeof(struct lwip_callback_msg),
LWIP_MESSAGE_TAG,
0);
ExInitializeNPagedLookasideList(&QueueEntryLookasideList,
NULL,
NULL,
0,
sizeof(QUEUE_ENTRY),
LWIP_QUEUE_TAG,
0);
}
void
sys_shutdown(void)
{
PLIST_ENTRY CurrentEntry;
thread_t Container;
/* Set the termination event */
KeSetEvent(&TerminationEvent, IO_NO_INCREMENT, FALSE);
/* Loop through the thread list and wait for each to die */
while ((CurrentEntry = ExInterlockedRemoveHeadList(&ThreadListHead, &ThreadListLock)))
{
Container = CONTAINING_RECORD(CurrentEntry, struct _thread_t, ListEntry);
if (Container->ThreadFunction)
{
KeWaitForSingleObject(Container->Handle,
Executive,
KernelMode,
FALSE,
NULL);
ZwClose(Container->Handle);
}
}
ExDeleteNPagedLookasideList(&MessageLookasideList);
ExDeleteNPagedLookasideList(&QueueEntryLookasideList);
}