|
|
/*++
Copyright (c) 1989-1993 Microsoft Corporation
Module Name:
cache.c
Abstract:
This module contains the name cache routines for the Netbios module of the ISN transport.
Author:
Adam Barr (adamba) 20-December-1993
Environment:
Kernel mode
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,CreateNetbiosCacheTable)
#endif
#ifdef RASAUTODIAL
#include <acd.h>
#include <acdapi.h>
extern BOOLEAN fAcdLoadedG; extern ACD_DRIVER AcdDriverG;
BOOLEAN NbiAttemptAutoDial( IN PDEVICE pDevice, IN PCONNECTION pConnection, IN ULONG ulFlags, IN ACD_CONNECT_CALLBACK pProc, IN PREQUEST pRequest );
VOID NbiRetryTdiConnect( IN BOOLEAN fSuccess, IN PVOID *pArgs ); #endif // RASAUTODIAL
//
// We should change to monitor add name packets better,
// so if we get an add for a different place we attempt to determine
// if it is real or bogus and update if possible.
//
NTSTATUS CacheFindName( IN PDEVICE Device, IN FIND_NAME_TYPE Type, IN PUCHAR RemoteName OPTIONAL, OUT PNETBIOS_CACHE * CacheName )
/*++
Routine Description:
This routine looks up a particular remote name in the Netbios name cache. If it cannot find it, a find name request is queued up.
THIS REQUEST IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS WITH IT HELD.
Arguments:
Device - The netbios device.
Type - Defines the type. The effect this has is: FindNameConnect - On connects we will ignore an existing cache entry if it got no response before. FindNameNetbiosFindName - For these we ignore an existing cache entry if it is for a group name -- this is because the find name wants the address of every machine, not just the network list. FindNameOther - Normal handling is done.
RemoteName - The name to be discovered -- will be NULL if it is the broadcast address.
CacheName - Returns the cache entry that was discovered.
Return Value:
None.
--*/
{ PLIST_ENTRY p; PSINGLE_LIST_ENTRY s; PNETBIOS_CACHE FoundCacheName; PNB_SEND_RESERVED Reserved; PUCHAR RealRemoteName; // RemoteName or NetbiosBroadcastName
//
// First scan the netbios name cache to see if we know
// about this remote.
//
if (RemoteName) { RealRemoteName = RemoteName; } else { RealRemoteName = NetbiosBroadcastName; }
if ( FindInNetbiosCacheTable ( Device->NameCache, RealRemoteName, &FoundCacheName ) == STATUS_SUCCESS ) {
//
// If this is a netbios find name, we only can use unique
// names in the cache; for the group ones we need to requery
// because the cache only lists networks, not individual machines.
// For connect requests, if we find an empty cache entry we
// remove it and requery.
//
if ( FoundCacheName->Unique || (Type != FindNameNetbiosFindName) ) {
if (FoundCacheName->NetworksUsed > 0) {
*CacheName = FoundCacheName; NB_DEBUG2 (CACHE, ("Found cache name <%.16s>\n", RemoteName ? RemoteName : "<broadcast>")); return STATUS_SUCCESS;
} else {
if (Type != FindNameConnect) {
if (FoundCacheName->FailedOnDownWan) { NB_DEBUG2 (CACHE, ("Found cache name, but down wan <%.16s>\n", RemoteName ? RemoteName : "<broadcast>")); return STATUS_DEVICE_DOES_NOT_EXIST; } else { NB_DEBUG2 (CACHE, ("Found cache name, but no nets <%.16s>\n", RemoteName ? RemoteName : "<broadcast>")); return STATUS_BAD_NETWORK_PATH; }
} else {
//
// This is a connect and the current cache entry
// has zero names; delete it.
//
RemoveFromNetbiosCacheTable ( Device->NameCache, FoundCacheName ); CTEAssert (FoundCacheName->ReferenceCount == 1); if (--FoundCacheName->ReferenceCount == 0) {
NB_DEBUG2 (CACHE, ("Free unneeded empty cache entry %lx\n", FoundCacheName)); NbiFreeMemory( FoundCacheName, sizeof(NETBIOS_CACHE) + ((FoundCacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), MEMORY_CACHE, "Free due to replacement"); }
} } } }
//
// There was no suitable cache entry for this network, first see
// if there is one pending.
//
for (p = Device->WaitingFindNames.Flink; p != &Device->WaitingFindNames; p = p->Flink) {
Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage);
//
// For this purpose we ignore a packet if a route
// has been found and it was for a unique name. This
// is because the cache information has already been
// inserted for this name. Otherwise if the name has
// since been deleted from the cache, the request
// that is looking for this name will starve because
// FindNameTimeout will just destroy the packet.
//
if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) { continue; }
if (RtlEqualMemory( Reserved->u.SR_FN.NetbiosName, RealRemoteName, 16)) {
NB_DEBUG2 (CACHE, ("Cache name already pending <%.16s>\n", RemoteName ? RemoteName : "<broadcast>"));
//
// There is already one pending. If it is for a group
// name and this is a netbios find name, we make sure
// the retry count is such that at least one more
// query will be sent, so the netbios find name
// buffer can be filled with the responses from this.
//
if ((Type == FindNameNetbiosFindName) && (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseGroup) && (Reserved->u.SR_FN.RetryCount == Device->BroadcastCount)) {
--Reserved->u.SR_FN.RetryCount; }
return STATUS_PENDING; } }
s = NbiPopSendPacket(Device, TRUE);
if (s == NULL) { NB_DEBUG (CACHE, ("Couldn't get packet to find <%.16s>\n", RemoteName ? RemoteName : "<broadcast>")); return STATUS_INSUFFICIENT_RESOURCES; }
Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage);
//
// We have the packet, fill it in for this request.
//
CTEAssert (Reserved->SendInProgress == FALSE); Reserved->SendInProgress = FALSE; Reserved->Type = SEND_TYPE_FIND_NAME; RtlCopyMemory (Reserved->u.SR_FN.NetbiosName, RealRemoteName, 16); Reserved->u.SR_FN.StatusAndSentOnUpLine = FNStatusNoResponse; // SentOnUpLine is FALSE
Reserved->u.SR_FN.RetryCount = 0; Reserved->u.SR_FN.NewCache = NULL; Reserved->u.SR_FN.SendTime = Device->FindNameTime; #if !defined(_PNP_POWER)
Reserved->u.SR_FN.CurrentNicId = 1;
(VOID)(*Device->Bind.QueryHandler)( // Check return code ?
IPX_QUERY_MAX_TYPE_20_NIC_ID, (USHORT)0, &Reserved->u.SR_FN.MaximumNicId, sizeof(USHORT), NULL);
if (Reserved->u.SR_FN.MaximumNicId == 0) { Reserved->u.SR_FN.MaximumNicId = 1; // code assumes at least one
} #endif !_PNP_POWER
NB_DEBUG2 (CACHE, ("Queued FIND_NAME %lx for <%.16s>\n", Reserved, RemoteName ? RemoteName : "<broadcast>"));
InsertHeadList( &Device->WaitingFindNames, &Reserved->WaitLinkage);
++Device->FindNamePacketCount;
if (!Device->FindNameTimerActive) {
Device->FindNameTimerActive = TRUE; NbiReferenceDevice (Device, DREF_FN_TIMER);
CTEStartTimer( &Device->FindNameTimer, 1, // 1 ms, i.e. expire immediately
FindNameTimeout, (PVOID)Device); }
NbiReferenceDevice (Device, DREF_FIND_NAME);
return STATUS_PENDING;
} /* CacheFindName */
VOID FindNameTimeout( CTEEvent * Event, PVOID Context )
/*++
Routine Description:
This routine is called when the find name timer expires. It is called every FIND_NAME_GRANULARITY milliseconds unless there is nothing to do.
Arguments:
Event - The event used to queue the timer.
Context - The context, which is the device pointer.
Return Value:
None.
--*/
{ PDEVICE Device = (PDEVICE)Context; PLIST_ENTRY p, q; PNB_SEND_RESERVED Reserved; PNDIS_PACKET Packet; NB_CONNECTIONLESS UNALIGNED * Header; PNETBIOS_CACHE FoundCacheName; NDIS_STATUS NdisStatus; #if !defined(_PNP_POWER)
static IPX_LOCAL_TARGET BroadcastTarget = { 0, { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }; #endif !_PNP_POWER
NB_DEFINE_LOCK_HANDLE (LockHandle)
NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle);
++Device->FindNameTime;
if (Device->FindNamePacketCount == 0) {
NB_DEBUG2 (CACHE, ("FindNameTimeout exiting\n"));
Device->FindNameTimerActive = FALSE; NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); NbiDereferenceDevice (Device, DREF_FN_TIMER);
return; }
//
// Check what is on the queue; this is set up as a
// loop but in fact it rarely does (under no
// circumstances can we send more than one packet
// each time this function executes).
//
while (TRUE) {
p = Device->WaitingFindNames.Flink; if (p == &Device->WaitingFindNames) { NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); break; }
Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage);
if (Reserved->SendInProgress) { NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); break; }
if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) {
//
// This was a find name for a unique name which got a
// response but was not freed at the time (because
// SendInProgress was still TRUE) so we free it now.
//
(VOID)RemoveHeadList (&Device->WaitingFindNames); ExInterlockedPushEntrySList( &Device->SendPacketList, &Reserved->PoolLinkage, &NbiGlobalPoolInterlock); --Device->FindNamePacketCount;
//
// It is OK to do this with the lock held because
// it won't be the last one (we have the RIP_TIMER ref).
//
NbiDereferenceDevice (Device, DREF_FIND_NAME); continue; }
if (((SHORT) (Device->FindNameTime - Reserved->u.SR_FN.SendTime)) < 0) { NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); break; }
(VOID)RemoveHeadList (&Device->WaitingFindNames);
//
// Increment the counter and see if we have sent
// all the frames we need to (we will age out
// here if we got no response for a unique query,
// or if we are doing a global name or broadcast
// search). We also kill the query right now if
// we have not found anything but down wan lines
// to send it on.
//
if ((++Reserved->u.SR_FN.RetryCount > Device->BroadcastCount) || ((Reserved->u.SR_FN.RetryCount > 1) && (!NB_GET_SR_FN_SENT_ON_UP_LINE(Reserved)))) {
#if DBG
if (Reserved->u.SR_FN.RetryCount > Device->BroadcastCount) { NB_DEBUG2 (CACHE, ("FindNameTimeout aging out %lx\n", Reserved)); } else { NB_DEBUG2 (CACHE, ("FindNameTimeout no active nets %lx\n", Reserved)); } #endif
//
// This packet is stale, clean it up and continue.
//
if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseGroup) {
CTEAssert (Reserved->u.SR_FN.NewCache != NULL);
//
// If this was a group name and we have a new
// cache entry that we have been building for it,
// then insert that in the queue and use it
// to succeed any pending connects. Because
// netbios find name requests can cause cache
// requests for group names to be queued even
// if we already have on in the database, we
// first scan for old ones and remove them.
//
if ( FindInNetbiosCacheTable( Device->NameCache, Reserved->u.SR_FN.NetbiosName, &FoundCacheName ) == STATUS_SUCCESS ) {
NB_DEBUG2 (CACHE, ("Found old group cache name <%.16s>\n", FoundCacheName->NetbiosName));
RemoveFromNetbiosCacheTable ( Device->NameCache, FoundCacheName );
if (--FoundCacheName->ReferenceCount == 0) {
NB_DEBUG2 (CACHE, ("Free replaced cache entry %lx\n", FoundCacheName)); NbiFreeMemory( FoundCacheName, sizeof(NETBIOS_CACHE) + ((FoundCacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), MEMORY_CACHE, "Free due to replacement");
}
}
Reserved->u.SR_FN.NewCache->TimeStamp = Device->CacheTimeStamp;
InsertInNetbiosCacheTable( Device->NameCache, Reserved->u.SR_FN.NewCache);
//
// Reference it for the moment since CacheHandlePending
// uses it after releasing the lock. CacheHandlePending
// will dereference it.
//
++Reserved->u.SR_FN.NewCache->ReferenceCount;
//
// This call releases the locks
//
CacheHandlePending( Device, Reserved->u.SR_FN.NetbiosName, NetbiosNameFound, Reserved->u.SR_FN.NewCache NB_LOCK_HANDLE_ARG(LockHandle));
} else {
CTEAssert (Reserved->u.SR_FN.NewCache == NULL);
//
// Allocate an empty cache entry to record the
// fact that we could not find this name, unless
// there is already an entry for this name.
//
if ( FindInNetbiosCacheTable( Device->NameCache, Reserved->u.SR_FN.NetbiosName, &FoundCacheName ) == STATUS_SUCCESS ) {
NB_DEBUG2 (CACHE, ("Don't replace old group cache name with empty <%16.16s>\n", FoundCacheName->NetbiosName)); } else {
PNETBIOS_CACHE EmptyCache;
//
// Nothing found.
//
EmptyCache = NbiAllocateMemory (sizeof(NETBIOS_CACHE), MEMORY_CACHE, "Cache Entry"); if (EmptyCache != NULL) {
RtlZeroMemory (EmptyCache, sizeof(NETBIOS_CACHE));
NB_DEBUG2 (CACHE, ("Allocate new empty cache %lx for <%.16s>\n", EmptyCache, Reserved->u.SR_FN.NetbiosName));
RtlCopyMemory (EmptyCache->NetbiosName, Reserved->u.SR_FN.NetbiosName, 16); EmptyCache->Unique = TRUE; // so we'll delete it if we see an add name
EmptyCache->ReferenceCount = 1; EmptyCache->NetworksAllocated = 1; EmptyCache->TimeStamp = Device->CacheTimeStamp; EmptyCache->NetworksUsed = 0; EmptyCache->FailedOnDownWan = (BOOLEAN) !NB_GET_SR_FN_SENT_ON_UP_LINE(Reserved);
InsertInNetbiosCacheTable ( Device->NameCache, EmptyCache); } }
//
// Fail all datagrams, etc. that were waiting for
// this route. This call releases the lock.
//
CacheHandlePending( Device, Reserved->u.SR_FN.NetbiosName, NB_GET_SR_FN_SENT_ON_UP_LINE(Reserved) ? NetbiosNameNotFoundNormal : NetbiosNameNotFoundWanDown, NULL NB_LOCK_HANDLE_ARG(LockHandle));
}
ExInterlockedPushEntrySList( &Device->SendPacketList, &Reserved->PoolLinkage, &NbiGlobalPoolInterlock);
NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle);
--Device->FindNamePacketCount; NbiDereferenceDevice (Device, DREF_FIND_NAME); continue; }
//
// Send the packet out again. We first set the time so
// it won't be sent again until the appropriate timeout.
//
Reserved->u.SR_FN.SendTime = (USHORT)(Device->FindNameTime + Device->FindNameTimeout);
InsertTailList (&Device->WaitingFindNames, &Reserved->WaitLinkage);
CTEAssert (Reserved->Identifier == IDENTIFIER_NB); CTEAssert (!Reserved->SendInProgress); Reserved->SendInProgress = TRUE;
NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle);
//
// If this is the first retry, we need to initialize the packet
//
if ( Reserved->u.SR_FN.RetryCount == 1 ) { //
// Fill in the IPX header -- the default header has the broadcast
// address on net 0 as the destination IPX address, which is
// what we want.
//
Header = (NB_CONNECTIONLESS UNALIGNED *)(&Reserved->Header[Device->Bind.IncludedHeaderOffset]); RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER)); Header->IpxHeader.PacketLength[0] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) / 256; Header->IpxHeader.PacketLength[1] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) % 256;
Header->IpxHeader.PacketType = (UCHAR)(Device->Internet ? 0x014 : 0x04);
//
// Now fill in the Netbios header.
//
RtlZeroMemory (Header->NameFrame.RoutingInfo, 32); Header->NameFrame.ConnectionControlFlag = 0x00; // Header->NameFrame.DataStreamType = NB_CMD_FIND_NAME;
Header->NameFrame.DataStreamType2 = NB_CMD_FIND_NAME; Header->NameFrame.NameTypeFlag = 0x00;
RtlCopyMemory( Header->NameFrame.Name, Reserved->u.SR_FN.NetbiosName, 16);
} //
// Now submit the packet to IPX.
//
Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]);
NB_DEBUG2 (CACHE, ("FindNameTimeout sending %lx\n", Reserved));
NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME)); if ((NdisStatus = (*Device->Bind.SendHandler)( &BroadcastTarget, Packet, sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME), sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME))) != STATUS_PENDING) {
NbiSendComplete( Packet, NdisStatus);
}
break;
}
//
// Since we did something this time, we restart the timer.
//
CTEStartTimer( &Device->FindNameTimer, FIND_NAME_GRANULARITY, FindNameTimeout, (PVOID)Device);
} /* FindNameTimeout */
VOID CacheHandlePending( IN PDEVICE Device, IN PUCHAR RemoteName, IN NETBIOS_NAME_RESULT Result, IN PNETBIOS_CACHE CacheName OPTIONAL IN NB_LOCK_HANDLE_PARAM(LockHandle) )
/*++
Routine Description:
This routine cleans up pending datagrams and connects that were waiting for a route to be discovered to a given Netbios NAME. THIS ROUTINE IS CALLED WITH DEVICE->LOCK ACQUIRED AND RETURNS WITH IT RELEASED.
Arguments:
Device - The device.
RemoteName - The netbios name that was being searched for.
Result - Indicates if the name was found, or not found due to no response or wan lines being down.
CacheName - If Result is NetbiosNameFound, the cache entry for this name. This entry has been referenced and this routine will deref it.
LockHandle - The handle used to acquire the lock.
Return Value:
None.
--*/
{
LIST_ENTRY DatagramList; LIST_ENTRY ConnectList; LIST_ENTRY AdapterStatusList; LIST_ENTRY NetbiosFindNameList; PNB_SEND_RESERVED Reserved; PNDIS_PACKET Packet; PLIST_ENTRY p; PREQUEST ConnectRequest, DatagramRequest, AdapterStatusRequest, NetbiosFindNameRequest; PCONNECTION Connection; PADDRESS_FILE AddressFile; TDI_ADDRESS_NETBIOS * RemoteAddress; CTELockHandle CancelLH; NB_DEFINE_LOCK_HANDLE (LockHandle1)
InitializeListHead (&DatagramList); InitializeListHead (&ConnectList); InitializeListHead (&AdapterStatusList); InitializeListHead (&NetbiosFindNameList);
//
// Put all connect requests on ConnectList. They will
// be continued or failed later.
//
p = Device->WaitingConnects.Flink;
while (p != &Device->WaitingConnects) {
ConnectRequest = LIST_ENTRY_TO_REQUEST(p); Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(ConnectRequest); p = p->Flink;
if (RtlEqualMemory (Connection->RemoteName, RemoteName, 16)) {
RemoveEntryList (REQUEST_LINKAGE(ConnectRequest)); InsertTailList (&ConnectList, REQUEST_LINKAGE(ConnectRequest));
Connection->SubState = CONNECTION_SUBSTATE_C_W_ACK; }
}
//
// Put all the datagrams on Datagram list. They will be
// sent or failed later.
//
p = Device->WaitingDatagrams.Flink;
while (p != &Device->WaitingDatagrams) {
Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage);
p = p->Flink;
//
// Check differently based on whether we were looking for
// the broadcast address or not.
//
if (Reserved->u.SR_DG.RemoteName == (PVOID)-1) { if (!RtlEqualMemory (RemoteName, NetbiosBroadcastName, 16)) { continue; } } else {
if (!RtlEqualMemory (RemoteName, Reserved->u.SR_DG.RemoteName->NetbiosName, 16)) { continue; } }
RemoveEntryList (&Reserved->WaitLinkage); InsertTailList (&DatagramList, &Reserved->WaitLinkage);
//
// Reference this here with the lock held.
//
if (Result == NetbiosNameFound) { ++CacheName->ReferenceCount; }
}
//
// Put all the adapter status requests on AdapterStatus
// list. They will be sent or failed later.
//
p = Device->WaitingAdapterStatus.Flink;
while (p != &Device->WaitingAdapterStatus) {
AdapterStatusRequest = LIST_ENTRY_TO_REQUEST(p);
p = p->Flink;
RemoteAddress = (TDI_ADDRESS_NETBIOS *)REQUEST_INFORMATION(AdapterStatusRequest);
if (!RtlEqualMemory( RemoteName, RemoteAddress->NetbiosName, 16)) { continue; }
RemoveEntryList (REQUEST_LINKAGE(AdapterStatusRequest)); InsertTailList (&AdapterStatusList, REQUEST_LINKAGE(AdapterStatusRequest));
//
// Reference this here with the lock held.
//
if (Result == NetbiosNameFound) { ++CacheName->ReferenceCount; }
}
//
// Put all the netbios find name requests on NetbiosFindName
// list. They will be completed later.
//
p = Device->WaitingNetbiosFindName.Flink;
while (p != &Device->WaitingNetbiosFindName) {
NetbiosFindNameRequest = LIST_ENTRY_TO_REQUEST(p);
p = p->Flink;
RemoteAddress = (TDI_ADDRESS_NETBIOS *)REQUEST_INFORMATION(NetbiosFindNameRequest);
if (!RtlEqualMemory( RemoteName, RemoteAddress->NetbiosName, 16)) { continue; }
RemoveEntryList (REQUEST_LINKAGE(NetbiosFindNameRequest)); InsertTailList (&NetbiosFindNameList, REQUEST_LINKAGE(NetbiosFindNameRequest));
}
NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle);
//
// Now that the lock is free, process all the packets on
// the various lists.
//
for (p = ConnectList.Flink; p != &ConnectList; ) {
ConnectRequest = LIST_ENTRY_TO_REQUEST(p); p = p->Flink;
Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(ConnectRequest);
NB_GET_CANCEL_LOCK( &CancelLH ); NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1);
if ((Connection->State == CONNECTION_STATE_CONNECTING) && (Connection->SubState != CONNECTION_SUBSTATE_C_DISCONN)) {
if (Result == NetbiosNameFound) {
NB_DEBUG2 (CONNECTION, ("Found queued connect %lx on %lx\n", ConnectRequest, Connection));
//
// Continue with the connection sequence.
//
Connection->SubState = CONNECTION_SUBSTATE_C_W_ROUTE; }
if ((Result == NetbiosNameFound) && (!ConnectRequest->Cancel)) {
IoSetCancelRoutine (ConnectRequest, NbiCancelConnectWaitResponse);
NB_SYNC_SWAP_IRQL( CancelLH, LockHandle1 ); NB_FREE_CANCEL_LOCK ( CancelLH );
Connection->LocalTarget = CacheName->Networks[0].LocalTarget; RtlCopyMemory(&Connection->RemoteHeader.DestinationNetwork, &CacheName->FirstResponse, 12); NbiReferenceConnectionSync (Connection, CREF_FIND_ROUTE);
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1);
*(UNALIGNED ULONG *)Connection->FindRouteRequest.Network = CacheName->FirstResponse.NetworkAddress; RtlCopyMemory(Connection->FindRouteRequest.Node,CacheName->FirstResponse.NodeAddress,6); Connection->FindRouteRequest.Identifier = IDENTIFIER_NB; Connection->FindRouteRequest.Type = IPX_FIND_ROUTE_RIP_IF_NEEDED;
//
// When this completes, we will send the session init.
// We don't call it if the client is for network 0,
// instead just fake as if no route could be found
// and we will use the local target we got here.
//
if (CacheName->FirstResponse.NetworkAddress != 0) { (*Device->Bind.FindRouteHandler) (&Connection->FindRouteRequest); } else { NbiFindRouteComplete( &Connection->FindRouteRequest, FALSE); }
} else { BOOLEAN bAutodialAttempt = FALSE;
if (ConnectRequest->Cancel) { NB_DEBUG2 (CONNECTION, ("Cancelling connect %lx on %lx\n", ConnectRequest, Connection)); } else { NB_DEBUG2 (CONNECTION, ("Timing out connect %lx on %lx\n", ConnectRequest, Connection)); }
ASSERT (Connection->ConnectRequest == ConnectRequest);
#ifdef RASAUTODIAL
if (fAcdLoadedG) { CTELockHandle adirql; BOOLEAN fEnabled;
//
// See if the automatic connection driver knows
// about this address before we search the
// network. If it does, we return STATUS_PENDING,
// and we will come back here via NbfRetryTdiConnect().
//
CTEGetLock(&AcdDriverG.SpinLock, &adirql); fEnabled = AcdDriverG.fEnabled; CTEFreeLock(&AcdDriverG.SpinLock, adirql); if (fEnabled && NbiAttemptAutoDial( Device, Connection, 0, NbiRetryTdiConnect, ConnectRequest)) { NB_SYNC_FREE_LOCK(&Connection->Lock, LockHandle1); NB_FREE_CANCEL_LOCK(CancelLH);
bAutodialAttempt = TRUE; } } #endif // RASAUTODIAL
if (!bAutodialAttempt) { Connection->ConnectRequest = NULL; Connection->SubState = CONNECTION_SUBSTATE_C_DISCONN;
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1);
IoSetCancelRoutine( ConnectRequest, (PDRIVER_CANCEL)NULL ); NB_FREE_CANCEL_LOCK( CancelLH );
REQUEST_STATUS(ConnectRequest) = STATUS_BAD_NETWORK_PATH;
NbiCompleteRequest(ConnectRequest); NbiFreeRequest (Device, ConnectRequest); }
NbiDereferenceConnection (Connection, CREF_CONNECT);
}
} else {
CTEAssert (0); // What happens to the IRP? Who completes it?
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1); NB_FREE_CANCEL_LOCK( CancelLH );
}
NbiDereferenceConnection (Connection, CREF_WAIT_CACHE);
}
for (p = DatagramList.Flink; p != &DatagramList; ) {
Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage); p = p->Flink;
if (Result == NetbiosNameFound) {
NB_DEBUG2 (DATAGRAM, ("Found queued datagram %lx on %lx\n", Reserved->u.SR_DG.DatagramRequest, Reserved->u.SR_DG.AddressFile));
Reserved->u.SR_DG.Cache = CacheName; Reserved->u.SR_DG.CurrentNetwork = 0;
//
// CacheName was referenced above.
//
Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); if ( REQUEST_NDIS_BUFFER( Reserved->u.SR_DG.DatagramRequest )) { NdisChainBufferAtBack (Packet, REQUEST_NDIS_BUFFER(Reserved->u.SR_DG.DatagramRequest)); }
NbiTransmitDatagram (Reserved);
} else {
//
// Should we send it once as a broadcast
// on net 0, just in case??
//
AddressFile = Reserved->u.SR_DG.AddressFile; DatagramRequest = Reserved->u.SR_DG.DatagramRequest;
NB_DEBUG2 (DATAGRAM, ("Timing out datagram %lx on %lx\n", DatagramRequest, AddressFile));
//
// If the failure was due to a down wan line indicate
// that, otherwise return success (so the browser won't
// confuse this with a down wan line).
//
if (Result == NetbiosNameNotFoundWanDown) { REQUEST_STATUS(DatagramRequest) = STATUS_DEVICE_DOES_NOT_EXIST; } else { REQUEST_STATUS(DatagramRequest) = STATUS_BAD_NETWORK_PATH; } REQUEST_INFORMATION(DatagramRequest) = 0;
NbiCompleteRequest(DatagramRequest); NbiFreeRequest (Device, DatagramRequest);
NbiDereferenceAddressFile (AddressFile, AFREF_SEND_DGRAM);
ExInterlockedPushEntrySList( &Device->SendPacketList, &Reserved->PoolLinkage, &NbiGlobalPoolInterlock); }
}
for (p = AdapterStatusList.Flink; p != &AdapterStatusList; ) {
AdapterStatusRequest = LIST_ENTRY_TO_REQUEST(p); p = p->Flink;
if (Result == NetbiosNameFound) {
NB_DEBUG2 (QUERY, ("Found queued AdapterStatus %lx\n", AdapterStatusRequest));
//
// Continue with the AdapterStatus sequence. We put
// it in ActiveAdapterStatus, it will either get
// completed when a response is received or timed
// out by the long timeout.
//
REQUEST_STATUSPTR(AdapterStatusRequest) = (PVOID)CacheName;
//
// CacheName was referenced above.
//
REQUEST_INFORMATION (AdapterStatusRequest) = 0;
NB_INSERT_TAIL_LIST( &Device->ActiveAdapterStatus, REQUEST_LINKAGE (AdapterStatusRequest), &Device->Lock);
NbiSendStatusQuery (AdapterStatusRequest);
} else {
NB_DEBUG2 (QUERY, ("Timing out AdapterStatus %lx\n", AdapterStatusRequest));
REQUEST_STATUS(AdapterStatusRequest) = STATUS_IO_TIMEOUT;
NbiCompleteRequest(AdapterStatusRequest); NbiFreeRequest (Device, AdapterStatusRequest);
NbiDereferenceDevice (Device, DREF_STATUS_QUERY);
}
}
for (p = NetbiosFindNameList.Flink; p != &NetbiosFindNameList; ) {
NetbiosFindNameRequest = LIST_ENTRY_TO_REQUEST(p); p = p->Flink;
//
// In fact there is not much difference between success or
// failure, since in the successful case the information
// will already have been written to the buffer. Just
// complete the request with the appropriate status,
// which will already be stored in the request.
//
if (Result == NetbiosNameFound) {
if (CacheName->Unique) {
NB_DEBUG2 (QUERY, ("Found queued unique NetbiosFindName %lx\n", NetbiosFindNameRequest));
} else {
NB_DEBUG2 (QUERY, ("Found queued group NetbiosFindName %lx\n", NetbiosFindNameRequest));
}
} else {
CTEAssert (REQUEST_STATUS(NetbiosFindNameRequest) == STATUS_IO_TIMEOUT); NB_DEBUG2 (QUERY, ("Timed out NetbiosFindName %lx\n", NetbiosFindNameRequest));
}
//
// This sets REQUEST_INFORMATION(Request) to the correct value.
//
NbiSetNetbiosFindNameInformation (NetbiosFindNameRequest);
NbiCompleteRequest(NetbiosFindNameRequest); NbiFreeRequest (Device, NetbiosFindNameRequest);
NbiDereferenceDevice (Device, DREF_NB_FIND_NAME);
}
//
// We referenced this temporarily so we could use it in here,
// deref and check if we need to delete it.
//
if (Result == NetbiosNameFound) {
NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle1);
if (--CacheName->ReferenceCount == 0) {
NB_DEBUG2 (CACHE, ("Free newly allocated cache entry %lx\n", CacheName)); NbiFreeMemory( CacheName, sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), MEMORY_CACHE, "Free in CacheHandlePending");
}
NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle1);
}
} /* CacheHandlePending */
VOID NbiProcessNameRecognized( IN PIPX_LOCAL_TARGET RemoteAddress, IN ULONG MacOptions, IN PUCHAR PacketBuffer, IN UINT PacketSize )
/*++
Routine Description:
This routine handles NB_CMD_NAME_RECOGNIZED frames.
Arguments:
RemoteAddress - The local target this packet was received from.
MacOptions - The MAC options for the underlying NDIS binding.
LookaheadBuffer - The packet data, starting at the IPX header.
PacketSize - The total length of the packet, starting at the IPX header.
Return Value:
None.
--*/
{ PLIST_ENTRY p; PDEVICE Device = NbiDevice; PNETBIOS_CACHE NameCache; PREQUEST NetbiosFindNameRequest; PNB_SEND_RESERVED Reserved; TDI_ADDRESS_NETBIOS * RemoteNetbiosAddress; NB_CONNECTIONLESS UNALIGNED * Connectionless = (NB_CONNECTIONLESS UNALIGNED *)PacketBuffer; NB_DEFINE_LOCK_HANDLE(LockHandle)
#if 0
//
// We should handle responses from network 0
// differently -- if they are for a group name, we should
// keep them around but only until we get a non-zero
// response from the same card.
//
if (*(UNALIGNED ULONG *)(Connectionless->IpxHeader.SourceNetwork) == 0) { return; } #endif
//
// We need to scan our queue of pending find name packets
// to see if someone is waiting for this name.
//
NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle);
for (p = Device->WaitingFindNames.Flink; p != &Device->WaitingFindNames; p = p->Flink) {
Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage);
//
// Find names which have already found unique names are
// "dead", waiting for FindNameTimeout to remove them,
// and should be ignored when scanning the list.
//
if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) {
continue; }
if (RtlEqualMemory (Reserved->u.SR_FN.NetbiosName, Connectionless->NameFrame.Name, 16)) { break; } }
if (p == &Device->WaitingFindNames) { if ((FindInNetbiosCacheTable (Device->NameCache, Connectionless->NameFrame.Name, &NameCache ) == STATUS_SUCCESS) && (NameCache->NetworksUsed == 0)) { //
// Update our information about this network if needed.
//
NameCache->Unique = (BOOLEAN)((Connectionless->NameFrame.NameTypeFlag & NB_NAME_GROUP) == 0); if (RtlEqualMemory (Connectionless->NameFrame.Name, NetbiosBroadcastName, 16)) { NameCache->Unique = FALSE; }
RtlCopyMemory (&NameCache->FirstResponse, Connectionless->IpxHeader.SourceNetwork, 12); NameCache->NetworksUsed = 1; NameCache->Networks[0].Network = *(UNALIGNED ULONG*)(Connectionless->IpxHeader.SourceNetwork);
//
// If this packet was not routed to us and is for a group name,
// rather than use whatever local target it happened to come
// from we set it up so that it is broadcast on that net.
//
if ((RtlEqualMemory (RemoteAddress->MacAddress, Connectionless->IpxHeader.SourceNode, 6)) && (!NameCache->Unique)) { NameCache->Networks[0].LocalTarget.NicHandle = RemoteAddress->NicHandle; RtlCopyMemory (NameCache->Networks[0].LocalTarget.MacAddress, BroadcastAddress, 6); RtlCopyMemory (NameCache->FirstResponse.NodeAddress, BroadcastAddress, 6); } else { NameCache->Networks[0].LocalTarget = *RemoteAddress; } }
NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); return; }
//
// Scan for any netbios find name requests on the queue, and
// inform them about this remote. We need to do this on every
// response because group names need every computer recorded,
// but the normal cache only includes one entry per network.
//
for (p = Device->WaitingNetbiosFindName.Flink; p != &Device->WaitingNetbiosFindName; p = p->Flink) {
NetbiosFindNameRequest = LIST_ENTRY_TO_REQUEST(p);
RemoteNetbiosAddress = (TDI_ADDRESS_NETBIOS *)REQUEST_INFORMATION(NetbiosFindNameRequest);
if (!RtlEqualMemory( Connectionless->NameFrame.Name, RemoteNetbiosAddress->NetbiosName, 16)) { continue; }
//
// This will update the request status if needed.
//
NbiUpdateNetbiosFindName( NetbiosFindNameRequest, #if defined(_PNP_POWER)
&RemoteAddress->NicHandle, #else
RemoteAddress->NicId, #endif _PNP_POWER
(TDI_ADDRESS_IPX UNALIGNED *)Connectionless->IpxHeader.SourceNetwork, (BOOLEAN)((Connectionless->NameFrame.NameTypeFlag & NB_NAME_GROUP) == 0));
}
//
// See what is up with this pending find name packet.
//
if (Reserved->u.SR_FN.NewCache == NULL) { //
// This is the first response we have received, so we
// allocate the initial entry with room for a single
// entry.
//
NameCache = NbiAllocateMemory (sizeof(NETBIOS_CACHE), MEMORY_CACHE, "Cache Entry"); if (NameCache == NULL) { NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); return; }
NB_DEBUG2 (CACHE, ("Alloc new cache %lx for <%.16s>, net %lx\n", NameCache, Reserved->u.SR_FN.NetbiosName, *(UNALIGNED ULONG *)(Connectionless->IpxHeader.SourceNetwork)));
RtlCopyMemory (NameCache->NetbiosName, Connectionless->NameFrame.Name, 16); NameCache->Unique = (BOOLEAN)((Connectionless->NameFrame.NameTypeFlag & NB_NAME_GROUP) == 0); NameCache->ReferenceCount = 1; RtlCopyMemory (&NameCache->FirstResponse, Connectionless->IpxHeader.SourceNetwork, 12); NameCache->NetworksAllocated = 1; NameCache->NetworksUsed = 1; NameCache->Networks[0].Network = *(UNALIGNED ULONG *)(Connectionless->IpxHeader.SourceNetwork);
if (RtlEqualMemory (Connectionless->NameFrame.Name, NetbiosBroadcastName, 16)) {
NB_SET_SR_FN_STATUS (Reserved, FNStatusResponseGroup); NameCache->Unique = FALSE;
} else {
NB_SET_SR_FN_STATUS( Reserved, NameCache->Unique ? FNStatusResponseUnique : FNStatusResponseGroup);
}
Reserved->u.SR_FN.NewCache = NameCache;
//
// If this packet was not routed to us and is for a group name,
// rather than use whatever local target it happened to come
// from we set it up so that it is broadcast on that net.
//
if ((RtlEqualMemory (RemoteAddress->MacAddress, Connectionless->IpxHeader.SourceNode, 6)) && (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseGroup)) { #if defined(_PNP_POWER)
NameCache->Networks[0].LocalTarget.NicHandle = RemoteAddress->NicHandle; #else
NameCache->Networks[0].LocalTarget.NicId = RemoteAddress->NicId; #endif _PNP_POWER
RtlCopyMemory (NameCache->Networks[0].LocalTarget.MacAddress, BroadcastAddress, 6); RtlCopyMemory (NameCache->FirstResponse.NodeAddress, BroadcastAddress, 6); } else { NameCache->Networks[0].LocalTarget = *RemoteAddress; }
if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) {
//
// Complete pending requests now, since it is a unique
// name we have all the information we will get.
//
NameCache->TimeStamp = Device->CacheTimeStamp;
InsertInNetbiosCacheTable( Device->NameCache, NameCache);
//
// Reference it since CacheHandlePending uses it
// with the lock released. CacheHandlePending
// will dereference it.
//
++NameCache->ReferenceCount;
//
// This call releases the lock.
//
CacheHandlePending( Device, Reserved->u.SR_FN.NetbiosName, NetbiosNameFound, NameCache NB_LOCK_HANDLE_ARG(LockHandle));
} else {
NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle);
}
} else {
//
// We already have a response to this frame.
//
if (NB_GET_SR_FN_STATUS(Reserved) == FNStatusResponseUnique) {
//
// Should we check that the response is also
// unique? Not much to do since I don't know of an
// equivalent to the netbeui NAME_IN_CONFLICT.
//
} else {
//
// This is a group name.
//
if (Connectionless->NameFrame.NameTypeFlag & NB_NAME_GROUP) {
//
// Update our information about this network if needed.
// This may free the existing cache and allocate a new one.
//
Reserved->u.SR_FN.NewCache = CacheUpdateNameCache( Reserved->u.SR_FN.NewCache, RemoteAddress, (TDI_ADDRESS_IPX UNALIGNED *) Connectionless->IpxHeader.SourceNetwork, FALSE);
} else {
//
// Hmmm... This respondent thinks it is a unique name
// but we think it is group, should we do something?
//
} }
NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle);
}
} /* NbiProcessNameRecognized */
PNETBIOS_CACHE CacheUpdateNameCache( IN PNETBIOS_CACHE NameCache, IN PIPX_LOCAL_TARGET RemoteAddress, IN TDI_ADDRESS_IPX UNALIGNED * SourceAddress, IN BOOLEAN ModifyQueue )
/*++
Routine Description:
This routine is called to update a netbios cache entry with a new network, if it is does not already contain information about the network. It is called when a frame is received advertising the appropriate cache entry, which is either a group name or the broadcast name.
THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS WITH IT HELD.
Arguments:
NameCache - The name cache entry to update.
RemoteAddress - The remote address on which a frame was received.
IpxAddress - The source IPX address of the frame.
ModifyQueue - TRUE if we should update the queue which this cache entry is in, if we reallocate it.
Return Value:
The netbios cache entry, either the original or a reallocated one.
--*/
{
PDEVICE Device = NbiDevice; USHORT NewNetworks; PNETBIOS_CACHE NewNameCache; PLIST_ENTRY OldPrevious; UINT i;
//
// See if we already know about this network.
//
for (i = 0; i < NameCache->NetworksUsed; i++) { if (NameCache->Networks[i].Network == SourceAddress->NetworkAddress) { return NameCache; } }
//
// We need to add information about this network
// to the name cache entry. If we have to allocate
// a new one we do that.
//
NB_DEBUG2 (CACHE, ("Got new net %lx for <%.16s>\n", SourceAddress->NetworkAddress, NameCache->NetbiosName));
if (NameCache->NetworksUsed == NameCache->NetworksAllocated) {
//
// We double the number of entries allocated until
// we hit 16, then add 8 at a time.
//
if (NameCache->NetworksAllocated < 16) { NewNetworks = NameCache->NetworksAllocated * 2; } else { NewNetworks = NameCache->NetworksAllocated + 8; }
NewNameCache = NbiAllocateMemory( sizeof(NETBIOS_CACHE) + ((NewNetworks-1) * sizeof(NETBIOS_NETWORK)), MEMORY_CACHE, "Enlarge cache entry");
if (NewNameCache == NULL) { return NameCache; }
NB_DEBUG2 (CACHE, ("Expand cache %lx to %lx for <%.16s>\n", NameCache, NewNameCache, NameCache->NetbiosName));
//
// Copy the new current data to the new one.
//
RtlCopyMemory( NewNameCache, NameCache, sizeof(NETBIOS_CACHE) + ((NameCache->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)));
NewNameCache->NetworksAllocated = NewNetworks; NewNameCache->ReferenceCount = 1;
if (ModifyQueue) {
//
// Insert at the same place as the old one. The time
// stamp is the same as the old one.
//
ReinsertInNetbiosCacheTable( Device->NameCache, NameCache, NewNameCache );
}
if (--NameCache->ReferenceCount == 0) {
NB_DEBUG2 (CACHE, ("Free replaced cache entry %lx\n", NameCache)); NbiFreeMemory( NameCache, sizeof(NETBIOS_CACHE) + ((NameCache->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), MEMORY_CACHE, "Enlarge existing");
}
NameCache = NewNameCache;
}
NameCache->Networks[NameCache->NetworksUsed].Network = SourceAddress->NetworkAddress;
//
// If this packet was not routed to us, then store the local
// target for a correct broadcast.
//
if (RtlEqualMemory (RemoteAddress->MacAddress, SourceAddress->NodeAddress, 6)) { #if defined(_PNP_POWER)
NameCache->Networks[NameCache->NetworksUsed].LocalTarget.NicHandle = RemoteAddress->NicHandle; #else
NameCache->Networks[NameCache->NetworksUsed].LocalTarget.NicId = RemoteAddress->NicId; #endif _PNP_POWER
RtlCopyMemory (NameCache->Networks[NameCache->NetworksUsed].LocalTarget.MacAddress, BroadcastAddress, 6); } else { NameCache->Networks[NameCache->NetworksUsed].LocalTarget = *RemoteAddress; }
++NameCache->NetworksUsed; return NameCache;
} /* CacheUpdateNameCache */
VOID CacheUpdateFromAddName( IN PIPX_LOCAL_TARGET RemoteAddress, IN NB_CONNECTIONLESS UNALIGNED * Connectionless, IN BOOLEAN LocalFrame )
/*++
Routine Description:
This routine is called when an add name frame is received. If it is for a group name it checks if our cache entry for that group name needs to be updated to include a new network; for all frames it checks if our broadcast cache entry needs to be updated to include a new network.
Arguments:
RemoteAddress - The address the frame was received from.
Connectionless - The header of the received add name.
LocalFrame - TRUE if the frame was sent locally.
Return Value:
None.
--*/
{ PUCHAR NetbiosName; PNETBIOS_CACHE NameCache; PLIST_ENTRY p; PDEVICE Device = NbiDevice; NB_DEFINE_LOCK_HANDLE (LockHandle)
NetbiosName = (PUCHAR)Connectionless->NameFrame.Name;
//
// First look up the broadcast name.
//
NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle);
if (!LocalFrame) {
if ( FindInNetbiosCacheTable( Device->NameCache, NetbiosBroadcastName, &NameCache ) == STATUS_SUCCESS ) { //
// This will reallocate a cache entry and update the
// queue if necessary.
//
(VOID)CacheUpdateNameCache( NameCache, RemoteAddress, (TDI_ADDRESS_IPX UNALIGNED *)(Connectionless->IpxHeader.SourceNetwork), TRUE); }
}
//
// Now see if our database needs to be updated based on this.
//
if ( FindInNetbiosCacheTable( Device->NameCache, Connectionless->NameFrame.Name, &NameCache ) == STATUS_SUCCESS ) {
if (!NameCache->Unique) {
if (!LocalFrame) {
//
// This will reallocate a cache entry and update the
// queue if necessary.
//
(VOID)CacheUpdateNameCache( NameCache, RemoteAddress, (TDI_ADDRESS_IPX UNALIGNED *)(Connectionless->IpxHeader.SourceNetwork), TRUE);
}
} else {
//
// To be safe, delete any unique names we get add
// names for (we will requery next time we need it).
//
RemoveFromNetbiosCacheTable ( Device->NameCache, NameCache );
if (--NameCache->ReferenceCount == 0) {
NB_DEBUG2 (CACHE, ("Free add named cache entry %lx\n", NameCache)); NbiFreeMemory( NameCache, sizeof(NETBIOS_CACHE) + ((NameCache->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), MEMORY_CACHE, "Enlarge existing");
}
}
}
NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle);
} /* CacheUpdateFromAddName */
VOID NbiProcessDeleteName( IN PIPX_LOCAL_TARGET RemoteAddress, IN ULONG MacOptions, IN PUCHAR PacketBuffer, IN UINT PacketSize )
/*++
Routine Description:
This routine handles NB_CMD_DELETE_NAME frames.
Arguments:
RemoteAddress - The local target this packet was received from.
MacOptions - The MAC options for the underlying NDIS binding.
LookaheadBuffer - The packet data, starting at the IPX header.
PacketSize - The total length of the packet, starting at the IPX header.
Return Value:
None.
--*/
{ NB_CONNECTIONLESS UNALIGNED * Connectionless = (NB_CONNECTIONLESS UNALIGNED *)PacketBuffer; PUCHAR NetbiosName; PNETBIOS_CACHE CacheName; PDEVICE Device = NbiDevice; NB_DEFINE_LOCK_HANDLE (LockHandle)
if (PacketSize != sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME)) { return; }
//
// We want to update our netbios cache to reflect the
// fact that this name is no longer valid.
//
NetbiosName = (PUCHAR)Connectionless->NameFrame.Name;
NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle);
if ( FindInNetbiosCacheTable( Device->NameCache, NetbiosName, &CacheName ) == STATUS_SUCCESS ) {
//
// We don't track group names since we don't know if
// this is the last person that owns it. We also drop
// the frame if does not come from the person we think
// owns this name.
//
if ((!CacheName->Unique) || (CacheName->NetworksUsed == 0) || (!RtlEqualMemory (&CacheName->FirstResponse, Connectionless->IpxHeader.SourceNetwork, 12))) { NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); return; }
NB_DEBUG2 (CACHE, ("Found cache name to delete <%.16s>\n", NetbiosName));
}else { NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); return; }
//
// We have a cache entry, take it out of the list. If no
// one else is using it, delete it; if not, they will delete
// it when they are done.
//
RemoveFromNetbiosCacheTable ( Device->NameCache, CacheName);
if (--CacheName->ReferenceCount == 0) {
NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle);
NB_DEBUG2 (CACHE, ("Free delete name cache entry %lx\n", CacheName)); NbiFreeMemory( CacheName, sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), MEMORY_CACHE, "Name deleted");
} else {
NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle);
}
} /* NbiProcessDeleteName */
VOID InsertInNetbiosCacheTable( IN PNETBIOS_CACHE_TABLE CacheTable, IN PNETBIOS_CACHE CacheEntry )
/*++
Routine Description:
This routine inserts a new cache entry in the hash table
THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS WITH THE LOCK HELD.
Arguments:
CacheTable - The pointer of the Hash Table.
CacheEntry - Entry to be inserted.
Return Value:
None
--*/
{ USHORT HashIndex;
//
// Keep a threshold of how many entries do we keep in the table.
// If it crosses the threshold, just remove the oldest entry
//
if ( CacheTable->CurrentEntries >= CacheTable->MaxHashIndex * NB_MAX_AVG_CACHE_ENTRIES_PER_BUCKET ) { PNETBIOS_CACHE OldestCacheEntry = NULL; PNETBIOS_CACHE NextEntry; PLIST_ENTRY p;
for ( HashIndex = 0; HashIndex < CacheTable->MaxHashIndex; HashIndex++) { if ( (p = CacheTable->Bucket[ HashIndex ].Blink ) != &CacheTable->Bucket[ HashIndex ] ) { NextEntry = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage);
if ( OldestCacheEntry ) { if ( NextEntry->TimeStamp < OldestCacheEntry->TimeStamp ) { OldestCacheEntry = NextEntry; } } else { OldestCacheEntry = NextEntry; } } }
CTEAssert( OldestCacheEntry );
NB_DEBUG2 (CACHE, ("Threshold exceeded, removing oldest cache entry %lx\n", OldestCacheEntry)); RemoveEntryList (&OldestCacheEntry->Linkage); CacheTable->CurrentEntries--;
if (--OldestCacheEntry->ReferenceCount == 0) {
NB_DEBUG2 (CACHE, ("Freed cache entry %lx\n", OldestCacheEntry));
NbiFreeMemory( OldestCacheEntry, sizeof(NETBIOS_CACHE) + ((OldestCacheEntry->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), MEMORY_CACHE, "Aged out");
}
} HashIndex = ( ( CacheEntry->NetbiosName[0] & 0x0f ) << 4 ) + ( CacheEntry->NetbiosName[1] & 0x0f ); HashIndex = HashIndex % CacheTable->MaxHashIndex;
InsertHeadList( &CacheTable->Bucket[HashIndex], &CacheEntry->Linkage ); CacheTable->CurrentEntries++; } /* InsertInNetbiosCacheTable */
__inline VOID ReinsertInNetbiosCacheTable( IN PNETBIOS_CACHE_TABLE CacheTable, IN PNETBIOS_CACHE OldEntry, IN PNETBIOS_CACHE NewEntry )
/*++
Routine Description:
This routine inserts a new cache entry at the same place where the old entry was.
THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS WITH THE LOCK HELD.
Arguments:
CacheTable - The pointer of the Hash Table.
CacheEntry - Entry to be inserted.
Return Value:
None
--*/
{ PLIST_ENTRY OldPrevious;
OldPrevious = OldEntry->Linkage.Blink; RemoveEntryList (&OldEntry->Linkage); InsertHeadList (OldPrevious, &NewEntry->Linkage); } /* ReinsertInNetbiosCacheTable */
__inline VOID RemoveFromNetbiosCacheTable( IN PNETBIOS_CACHE_TABLE CacheTable, IN PNETBIOS_CACHE CacheEntry )
/*++
Routine Description:
This routine removes an entry from the cache table.
Arguments:
CacheTable - The pointer of the Hash Table.
CacheEntry - Entry to be removed.
THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS WITH THE LOCK HELD.
Return Value:
None. --*/
{ RemoveEntryList( &CacheEntry->Linkage ); CacheTable->CurrentEntries--; } /* RemoveFromNetbiosCacheTable */
VOID FlushOldFromNetbiosCacheTable( IN PNETBIOS_CACHE_TABLE CacheTable, IN USHORT AgeLimit )
/*++
Routine Description:
This routine removes all the old entries from the hash table.
Arguments:
CacheTable - The pointer of the Hash Table.
AgeLimit - All the entries older than AgeLimit will be removed.
THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS WITH THE LOCK HELD.
Return Value:
None. --*/
{ USHORT HashIndex; PLIST_ENTRY p; PNETBIOS_CACHE CacheName;
//
// run the hash table looking for old entries. Since new entries
// are stored at the head and all entries are time stamped when
// they are inserted, we scan backwards and stop once we find
// an entry which does not need to be aged.
// we repeat this for each bucket.
for ( HashIndex = 0; HashIndex < CacheTable->MaxHashIndex; HashIndex++) { for (p = CacheTable->Bucket[ HashIndex ].Blink; p != &CacheTable->Bucket[ HashIndex ]; ) {
CacheName = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage); p = p->Blink;
//
// see if any entries have been around for more than agelimit
//
if ((USHORT)(NbiDevice->CacheTimeStamp - CacheName->TimeStamp) >= AgeLimit ) {
RemoveEntryList (&CacheName->Linkage); CacheTable->CurrentEntries--;
if (--CacheName->ReferenceCount == 0) {
NB_DEBUG2 (CACHE, ("Aging out name cache entry %lx\n", CacheName));
NbiFreeMemory( CacheName, sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), MEMORY_CACHE, "Aged out");
}
} else {
break;
} } // for loop
} // for loop
} /* FlushOldFromNetbiosCacheTable */
VOID FlushFailedNetbiosCacheEntries( IN PNETBIOS_CACHE_TABLE CacheTable )
/*++
Routine Description:
This routine removes all the failed entries from the hash table.
Arguments:
CacheTable - The pointer of the Hash Table.
THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS WITH THE LOCK HELD.
Return Value:
None. --*/
{ USHORT HashIndex; PLIST_ENTRY p; PNETBIOS_CACHE CacheName;
if (NULL == CacheTable) { return; }
//
// run the hash table looking for old entries. Since new entries
// are stored at the head and all entries are time stamped when
// they are inserted, we scan backwards and stop once we find
// an entry which does not need to be aged.
// we repeat this for each bucket.
for ( HashIndex = 0; HashIndex < CacheTable->MaxHashIndex; HashIndex++) { for (p = CacheTable->Bucket[ HashIndex ].Blink; p != &CacheTable->Bucket[ HashIndex ]; ) {
CacheName = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage); p = p->Blink;
//
// flush all the failed cache entries.
// We do this when a new adapter appears, and there's a possiblity that
// the failed entries might succeed now on the new adapter.
//
if (CacheName->NetworksUsed == 0) { RemoveEntryList (&CacheName->Linkage); CacheTable->CurrentEntries--; CTEAssert( CacheName->ReferenceCount == 1 ); CTEAssert( CacheName->NetworksAllocated == 1 );
NB_DEBUG2 (CACHE, ("Flushing out failed name cache entry %lx\n", CacheName));
NbiFreeMemory( CacheName, sizeof(NETBIOS_CACHE), MEMORY_CACHE, "Aged out");
} } // for loop
} // for loop
} /* FlushFailedNetbiosCacheEntries */
VOID RemoveInvalidRoutesFromNetbiosCacheTable( IN PNETBIOS_CACHE_TABLE CacheTable, IN NIC_HANDLE UNALIGNED *InvalidNicHandle )
/*++
Routine Description:
This routine removes all invalid route entries from the hash table. Routes become invalid when the binding is deleted in Ipx due to PnP event.
Arguments:
CacheTable - The pointer of the Hash Table.
InvalidRouteNicId - NicId of the invalid routes.
THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS WITH THE LOCK HELD.
Return Value:
None. --*/
{ PLIST_ENTRY p; PNETBIOS_CACHE CacheName; USHORT i,j,NetworksRemoved; USHORT HashIndex; PDEVICE Device = NbiDevice;
//
// Flush all the cache entries that are using this NicId in the local
// target.
//
for ( HashIndex = 0; HashIndex < Device->NameCache->MaxHashIndex; HashIndex++) { for (p = Device->NameCache->Bucket[ HashIndex ].Flink; p != &Device->NameCache->Bucket[ HashIndex ]; ) {
CacheName = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage); p = p->Flink;
//
// Remove each of those routes which is using this NicId.
// if no routes left, then flush the cache entry also.
// ( unique names have only one route anyways )
//
for ( i = 0, NetworksRemoved = 0; i < CacheName->NetworksUsed; i++ ) { if ( CacheName->Networks[i].LocalTarget.NicHandle.NicId == InvalidNicHandle->NicId ) { CTEAssert( RtlEqualMemory( &CacheName->Networks[i].LocalTarget.NicHandle, InvalidNicHandle, sizeof(NIC_HANDLE))); for ( j = i+1; j < CacheName->NetworksUsed; j++ ) { CacheName->Networks[j-1] = CacheName->Networks[j]; } NetworksRemoved++; } else if ( CacheName->Networks[i].LocalTarget.NicHandle.NicId > InvalidNicHandle->NicId ) { CacheName->Networks[i].LocalTarget.NicHandle.NicId--; } } CTEAssert( NetworksRemoved <= CacheName->NetworksUsed ); if ( ! ( CacheName->NetworksUsed -= NetworksRemoved ) ) { RemoveEntryList (&CacheName->Linkage); CacheTable->CurrentEntries--;
NB_DEBUG2 (CACHE, ("Removed cache entry %lx bcoz route(NicId %d) deleted\n", CacheName, InvalidNicHandle->NicId )); if (--CacheName->ReferenceCount == 0) {
NB_DEBUG2 (CACHE, ("Freed name cache entry %lx\n", CacheName));
NbiFreeMemory( CacheName, sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), MEMORY_CACHE, "Aged out");
} } } // for loop
} // for loop
} /* RemoveInvalidRoutesFromNetbiosCacheTable */
NTSTATUS FindInNetbiosCacheTable( IN PNETBIOS_CACHE_TABLE CacheTable, IN PUCHAR NameToBeFound, OUT PNETBIOS_CACHE *CacheEntry )
/*++
Routine Description:
This routine finds a netbios name in the Hash Table and returns the corresponding cache entry.
THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS WITH THE LOCK HELD.
Arguments:
CacheTable - The pointer of the Hash Table.
CacheEntry - Pointer to the netbios cache entry if found.
Return Value:
STATUS_SUCCESS - if successful.
STATUS_UNSUCCESSFUL - otherwise.
--*/
{ USHORT HashIndex; PLIST_ENTRY p; PNETBIOS_CACHE FoundCacheName;
HashIndex = ( ( NameToBeFound[0] & 0x0f ) << 4 ) + ( NameToBeFound[1] & 0x0f ); HashIndex = HashIndex % CacheTable->MaxHashIndex;
for (p = ( CacheTable->Bucket[ HashIndex ] ).Flink; p != &CacheTable->Bucket[ HashIndex ]; p = p->Flink) {
FoundCacheName = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage);
//
// See if this entry is for the same name we are looking for.
if ( RtlEqualMemory (FoundCacheName->NetbiosName, NameToBeFound, 16) ) { *CacheEntry = FoundCacheName; return STATUS_SUCCESS; } }
return STATUS_UNSUCCESSFUL; } /* FindInNetbiosCacheTable */
NTSTATUS CreateNetbiosCacheTable( IN OUT PNETBIOS_CACHE_TABLE *NewTable, IN USHORT MaxHashIndex )
/*++
Routine Description:
This routine creates a new hash table for netbios cache and initializes it.
THIS ROUTINE IS CALLED WITH THE DEVICE LOCK HELD AND RETURNS WITH THE LOCK HELD.
Arguments:
NewTable - The pointer of the table to be created.
MaxHashIndex - Number of buckets in the hash table.
Return Value:
STATUS_SUCCESS - if successful.
STATUS_INSUFFICIENT_RESOURCES - If cannot allocate memory.
--*/
{ USHORT i;
*NewTable = NbiAllocateMemory (sizeof(NETBIOS_CACHE_TABLE) + sizeof(LIST_ENTRY) * ( MaxHashIndex - 1) , MEMORY_CACHE, "Cache Table");
if ( *NewTable ) { for ( i = 0; i < MaxHashIndex; i++ ) { InitializeListHead(& (*NewTable)->Bucket[i] ); }
(*NewTable)->MaxHashIndex = MaxHashIndex; (*NewTable)->CurrentEntries = 0; return STATUS_SUCCESS; } else { NB_DEBUG( CACHE, ("Cannot create Netbios Cache Table\n") ); return STATUS_INSUFFICIENT_RESOURCES; }
} /* CreateNetbiosCacheTable */
VOID DestroyNetbiosCacheTable( IN PNETBIOS_CACHE_TABLE CacheTable )
/*++
Routine Description:
This routine removes all entries from the hash table. and free up the hash table.
Arguments:
CacheTable - The pointer of the Hash Table.
Return Value:
None. --*/
{ USHORT HashIndex; PLIST_ENTRY p; PNETBIOS_CACHE CacheName;
for ( HashIndex = 0; HashIndex < CacheTable->MaxHashIndex; HashIndex++) { while (!IsListEmpty ( &( CacheTable->Bucket[ HashIndex ] ) ) ) {
p = RemoveHeadList ( &( CacheTable->Bucket[ HashIndex ] )); CacheTable->CurrentEntries--; CacheName = CONTAINING_RECORD (p, NETBIOS_CACHE, Linkage);
NB_DEBUG2 (CACHE, ("Free cache entry %lx\n", CacheName));
NbiFreeMemory( CacheName, sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)), MEMORY_CACHE, "Free entries");
} } // for loop
CTEAssert( CacheTable->CurrentEntries == 0 );
NbiFreeMemory (CacheTable, sizeof(NETBIOS_CACHE_TABLE) + sizeof(LIST_ENTRY) * ( CacheTable->MaxHashIndex - 1) , MEMORY_CACHE, "Free Cache Table");
} /* DestroyNetbiosCacheTable */
|