|
|
/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
director.c
Abstract:
This module contains the code for director management.
Author:
Abolade Gbadegesin (t-abolag) 16-Feb-1998
Revision History:
Abolade Gbadegesin (aboladeg) 19-Apr-1998
Added support for wildcards in protocol/port of a director registration.
--*/
#include "precomp.h"
#pragma hdrstop
//
// GLOBAL DATA DEFINITIONS
//
//
// Count of NAT directors
//
ULONG DirectorCount;
//
// List of NAT directors
//
LIST_ENTRY DirectorList;
//
// Spin-lock controlling access to 'DirectorList'
//
KSPIN_LOCK DirectorLock;
//
// Spin-lock controlling access to the 'MappingList' field of all directors
//
KSPIN_LOCK DirectorMappingLock;
VOID NatCleanupDirector( PNAT_DIRECTOR Director )
/*++
Routine Description:
Called to perform final cleanup for an director.
Arguments:
Director - the director to be cleaned up.
Return Value:
none.
--*/
{ KIRQL Irql; PLIST_ENTRY Link; PNAT_DYNAMIC_MAPPING Mapping;
CALLTRACE(("NatCleanupDirector\n"));
//
// Detach the director from all of its mappings
//
KeAcquireSpinLock(&DirectorLock, &Irql); KeAcquireSpinLockAtDpcLevel(&DirectorMappingLock); for (Link = Director->MappingList.Flink; Link != &Director->MappingList; Link = Link->Flink) { Mapping = CONTAINING_RECORD(Link, NAT_DYNAMIC_MAPPING, DirectorLink); Link = Link->Blink; NatMappingDetachDirector( Director, Mapping->DirectorContext, Mapping, NatCleanupDirectorDeleteReason ); } KeReleaseSpinLockFromDpcLevel(&DirectorMappingLock); KeReleaseSpinLock(&DirectorLock, Irql);
if (Director->UnloadHandler) { Director->UnloadHandler(Director->Context); }
ExFreePool(Director);
} // NatCleanupDirector
NTSTATUS NatCreateDirector( PIP_NAT_REGISTER_DIRECTOR RegisterContext )
/*++
Routine Description:
This routine is invoked when an director attempts to register. It handles creation of a context-block for the director.
Arguments:
RegisterContext - information about the registering director
Return Value:
NTSTATUS - status code.
--*/
{ PNAT_DIRECTOR Director; PLIST_ENTRY InsertionPoint; KIRQL Irql; ULONG Key;
CALLTRACE(("NatCreateDirector\n"));
RegisterContext->DirectorHandle = NULL;
//
// Validate the registration information
//
if (!RegisterContext->QueryHandler && !RegisterContext->CreateHandler && !RegisterContext->DeleteHandler && !RegisterContext->UnloadHandler) { ERROR(("NatCreateDirector: bad argument\n")); return STATUS_INVALID_PARAMETER; }
//
// Allocate a new director-struct
//
Director = ExAllocatePoolWithTag( NonPagedPool, sizeof(NAT_DIRECTOR), NAT_TAG_DIRECTOR );
if (!Director) { ERROR(("NatCreateDirector: allocation failed\n")); return STATUS_NO_MEMORY; }
KeInitializeSpinLock(&Director->Lock); Director->ReferenceCount = 1; Director->Flags = RegisterContext->Flags; Director->Key = MAKE_DIRECTOR_KEY(RegisterContext->Protocol, RegisterContext->Port); InitializeListHead(&Director->MappingList); Director->Context = RegisterContext->DirectorContext; Director->CreateHandler = RegisterContext->CreateHandler; Director->DeleteHandler = RegisterContext->DeleteHandler; Director->QueryHandler = RegisterContext->QueryHandler; Director->UnloadHandler = RegisterContext->UnloadHandler;
KeAcquireSpinLock(&DirectorLock, &Irql); if (NatLookupDirector(Director->Key, &InsertionPoint)) { KeReleaseSpinLock(&DirectorLock, Irql); ERROR( ("NatCreateDirector: duplicate director %d/%d\n", RegisterContext->Protocol, RegisterContext->Port) ); ExFreePool(Director); return STATUS_UNSUCCESSFUL; } InsertTailList(InsertionPoint, &Director->Link); KeReleaseSpinLock(&DirectorLock, Irql);
InterlockedIncrement(&DirectorCount);
//
// Supply the caller with 'out' information
//
RegisterContext->DirectorHandle = (PVOID)Director; RegisterContext->QueryInfoSession = NatDirectorQueryInfoSession; RegisterContext->Deregister = NatDirectorDeregister; RegisterContext->DissociateSession = NatDirectorDissociateSession;
return STATUS_SUCCESS;
} // NatCreateDirector
NTSTATUS NatDeleteDirector( PNAT_DIRECTOR Director )
/*++
Routine Description:
Handles director deletion.
Arguments:
Director - specifies the director to be deleted.
Return Value
NTSTATUS - status code.
--*/
{ KIRQL Irql; CALLTRACE(("NatDeleteDirector\n")); if (!Director) { return STATUS_INVALID_PARAMETER; } InterlockedDecrement(&DirectorCount);
//
// Remove the director from the list
//
KeAcquireSpinLock(&DirectorLock, &Irql); RemoveEntryList(&Director->Link); Director->Flags |= NAT_DIRECTOR_FLAG_DELETED; KeReleaseSpinLock(&DirectorLock, Irql);
//
// Drop its reference count and cleanup if necessary
//
if (InterlockedDecrement(&Director->ReferenceCount) > 0) { return STATUS_PENDING; } NatCleanupDirector(Director); return STATUS_SUCCESS;
} // NatDeleteDirector
VOID NatInitializeDirectorManagement( VOID )
/*++
Routine Description:
This routine prepares the director-management module for operation.
Arguments:
none.
Return Value:
none.
--*/
{ CALLTRACE(("NatInitializeDirectorManagement\n"));
DirectorCount = 0; KeInitializeSpinLock(&DirectorLock); InitializeListHead(&DirectorList); KeInitializeSpinLock(&DirectorMappingLock);
} // NatInitializeDirectorManagement
PNAT_DIRECTOR NatLookupAndReferenceDirector( UCHAR Protocol, USHORT Port )
/*++
Routine Description:
This routine is called to search for a director for the given incoming protocol and port, and to obtain a referenced pointer to such a director.
This routine must be invoked at DISPATCH_LEVEL.
Arguments:
Protocol - the protocol of the director to be looked up
Port - the port-number of the director to be looked up
Return Value:
PNAT_DIRECTOR - the references director if found; NULL otherwise.
--*/
{ PNAT_DIRECTOR Director; ULONG Key; PLIST_ENTRY Link;
KeAcquireSpinLockAtDpcLevel(&DirectorLock);
if (IsListEmpty(&DirectorList)) { KeReleaseSpinLockFromDpcLevel(&DirectorLock); return NULL; } Key = MAKE_DIRECTOR_KEY(Protocol, Port);
//
// Our support for wildcards takes advantage of the fact that
// all wildcards are designated by zero; hence, since our list
// is in descending order we only need to look for wildcards
// at the point where we would break off a normal search.
//
for (Link = DirectorList.Flink; Link != &DirectorList; Link = Link->Flink) { Director = CONTAINING_RECORD(Link, NAT_DIRECTOR, Link); if (Key < Director->Key) { continue; } else if (Key > Director->Key) { //
// End of normal search. Now look for wildcards
//
do { if ((!DIRECTOR_KEY_PROTOCOL(Director->Key) || Protocol == DIRECTOR_KEY_PROTOCOL(Director->Key)) && (!DIRECTOR_KEY_PORT(Director->Key) || Port == DIRECTOR_KEY_PORT(Director->Key))) { //
// We have a matching wildcard.
//
break; } Link = Link->Flink; } while (Link != &DirectorList); if (Link == &DirectorList) { break; } }
//
// We've found it. Reference it and return.
//
if (!NatReferenceDirector(Director)) { Director = NULL; } KeReleaseSpinLockFromDpcLevel(&DirectorLock); return Director; }
KeReleaseSpinLockFromDpcLevel(&DirectorLock);
return NULL;
} // NatLookupAndReferenceDirector
PNAT_DIRECTOR NatLookupDirector( ULONG Key, PLIST_ENTRY* InsertionPoint )
/*++
Routine Description:
This routine is called to retrieve the director corresponding to the given key.
Arguments:
Key - the key for which an director is to be found
InsertionPoint - receives the point at which the director should be inserted if not found
Return Value:
PNAT_DIRECTOR - the required director, if found
--*/
{ PNAT_DIRECTOR Director; PLIST_ENTRY Link; for (Link = DirectorList.Flink; Link != &DirectorList; Link = Link->Flink) { Director = CONTAINING_RECORD(Link, NAT_DIRECTOR, Link); if (Key < Director->Key) { continue; } else if (Key > Director->Key) { break; } return Director; } if (InsertionPoint) { *InsertionPoint = Link; } return NULL; } // NatLookupDirector
VOID NatMappingAttachDirector( PNAT_DIRECTOR Director, PVOID DirectorSessionContext, PNAT_DYNAMIC_MAPPING Mapping )
/*++
Routine Description:
This routine is invoked to attach a mapping to a director. It serves as a notification that there is one more mapping associated with the director.
Arguments:
Director - the director for the mapping
DirectorSessionContext - context associated with the mapping by the director
Mapping - the mapping to be attached.
Return Value:
none.
Environment:
Always invoked at dispatch level with 'DirectorLock' and 'DirectorMappingLock' held by the caller.
--*/
{ Mapping->Director = Director; Mapping->DirectorContext = DirectorSessionContext; InsertTailList(&Director->MappingList, &Mapping->DirectorLink); if (Director->CreateHandler) { Director->CreateHandler( Mapping, Director->Context, DirectorSessionContext ); } } // NatMappingAttachDirector
VOID NatMappingDetachDirector( PNAT_DIRECTOR Director, PVOID DirectorSessionContext, PNAT_DYNAMIC_MAPPING Mapping, IP_NAT_DELETE_REASON DeleteReason )
/*++
Routine Description:
This routine is invoked to detach a mapping from a director. It serves as a notification that there is one less mapping associated with the director.
Arguments:
Director - director to be detached
DirectorSessionContext - context associated with the director
Mapping - the mapping to be detached, or NULL if a mapping could not be created.
Return Value:
none.
Environment:
Always invoked at dispatch level with 'DirectorLock' and 'DirectorMappingLock' held, in that order.
--*/
{ KIRQL Irql; if (!Mapping) { if (Director->DeleteHandler) { Director->DeleteHandler( NULL, Director->Context, DirectorSessionContext, DeleteReason ); } } else { if (Director->DeleteHandler) { Director->DeleteHandler( Mapping, Director->Context, Mapping->DirectorContext, DeleteReason ); } RemoveEntryList(&Mapping->DirectorLink); Mapping->Director = NULL; Mapping->DirectorContext = NULL; } } // NatMappingDetachDirector
NTSTATUS NatQueryDirectorTable( IN PIP_NAT_ENUMERATE_DIRECTORS InputBuffer, IN PIP_NAT_ENUMERATE_DIRECTORS OutputBuffer, IN PULONG OutputBufferLength )
/*++
Routine Description:
This routine is used for enumerating the registered directors.
Arguments:
InputBuffer - supplies context information for the information
OutputBuffer - receives the result of the enumeration
OutputBufferLength - size of the i/o buffer
Return Value:
STATUS_SUCCESS if successful, error code otherwise.
--*/
{ ULONG Count; ULONG i; KIRQL Irql; ULONG Key; PLIST_ENTRY Link; PNAT_DIRECTOR Director; NTSTATUS status; PIP_NAT_DIRECTOR Table;
CALLTRACE(("NatQueryDirectorTable\n"));
Key = InputBuffer->EnumerateContext; KeAcquireSpinLock(&DirectorLock, &Irql);
//
// See if this is a new enumeration or a continuation of an old one.
//
if (!Key) {
//
// This is a new enumeration. We start with the first item
// in the list of entries
//
Director = IsListEmpty(&DirectorList) ? NULL : CONTAINING_RECORD(DirectorList.Flink, NAT_DIRECTOR, Link); } else {
//
// This is a continuation. The context therefore contains
// the key for the next entry.
//
Director = NatLookupDirector(Key, NULL); }
if (!Director) { OutputBuffer->EnumerateCount = 0; OutputBuffer->EnumerateContext = 0; OutputBuffer->EnumerateTotalHint = DirectorCount; *OutputBufferLength = FIELD_OFFSET(IP_NAT_ENUMERATE_DIRECTORS, EnumerateTable); KeReleaseSpinLock(&DirectorLock, Irql); return STATUS_SUCCESS; }
//
// Compute the maximum number of entries we can store
//
Count = *OutputBufferLength - FIELD_OFFSET(IP_NAT_ENUMERATE_DIRECTORS, EnumerateTable); Count /= sizeof(IP_NAT_DIRECTOR);
//
// Walk the list storing entries in the caller's buffer
//
Table = OutputBuffer->EnumerateTable;
for (i = 0, Link = &Director->Link; i < Count && Link != &DirectorList; i++, Link = Link->Flink) { Director = CONTAINING_RECORD(Link, NAT_DIRECTOR, Link); Table[i].Protocol = DIRECTOR_KEY_PROTOCOL(Director->Key); Table[i].Port = DIRECTOR_KEY_PORT(Director->Key); }
//
// The enumeration is over; update the output structure
//
*OutputBufferLength = i * sizeof(IP_NAT_DIRECTOR) + FIELD_OFFSET(IP_NAT_ENUMERATE_DIRECTORS, EnumerateTable); OutputBuffer->EnumerateCount = i; OutputBuffer->EnumerateTotalHint = DirectorCount; if (Link == &DirectorList) { //
// We reached the end of the list
//
OutputBuffer->EnumerateContext = 0; } else { //
// Save the continuation context
//
OutputBuffer->EnumerateContext = CONTAINING_RECORD(Link, NAT_DIRECTOR, Link)->Key; }
KeReleaseSpinLock(&DirectorLock, Irql); return STATUS_SUCCESS;
} // NatQueryDirectorTable
VOID NatShutdownDirectorManagement( VOID )
/*++
Routine Description:
This routine shuts down the director-management module.
Arguments:
none.
Return Value:
none.
--*/
{ PNAT_DIRECTOR Director; KIRQL Irql;
CALLTRACE(("NatShutdownDirectorManagement\n"));
//
// Delete all directors
//
KeAcquireSpinLock(&DirectorLock, &Irql); while (!IsListEmpty(&DirectorList)) { Director = CONTAINING_RECORD(DirectorList.Flink, NAT_DIRECTOR, Link); RemoveEntryList(&Director->Link); KeReleaseSpinLockFromDpcLevel(&DirectorLock); NatCleanupDirector(Director); KeAcquireSpinLockAtDpcLevel(&DirectorLock); } KeReleaseSpinLock(&DirectorLock, Irql);
} // NatShutdownDirectorManagement
//
// DIRECTOR HELPER ROUTINES
//
// The caller is assumed to be running at DISPATCH_LEVEL.
//
NTSTATUS NatDirectorDeregister( IN PVOID DirectorHandle )
/*++
Routine Description:
This routine is called by a director to remove itself from the director list.
Arguments:
DirectorHandle - handle of the director to be removed.
Return Value:
NTSTATUS - status code.
--*/
{ CALLTRACE(("NatDirectorDeregister\n")); return NatDeleteDirector((PNAT_DIRECTOR)DirectorHandle);
} // NatDirectorDeregister
NTSTATUS NatDirectorDissociateSession( IN PVOID DirectorHandle, IN PVOID SessionHandle )
/*++
Routine Description:
This routine is called by a director to dissociate itself from a specific session.
Arguments:
DirectorHandle - the director which wishes to dissociate itself.
SessionHandle - the session from which the director is disssociating itself.
Return Value:
NTSTATUS - indicates success/failure
Environment:
Invoked at dispatch level with neither 'DirectorLock' nor 'DirectorMappingLock' held by the caller.
--*/
{
PNAT_DIRECTOR Director = (PNAT_DIRECTOR)DirectorHandle; KIRQL Irql; PNAT_DYNAMIC_MAPPING Mapping = (PNAT_DYNAMIC_MAPPING)SessionHandle; CALLTRACE(("NatDirectorDissociateSession\n")); KeAcquireSpinLock(&DirectorLock, &Irql); if (Mapping->Director != Director) { KeReleaseSpinLock(&DirectorLock, Irql); return STATUS_INVALID_PARAMETER; } KeAcquireSpinLockAtDpcLevel(&DirectorMappingLock); NatMappingDetachDirector( Director, Mapping->DirectorContext, Mapping, NatDissociateDirectorDeleteReason ); KeReleaseSpinLockFromDpcLevel(&DirectorMappingLock); if (!NAT_MAPPING_DELETE_ON_DISSOCIATE_DIRECTOR(Mapping)) { KeReleaseSpinLock(&DirectorLock, Irql); } else { KeReleaseSpinLockFromDpcLevel(&DirectorLock); KeAcquireSpinLockAtDpcLevel(&MappingLock); NatDeleteMapping(Mapping); KeReleaseSpinLock(&MappingLock, Irql); } return STATUS_SUCCESS;
} // NatDirectorDissociateSession
VOID NatDirectorQueryInfoSession( IN PVOID SessionHandle, OUT PIP_NAT_SESSION_MAPPING_STATISTICS Statistics OPTIONAL )
/*++
Routine Description:
This routine is invoked by a director to obtain information about a session.
Arguments:
SessionHandle - the session for which information is required
Statistics - receives statistics for the session
Return Value:
none.
Environment:
Invoked --*/
{ KIRQL Irql; KeAcquireSpinLock(&MappingLock, &Irql); NatQueryInformationMapping( (PNAT_DYNAMIC_MAPPING)SessionHandle, NULL, NULL, NULL, NULL, NULL, NULL, NULL, Statistics ); KeReleaseSpinLock(&MappingLock, Irql); } // NatDirectorQueryInfoSession
|