You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2449 lines
84 KiB
2449 lines
84 KiB
// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil -*- (for GNU Emacs)
|
|
//
|
|
// Copyright (c) 1985-2000 Microsoft Corporation
|
|
//
|
|
// This file is part of the Microsoft Research IPv6 Network Protocol Stack.
|
|
// You should have received a copy of the Microsoft End-User License Agreement
|
|
// for this software along with this release; see the file "license.txt".
|
|
// If not, please see http://www.research.microsoft.com/msripv6/license.htm,
|
|
// or write to Microsoft Research, One Microsoft Way, Redmond, WA 98052-6399.
|
|
//
|
|
// Abstract:
|
|
//
|
|
// Common transport layer address object handling code.
|
|
//
|
|
// This file contains the TDI address object related procedures,
|
|
// including TDI open address, TDI close address, etc.
|
|
//
|
|
// The local address objects are stored in a hash table, protected
|
|
// by the AddrObjTableLock. In order to insert or delete from the
|
|
// hash table this lock must be held, as well as the address object
|
|
// lock. The table lock must always be taken before the object lock.
|
|
//
|
|
|
|
|
|
#include "oscfg.h"
|
|
#include "ndis.h"
|
|
#include "tdi.h"
|
|
#include "tdistat.h"
|
|
#include "ip6imp.h"
|
|
#include "ip6def.h"
|
|
#include "route.h"
|
|
#include "mld.h"
|
|
#include "tdint.h"
|
|
#include "tdistat.h"
|
|
#include "queue.h"
|
|
#include "transprt.h"
|
|
#include "addr.h"
|
|
#include "udp.h"
|
|
#include "raw.h"
|
|
#ifndef UDP_ONLY
|
|
#include "tcp.h"
|
|
#include "tcpconn.h"
|
|
#include "tcpdeliv.h"
|
|
#else
|
|
#include "tcpdeb.h"
|
|
#endif
|
|
#include "info.h"
|
|
#include "tcpcfg.h"
|
|
#include "ntddip6.h" // included only to get the common socket -> kernel interface structures.
|
|
|
|
extern KSPIN_LOCK DGSendReqLock;
|
|
|
|
|
|
// Addess object hash table.
|
|
uint AddrObjTableSize;
|
|
AddrObj **AddrObjTable;
|
|
AddrObj *LastAO; // one element lookup cache.
|
|
KSPIN_LOCK AddrObjTableLock;
|
|
KMUTEX AddrSDMutex;
|
|
#define AO_HASH(a, p) (((a).u.Word[7] + (uint)(p)) % AddrObjTableSize)
|
|
|
|
ushort NextUserPort = MIN_USER_PORT;
|
|
|
|
RTL_BITMAP PortBitmapTcp;
|
|
RTL_BITMAP PortBitmapUdp;
|
|
ulong PortBitmapBufferTcp[(1 << 16) / (sizeof(ulong) * 8)];
|
|
ulong PortBitmapBufferUdp[(1 << 16) / (sizeof(ulong) * 8)];
|
|
|
|
//
|
|
// All of the init code can be discarded.
|
|
//
|
|
#ifdef ALLOC_PRAGMA
|
|
|
|
int InitAddr();
|
|
|
|
#pragma alloc_text(INIT, InitAddr)
|
|
|
|
#endif // ALLOC_PRAGMA
|
|
|
|
|
|
//* ReadNextAO - Read the next AddrObj in the table.
|
|
//
|
|
// Called to read the next AddrObj in the table. The needed information
|
|
// is derived from the incoming context, which is assumed to be valid.
|
|
// We'll copy the information, and then update the context value with
|
|
// the next AddrObj to be read.
|
|
//
|
|
uint // Returns: TRUE if more data is available to be read, FALSE is not.
|
|
ReadNextAO(
|
|
void *Context, // Pointer to a UDPContext.
|
|
void *Buffer) // Pointer to a UDP6ListenerEntry structure.
|
|
{
|
|
UDPContext *UContext = (UDPContext *)Context;
|
|
UDP6ListenerEntry *UEntry = (UDP6ListenerEntry *)Buffer;
|
|
AddrObj *CurrentAO;
|
|
uint i;
|
|
|
|
CurrentAO = UContext->uc_ao;
|
|
CHECK_STRUCT(CurrentAO, ao);
|
|
|
|
UEntry->ule_localaddr = CurrentAO->ao_addr;
|
|
UEntry->ule_localscopeid = CurrentAO->ao_scope_id;
|
|
UEntry->ule_localport = CurrentAO->ao_port;
|
|
UEntry->ule_owningpid = CurrentAO->ao_owningpid;
|
|
|
|
// We've filled it in. Now update the context.
|
|
CurrentAO = CurrentAO->ao_next;
|
|
if (CurrentAO != NULL && CurrentAO->ao_prot == IP_PROTOCOL_UDP) {
|
|
UContext->uc_ao = CurrentAO;
|
|
return TRUE;
|
|
} else {
|
|
// The next AO is NULL, or not a UDP AO. Loop through the AddrObjTable
|
|
// looking for a new one.
|
|
i = UContext->uc_index;
|
|
|
|
for (;;) {
|
|
while (CurrentAO != NULL) {
|
|
if (CurrentAO->ao_prot == IP_PROTOCOL_UDP)
|
|
break;
|
|
else
|
|
CurrentAO = CurrentAO->ao_next;
|
|
}
|
|
|
|
if (CurrentAO != NULL)
|
|
break; // Get out of for (;;) loop.
|
|
|
|
ASSERT(CurrentAO == NULL);
|
|
|
|
// Didn't find one on this chain. Walk down the table, looking
|
|
// for the next one.
|
|
while (++i < AddrObjTableSize) {
|
|
if (AddrObjTable[i] != NULL) {
|
|
CurrentAO = AddrObjTable[i];
|
|
break; // Out of while loop.
|
|
}
|
|
}
|
|
|
|
if (i == AddrObjTableSize)
|
|
break; // Out of for (;;) loop.
|
|
}
|
|
|
|
// If we found one, return it.
|
|
if (CurrentAO != NULL) {
|
|
UContext->uc_ao = CurrentAO;
|
|
UContext->uc_index = i;
|
|
return TRUE;
|
|
} else {
|
|
UContext->uc_index = 0;
|
|
UContext->uc_ao = NULL;
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//* ValidateAOContext - Validate the context for reading the AddrObj table.
|
|
//
|
|
// Called to start reading the AddrObj table sequentially. We take in
|
|
// a context, and if the values are 0 we return information about the
|
|
// first AddrObj in the table. Otherwise we make sure that the context value
|
|
// is valid, and if it is we return TRUE.
|
|
// We assume the caller holds the AddrObjTable lock.
|
|
//
|
|
uint // Returns: TRUE if data in table, FALSE if not.
|
|
ValidateAOContext(
|
|
void *Context, // Pointer to a UDPContext.
|
|
uint *Valid) // Pointer to value to set to true if context is valid.
|
|
{
|
|
UDPContext *UContext = (UDPContext *)Context;
|
|
uint i;
|
|
AddrObj *TargetAO;
|
|
AddrObj *CurrentAO;
|
|
|
|
i = UContext->uc_index;
|
|
TargetAO = UContext->uc_ao;
|
|
|
|
// If the context values are 0 and NULL, we're starting from the beginning.
|
|
if (i == 0 && TargetAO == NULL) {
|
|
*Valid = TRUE;
|
|
do {
|
|
if ((CurrentAO = AddrObjTable[i]) != NULL) {
|
|
CHECK_STRUCT(CurrentAO, ao);
|
|
while (CurrentAO != NULL &&
|
|
CurrentAO->ao_prot != IP_PROTOCOL_UDP)
|
|
CurrentAO = CurrentAO->ao_next;
|
|
|
|
if (CurrentAO != NULL)
|
|
break;
|
|
}
|
|
i++;
|
|
} while (i < AddrObjTableSize);
|
|
|
|
if (CurrentAO != NULL) {
|
|
UContext->uc_index = i;
|
|
UContext->uc_ao = CurrentAO;
|
|
return TRUE;
|
|
} else
|
|
return FALSE;
|
|
|
|
} else {
|
|
|
|
// We've been given a context. We just need to make sure that it's
|
|
// valid.
|
|
|
|
if (i < AddrObjTableSize) {
|
|
CurrentAO = AddrObjTable[i];
|
|
while (CurrentAO != NULL) {
|
|
if (CurrentAO == TargetAO) {
|
|
if (CurrentAO->ao_prot == IP_PROTOCOL_UDP) {
|
|
*Valid = TRUE;
|
|
return TRUE;
|
|
}
|
|
break;
|
|
} else {
|
|
CurrentAO = CurrentAO->ao_next;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we get here, we didn't find the matching AddrObj.
|
|
*Valid = FALSE;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
//* FindIfIndexOnAO - Find an interface index in an address-object's list.
|
|
//
|
|
// This routine is called to determine whether a given interface index
|
|
// appears in the list of interfaces with which the given address-object
|
|
// is associated.
|
|
//
|
|
// The routine is called from 'GetAddrObj' with the table lock held
|
|
// but with the object lock not held. We take the object lock to look at
|
|
// its interface list, and release the lock before returning control.
|
|
//
|
|
uint // Returns: The index if found, or 0 if none.
|
|
FindIfIndexOnAO(
|
|
AddrObj *AO, // Address object to be searched.
|
|
Interface *IF) // The interface to be found.
|
|
{
|
|
uint *IfList;
|
|
KIRQL Irql;
|
|
|
|
KeAcquireSpinLock(&AO->ao_lock, &Irql);
|
|
if ((IfList = AO->ao_iflist) != NULL) {
|
|
while (*IfList) {
|
|
if (*IfList == IF->Index) {
|
|
KeReleaseSpinLock(&AO->ao_lock, Irql);
|
|
return IF->Index;
|
|
}
|
|
IfList++;
|
|
}
|
|
}
|
|
KeReleaseSpinLock(&AO->ao_lock, Irql);
|
|
|
|
//
|
|
// If an interface list was present and the interface was not found,
|
|
// return zero. Otherwise, if no interface list was present there is no
|
|
// restriction on the object, so return the interface index as though the
|
|
// interface appeared in the list.
|
|
//
|
|
return IfList ? 0 : IF->Index;
|
|
}
|
|
|
|
|
|
//* GetAddrObj - Find a local address object.
|
|
//
|
|
// This is the local address object lookup routine. We take as input the
|
|
// local address and port and a pointer to a 'previous' address object.
|
|
// The hash table entries in each bucket are sorted in order of increasing
|
|
// address, and we skip over any object that has an address lower than the
|
|
// 'previous' address. To get the first address object, pass in a previous
|
|
// value of NULL.
|
|
//
|
|
// We assume that the table lock is held while we're in this routine. We
|
|
// don't take each object lock, since the local address and port can't change
|
|
// while the entry is in the table and the table lock is held so nothing can
|
|
// be inserted or deleted.
|
|
//
|
|
AddrObj * // Returns: A pointer to the Address object, or NULL if none.
|
|
GetAddrObj(
|
|
IPv6Addr *LocalAddr, // Local IP address of object to find (may be NULL).
|
|
IPv6Addr *RemoteAddr, // Remote IP address to check against (may be NULL
|
|
// if IF is also NULL).
|
|
uint LocalScopeId, // Scope identifier for local IP address.
|
|
ushort LocalPort, // Local port of object to find.
|
|
uchar Protocol, // Protocol to find address.
|
|
AddrObj *PreviousAO, // Pointer to last address object found.
|
|
Interface *IF) // Interface to find in interface list (may be NULL).
|
|
{
|
|
AddrObj *CurrentAO; // Current address object we're examining.
|
|
|
|
#if DBG
|
|
if (PreviousAO != NULL)
|
|
CHECK_STRUCT(PreviousAO, ao);
|
|
#endif
|
|
|
|
#if 0
|
|
//
|
|
// Check our 1-element cache for a match.
|
|
//
|
|
if ((PreviousAO == NULL) && (LastAO != NULL)) {
|
|
CHECK_STRUCT(LastAO, ao);
|
|
if ((LastAO->ao_prot == Protocol) &&
|
|
IP6_ADDR_EQUAL(LastAO->ao_addr, LocalAddr) &&
|
|
(LastAO->ao_port == LocalPort))
|
|
{
|
|
return LastAO;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Find the appropriate bucket in the hash table, and search for a match.
|
|
// If we don't find one the first time through, we'll try again with a
|
|
// wildcard local address.
|
|
|
|
for (;;) {
|
|
|
|
CurrentAO = AddrObjTable[AO_HASH(*LocalAddr, LocalPort)];
|
|
|
|
// While we haven't hit the end of the list, examine each element.
|
|
while (CurrentAO != NULL) {
|
|
|
|
CHECK_STRUCT(CurrentAO, ao);
|
|
|
|
// If the current one is greater than one we were given, check it.
|
|
if (CurrentAO > PreviousAO) {
|
|
if (!(CurrentAO->ao_flags & AO_RAW_FLAG)) {
|
|
//
|
|
// This is an ordinary (non-raw) socket.
|
|
// Only match if we meet all the criteria.
|
|
//
|
|
if (IP6_ADDR_EQUAL(&CurrentAO->ao_addr, LocalAddr) &&
|
|
(CurrentAO->ao_scope_id == LocalScopeId) &&
|
|
(CurrentAO->ao_port == LocalPort) &&
|
|
(CurrentAO->ao_prot == Protocol)) {
|
|
|
|
if ((IF == NULL) ||
|
|
DoesAOAllowPacket(CurrentAO, IF, RemoteAddr)) {
|
|
// Found a match. Update cache and return.
|
|
LastAO = CurrentAO;
|
|
return CurrentAO;
|
|
}
|
|
}
|
|
} else {
|
|
//
|
|
// This is a raw socket.
|
|
//
|
|
if ((Protocol != IP_PROTOCOL_UDP)
|
|
#ifndef UDP_ONLY
|
|
&& (Protocol != IP_PROTOCOL_TCP)
|
|
#endif
|
|
)
|
|
{
|
|
IF_TCPDBG(TCP_DEBUG_RAW) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"matching <p, a> <%u, %lx> ao %lx <%u, %lx>\n",
|
|
Protocol, LocalAddr, CurrentAO,
|
|
CurrentAO->ao_prot, CurrentAO->ao_addr));
|
|
}
|
|
|
|
if (IP6_ADDR_EQUAL(&CurrentAO->ao_addr, LocalAddr) &&
|
|
(CurrentAO->ao_scope_id == LocalScopeId) &&
|
|
((CurrentAO->ao_prot == Protocol) ||
|
|
(CurrentAO->ao_prot == 0))) {
|
|
|
|
if ((IF == NULL) ||
|
|
DoesAOAllowPacket(CurrentAO, IF, RemoteAddr)) {
|
|
// Found a match. Update cache and return.
|
|
LastAO = CurrentAO;
|
|
return CurrentAO;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Either it was less than the previous one, or they didn't match.
|
|
// Keep searching in this bucket.
|
|
CurrentAO = CurrentAO->ao_next;
|
|
}
|
|
|
|
//
|
|
// When we get here, we've hit the end of the list we were examining.
|
|
// If we weren't examining a wildcard address, look for a wild card
|
|
// address.
|
|
//
|
|
if (!IP6_ADDR_EQUAL(LocalAddr, &UnspecifiedAddr)) {
|
|
LocalAddr = &UnspecifiedAddr;
|
|
LocalScopeId = 0;
|
|
PreviousAO = NULL;
|
|
} else
|
|
return NULL; // We looked for a wildcard and couldn't find
|
|
// one, so fail.
|
|
}
|
|
}
|
|
|
|
|
|
//* GetNextAddrObj - Get the next address object in a sequential search.
|
|
//
|
|
// This is the 'get next' routine, called when we are reading the address
|
|
// object table sequentially. We pull the appropriate parameters from the
|
|
// search context, call GetAddrObj, and update the search context with what
|
|
// we find. This routine assumes the AddrObjTableLock is held by the caller.
|
|
//
|
|
AddrObj * // Returns: Pointer to AddrObj, or NULL if search failed.
|
|
GetNextAddrObj(
|
|
AOSearchContext *SearchContext) // Context for search taking place.
|
|
{
|
|
AddrObj *FoundAO; // Pointer to the address object we found.
|
|
|
|
ASSERT(SearchContext != NULL);
|
|
|
|
// Try and find a match.
|
|
FoundAO = GetAddrObj(&SearchContext->asc_local_addr,
|
|
&SearchContext->asc_remote_addr,
|
|
SearchContext->asc_scope_id,
|
|
SearchContext->asc_port, SearchContext->asc_prot,
|
|
SearchContext->asc_previous,
|
|
SearchContext->asc_interface);
|
|
|
|
// Found a match. Update the search context for next time.
|
|
if (FoundAO != NULL) {
|
|
SearchContext->asc_previous = FoundAO;
|
|
SearchContext->asc_local_addr = FoundAO->ao_addr;
|
|
SearchContext->asc_scope_id = FoundAO->ao_scope_id;
|
|
// Don't bother to update port or protocol, they don't change.
|
|
}
|
|
return FoundAO;
|
|
}
|
|
|
|
//* GetFirstAddrObj - Get the first matching address object.
|
|
//
|
|
// The routine called to start a sequential read of the AddrObj table. We
|
|
// initialize the provided search context and then call GetNextAddrObj to do
|
|
// the actual read. We assume the AddrObjTableLock is held by the caller.
|
|
//
|
|
AddrObj * // Returns: Pointer to AO found, or NULL if we couldn't find any.
|
|
GetFirstAddrObj(
|
|
IPv6Addr *LocalAddr, // Local IP address of object to be found.
|
|
IPv6Addr *RemoteAddr, // Remote IP address to check against.
|
|
uint LocalScopeId, // Scope identifier for local IP address.
|
|
ushort LocalPort, // Local port of object to be found.
|
|
uchar Protocol, // Protocol of object to be found.
|
|
Interface *IF, // Interface to check against.
|
|
AOSearchContext *SearchContext) // Context to be used during search.
|
|
{
|
|
ASSERT(SearchContext != NULL);
|
|
|
|
// Fill in the search context.
|
|
SearchContext->asc_previous = NULL; // Haven't found one yet.
|
|
SearchContext->asc_local_addr = *LocalAddr;
|
|
SearchContext->asc_remote_addr = *RemoteAddr;
|
|
SearchContext->asc_scope_id = LocalScopeId;
|
|
SearchContext->asc_port = LocalPort;
|
|
SearchContext->asc_prot = Protocol;
|
|
SearchContext->asc_interface = IF;
|
|
return GetNextAddrObj(SearchContext);
|
|
}
|
|
|
|
//* InsertAddrObj - Insert an address object into the AddrObj table.
|
|
//
|
|
// Called to insert an AO into the table, assuming the table lock is held.
|
|
// We hash on the addr and port, and then insert in into the correct place
|
|
// (sorted by address of the objects).
|
|
//
|
|
void // Returns: Nothing.
|
|
InsertAddrObj(
|
|
AddrObj *NewAO) // Pointer to AddrObj to be inserted.
|
|
{
|
|
AddrObj *PrevAO; // Pointer to previous address object in hash chain.
|
|
AddrObj *CurrentAO; // Pointer to current AO in table.
|
|
|
|
CHECK_STRUCT(NewAO, ao);
|
|
|
|
PrevAO = CONTAINING_RECORD(&AddrObjTable[AO_HASH(NewAO->ao_addr,
|
|
NewAO->ao_port)],
|
|
AddrObj, ao_next);
|
|
CurrentAO = PrevAO->ao_next;
|
|
|
|
// Loop through the chain until we hit the end or until we find an entry
|
|
// whose address is greater than ours.
|
|
|
|
while (CurrentAO != NULL) {
|
|
|
|
CHECK_STRUCT(CurrentAO, ao);
|
|
ASSERT(CurrentAO != NewAO); // Debug check to make sure we aren't
|
|
// inserting the same entry.
|
|
if (NewAO < CurrentAO)
|
|
break;
|
|
PrevAO = CurrentAO;
|
|
CurrentAO = CurrentAO->ao_next;
|
|
}
|
|
|
|
// At this point, PrevAO points to the AO before the new one. Insert it
|
|
// there.
|
|
ASSERT(PrevAO != NULL);
|
|
ASSERT(PrevAO->ao_next == CurrentAO);
|
|
|
|
NewAO->ao_next = CurrentAO;
|
|
PrevAO->ao_next = NewAO;
|
|
if (NewAO->ao_prot == IP_PROTOCOL_UDP)
|
|
UStats.us_numaddrs++;
|
|
}
|
|
|
|
//* RemoveAddrObj - Remove an address object from the table.
|
|
//
|
|
// Called when we need to remove an address object from the table. We hash on
|
|
// the addr and port, then walk the table looking for the object. We assume
|
|
// that the table lock is held.
|
|
//
|
|
// The AddrObj may have already been removed from the table if it was
|
|
// invalidated for some reason, so we need to check for the case of not
|
|
// finding it.
|
|
//
|
|
void // Returns: Nothing.
|
|
RemoveAddrObj(
|
|
AddrObj *RemovedAO) // AddrObj to delete.
|
|
{
|
|
AddrObj *PrevAO; // Pointer to previous address object in hash chain.
|
|
AddrObj *CurrentAO; // Pointer to current AO in table.
|
|
|
|
CHECK_STRUCT(RemovedAO, ao);
|
|
|
|
PrevAO = CONTAINING_RECORD(&AddrObjTable[AO_HASH(RemovedAO->ao_addr,
|
|
RemovedAO->ao_port)],
|
|
AddrObj, ao_next);
|
|
CurrentAO = PrevAO->ao_next;
|
|
|
|
// Walk the table, looking for a match.
|
|
while (CurrentAO != NULL) {
|
|
CHECK_STRUCT(CurrentAO, ao);
|
|
|
|
if (CurrentAO == RemovedAO) {
|
|
PrevAO->ao_next = CurrentAO->ao_next;
|
|
if (CurrentAO->ao_prot == IP_PROTOCOL_UDP) {
|
|
UStats.us_numaddrs--;
|
|
}
|
|
if (CurrentAO == LastAO) {
|
|
LastAO = NULL;
|
|
}
|
|
return;
|
|
} else {
|
|
PrevAO = CurrentAO;
|
|
CurrentAO = CurrentAO->ao_next;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we get here, we didn't find him. This is OK, but we should say
|
|
// something about it.
|
|
//
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_RARE,
|
|
"RemoveAddrObj: Object not found.\n"));
|
|
}
|
|
|
|
//* FindAnyAddrObj - Find an AO with matching port on any local address.
|
|
//
|
|
// Called for wildcard address opens. We go through the entire addrobj table,
|
|
// and see if anyone has the specified port. We assume that the lock is
|
|
// already held on the table.
|
|
//
|
|
AddrObj * // Returns: Pointer to AO found, or NULL is noone has it.
|
|
FindAnyAddrObj(
|
|
ushort Port, // Port to be looked for.
|
|
uchar Protocol) // Protocol on which to look.
|
|
{
|
|
uint i; // Index variable.
|
|
AddrObj *CurrentAO; // Current AddrObj being examined.
|
|
|
|
for (i = 0; i < AddrObjTableSize; i++) {
|
|
CurrentAO = AddrObjTable[i];
|
|
while (CurrentAO != NULL) {
|
|
CHECK_STRUCT(CurrentAO, ao);
|
|
|
|
if (CurrentAO->ao_port == Port && CurrentAO->ao_prot == Protocol)
|
|
return CurrentAO;
|
|
else
|
|
CurrentAO = CurrentAO->ao_next;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//* RebuildAddrObjBitmap - reconstruct the address-object bitmap from scratch.
|
|
//
|
|
// Called when we need to reconcile the contents of our lookaside bitmap
|
|
// with the actual contents of the address-object table. We clear the bitmap,
|
|
// then scan the address-object table and mark each entry's bit as 'in-use'.
|
|
// Assumes the caller holds the AddrObjTableLock.
|
|
//
|
|
// Input: nothing.
|
|
//
|
|
// Return: nothing.
|
|
//
|
|
void
|
|
RebuildAddrObjBitmap(void)
|
|
{
|
|
uint i;
|
|
AddrObj* CurrentAO;
|
|
|
|
RtlClearAllBits(&PortBitmapTcp);
|
|
RtlClearAllBits(&PortBitmapUdp);
|
|
|
|
for (i = 0; i < AddrObjTableSize; i++) {
|
|
CurrentAO = AddrObjTable[i];
|
|
while (CurrentAO != NULL) {
|
|
CHECK_STRUCT(CurrentAO, ao);
|
|
|
|
if (CurrentAO->ao_prot == IP_PROTOCOL_TCP) {
|
|
RtlSetBit(&PortBitmapTcp, net_short(CurrentAO->ao_port));
|
|
} else if (CurrentAO->ao_prot == IP_PROTOCOL_UDP) {
|
|
RtlSetBit(&PortBitmapUdp, net_short(CurrentAO->ao_port));
|
|
}
|
|
CurrentAO = CurrentAO->ao_next;
|
|
}
|
|
}
|
|
}
|
|
|
|
//* GetAddress - Get an IP address and port from a TDI address structure.
|
|
//
|
|
// Called when we need to get our addressing information from a TDI
|
|
// address structure. We go through the structure, and return what we
|
|
// find.
|
|
//
|
|
uchar // Return: TRUE if we find an address, FALSE if we don't.
|
|
GetAddress(
|
|
TRANSPORT_ADDRESS UNALIGNED *AddrList, // Pointer to structure to search.
|
|
IPv6Addr *Addr, // Where to return IP address.
|
|
ulong *ScopeId, // Where to return address scope.
|
|
ushort *Port) // Where to return Port.
|
|
{
|
|
int i; // Index variable.
|
|
TA_ADDRESS *CurrentAddr; // Address we're examining and may use.
|
|
|
|
// First, verify that someplace in Address is an address we can use.
|
|
CurrentAddr = (TA_ADDRESS *) AddrList->Address;
|
|
|
|
for (i = 0; i < AddrList->TAAddressCount; i++) {
|
|
if (CurrentAddr->AddressType == TDI_ADDRESS_TYPE_IP6) {
|
|
if (CurrentAddr->AddressLength >= TDI_ADDRESS_LENGTH_IP6) {
|
|
//
|
|
// It's an IPv6 address. Pull out the values.
|
|
//
|
|
TDI_ADDRESS_IP6 UNALIGNED *ValidAddr =
|
|
(TDI_ADDRESS_IP6 UNALIGNED *)CurrentAddr->Address;
|
|
|
|
*Port = ValidAddr->sin6_port;
|
|
RtlCopyMemory(Addr, ValidAddr->sin6_addr, sizeof(IPv6Addr));
|
|
*ScopeId = ValidAddr->sin6_scope_id;
|
|
return TRUE;
|
|
} else
|
|
return FALSE; // Wrong length for address.
|
|
} else {
|
|
//
|
|
// Wrong address type. Skip over it to next one in list.
|
|
//
|
|
CurrentAddr = (TA_ADDRESS *)(CurrentAddr->Address +
|
|
CurrentAddr->AddressLength);
|
|
}
|
|
}
|
|
|
|
return FALSE; // Didn't find a match.
|
|
}
|
|
|
|
//* InvalidateAddrs - Invalidate all AOs for a specific address.
|
|
//
|
|
// Called when we need to invalidate all AOs for a specific address. Walk
|
|
// down the table with the lock held, and take the lock on each AddrObj.
|
|
// If the address matches, mark it as invalid, pull off all requests,
|
|
// and continue. At the end we'll complete all requests with an error.
|
|
//
|
|
void // Returns: Nothing.
|
|
InvalidateAddrs(
|
|
IPv6Addr *Addr, // Addr to be invalidated.
|
|
uint ScopeId) // Scope id for Addr.
|
|
{
|
|
Queue SendQ, RcvQ;
|
|
AORequest *ReqList;
|
|
KIRQL Irql0, Irql1; // One per lock nesting level.
|
|
uint i;
|
|
AddrObj *AO;
|
|
DGSendReq *SendReq;
|
|
DGRcvReq *RcvReq;
|
|
AOMCastAddr *MA;
|
|
|
|
INITQ(&SendQ);
|
|
INITQ(&RcvQ);
|
|
ReqList = NULL;
|
|
|
|
KeAcquireSpinLock(&AddrObjTableLock, &Irql0);
|
|
for (i = 0; i < AddrObjTableSize; i++) {
|
|
// Walk down each hash bucket, looking for a match.
|
|
AO = AddrObjTable[i];
|
|
while (AO != NULL) {
|
|
CHECK_STRUCT(AO, ao);
|
|
|
|
KeAcquireSpinLock(&AO->ao_lock, &Irql1);
|
|
if (IP6_ADDR_EQUAL(&AO->ao_addr, Addr) &&
|
|
(AO->ao_scope_id == ScopeId) && AO_VALID(AO)) {
|
|
// This one matches. Mark as invalid, then pull his requests.
|
|
SET_AO_INVALID(AO);
|
|
|
|
// If he has a request on him, pull him off.
|
|
if (AO->ao_request != NULL) {
|
|
AORequest *Temp;
|
|
|
|
Temp = CONTAINING_RECORD(&AO->ao_request, AORequest,
|
|
aor_next);
|
|
do {
|
|
Temp = Temp->aor_next;
|
|
} while (Temp->aor_next != NULL);
|
|
|
|
Temp->aor_next = ReqList;
|
|
ReqList = AO->ao_request;
|
|
AO->ao_request = NULL;
|
|
}
|
|
|
|
// Go down his send list, pulling things off the send q and
|
|
// putting them on our local queue.
|
|
while (!EMPTYQ(&AO->ao_sendq)) {
|
|
DEQUEUE(&AO->ao_sendq, SendReq, DGSendReq, dsr_q);
|
|
CHECK_STRUCT(SendReq, dsr);
|
|
ENQUEUE(&SendQ, &SendReq->dsr_q);
|
|
}
|
|
|
|
// Do the same for the receive queue.
|
|
while (!EMPTYQ(&AO->ao_rcvq)) {
|
|
DEQUEUE(&AO->ao_rcvq, RcvReq, DGRcvReq, drr_q);
|
|
CHECK_STRUCT(RcvReq, drr);
|
|
ENQUEUE(&RcvQ, &RcvReq->drr_q);
|
|
}
|
|
|
|
// Free any multicast addresses he may have. IP will have
|
|
// deleted them at that level before we get here, so all we
|
|
// need to do if free the memory.
|
|
MA = AO->ao_mcastlist;
|
|
while (MA != NULL) {
|
|
AOMCastAddr *Temp;
|
|
|
|
Temp = MA;
|
|
MA = MA->ama_next;
|
|
ExFreePool(Temp);
|
|
}
|
|
AO->ao_mcastlist = NULL;
|
|
|
|
}
|
|
KeReleaseSpinLock(&AO->ao_lock, Irql1);
|
|
AO = AO->ao_next; // Go to the next one.
|
|
}
|
|
}
|
|
KeReleaseSpinLock(&AddrObjTableLock, Irql0);
|
|
|
|
// OK, now walk what we've collected, complete it, and free it.
|
|
while (ReqList != NULL) {
|
|
AORequest *Req;
|
|
|
|
Req = ReqList;
|
|
ReqList = Req->aor_next;
|
|
(*Req->aor_rtn)(Req->aor_context, (uint) TDI_ADDR_INVALID, 0);
|
|
FreeAORequest(Req);
|
|
}
|
|
|
|
// Walk down the rcv. q, completing and freeing requests.
|
|
while (!EMPTYQ(&RcvQ)) {
|
|
|
|
DEQUEUE(&RcvQ, RcvReq, DGRcvReq, drr_q);
|
|
CHECK_STRUCT(RcvReq, drr);
|
|
|
|
(*RcvReq->drr_rtn)(RcvReq->drr_context, (uint) TDI_ADDR_INVALID, 0);
|
|
|
|
FreeDGRcvReq(RcvReq);
|
|
|
|
}
|
|
|
|
// Now do the same for sends.
|
|
while (!EMPTYQ(&SendQ)) {
|
|
|
|
DEQUEUE(&SendQ, SendReq, DGSendReq, dsr_q);
|
|
CHECK_STRUCT(SendReq, dsr);
|
|
|
|
(*SendReq->dsr_rtn)(SendReq->dsr_context, (uint) TDI_ADDR_INVALID, 0);
|
|
|
|
KeAcquireSpinLock(&DGSendReqLock, &Irql0);
|
|
FreeDGSendReq(SendReq);
|
|
KeReleaseSpinLock(&DGSendReqLock, Irql0);
|
|
}
|
|
}
|
|
|
|
//* RequestWorker - Handle a deferred request.
|
|
//
|
|
// This is the work item callback routine, called by a system worker
|
|
// thread when the work item queued by DelayDerefAO is handled.
|
|
// We just call ProcessAORequest on the AO.
|
|
//
|
|
void // Returns: Nothing.
|
|
RequestWorker(
|
|
void *Context) // Pointer to AddrObj.
|
|
{
|
|
AddrObj *AO = (AddrObj *)Context;
|
|
|
|
CHECK_STRUCT(AO, ao);
|
|
ASSERT(AO_BUSY(AO));
|
|
|
|
ProcessAORequests(AO);
|
|
}
|
|
|
|
//* GetAddrOptions - Get the address options.
|
|
//
|
|
// Called when we're opening an address. We take in a pointer, and walk
|
|
// down it looking for address options we know about.
|
|
//
|
|
void // Returns: Nothing.
|
|
GetAddrOptions(
|
|
void *Ptr, // Pointer to search.
|
|
uchar *Reuse, // Pointer to reuse flag.
|
|
uchar *DHCPAddr, // Pointer to DHCP flag.
|
|
uchar *RawSock) // Pointer to raw socket flag.
|
|
{
|
|
uchar *OptPtr;
|
|
|
|
*Reuse = 0;
|
|
*DHCPAddr = 0;
|
|
*RawSock = 0;
|
|
|
|
if (Ptr == NULL)
|
|
return;
|
|
|
|
OptPtr = (uchar *)Ptr;
|
|
|
|
while (*OptPtr != TDI_OPTION_EOL) {
|
|
if (*OptPtr == TDI_ADDRESS_OPTION_REUSE)
|
|
*Reuse = 1;
|
|
else if (*OptPtr == TDI_ADDRESS_OPTION_DHCP)
|
|
*DHCPAddr = 1;
|
|
else if (*OptPtr == TDI_ADDRESS_OPTION_RAW)
|
|
*RawSock = 1;
|
|
|
|
OptPtr++;
|
|
}
|
|
}
|
|
|
|
|
|
//* CheckAddrReuse - enforce port-sharing rules for a new TDI address object.
|
|
//
|
|
// Called when opening an address, to determine whether the open should
|
|
// succeed in the presence of previous binds to the same port.
|
|
//
|
|
// N.B. Assumes the caller holds both AddrSDMutex and AddrObjTableLock.
|
|
// The latter is freed and reacquired in this routine.
|
|
//
|
|
TDI_STATUS // Returns: TDI_STATUS code of attempt.
|
|
CheckAddrReuse(
|
|
PTDI_REQUEST Request, // Pointer to a TDI request for this open.
|
|
uint Protocol, // Protocol on which to open the address.
|
|
IPv6Addr* Addr, // Local IP address to open.
|
|
ulong ScopeId, // Scope identifier for local IP address.
|
|
ushort Port, // Local port number to open.
|
|
BOOLEAN NewReuse, // Indicates if reuse requested for the open.
|
|
PSECURITY_DESCRIPTOR NewSD, // Captured security-descriptor for the open.
|
|
KIRQL* TableIrql) // IRQL at which AddrObjTableLock was taken.
|
|
{
|
|
PACCESS_STATE AccessState;
|
|
BOOLEAN AllowReuse;
|
|
AddrObj* ExistingAO;
|
|
BOOLEAN ExistingReuse;
|
|
PSECURITY_DESCRIPTOR ExistingSD;
|
|
PIRP Irp;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
ACCESS_MASK GrantedAccess;
|
|
NTSTATUS status;
|
|
|
|
// Look for an existing valid AO and succeed if none.
|
|
// Otherwise, capture its reuse flag and security-descriptor.
|
|
|
|
ExistingAO = GetBestAddrObj(Addr, NULL, ScopeId, Port, (uchar)Protocol,
|
|
NULL);
|
|
if (ExistingAO == NULL && IP6_ADDR_EQUAL(Addr, &UnspecifiedAddr) &&
|
|
NewSD == NULL) {
|
|
ExistingAO = FindAnyAddrObj(Port, (uchar)Protocol);
|
|
}
|
|
|
|
if (ExistingAO == NULL) {
|
|
return TDI_SUCCESS;
|
|
}
|
|
|
|
do {
|
|
// We've got at least one AO, so see if it allows reuse.
|
|
// Note that we may need to repeat this for every AO on this port
|
|
// in the case where we have multiple AOs sharing the port already,
|
|
// since each AO has its own security descriptor. In that event,
|
|
// we look at the AOs until one denies access or we've seen them all.
|
|
|
|
ExistingReuse = !!AO_SHARE(ExistingAO);
|
|
ExistingSD = ExistingAO->ao_sd;
|
|
|
|
// Succeed immediately if reuse enabled on both instances.
|
|
// Otherwise, fail if the two instances have the exact same address
|
|
// (whether it's unspecified or specific).
|
|
|
|
if (ExistingReuse && NewReuse) {
|
|
return TDI_SUCCESS;
|
|
}
|
|
|
|
if (IP6_ADDR_EQUAL(&ExistingAO->ao_addr, Addr)) {
|
|
return TDI_ADDR_IN_USE;
|
|
}
|
|
|
|
// The two instances have different addresses, and at least one of them
|
|
// doesn't have reuse enabled. If the new instance is on the unspecified
|
|
// address, the old instance must be on a specific address.
|
|
// Allow the bind unless the new instance wants exclusive access
|
|
// (i.e. NewSD == NULL).
|
|
|
|
if (IP6_ADDR_EQUAL(Addr, &UnspecifiedAddr)) {
|
|
if (NewSD == NULL) {
|
|
return TDI_ADDR_IN_USE;
|
|
}
|
|
return TDI_SUCCESS;
|
|
}
|
|
|
|
// The two instances have different addresses, and the new instance is
|
|
// on a specific address. If the old instance is on a specific address
|
|
// too, the two are disjoint and can peacefully coexist.
|
|
|
|
if (!IP6_ADDR_EQUAL(&ExistingAO->ao_addr, &UnspecifiedAddr)) {
|
|
return TDI_SUCCESS;
|
|
}
|
|
|
|
// The new instance is on a specific address and the old instance is on
|
|
// the wildcard address. If the old instance wanted exclusive access
|
|
// (i.e. ExistingSD == NULL) fail the new instance right away.
|
|
// Otherwise, drop the AO table lock and perform an access check
|
|
// to see if it's OK for the new instance to steal some traffic from
|
|
// the old instance.
|
|
//
|
|
// N.B. Even though we've dropped the AO table lock, ExistingSD is safe
|
|
// since we still have the AO SD mutex.
|
|
|
|
if (ExistingSD == NULL) {
|
|
return STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
ASSERT(*TableIrql <= PASSIVE_LEVEL);
|
|
KeReleaseSpinLock(&AddrObjTableLock, *TableIrql);
|
|
|
|
Irp = (PIRP)Request->RequestContext;
|
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState;
|
|
|
|
SeLockSubjectContext(&AccessState->SubjectSecurityContext);
|
|
AllowReuse = SeAccessCheck(ExistingSD, &AccessState->SubjectSecurityContext,
|
|
TRUE, FILE_READ_DATA|FILE_WRITE_DATA, 0,
|
|
NULL, IoGetFileObjectGenericMapping(),
|
|
(IrpSp->Flags & SL_FORCE_ACCESS_CHECK)
|
|
? UserMode : Irp->RequestorMode,
|
|
&GrantedAccess, &status);
|
|
SeUnlockSubjectContext(&AccessState->SubjectSecurityContext);
|
|
|
|
KeAcquireSpinLock(&AddrObjTableLock, TableIrql);
|
|
if (!AllowReuse) {
|
|
return status;
|
|
}
|
|
|
|
// The existing wildcard AO doesn't mind if the new instance takes
|
|
// some of its traffic. If the existing AO has reuse enabled, there
|
|
// might be others too on the port, so we'll look for them and do an
|
|
// access check against their security-descriptors too.
|
|
|
|
} while(ExistingReuse &&
|
|
(ExistingAO = GetAddrObj(&UnspecifiedAddr, NULL, ScopeId, Port,
|
|
(uchar)Protocol, ExistingAO, NULL))
|
|
!= NULL);
|
|
|
|
return TDI_SUCCESS;
|
|
}
|
|
|
|
//* TdiOpenAddress - Open a TDI address object.
|
|
//
|
|
// This is the external interface to open an address. The caller provides a
|
|
// TDI_REQUEST structure and a TRANSPORT_ADDRESS structure, as well a pointer
|
|
// to a variable identifying whether or not we are to allow reuse of an
|
|
// address while it's still open.
|
|
//
|
|
TDI_STATUS // Returns: TDI_STATUS code of attempt.
|
|
TdiOpenAddress(
|
|
PTDI_REQUEST Request, // TDI request structure for this request.
|
|
TRANSPORT_ADDRESS UNALIGNED *AddrList, // Address to be opened.
|
|
uint Protocol, // Protocol on which to open the address (LSB only).
|
|
void *Ptr, // Pointer to option buffer.
|
|
PSECURITY_DESCRIPTOR AddrSD) // SD for port-reuse access checks.
|
|
{
|
|
uint i; // Index variable.
|
|
ushort Port; // Local Port we'll use.
|
|
IPv6Addr LocalAddr; // Actual address we'll use.
|
|
ulong ScopeId; // Address scope.
|
|
AddrObj *NewAO; // New AO we'll use.
|
|
AddrObj *ExistingAO; // Pointer to existing AO, if any.
|
|
KIRQL OldIrql;
|
|
uchar Reuse, DHCPAddr, RawSock;
|
|
PRTL_BITMAP PortBitmap;
|
|
|
|
if (!GetAddress(AddrList, &LocalAddr, &ScopeId, &Port))
|
|
return TDI_BAD_ADDR;
|
|
|
|
// Find the address options we might need.
|
|
GetAddrOptions(Ptr, &Reuse, &DHCPAddr, &RawSock);
|
|
|
|
//
|
|
// Allocate the new addr obj now, assuming that we need it,
|
|
// so we don't have to do it with locks held later.
|
|
//
|
|
NewAO = ExAllocatePool(NonPagedPool, sizeof(AddrObj));
|
|
if (NewAO == NULL) {
|
|
// Couldn't allocate an address object.
|
|
return TDI_NO_RESOURCES;
|
|
}
|
|
RtlZeroMemory(NewAO, sizeof(AddrObj));
|
|
|
|
//
|
|
// Check to make sure IP address is one of our local addresses. This
|
|
// is protected with the address table lock, so we can interlock an IP
|
|
// address going away through DHCP.
|
|
//
|
|
KeWaitForSingleObject(&AddrSDMutex, Executive, KernelMode, FALSE, NULL);
|
|
KeAcquireSpinLock(&AddrObjTableLock, &OldIrql);
|
|
|
|
if (!IP6_ADDR_EQUAL(&LocalAddr, &UnspecifiedAddr)) {
|
|
NetTableEntry *NTE;
|
|
|
|
//
|
|
// The user specified a local address (i.e. not wildcarded).
|
|
// Call IP to check that this is a valid local address.
|
|
// We do this by looking up an NTE for the address; note
|
|
// that this will fail if the scope id is specified
|
|
// improperly or doesn't match exactly.
|
|
//
|
|
NTE = FindNetworkWithAddress(&LocalAddr, ScopeId);
|
|
if (NTE == NULL) {
|
|
// Not a local address. Fail the request.
|
|
BadAddr:
|
|
KeReleaseSpinLock(&AddrObjTableLock, OldIrql);
|
|
KeReleaseMutex(&AddrSDMutex, FALSE);
|
|
ExFreePool(NewAO);
|
|
return TDI_BAD_ADDR;
|
|
}
|
|
|
|
//
|
|
// We don't actually want the NTE, we were just checking that
|
|
// it exists. So release our reference.
|
|
//
|
|
ReleaseNTE(NTE);
|
|
|
|
} else {
|
|
//
|
|
// The user specified the wildcard address.
|
|
// Insist that the scope id is zero.
|
|
//
|
|
if (ScopeId != 0)
|
|
goto BadAddr;
|
|
}
|
|
|
|
//
|
|
// The specified IP address is a valid local address. Now we do
|
|
// protocol-specific processing. An exception is raw sockets: we
|
|
// don't allocate port space for them, regardless of their protocol.
|
|
//
|
|
|
|
if (Protocol == IP_PROTOCOL_TCP) {
|
|
PortBitmap = &PortBitmapTcp;
|
|
} else if (Protocol == IP_PROTOCOL_UDP) {
|
|
PortBitmap = &PortBitmapUdp;
|
|
} else {
|
|
PortBitmap = NULL;
|
|
}
|
|
|
|
if (!RawSock && PortBitmap) {
|
|
//
|
|
// If no port is specified we have to assign one. If there is a
|
|
// port specified, we need to make sure that the IPAddress/Port
|
|
// combo isn't already open (unless Reuse is specified). If the
|
|
// input address is a wildcard, we need to make sure the address
|
|
// isn't open on any local ip address.
|
|
//
|
|
if (Port == WILDCARD_PORT) {
|
|
// Have a wildcard port, need to assign an address.
|
|
Port = NextUserPort;
|
|
|
|
for (i = 0; i < NUM_USER_PORTS; i++, Port++) {
|
|
ushort NetPort; // Port in net byte order.
|
|
|
|
if (Port > MaxUserPort) {
|
|
Port = MIN_USER_PORT;
|
|
RebuildAddrObjBitmap();
|
|
}
|
|
|
|
NetPort = net_short(Port);
|
|
|
|
if (IP6_ADDR_EQUAL(&LocalAddr, &UnspecifiedAddr)) {
|
|
// Wildcard IP address.
|
|
if (PortBitmap) {
|
|
if (!RtlCheckBit(PortBitmap, Port))
|
|
break;
|
|
else
|
|
continue;
|
|
} else {
|
|
ExistingAO = FindAnyAddrObj(NetPort, (uchar)Protocol);
|
|
}
|
|
} else {
|
|
ExistingAO = GetBestAddrObj(&LocalAddr, NULL, ScopeId,
|
|
NetPort, (uchar)Protocol, NULL);
|
|
}
|
|
|
|
if (ExistingAO == NULL)
|
|
break; // Found an unused port.
|
|
}
|
|
|
|
if (i == NUM_USER_PORTS) {
|
|
// Couldn't find a free port.
|
|
KeReleaseSpinLock(&AddrObjTableLock, OldIrql);
|
|
KeReleaseMutex(&AddrSDMutex, FALSE);
|
|
ExFreePool(NewAO);
|
|
return TDI_NO_FREE_ADDR;
|
|
}
|
|
NextUserPort = Port + 1;
|
|
Port = net_short(Port);
|
|
|
|
} else {
|
|
//
|
|
// A particular port was specified.
|
|
//
|
|
|
|
// Don't check if a DHCP address is specified.
|
|
if (!DHCPAddr) {
|
|
TDI_STATUS Status;
|
|
|
|
//
|
|
// See if we already have this address open and, if so,
|
|
// decide whether this request should succeed.
|
|
//
|
|
Status = CheckAddrReuse(Request, Protocol, &LocalAddr, ScopeId,
|
|
Port, Reuse, AddrSD, &OldIrql);
|
|
if (Status != TDI_SUCCESS) {
|
|
KeReleaseSpinLock(&AddrObjTableLock, OldIrql);
|
|
KeReleaseMutex(&AddrSDMutex, FALSE);
|
|
ExFreePool(NewAO);
|
|
return Status;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// We have a new AO. Set up the protocol specific portions.
|
|
//
|
|
if (Protocol == IP_PROTOCOL_UDP) {
|
|
NewAO->ao_dgsend = UDPSend;
|
|
NewAO->ao_maxdgsize = 0xFFFF - sizeof(UDPHeader);
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Either we have a raw socket or this is a protocol for which
|
|
// we don't allocate a port. Open over Raw IP.
|
|
//
|
|
|
|
ASSERT(!DHCPAddr);
|
|
|
|
//
|
|
// We must set the port to zero. This puts all the raw sockets
|
|
// in one hash bucket, which is necessary for GetAddrObj to
|
|
// work correctly. It wouldn't be a bad idea to come up with
|
|
// a better scheme...
|
|
//
|
|
Port = 0;
|
|
NewAO->ao_dgsend = RawSend;
|
|
NewAO->ao_maxdgsize = 0xFFFF;
|
|
NewAO->ao_flags |= AO_RAW_FLAG;
|
|
|
|
IF_TCPDBG(TCP_DEBUG_RAW) {
|
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_TCPDBG,
|
|
"raw open protocol %u AO %lx\n", Protocol, NewAO));
|
|
}
|
|
}
|
|
|
|
// When we get here, we know we're creating a brand new address object.
|
|
// Port contains the port in question, and NewAO points to the newly
|
|
// created AO.
|
|
|
|
KeInitializeSpinLock(&NewAO->ao_lock);
|
|
ExInitializeWorkItem(&NewAO->ao_workitem, RequestWorker, NewAO);
|
|
INITQ(&NewAO->ao_sendq);
|
|
INITQ(&NewAO->ao_rcvq);
|
|
INITQ(&NewAO->ao_activeq);
|
|
INITQ(&NewAO->ao_idleq);
|
|
INITQ(&NewAO->ao_listenq);
|
|
NewAO->ao_port = Port;
|
|
NewAO->ao_addr = LocalAddr;
|
|
NewAO->ao_scope_id = ScopeId;
|
|
NewAO->ao_prot = (uchar)Protocol;
|
|
NewAO->ao_ucast_hops = -1; // This causes system default to be used.
|
|
NewAO->ao_mcast_hops = -1; // This causes system default to be used.
|
|
NewAO->ao_mcast_loop = TRUE;// Multicast loopback is on by default.
|
|
#if DBG
|
|
NewAO->ao_sig = ao_signature;
|
|
#endif
|
|
NewAO->ao_sd = AddrSD;
|
|
NewAO->ao_flags |= AO_VALID_FLAG; // AO is valid.
|
|
NewAO->ao_protect = PROTECTION_LEVEL_DEFAULT;
|
|
|
|
if (DHCPAddr)
|
|
NewAO->ao_flags |= AO_DHCP_FLAG;
|
|
|
|
if (Reuse) {
|
|
SET_AO_SHARE(NewAO);
|
|
}
|
|
|
|
NewAO->ao_owningpid = HandleToUlong(PsGetCurrentProcessId());
|
|
|
|
//
|
|
// Note the following fields are left zero - which has this effect:
|
|
//
|
|
// ao_mcast_if - use the routing table by default.
|
|
// ao_udp_cksum_cover - checksum everything.
|
|
//
|
|
|
|
InsertAddrObj(NewAO);
|
|
if (PortBitmap) {
|
|
RtlSetBit(PortBitmap, net_short(Port));
|
|
}
|
|
KeReleaseSpinLock(&AddrObjTableLock, OldIrql);
|
|
KeReleaseMutex(&AddrSDMutex, FALSE);
|
|
Request->Handle.AddressHandle = NewAO;
|
|
return TDI_SUCCESS;
|
|
}
|
|
|
|
|
|
//* DeleteAO - Delete an address object.
|
|
//
|
|
// The internal routine to delete an address object. We complete any pending
|
|
// requests with errors, and remove and free the address object.
|
|
//
|
|
void // Returns: Nothing.
|
|
DeleteAO(
|
|
AddrObj *DeletedAO) // AddrObj to be deleted.
|
|
{
|
|
KIRQL Irql0; // One per lock nesting level.
|
|
#ifndef UDP_ONLY
|
|
TCB *TCBHead = NULL, *CurrentTCB;
|
|
TCPConn *Conn;
|
|
Queue *Temp;
|
|
Queue *CurrentQ;
|
|
RequestCompleteRoutine Rtn; // Completion routine.
|
|
PVOID Context; // User context for completion routine.
|
|
#endif
|
|
AOMCastAddr *AMA;
|
|
PSECURITY_DESCRIPTOR AddrSD;
|
|
|
|
CHECK_STRUCT(DeletedAO, ao);
|
|
ASSERT(!AO_VALID(DeletedAO));
|
|
ASSERT(DeletedAO->ao_usecnt == 0);
|
|
|
|
KeAcquireSpinLock(&AddrObjTableLock, &Irql0);
|
|
KeAcquireSpinLockAtDpcLevel(&DGSendReqLock);
|
|
KeAcquireSpinLockAtDpcLevel(&DeletedAO->ao_lock);
|
|
|
|
// If he's on an oor queue, remove him.
|
|
if (AO_OOR(DeletedAO))
|
|
REMOVEQ(&DeletedAO->ao_pendq);
|
|
KeReleaseSpinLockFromDpcLevel(&DGSendReqLock);
|
|
|
|
RemoveAddrObj(DeletedAO);
|
|
|
|
#ifndef UDP_ONLY
|
|
// Walk down the list of associated connections and zap their AO pointers.
|
|
// For each connection, we need to shut down the connection if it's active.
|
|
// If the connection isn't already closing, we'll put a reference on it
|
|
// so that it can't go away while we're dealing with the AO, and put it
|
|
// on a list. On our way out we'll walk down that list and zap each
|
|
// connection.
|
|
CurrentQ = &DeletedAO->ao_activeq;
|
|
|
|
for (;;) {
|
|
Temp = QHEAD(CurrentQ);
|
|
while (Temp != QEND(CurrentQ)) {
|
|
Conn = QSTRUCT(TCPConn, Temp, tc_q);
|
|
|
|
//
|
|
// Move our temp pointer to the next connection now,
|
|
// since we may free this connection below.
|
|
//
|
|
Temp = QNEXT(Temp);
|
|
|
|
//
|
|
// We'll need to unlock the AO in order to look at the Conn.
|
|
// While the AO is unlocked, we need to worry about the following
|
|
// requests being issued on the Conn:
|
|
//
|
|
// TdiDisAssociateAddress, TdiConnect: we hold AddrObjTableLock
|
|
// so the requests are blocked.
|
|
// TdiListen, FindListenConn: the AO is marked invalid already,
|
|
// so the requests will fail.
|
|
// TdiAccept: this request doesn't update the AO<->Conn link
|
|
// (just Conn<->TCB) so we don't care about it.
|
|
//
|
|
KeReleaseSpinLockFromDpcLevel(&DeletedAO->ao_lock);
|
|
KeAcquireSpinLockAtDpcLevel(&Conn->tc_ConnBlock->cb_lock);
|
|
|
|
CHECK_STRUCT(Conn, tc);
|
|
CurrentTCB = Conn->tc_tcb;
|
|
if (CurrentTCB != NULL) {
|
|
|
|
// We have a TCB.
|
|
CHECK_STRUCT(CurrentTCB, tcb);
|
|
KeAcquireSpinLockAtDpcLevel(&CurrentTCB->tcb_lock);
|
|
if (CurrentTCB->tcb_state != TCB_CLOSED &&
|
|
!CLOSING(CurrentTCB)) {
|
|
// It's not closing. Put a reference on it and save it
|
|
// on the list.
|
|
CurrentTCB->tcb_refcnt++;
|
|
CurrentTCB->tcb_aonext = TCBHead;
|
|
TCBHead = CurrentTCB;
|
|
}
|
|
CurrentTCB->tcb_conn = NULL;
|
|
CurrentTCB->tcb_rcvind = NULL;
|
|
if (CurrentTCB->tcb_rcvhndlr == IndicateData &&
|
|
CurrentTCB->tcb_indicated == 0) {
|
|
if (CurrentTCB->tcb_currcv != NULL) {
|
|
CurrentTCB->tcb_rcvhndlr = BufferData;
|
|
} else {
|
|
CurrentTCB->tcb_rcvhndlr = PendData;
|
|
}
|
|
}
|
|
KeReleaseSpinLockFromDpcLevel(&CurrentTCB->tcb_lock);
|
|
|
|
//
|
|
// Subtract one from the connection's ref count, since we
|
|
// are about to remove this TCB from the connection.
|
|
//
|
|
if (--(Conn->tc_refcnt) == 0) {
|
|
|
|
//
|
|
// We need to execute the code for the done
|
|
// routine. There are only three done routines that can
|
|
// be called. CloseDone(), DisassocDone(), and DummyDone().
|
|
// We execute the respective code here to avoid freeing locks.
|
|
// Note: DummyDone() does nothing.
|
|
//
|
|
|
|
if (Conn->tc_flags & CONN_CLOSING) {
|
|
|
|
//
|
|
// This is the relevant CloseDone() code.
|
|
//
|
|
|
|
Rtn = Conn->tc_rtn;
|
|
Context = Conn->tc_rtncontext;
|
|
KeReleaseSpinLockFromDpcLevel(
|
|
&Conn->tc_ConnBlock->cb_lock);
|
|
|
|
ExFreePool(Conn);
|
|
(*Rtn) (Context, TDI_SUCCESS, 0);
|
|
|
|
} else if (Conn->tc_flags & CONN_DISACC) {
|
|
|
|
//
|
|
// This is the relevant DisassocDone() code.
|
|
//
|
|
|
|
Rtn = Conn->tc_rtn;
|
|
Context = Conn->tc_rtncontext;
|
|
Conn->tc_flags &= ~CONN_DISACC;
|
|
Conn->tc_ao = NULL;
|
|
Conn->tc_tcb = NULL;
|
|
KeReleaseSpinLockFromDpcLevel(
|
|
&Conn->tc_ConnBlock->cb_lock);
|
|
|
|
(*Rtn) (Context, TDI_SUCCESS, 0);
|
|
} else {
|
|
Conn->tc_ao = NULL;
|
|
Conn->tc_tcb = NULL;
|
|
KeReleaseSpinLockFromDpcLevel(
|
|
&Conn->tc_ConnBlock->cb_lock);
|
|
|
|
}
|
|
} else {
|
|
Conn->tc_ao = NULL;
|
|
Conn->tc_tcb = NULL;
|
|
KeReleaseSpinLockFromDpcLevel(&Conn->tc_ConnBlock->cb_lock);
|
|
}
|
|
} else {
|
|
Conn->tc_ao = NULL;
|
|
KeReleaseSpinLockFromDpcLevel(&Conn->tc_ConnBlock->cb_lock);
|
|
}
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&DeletedAO->ao_lock);
|
|
}
|
|
|
|
if (CurrentQ == &DeletedAO->ao_activeq) {
|
|
CurrentQ = &DeletedAO->ao_idleq;
|
|
} else if (CurrentQ == &DeletedAO->ao_idleq) {
|
|
CurrentQ = &DeletedAO->ao_listenq;
|
|
} else {
|
|
ASSERT(CurrentQ == &DeletedAO->ao_listenq);
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Release excess locks. Note we release the locks in a different order
|
|
// from which we acquired them, but must return to IRQ levels in the order
|
|
// we left them.
|
|
//
|
|
KeReleaseSpinLockFromDpcLevel(&AddrObjTableLock);
|
|
|
|
// We've removed him from the queues, and he's marked as invalid. Return
|
|
// pending requests with errors.
|
|
|
|
// We still hold the lock on the AddrObj, although this may not be
|
|
// neccessary.
|
|
|
|
while (!EMPTYQ(&DeletedAO->ao_rcvq)) {
|
|
DGRcvReq *Rcv;
|
|
|
|
DEQUEUE(&DeletedAO->ao_rcvq, Rcv, DGRcvReq, drr_q);
|
|
CHECK_STRUCT(Rcv, drr);
|
|
|
|
KeReleaseSpinLock(&DeletedAO->ao_lock, Irql0);
|
|
(*Rcv->drr_rtn)(Rcv->drr_context, (uint) TDI_ADDR_DELETED, 0);
|
|
|
|
FreeDGRcvReq(Rcv);
|
|
|
|
KeAcquireSpinLock(&DeletedAO->ao_lock, &Irql0);
|
|
}
|
|
|
|
// Now destroy any sends.
|
|
while (!EMPTYQ(&DeletedAO->ao_sendq)) {
|
|
DGSendReq *Send;
|
|
|
|
DEQUEUE(&DeletedAO->ao_sendq, Send, DGSendReq, dsr_q);
|
|
CHECK_STRUCT(Send, dsr);
|
|
|
|
KeReleaseSpinLock(&DeletedAO->ao_lock, Irql0);
|
|
(*Send->dsr_rtn)(Send->dsr_context, (uint) TDI_ADDR_DELETED, 0);
|
|
|
|
KeAcquireSpinLock(&DGSendReqLock, &Irql0);
|
|
FreeDGSendReq(Send);
|
|
KeReleaseSpinLock(&DGSendReqLock, Irql0);
|
|
|
|
KeAcquireSpinLock(&DeletedAO->ao_lock, &Irql0);
|
|
}
|
|
|
|
AddrSD = DeletedAO->ao_sd;
|
|
|
|
KeReleaseSpinLock(&DeletedAO->ao_lock, Irql0);
|
|
|
|
// Free any associated multicast addresses.
|
|
AMA = DeletedAO->ao_mcastlist;
|
|
while (AMA != NULL) {
|
|
AOMCastAddr *CurrentAMA;
|
|
|
|
// Remove the group address from the IP layer.
|
|
MLDDropMCastAddr(AMA->ama_if, &AMA->ama_addr);
|
|
|
|
CurrentAMA = AMA;
|
|
AMA = AMA->ama_next;
|
|
ExFreePool(CurrentAMA);
|
|
}
|
|
|
|
if (DeletedAO->ao_iflist != NULL) {
|
|
ExFreePool(DeletedAO->ao_iflist);
|
|
}
|
|
|
|
if (AddrSD != NULL) {
|
|
KeWaitForSingleObject(&AddrSDMutex, Executive, KernelMode, FALSE, NULL);
|
|
ObDereferenceSecurityDescriptor(AddrSD, 1);
|
|
KeReleaseMutex(&AddrSDMutex, FALSE);
|
|
}
|
|
|
|
ExFreePool(DeletedAO);
|
|
|
|
#ifndef UDP_ONLY
|
|
// Now go down the TCB list, and destroy any we need to.
|
|
CurrentTCB = TCBHead;
|
|
while (CurrentTCB != NULL) {
|
|
TCB *NextTCB;
|
|
|
|
KeAcquireSpinLock(&CurrentTCB->tcb_lock, &Irql0);
|
|
CurrentTCB->tcb_flags |= NEED_RST; // Make sure we send a RST.
|
|
NextTCB = CurrentTCB->tcb_aonext;
|
|
TryToCloseTCB(CurrentTCB, TCB_CLOSE_ABORTED, Irql0);
|
|
KeAcquireSpinLock(&CurrentTCB->tcb_lock, &Irql0);
|
|
DerefTCB(CurrentTCB, Irql0);
|
|
CurrentTCB = NextTCB;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//* GetAORequest - Get an AO request structure.
|
|
//
|
|
// A routine to allocate a request structure from our free list.
|
|
//
|
|
AORequest * // Returns: Ptr to request struct, or NULL if we couldn't get one.
|
|
GetAORequest()
|
|
{
|
|
AORequest *NewRequest;
|
|
|
|
NewRequest = ExAllocatePool(NonPagedPool, sizeof(AORequest));
|
|
if (NewRequest != NULL) {
|
|
#if DBG
|
|
NewRequest->aor_sig = aor_signature;
|
|
#endif
|
|
}
|
|
|
|
return NewRequest;
|
|
}
|
|
|
|
//* FreeAORequest - Free an AO request structure.
|
|
//
|
|
// Called to free an AORequest structure.
|
|
//
|
|
void // Returns: Nothing.
|
|
FreeAORequest(
|
|
AORequest *Request) // AORequest structure to be freed.
|
|
{
|
|
CHECK_STRUCT(Request, aor);
|
|
|
|
ExFreePool(Request);
|
|
}
|
|
|
|
|
|
//* TDICloseAddress - Close an address.
|
|
//
|
|
// The user API to delete an address. Basically, we destroy the local address
|
|
// object if we can.
|
|
//
|
|
// This routine is interlocked with the AO busy bit - if the busy bit is set,
|
|
// we'll just flag the AO for later deletion.
|
|
//
|
|
TDI_STATUS // Returns: Status of attempt to delete the address -
|
|
// (either pending or success).
|
|
TdiCloseAddress(
|
|
PTDI_REQUEST Request) // TDI_REQUEST structure for this request.
|
|
{
|
|
AddrObj *DeletingAO;
|
|
KIRQL OldIrql;
|
|
|
|
DeletingAO = Request->Handle.AddressHandle;
|
|
|
|
CHECK_STRUCT(DeletingAO, ao);
|
|
|
|
KeAcquireSpinLock(&DeletingAO->ao_lock, &OldIrql);
|
|
|
|
if (!AO_BUSY(DeletingAO) && !(DeletingAO->ao_usecnt)) {
|
|
SET_AO_BUSY(DeletingAO);
|
|
SET_AO_INVALID(DeletingAO); // This address object is deleting.
|
|
|
|
KeReleaseSpinLock(&DeletingAO->ao_lock, OldIrql);
|
|
DeleteAO(DeletingAO);
|
|
return TDI_SUCCESS;
|
|
|
|
} else {
|
|
|
|
AORequest *NewRequest, *OldRequest;
|
|
RequestCompleteRoutine CmpltRtn;
|
|
PVOID ReqContext;
|
|
TDI_STATUS Status;
|
|
|
|
// Check and see if we already have a delete in progress. If we don't
|
|
// allocate and link up a delete request structure.
|
|
if (!AO_REQUEST(DeletingAO, AO_DELETE)) {
|
|
|
|
OldRequest = DeletingAO->ao_request;
|
|
|
|
NewRequest = GetAORequest();
|
|
|
|
if (NewRequest != NULL) {
|
|
// Got a request.
|
|
NewRequest->aor_rtn = Request->RequestNotifyObject;
|
|
NewRequest->aor_context = Request->RequestContext;
|
|
// Clear the option request, if there is one.
|
|
CLEAR_AO_REQUEST(DeletingAO, AO_OPTIONS);
|
|
|
|
SET_AO_REQUEST(DeletingAO, AO_DELETE);
|
|
SET_AO_INVALID(DeletingAO); // Address object is deleting.
|
|
|
|
DeletingAO->ao_request = NewRequest;
|
|
NewRequest->aor_next = NULL;
|
|
KeReleaseSpinLock(&DeletingAO->ao_lock, OldIrql);
|
|
|
|
while (OldRequest != NULL) {
|
|
AORequest *Temp;
|
|
|
|
CmpltRtn = OldRequest->aor_rtn;
|
|
ReqContext = OldRequest->aor_context;
|
|
|
|
(*CmpltRtn)(ReqContext, (uint) TDI_ADDR_DELETED, 0);
|
|
Temp = OldRequest;
|
|
OldRequest = OldRequest->aor_next;
|
|
FreeAORequest(Temp);
|
|
}
|
|
|
|
return TDI_PENDING;
|
|
} else
|
|
Status = TDI_NO_RESOURCES;
|
|
} else
|
|
Status = TDI_ADDR_INVALID; // Delete already in progress.
|
|
|
|
KeReleaseSpinLock(&DeletingAO->ao_lock, OldIrql);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
//* FindAOMCastAddr - Find a multicast address on an AddrObj.
|
|
//
|
|
// A utility routine to find a multicast address on an AddrObj. We also
|
|
// return a pointer to it's predecessor, for use in deleting.
|
|
//
|
|
// A loose comparison treats the unspecified interface (IFNo is 0)
|
|
// specially, selecting the first matching multicast address.
|
|
//
|
|
AOMCastAddr * // Returns: matching AMA structure, or NULL if there is none.
|
|
FindAOMCastAddr(
|
|
AddrObj *AO, // AddrObj to search.
|
|
IPv6Addr *Addr, // MCast address to search for.
|
|
uint IFNo, // The interface number.
|
|
AOMCastAddr **PrevAMA, // Pointer to where to return predecessor.
|
|
BOOLEAN Loose) // Special case the unspecified interface.
|
|
{
|
|
AOMCastAddr *FoundAMA, *Temp;
|
|
|
|
Temp = CONTAINING_RECORD(&AO->ao_mcastlist, AOMCastAddr, ama_next);
|
|
FoundAMA = AO->ao_mcastlist;
|
|
|
|
while (FoundAMA != NULL) {
|
|
if (IP6_ADDR_EQUAL(Addr, &FoundAMA->ama_addr) &&
|
|
((IFNo == FoundAMA->ama_if) || ((IFNo == 0) && Loose)))
|
|
break;
|
|
Temp = FoundAMA;
|
|
FoundAMA = FoundAMA->ama_next;
|
|
}
|
|
|
|
*PrevAMA = Temp;
|
|
return FoundAMA;
|
|
}
|
|
|
|
//* MCastAddrOnAO - Test to see if a multicast address on an AddrObj.
|
|
//
|
|
// A utility routine to test to see if a multicast address is on an AddrObj.
|
|
//
|
|
uint // Returns: TRUE if Addr is on AO.
|
|
MCastAddrOnAO(
|
|
AddrObj *AO, // AddrObj to search.
|
|
IPv6Addr *Addr) // MCast address to search for.
|
|
{
|
|
AOMCastAddr *FoundAMA;
|
|
|
|
FoundAMA = AO->ao_mcastlist;
|
|
|
|
while (FoundAMA != NULL) {
|
|
if (IP6_ADDR_EQUAL(Addr, &FoundAMA->ama_addr))
|
|
return(TRUE);
|
|
FoundAMA = FoundAMA->ama_next;
|
|
}
|
|
return(FALSE);
|
|
}
|
|
|
|
//* DoesAOAllowPacket - Test to see if an AO is allowed to receive a packet.
|
|
//
|
|
// A utility routine to test whether the interface list and protection level
|
|
// of an AddrObj will accept a packet that arrives on a given interface,
|
|
// from a given remote address.
|
|
//
|
|
int // Returns: TRUE if AO can accept packet.
|
|
DoesAOAllowPacket(
|
|
AddrObj *AO, // AddrObj to test.
|
|
Interface *IF, // Interface on which the packet arrived.
|
|
IPv6Addr *RemoteAddr) // Remote address from which the packet arrived.
|
|
{
|
|
//
|
|
// See if an interface list was specified.
|
|
//
|
|
if ((AO->ao_iflist != NULL) &&
|
|
IP6_ADDR_EQUAL(&AO->ao_addr, &UnspecifiedAddr) &&
|
|
(FindIfIndexOnAO(AO, IF) == 0))
|
|
return FALSE;
|
|
|
|
//
|
|
// Only accept Teredo if the protection level is unrestricted.
|
|
//
|
|
if ((IF->Type == IF_TYPE_TUNNEL_TEREDO) &&
|
|
(AO->ao_protect != PROTECTION_LEVEL_UNRESTRICTED))
|
|
return FALSE;
|
|
|
|
//
|
|
// If the level is restricted, disallow global addresses which
|
|
// are not known to be within one of our sites. Note that we
|
|
// cannot use the IsGlobal() macro here, since we want v4-compatible
|
|
// addresses, etc., to be treated as global.
|
|
//
|
|
if ((AO->ao_protect == PROTECTION_LEVEL_RESTRICTED) &&
|
|
!IsSiteLocal(RemoteAddr) &&
|
|
!IsLinkLocal(RemoteAddr) &&
|
|
!IsLoopback(RemoteAddr) &&
|
|
(SitePrefixMatch(RemoteAddr) == 0))
|
|
return FALSE;
|
|
|
|
//
|
|
// TODO: Should eventually replace the simple firewall check here
|
|
// with hooks for ICF.
|
|
//
|
|
if ((IF->Flags & IF_FLAG_FIREWALL_ENABLED) &&
|
|
(AO->ao_dgsend != NULL) &&
|
|
!AO_SENTDATA(AO))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//* SetAOOptions - Set AddrObj options.
|
|
//
|
|
// The set options worker routine, called when we've validated the buffer
|
|
// and know that the AddrObj isn't busy.
|
|
//
|
|
TDI_STATUS // Returns: TDI_STATUS of attempt.
|
|
SetAOOptions(
|
|
AddrObj *OptionAO, // AddrObj for which options are being set.
|
|
uint ID,
|
|
uint Length,
|
|
uchar *Options) // AOOption buffer of options.
|
|
{
|
|
IP_STATUS IPStatus; // Status of IP option set request.
|
|
KIRQL OldIrql;
|
|
TDI_STATUS Status;
|
|
AOMCastAddr *AMA, *PrevAMA;
|
|
|
|
ASSERT(AO_BUSY(OptionAO));
|
|
|
|
if (Length == 0)
|
|
return TDI_BAD_OPTION;
|
|
|
|
if (ID == AO_OPTION_UNBIND) {
|
|
KeAcquireSpinLock(&AddrObjTableLock, &OldIrql);
|
|
RemoveAddrObj(OptionAO);
|
|
KeReleaseSpinLock(&AddrObjTableLock, OldIrql);
|
|
return TDI_SUCCESS;
|
|
}
|
|
|
|
Status = TDI_SUCCESS;
|
|
KeAcquireSpinLock(&OptionAO->ao_lock, &OldIrql);
|
|
|
|
switch (ID) {
|
|
|
|
case AO_OPTION_TTL:
|
|
if (Length >= sizeof(int)) {
|
|
int Hops = (int) *Options;
|
|
if ((Hops >= -1) && (Hops <= 255)) {
|
|
OptionAO->ao_ucast_hops = Hops;
|
|
break;
|
|
}
|
|
}
|
|
Status = TDI_BAD_OPTION;
|
|
break;
|
|
|
|
case AO_OPTION_MCASTLOOP:
|
|
if (Length >= sizeof(int)) {
|
|
uint Loop = (uint) *Options;
|
|
if (Loop <= TRUE) {
|
|
OptionAO->ao_mcast_loop = Loop;
|
|
break;
|
|
}
|
|
}
|
|
Status = TDI_BAD_OPTION;
|
|
break;
|
|
|
|
case AO_OPTION_MCASTTTL:
|
|
if (Length >= sizeof(int)) {
|
|
int Hops = (int) *Options;
|
|
if ((Hops >= -1) && (Hops <= 255)) {
|
|
OptionAO->ao_mcast_hops = Hops;
|
|
break;
|
|
}
|
|
}
|
|
Status = TDI_BAD_OPTION;
|
|
break;
|
|
|
|
case AO_OPTION_MCASTIF:
|
|
if (Length >= sizeof(uint)) {
|
|
OptionAO->ao_mcast_if = (uint) *Options;
|
|
} else
|
|
Status = TDI_BAD_OPTION;
|
|
break;
|
|
|
|
case AO_OPTION_ADD_MCAST:
|
|
case AO_OPTION_DEL_MCAST:
|
|
if (Length >= sizeof(IPV6_MREQ)) {
|
|
IPV6_MREQ *Req = (IPV6_MREQ *)Options;
|
|
BOOLEAN IsInterfaceUnspecified = (Req->ipv6mr_interface == 0);
|
|
|
|
//
|
|
// Look for this multicast address on this Address Object.
|
|
//
|
|
// NOTE: A loose comparison of the interface index provides
|
|
// the following behavior (when ipv6mr_interface is 0):
|
|
// IPV6_ADD_MEMBERSHIP fails if the specified multicast
|
|
// group has already been added to any interface.
|
|
// IPV6_DROP_MEMBERSHIP drops the first matching multicast
|
|
// group.
|
|
//
|
|
AMA = FindAOMCastAddr(OptionAO, &Req->ipv6mr_multiaddr,
|
|
Req->ipv6mr_interface, &PrevAMA, TRUE);
|
|
|
|
if (ID == AO_OPTION_ADD_MCAST) {
|
|
// This is an add request. Fail it if it's already there.
|
|
if (AMA != NULL) {
|
|
// Address is already present on AO.
|
|
Status = TDI_BAD_OPTION;
|
|
break;
|
|
}
|
|
AMA = ExAllocatePool(NonPagedPool, sizeof(AOMCastAddr));
|
|
if (AMA == NULL) {
|
|
// Couldn't get the resource we need.
|
|
Status = TDI_NO_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
// Add it to the list.
|
|
AMA->ama_next = OptionAO->ao_mcastlist;
|
|
OptionAO->ao_mcastlist = AMA;
|
|
|
|
// Fill in the address and interface information.
|
|
AMA->ama_addr = Req->ipv6mr_multiaddr;
|
|
AMA->ama_if = Req->ipv6mr_interface;
|
|
|
|
} else {
|
|
// This is a delete request. Fail it if it's not there.
|
|
if (AMA == NULL) {
|
|
// Address is not present on AO.
|
|
Status = TDI_BAD_OPTION;
|
|
break;
|
|
}
|
|
|
|
// Remove it from the list.
|
|
PrevAMA->ama_next = AMA->ama_next;
|
|
ExFreePool(AMA);
|
|
}
|
|
|
|
// Drop the AO lock since MLDAddMCastAddr/MLDDropMCastAddr
|
|
// assume that they are called with no locks held.
|
|
KeReleaseSpinLock(&OptionAO->ao_lock, OldIrql);
|
|
if (ID == AO_OPTION_ADD_MCAST) {
|
|
// If the interface is unspecified, MLDAddMCastAddr will
|
|
// try to pick a reasonable interface and then return
|
|
// the interface number that it picked.
|
|
IPStatus = MLDAddMCastAddr(&Req->ipv6mr_interface,
|
|
&Req->ipv6mr_multiaddr);
|
|
} else
|
|
IPStatus = MLDDropMCastAddr(Req->ipv6mr_interface,
|
|
&Req->ipv6mr_multiaddr);
|
|
KeAcquireSpinLock(&OptionAO->ao_lock, &OldIrql);
|
|
|
|
// Since we dropped the AO lock, we have to search for our
|
|
// AMA again if we need it. In fact, it might even have
|
|
// been deleted!
|
|
if ((ID == AO_OPTION_ADD_MCAST) &&
|
|
((IPStatus != TDI_SUCCESS) || IsInterfaceUnspecified)) {
|
|
AMA = FindAOMCastAddr(
|
|
OptionAO, &Req->ipv6mr_multiaddr,
|
|
IsInterfaceUnspecified ? 0 : Req->ipv6mr_interface,
|
|
&PrevAMA, FALSE);
|
|
if (AMA != NULL) {
|
|
if (IPStatus != TDI_SUCCESS) {
|
|
// Some problem adding or deleting. If we were
|
|
// adding, we remove and free the one we just added.
|
|
PrevAMA->ama_next = AMA->ama_next;
|
|
ExFreePool(AMA);
|
|
} else {
|
|
// Interface 0 was specified, assign AMA->ama_if
|
|
// to the interface selected by MLDAddMCastAddr.
|
|
// Hence, incoming multicast packets will only
|
|
// be accepted on this AMA if they arrive on the
|
|
// chosen interface.
|
|
AMA->ama_if = Req->ipv6mr_interface;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (IPStatus != TDI_SUCCESS)
|
|
Status = (IPStatus == IP_NO_RESOURCES) ? TDI_NO_RESOURCES :
|
|
TDI_ADDR_INVALID;
|
|
|
|
} else
|
|
Status = TDI_BAD_OPTION;
|
|
break;
|
|
|
|
case AO_OPTION_UDP_CKSUM_COVER:
|
|
if (Length >= sizeof(ushort)) {
|
|
ushort Value = *(ushort *)Options;
|
|
if ((0 < Value) && (Value < sizeof(UDPHeader)))
|
|
Status = TDI_BAD_OPTION;
|
|
else
|
|
OptionAO->ao_udp_cksum_cover = Value;
|
|
} else
|
|
Status = TDI_BAD_OPTION;
|
|
break;
|
|
|
|
case AO_OPTION_IP_HDRINCL:
|
|
if (Length >= sizeof(int)) {
|
|
uint HdrIncl = (uint) *Options;
|
|
if (HdrIncl <= TRUE) {
|
|
if (HdrIncl)
|
|
SET_AO_HDRINCL(OptionAO);
|
|
else
|
|
CLEAR_AO_HDRINCL(OptionAO);
|
|
break;
|
|
}
|
|
}
|
|
Status = TDI_BAD_OPTION;
|
|
break;
|
|
|
|
case AO_OPTION_IFLIST:
|
|
|
|
//
|
|
// Determine whether the interface-list is being enabled or cleared.
|
|
// When enabled, an empty zero-terminated interface-list is set.
|
|
// When disabled, any existing interface-list is freed.
|
|
//
|
|
// In both cases, the 'ao_iflist' pointer in the object is replaced
|
|
// using an interlocked operation to allow us to check the field
|
|
// in the receive-path without first locking the address-object.
|
|
//
|
|
if (Options[0]) {
|
|
if (OptionAO->ao_iflist) {
|
|
Status = TDI_SUCCESS;
|
|
} else if (!IP6_ADDR_EQUAL(&OptionAO->ao_addr, &UnspecifiedAddr)) {
|
|
Status = TDI_INVALID_PARAMETER;
|
|
} else {
|
|
uint *IfList = ExAllocatePool(NonPagedPool, sizeof(uint));
|
|
if (IfList == NULL) {
|
|
Status = TDI_NO_RESOURCES;
|
|
} else {
|
|
*IfList = 0;
|
|
OptionAO->ao_iflist = IfList;
|
|
Status = TDI_SUCCESS;
|
|
}
|
|
}
|
|
} else {
|
|
if (OptionAO->ao_iflist) {
|
|
ExFreePool(OptionAO->ao_iflist);
|
|
OptionAO->ao_iflist = NULL;
|
|
}
|
|
Status = TDI_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
case AO_OPTION_ADD_IFLIST:
|
|
|
|
//
|
|
// An interface-index is being added to the object's interface-list
|
|
// so verify that an interface-list exists and, if not, fail.
|
|
// Otherwise, verify that the index specified is valid and, if so,
|
|
// verify that the index is not already in the interface list.
|
|
//
|
|
|
|
if (OptionAO->ao_iflist == NULL) {
|
|
Status = TDI_INVALID_PARAMETER;
|
|
} else {
|
|
uint IfIndex = *(uint *)Options;
|
|
Interface *IF = FindInterfaceFromIndex(IfIndex);
|
|
if (IF == NULL) {
|
|
Status = TDI_ADDR_INVALID;
|
|
} else {
|
|
uint i = 0;
|
|
ReleaseIF(IF);
|
|
while (OptionAO->ao_iflist[i] != 0 &&
|
|
OptionAO->ao_iflist[i] != IfIndex) {
|
|
i++;
|
|
}
|
|
if (OptionAO->ao_iflist[i] == IfIndex) {
|
|
Status = TDI_SUCCESS;
|
|
} else {
|
|
|
|
//
|
|
// The index to be added is not already present.
|
|
// Allocate space for an expanded interface-list,
|
|
// copy the old interface-list, append the new index,
|
|
// and replace the old interface-list using an
|
|
// interlocked operation.
|
|
//
|
|
uint *IfList = ExAllocatePool(NonPagedPool,
|
|
(i + 2) * sizeof(uint));
|
|
if (IfList == NULL) {
|
|
Status = TDI_NO_RESOURCES;
|
|
} else {
|
|
RtlCopyMemory(IfList, OptionAO->ao_iflist,
|
|
i * sizeof(uint));
|
|
IfList[i] = IfIndex;
|
|
IfList[i + 1] = 0;
|
|
ExFreePool(OptionAO->ao_iflist);
|
|
OptionAO->ao_iflist = IfList;
|
|
Status = TDI_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case AO_OPTION_DEL_IFLIST:
|
|
|
|
//
|
|
// An index is being removed from the object's interface-list,
|
|
// so verify that an interface-list exists and, if not, fail.
|
|
// Otherwise, search the list for the index and, if not found, fail.
|
|
//
|
|
// N.B. We do not validate the index first in this case, to allow
|
|
// an index to be removed even after the corresponding interface
|
|
// is no longer present.
|
|
//
|
|
if (OptionAO->ao_iflist == NULL) {
|
|
Status = TDI_INVALID_PARAMETER;
|
|
} else {
|
|
uint IfIndex = *(uint *) Options;
|
|
if (IfIndex == 0) {
|
|
Status = TDI_ADDR_INVALID;
|
|
} else {
|
|
uint j = (uint)-1;
|
|
uint i = 0;
|
|
while (OptionAO->ao_iflist[i] != 0) {
|
|
if (OptionAO->ao_iflist[i] == IfIndex) {
|
|
j = i;
|
|
}
|
|
i++;
|
|
}
|
|
if (j == (uint)-1) {
|
|
Status = TDI_ADDR_INVALID;
|
|
} else {
|
|
|
|
//
|
|
// We've found the index to be removed.
|
|
// Allocate a truncated interface-list, copy the old
|
|
// interface-list excluding the removed index, and
|
|
// replace the old interface-list using an interlocked
|
|
// operation.
|
|
//
|
|
uint *IfList = ExAllocatePool(NonPagedPool,
|
|
i * sizeof(uint));
|
|
if (IfList == NULL) {
|
|
Status = TDI_NO_RESOURCES;
|
|
} else {
|
|
i = 0;
|
|
j = 0;
|
|
while (OptionAO->ao_iflist[i] != 0) {
|
|
if (OptionAO->ao_iflist[i] != IfIndex) {
|
|
IfList[j++] = OptionAO->ao_iflist[i];
|
|
}
|
|
i++;
|
|
}
|
|
IfList[j] = 0;
|
|
ExFreePool(OptionAO->ao_iflist);
|
|
OptionAO->ao_iflist = IfList;
|
|
Status = TDI_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case AO_OPTION_IP_PKTINFO:
|
|
if (Options[0])
|
|
SET_AO_PKTINFO(OptionAO);
|
|
else
|
|
CLEAR_AO_PKTINFO(OptionAO);
|
|
break;
|
|
|
|
case AO_OPTION_RCV_HOPLIMIT:
|
|
if (Options[0])
|
|
SET_AO_RCV_HOPLIMIT(OptionAO);
|
|
else
|
|
CLEAR_AO_RCV_HOPLIMIT(OptionAO);
|
|
break;
|
|
|
|
case AO_OPTION_PROTECT:
|
|
if (Length >= sizeof(int)) {
|
|
int Level = (int) *Options;
|
|
if ((Level == PROTECTION_LEVEL_RESTRICTED) ||
|
|
(Level == PROTECTION_LEVEL_DEFAULT) ||
|
|
(Level == PROTECTION_LEVEL_UNRESTRICTED)) {
|
|
OptionAO->ao_protect = Level;
|
|
break;
|
|
}
|
|
}
|
|
Status = TDI_BAD_OPTION;
|
|
break;
|
|
|
|
default:
|
|
Status = TDI_BAD_OPTION;
|
|
break;
|
|
}
|
|
|
|
KeReleaseSpinLock(&OptionAO->ao_lock, OldIrql);
|
|
|
|
return Status;
|
|
}
|
|
|
|
//* SetAddrOptions - Set options on an address object.
|
|
//
|
|
// Called to set options on an address object. We validate the buffer,
|
|
// and if everything is OK we'll check the status of the AddrObj. If
|
|
// it's OK then we'll set them, otherwise we'll mark it for later use.
|
|
//
|
|
TDI_STATUS // Returns: TDI_STATUS of attempt.
|
|
SetAddrOptions(
|
|
PTDI_REQUEST Request, // Request describing AddrObj for option set.
|
|
uint ID, // ID for option to be set.
|
|
uint OptLength, // Length of options.
|
|
void *Options) // Pointer to options.
|
|
{
|
|
AddrObj *OptionAO;
|
|
TDI_STATUS Status;
|
|
KIRQL OldIrql;
|
|
|
|
OptionAO = Request->Handle.AddressHandle;
|
|
|
|
CHECK_STRUCT(OptionAO, ao);
|
|
|
|
KeAcquireSpinLock(&OptionAO->ao_lock, &OldIrql);
|
|
|
|
if (AO_VALID(OptionAO)) {
|
|
if (!AO_BUSY(OptionAO) && OptionAO->ao_usecnt == 0) {
|
|
SET_AO_BUSY(OptionAO);
|
|
KeReleaseSpinLock(&OptionAO->ao_lock, OldIrql);
|
|
|
|
Status = SetAOOptions(OptionAO, ID, OptLength, Options);
|
|
|
|
KeAcquireSpinLock(&OptionAO->ao_lock, &OldIrql);
|
|
if (!AO_PENDING(OptionAO)) {
|
|
CLEAR_AO_BUSY(OptionAO);
|
|
KeReleaseSpinLock(&OptionAO->ao_lock, OldIrql);
|
|
return Status;
|
|
} else {
|
|
KeReleaseSpinLock(&OptionAO->ao_lock, OldIrql);
|
|
ProcessAORequests(OptionAO);
|
|
return Status;
|
|
}
|
|
} else {
|
|
AORequest *NewRequest, *OldRequest;
|
|
|
|
// The AddrObj is busy somehow. We need to get a request, and link
|
|
// him on the request list.
|
|
|
|
NewRequest = GetAORequest();
|
|
|
|
if (NewRequest != NULL) {
|
|
// Got a request.
|
|
NewRequest->aor_rtn = Request->RequestNotifyObject;
|
|
NewRequest->aor_context = Request->RequestContext;
|
|
NewRequest->aor_id = ID;
|
|
NewRequest->aor_length = OptLength;
|
|
NewRequest->aor_buffer = Options;
|
|
NewRequest->aor_next = NULL;
|
|
// Set the option request.
|
|
SET_AO_REQUEST(OptionAO, AO_OPTIONS);
|
|
|
|
OldRequest = CONTAINING_RECORD(&OptionAO->ao_request,
|
|
AORequest, aor_next);
|
|
|
|
while (OldRequest->aor_next != NULL)
|
|
OldRequest = OldRequest->aor_next;
|
|
|
|
OldRequest->aor_next = NewRequest;
|
|
KeReleaseSpinLock(&OptionAO->ao_lock, OldIrql);
|
|
|
|
return TDI_PENDING;
|
|
} else
|
|
Status = TDI_NO_RESOURCES;
|
|
}
|
|
} else
|
|
Status = TDI_ADDR_INVALID;
|
|
|
|
KeReleaseSpinLock(&OptionAO->ao_lock, OldIrql);
|
|
return Status;
|
|
}
|
|
|
|
//* TDISetEvent - Set a handler for a particular event.
|
|
//
|
|
// This is the user API to set an event. It's pretty simple, we just
|
|
// grab the lock on the AddrObj and fill in the event.
|
|
//
|
|
// This routine never pends.
|
|
//
|
|
TDI_STATUS // Returns: TDI_SUCCESS if it works, an error if it doesn't.
|
|
TdiSetEvent(
|
|
PVOID Handle, // Pointer to address object.
|
|
int Type, // Event being set.
|
|
PVOID Handler, // Handler to call for event.
|
|
PVOID Context) // Context to pass to event.
|
|
{
|
|
AddrObj *EventAO;
|
|
KIRQL OldIrql;
|
|
TDI_STATUS Status;
|
|
|
|
EventAO = (AddrObj *)Handle;
|
|
|
|
CHECK_STRUCT(EventAO, ao);
|
|
if (!AO_VALID(EventAO))
|
|
return TDI_ADDR_INVALID;
|
|
|
|
KeAcquireSpinLock(&EventAO->ao_lock, &OldIrql);
|
|
|
|
Status = TDI_SUCCESS;
|
|
switch (Type) {
|
|
|
|
case TDI_EVENT_CONNECT:
|
|
EventAO->ao_connect = Handler;
|
|
EventAO->ao_conncontext = Context;
|
|
break;
|
|
case TDI_EVENT_DISCONNECT:
|
|
EventAO->ao_disconnect = Handler;
|
|
EventAO->ao_disconncontext = Context;
|
|
break;
|
|
case TDI_EVENT_ERROR:
|
|
EventAO->ao_error = Handler;
|
|
EventAO->ao_errcontext = Context;
|
|
break;
|
|
case TDI_EVENT_RECEIVE:
|
|
EventAO->ao_rcv = Handler;
|
|
EventAO->ao_rcvcontext = Context;
|
|
break;
|
|
case TDI_EVENT_RECEIVE_DATAGRAM:
|
|
EventAO->ao_rcvdg = Handler;
|
|
EventAO->ao_rcvdgcontext = Context;
|
|
break;
|
|
case TDI_EVENT_RECEIVE_EXPEDITED:
|
|
EventAO->ao_exprcv = Handler;
|
|
EventAO->ao_exprcvcontext = Context;
|
|
break;
|
|
|
|
case TDI_EVENT_ERROR_EX:
|
|
EventAO->ao_errorex = Handler;
|
|
EventAO->ao_errorexcontext = Context;
|
|
break;
|
|
|
|
default:
|
|
Status = TDI_BAD_EVENT_TYPE;
|
|
break;
|
|
}
|
|
|
|
KeReleaseSpinLock(&EventAO->ao_lock, OldIrql);
|
|
return Status;
|
|
}
|
|
|
|
//* ProcessAORequests - Process pending requests on an AddrObj.
|
|
//
|
|
// This is the delayed request processing routine, called when we've
|
|
// done something that used the busy bit. We examine the pending
|
|
// requests flags, and dispatch the requests appropriately.
|
|
//
|
|
void // Returns: Nothing.
|
|
ProcessAORequests(
|
|
AddrObj *RequestAO) // AddrObj to be processed.
|
|
{
|
|
KIRQL OldIrql;
|
|
AORequest *Request;
|
|
|
|
CHECK_STRUCT(RequestAO, ao);
|
|
ASSERT(AO_BUSY(RequestAO));
|
|
ASSERT(RequestAO->ao_usecnt == 0);
|
|
|
|
KeAcquireSpinLock(&RequestAO->ao_lock, &OldIrql);
|
|
|
|
while (AO_PENDING(RequestAO)) {
|
|
Request = RequestAO->ao_request;
|
|
|
|
if (AO_REQUEST(RequestAO, AO_DELETE)) {
|
|
ASSERT(Request != NULL);
|
|
ASSERT(!AO_REQUEST(RequestAO, AO_OPTIONS));
|
|
KeReleaseSpinLock(&RequestAO->ao_lock, OldIrql);
|
|
DeleteAO(RequestAO);
|
|
(*Request->aor_rtn)(Request->aor_context, TDI_SUCCESS, 0);
|
|
FreeAORequest(Request);
|
|
return; // Deleted him, so get out.
|
|
}
|
|
|
|
// Now handle options request.
|
|
while (AO_REQUEST(RequestAO, AO_OPTIONS)) {
|
|
TDI_STATUS Status;
|
|
|
|
// Have an option request.
|
|
Request = RequestAO->ao_request;
|
|
RequestAO->ao_request = Request->aor_next;
|
|
if (RequestAO->ao_request == NULL)
|
|
CLEAR_AO_REQUEST(RequestAO, AO_OPTIONS);
|
|
|
|
ASSERT(Request != NULL);
|
|
KeReleaseSpinLock(&RequestAO->ao_lock, OldIrql);
|
|
|
|
Status = SetAOOptions(RequestAO, Request->aor_id,
|
|
Request->aor_length, Request->aor_buffer);
|
|
(*Request->aor_rtn)(Request->aor_context, Status, 0);
|
|
FreeAORequest(Request);
|
|
|
|
KeAcquireSpinLock(&RequestAO->ao_lock, &OldIrql);
|
|
}
|
|
|
|
// We've done options, now try sends.
|
|
if (AO_REQUEST(RequestAO, AO_SEND)) {
|
|
DGSendProc SendProc;
|
|
DGSendReq *SendReq;
|
|
|
|
// Need to send. Clear the busy flag, bump the send count, and
|
|
// get the send request.
|
|
if (!EMPTYQ(&RequestAO->ao_sendq)) {
|
|
DEQUEUE(&RequestAO->ao_sendq, SendReq, DGSendReq, dsr_q);
|
|
CLEAR_AO_BUSY(RequestAO);
|
|
RequestAO->ao_usecnt++;
|
|
SendProc = RequestAO->ao_dgsend;
|
|
KeReleaseSpinLock(&RequestAO->ao_lock, OldIrql);
|
|
(*SendProc)(RequestAO, SendReq);
|
|
KeAcquireSpinLock(&RequestAO->ao_lock, &OldIrql);
|
|
// If there aren't any other pending sends, set the busy bit.
|
|
if (!(--RequestAO->ao_usecnt))
|
|
SET_AO_BUSY(RequestAO);
|
|
else
|
|
break; // Still sending, so get out.
|
|
} else {
|
|
// Had the send request set, but no send! Odd....
|
|
KdBreakPoint();
|
|
CLEAR_AO_REQUEST(RequestAO, AO_SEND);
|
|
}
|
|
}
|
|
}
|
|
|
|
// We're done here.
|
|
CLEAR_AO_BUSY(RequestAO);
|
|
KeReleaseSpinLock(&RequestAO->ao_lock, OldIrql);
|
|
}
|
|
|
|
|
|
//* DelayDerefAO - Dereference an AddrObj, and schedule an event.
|
|
//
|
|
// Called when we are done with an address object, and need to
|
|
// derefrence it. We dec the usecount, and if it goes to 0 and
|
|
// if there are pending actions we'll schedule an event to deal
|
|
// with them.
|
|
//
|
|
void // Returns: Nothing.
|
|
DelayDerefAO(
|
|
AddrObj *RequestAO) // AddrObj to be processed.
|
|
{
|
|
KIRQL OldIrql;
|
|
|
|
KeAcquireSpinLock(&RequestAO->ao_lock, &OldIrql);
|
|
|
|
RequestAO->ao_usecnt--;
|
|
|
|
if (!RequestAO->ao_usecnt && !AO_BUSY(RequestAO)) {
|
|
if (AO_PENDING(RequestAO)) {
|
|
SET_AO_BUSY(RequestAO);
|
|
KeReleaseSpinLock(&RequestAO->ao_lock, OldIrql);
|
|
ExQueueWorkItem(&RequestAO->ao_workitem, CriticalWorkQueue);
|
|
return;
|
|
}
|
|
}
|
|
KeReleaseSpinLock(&RequestAO->ao_lock, OldIrql);
|
|
}
|
|
|
|
//* DerefAO - Derefrence an AddrObj.
|
|
//
|
|
// Called when we are done with an address object, and need to
|
|
// derefrence it. We dec the usecount, and if it goes to 0 and
|
|
// if there are pending actions we'll call the process AO handler.
|
|
//
|
|
void // Returns: Nothing.
|
|
DerefAO(
|
|
AddrObj *RequestAO) // AddrObj to be processed.
|
|
{
|
|
KIRQL OldIrql;
|
|
|
|
KeAcquireSpinLock(&RequestAO->ao_lock, &OldIrql);
|
|
|
|
RequestAO->ao_usecnt--;
|
|
|
|
if (!RequestAO->ao_usecnt && !AO_BUSY(RequestAO)) {
|
|
if (AO_PENDING(RequestAO)) {
|
|
SET_AO_BUSY(RequestAO);
|
|
KeReleaseSpinLock(&RequestAO->ao_lock, OldIrql);
|
|
ProcessAORequests(RequestAO);
|
|
return;
|
|
}
|
|
}
|
|
|
|
KeReleaseSpinLock(&RequestAO->ao_lock, OldIrql);
|
|
}
|
|
|
|
#pragma BEGIN_INIT
|
|
|
|
//* InitAddr - Initialize the address object stuff.
|
|
//
|
|
// Called during init time to initalize the address object stuff.
|
|
//
|
|
int // Returns: True if we succeed, False if we fail.
|
|
InitAddr()
|
|
{
|
|
uint i;
|
|
|
|
KeInitializeSpinLock(&AddrObjTableLock);
|
|
KeInitializeMutex(&AddrSDMutex, FALSE);
|
|
if (MmIsThisAnNtAsSystem()) {
|
|
#if defined(_WIN64)
|
|
AddrObjTableSize = DEFAULT_AO_TABLE_SIZE_AS64;
|
|
#else
|
|
AddrObjTableSize = DEFAULT_AO_TABLE_SIZE_AS;
|
|
#endif
|
|
} else {
|
|
AddrObjTableSize = DEFAULT_AO_TABLE_SIZE_WS;
|
|
}
|
|
|
|
AddrObjTable = ExAllocatePool(NonPagedPool,
|
|
AddrObjTableSize * sizeof(AddrObj*));
|
|
if (AddrObjTable == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
for (i = 0; i < AddrObjTableSize; i++)
|
|
AddrObjTable[i] = NULL;
|
|
|
|
LastAO = NULL;
|
|
|
|
RtlInitializeBitMap(&PortBitmapTcp, PortBitmapBufferTcp, 1 << 16);
|
|
RtlInitializeBitMap(&PortBitmapUdp, PortBitmapBufferUdp, 1 << 16);
|
|
RtlClearAllBits(&PortBitmapTcp);
|
|
RtlClearAllBits(&PortBitmapUdp);
|
|
|
|
return TRUE;
|
|
}
|
|
#pragma END_INIT
|
|
|
|
//* AddrUnload
|
|
//
|
|
// Cleanup and prepare the address management code for stack unload.
|
|
//
|
|
void
|
|
AddrUnload(void)
|
|
{
|
|
ExFreePool(AddrObjTable);
|
|
AddrObjTable = NULL;
|
|
return;
|
|
}
|