- 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:
Cameron Gutman 2013-08-12 03:09:28 +00:00
parent 4ed049cc7b
commit 578fec3cac
6 changed files with 166 additions and 48 deletions

View file

@ -35,4 +35,6 @@ NTSTATUS FileOpenControlChannel(
NTSTATUS FileCloseControlChannel(
PTDI_REQUEST Request);
VOID LogActiveObjects(VOID);
/* EOF */

View file

@ -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);

View file

@ -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;

View file

@ -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,

View file

@ -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();

View file

@ -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,19 +246,16 @@ 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)
{
Connection->SocketContext = NULL;
tcp_arg(pcb, NULL);
}
/* Remotely initiated close */
TCPRecvEventHandler(arg);
}
else
{
/* Locally initated close */
TCPFinEventHandler(arg, ERR_CLSD);
}
}
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);
/* Deliver the error now */
TCPFinEventHandler(arg, err);
}
else
{
UnlockObject(Connection, OldIrql);
/* The PCB is dead now */
Connection->SocketContext = NULL;
/* Defer the error delivery until all data is gone */
Connection->ReceiveShutdown = TRUE;
Connection->ReceiveShutdownStatus = TCPTranslateError(err);
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)
if (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;
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: