mirror of
https://github.com/reactos/reactos.git
synced 2025-07-31 23:53:19 +00:00
[TCPIP]
- Add logging of address files and connections (on temporarily for testing changes) [LWIP] - Drastically simplify the closing state machine - All connection objects (and as a result address files too) are now getting properly reaped now - Tested with Firefox and Abyss Web Server svn path=/trunk/; revision=59705
This commit is contained in:
parent
4ed049cc7b
commit
578fec3cac
6 changed files with 166 additions and 48 deletions
|
@ -35,4 +35,6 @@ NTSTATUS FileOpenControlChannel(
|
|||
NTSTATUS FileCloseControlChannel(
|
||||
PTDI_REQUEST Request);
|
||||
|
||||
VOID LogActiveObjects(VOID);
|
||||
|
||||
/* EOF */
|
||||
|
|
|
@ -211,3 +211,6 @@ VOID
|
|||
FlushAllQueues(PCONNECTION_ENDPOINT Connection, NTSTATUS Status);
|
||||
|
||||
VOID CompleteBucket(PCONNECTION_ENDPOINT Connection, PTDI_BUCKET Bucket, const BOOLEAN Synchronous);
|
||||
|
||||
void
|
||||
LibTCPDumpPcb(PVOID SocketContext);
|
||||
|
|
|
@ -281,6 +281,7 @@ typedef struct _CONNECTION_ENDPOINT {
|
|||
BOOLEAN SendShutdown;
|
||||
BOOLEAN ReceiveShutdown;
|
||||
NTSTATUS ReceiveShutdownStatus;
|
||||
BOOLEAN Closing;
|
||||
|
||||
struct _CONNECTION_ENDPOINT *Next; /* Next connection in address file list */
|
||||
} CONNECTION_ENDPOINT, *PCONNECTION_ENDPOINT;
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
#include "precomp.h"
|
||||
|
||||
/* Uncomment for logging of connections and address files every 10 seconds */
|
||||
#define LOG_OBJECTS
|
||||
|
||||
/* List of all address file objects managed by this driver */
|
||||
LIST_ENTRY AddressFileListHead;
|
||||
|
@ -99,6 +101,88 @@ BOOLEAN AddrReceiveMatch(
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
VOID
|
||||
LogActiveObjects(VOID)
|
||||
{
|
||||
#ifdef LOG_OBJECTS
|
||||
PLIST_ENTRY CurrentEntry;
|
||||
KIRQL OldIrql;
|
||||
PADDRESS_FILE AddrFile;
|
||||
PCONNECTION_ENDPOINT Conn;
|
||||
|
||||
DbgPrint("----------- TCP/IP Active Object Dump -------------\n");
|
||||
|
||||
TcpipAcquireSpinLock(&AddressFileListLock, &OldIrql);
|
||||
|
||||
CurrentEntry = AddressFileListHead.Flink;
|
||||
while (CurrentEntry != &AddressFileListHead)
|
||||
{
|
||||
AddrFile = CONTAINING_RECORD(CurrentEntry, ADDRESS_FILE, ListEntry);
|
||||
|
||||
DbgPrint("Address File (%s, %d, %d) @ 0x%p | Ref count: %d | Sharers: %d\n",
|
||||
A2S(&AddrFile->Address), WN2H(AddrFile->Port), AddrFile->Protocol,
|
||||
AddrFile, AddrFile->RefCount, AddrFile->Sharers);
|
||||
DbgPrint("\tListener: ");
|
||||
if (AddrFile->Listener == NULL)
|
||||
DbgPrint("<None>\n");
|
||||
else
|
||||
DbgPrint("0x%p\n", AddrFile->Listener);
|
||||
DbgPrint("\tAssociated endpoints: ");
|
||||
if (AddrFile->Connection == NULL)
|
||||
DbgPrint("<None>\n");
|
||||
else
|
||||
{
|
||||
Conn = AddrFile->Connection;
|
||||
while (Conn)
|
||||
{
|
||||
DbgPrint("0x%p ", Conn);
|
||||
Conn = Conn->Next;
|
||||
}
|
||||
DbgPrint("\n");
|
||||
}
|
||||
|
||||
CurrentEntry = CurrentEntry->Flink;
|
||||
}
|
||||
|
||||
TcpipReleaseSpinLock(&AddressFileListLock, OldIrql);
|
||||
|
||||
TcpipAcquireSpinLock(&ConnectionEndpointListLock, &OldIrql);
|
||||
|
||||
CurrentEntry = ConnectionEndpointListHead.Flink;
|
||||
while (CurrentEntry != &ConnectionEndpointListHead)
|
||||
{
|
||||
Conn = CONTAINING_RECORD(CurrentEntry, CONNECTION_ENDPOINT, ListEntry);
|
||||
|
||||
DbgPrint("Connection @ 0x%p | Ref count: %d\n", Conn, Conn->RefCount);
|
||||
DbgPrint("\tPCB: ");
|
||||
if (Conn->SocketContext == NULL)
|
||||
DbgPrint("<None>\n");
|
||||
else
|
||||
{
|
||||
DbgPrint("0x%p\n", Conn->SocketContext);
|
||||
LibTCPDumpPcb(Conn->SocketContext);
|
||||
}
|
||||
DbgPrint("\tPacket queue status: %s\n", IsListEmpty(&Conn->PacketQueue) ? "Empty" : "Not Empty");
|
||||
DbgPrint("\tRequest lists: Connect: %s | Recv: %s | Send: %s | Shutdown: %s | Listen: %s\n",
|
||||
IsListEmpty(&Conn->ConnectRequest) ? "Empty" : "Not Empty",
|
||||
IsListEmpty(&Conn->ReceiveRequest) ? "Empty" : "Not Empty",
|
||||
IsListEmpty(&Conn->SendRequest) ? "Empty" : "Not Empty",
|
||||
IsListEmpty(&Conn->ShutdownRequest) ? "Empty" : "Not Empty",
|
||||
IsListEmpty(&Conn->ListenRequest) ? "Empty" : "Not Empty");
|
||||
DbgPrint("\tSend shutdown: %s\n", Conn->SendShutdown ? "Yes" : "No");
|
||||
DbgPrint("\tReceive shutdown: %s\n", Conn->ReceiveShutdown ? "Yes" : "No");
|
||||
if (Conn->ReceiveShutdown) DbgPrint("\tReceive shutdown status: 0x%x\n", Conn->ReceiveShutdownStatus);
|
||||
DbgPrint("\tClosing: %s\n", Conn->Closing ? "Yes" : "No");
|
||||
|
||||
CurrentEntry = CurrentEntry->Flink;
|
||||
}
|
||||
|
||||
TcpipReleaseSpinLock(&ConnectionEndpointListLock, OldIrql);
|
||||
|
||||
DbgPrint("---------------------------------------------------\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
PADDRESS_FILE AddrFindShared(
|
||||
PIP_ADDRESS BindAddress,
|
||||
USHORT Port,
|
||||
|
|
|
@ -24,6 +24,8 @@ BOOLEAN IpWorkItemQueued = FALSE;
|
|||
|
||||
IP_PROTOCOL_HANDLER ProtocolTable[IP_PROTOCOL_TABLE_SIZE];
|
||||
|
||||
ULONG IpTimerExpirations;
|
||||
|
||||
VOID
|
||||
TCPRegisterInterface(PIP_INTERFACE IF);
|
||||
|
||||
|
@ -119,9 +121,16 @@ VOID NTAPI IPTimeoutDpcFn(PKDPC Dpc,
|
|||
* SystemArgument1 = Unused
|
||||
* SystemArgument2 = Unused
|
||||
* NOTES:
|
||||
* This routine is dispatched once in a while to do maintainance jobs
|
||||
* This routine is dispatched once in a while to do maintenance jobs
|
||||
*/
|
||||
{
|
||||
IpTimerExpirations++;
|
||||
|
||||
if ((IpTimerExpirations % 10) == 0)
|
||||
{
|
||||
LogActiveObjects();
|
||||
}
|
||||
|
||||
/* Check if datagram fragments have taken too long to assemble */
|
||||
IPDatagramReassemblyTimeout();
|
||||
|
||||
|
|
|
@ -35,6 +35,21 @@ 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)
|
||||
|
@ -231,18 +246,15 @@ InternalRecvEventHandler(void *arg, PTCP_PCB pcb, struct pbuf *p, const err_t er
|
|||
Connection->ReceiveShutdown = TRUE;
|
||||
Connection->ReceiveShutdownStatus = STATUS_SUCCESS;
|
||||
|
||||
/* This code path executes for both remotely and locally initiated closures,
|
||||
* and we need to distinguish between them */
|
||||
if (Connection->SocketContext)
|
||||
/* If we already did a send shutdown, we're in TIME_WAIT so we can't use this PCB anymore */
|
||||
if (Connection->SendShutdown)
|
||||
{
|
||||
/* Remotely initiated close */
|
||||
TCPRecvEventHandler(arg);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Locally initated close */
|
||||
TCPFinEventHandler(arg, ERR_CLSD);
|
||||
Connection->SocketContext = NULL;
|
||||
tcp_arg(pcb, NULL);
|
||||
}
|
||||
|
||||
/* Remotely initiated close */
|
||||
TCPRecvEventHandler(arg);
|
||||
}
|
||||
|
||||
return ERR_OK;
|
||||
|
@ -285,30 +297,18 @@ void
|
|||
InternalErrorEventHandler(void *arg, const err_t err)
|
||||
{
|
||||
PCONNECTION_ENDPOINT Connection = arg;
|
||||
KIRQL OldIrql;
|
||||
|
||||
/* Make sure the socket didn't get closed */
|
||||
if (!arg) return;
|
||||
|
||||
/* Check if data is left to be read */
|
||||
LockObject(Connection, &OldIrql);
|
||||
if (IsListEmpty(&Connection->PacketQueue))
|
||||
{
|
||||
UnlockObject(Connection, OldIrql);
|
||||
/* The PCB is dead now */
|
||||
Connection->SocketContext = NULL;
|
||||
|
||||
/* Deliver the error now */
|
||||
TCPFinEventHandler(arg, err);
|
||||
}
|
||||
else
|
||||
{
|
||||
UnlockObject(Connection, OldIrql);
|
||||
/* Defer the error delivery until all data is gone */
|
||||
Connection->ReceiveShutdown = TRUE;
|
||||
Connection->ReceiveShutdownStatus = TCPTranslateError(err);
|
||||
|
||||
/* Defer the error delivery until all data is gone */
|
||||
Connection->ReceiveShutdown = TRUE;
|
||||
Connection->ReceiveShutdownStatus = TCPTranslateError(err);
|
||||
|
||||
TCPRecvEventHandler(arg);
|
||||
}
|
||||
TCPRecvEventHandler(arg);
|
||||
}
|
||||
|
||||
static
|
||||
|
@ -633,7 +633,12 @@ LibTCPShutdownCallback(void *arg)
|
|||
goto done;
|
||||
}
|
||||
|
||||
/* These need to be called separately, otherwise we get a tcp_close() */
|
||||
/* 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->Output.Shutdown.Error = tcp_shutdown(pcb, TRUE, FALSE);
|
||||
}
|
||||
|
@ -651,6 +656,14 @@ LibTCPShutdownCallback(void *arg)
|
|||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
|
@ -697,37 +710,43 @@ LibTCPCloseCallback(void *arg)
|
|||
/* Empty the queue even if we're already "closed" */
|
||||
LibTCPEmptyQueue(msg->Input.Close.Connection);
|
||||
|
||||
if (!msg->Input.Close.Connection->SocketContext)
|
||||
/* Check if we've already been closed */
|
||||
if (msg->Input.Close.Connection->Closing)
|
||||
{
|
||||
msg->Output.Close.Error = ERR_OK;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Clear the PCB pointer */
|
||||
msg->Input.Close.Connection->SocketContext = NULL;
|
||||
/* Enter "closing" mode if we're doing a normal close */
|
||||
if (msg->Input.Close.Callback)
|
||||
msg->Input.Close.Connection->Closing = TRUE;
|
||||
|
||||
switch (pcb->state)
|
||||
/* Check if the PCB was already "closed" but the client doesn't know it yet */
|
||||
if (!msg->Input.Close.Connection->SocketContext)
|
||||
{
|
||||
case CLOSED:
|
||||
case LISTEN:
|
||||
case SYN_SENT:
|
||||
msg->Output.Close.Error = tcp_close(pcb);
|
||||
|
||||
if (!msg->Output.Close.Error && msg->Input.Close.Callback)
|
||||
TCPFinEventHandler(msg->Input.Close.Connection, ERR_CLSD);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Abort the socket */
|
||||
tcp_abort(pcb);
|
||||
msg->Output.Close.Error = ERR_OK;
|
||||
break;
|
||||
if (msg->Input.Close.Callback)
|
||||
TCPFinEventHandler(msg->Input.Close.Connection, ERR_CLSD);
|
||||
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:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue