diff options
Diffstat (limited to '')
-rw-r--r-- | private/ntos/tdi/isn/fwd/tables.c | 1512 |
1 files changed, 1512 insertions, 0 deletions
diff --git a/private/ntos/tdi/isn/fwd/tables.c b/private/ntos/tdi/isn/fwd/tables.c new file mode 100644 index 000000000..baec3cced --- /dev/null +++ b/private/ntos/tdi/isn/fwd/tables.c @@ -0,0 +1,1512 @@ + +#include "precomp.h" + +// Memory zone for interfaces +ZONE_HEADER InterfaceZone; +// Segment size in interface sone +ULONG InterfaceSegmentSize= + sizeof(INTERFACE_CB)*NUM_INTERFACES_PER_SEGMENT + +sizeof (ZONE_SEGMENT_HEADER); +KSPIN_LOCK InterfaceZoneLock; + +// Interface tables +LIST_ENTRY *InterfaceIndexHash; // Hash by interface index +PINTERFACE_CB *ClientNodeHash; // Hash by node on qlobal net +INTERFACE_CB TheInternalInterface; // The internal interface +PINTERFACE_CB InternalInterface=&TheInternalInterface; +KSPIN_LOCK InterfaceTableLock; // Protection for interface hash tables + +// Memory Zone for routes +ZONE_HEADER RouteZone; +// Segment size in route sone +ULONG RouteSegmentSize=DEF_ROUTE_SEGMENT_SIZE; +KSPIN_LOCK RouteZoneLock; + +// Route tables +PFWD_ROUTE *RouteHash; +PFWD_ROUTE GlobalRoute; +ULONG GlobalNetwork; + + +// NB Route table +PNB_ROUTE *NBRouteHash; + + +// Reader-writer lock to wait for all readers to drain when +// updating the route tables +RW_LOCK RWLock; +// Mutex to serialize writers to route tables +FAST_MUTEX WriterMutex; + + +// Sizes of the tables +ULONG RouteHashSize; // Must be specified +ULONG InterfaceHashSize=DEF_INTERFACE_HASH_SIZE; +ULONG ClientHashSize=DEF_CLIENT_HASH_SIZE; +ULONG NBRouteHashSize=DEF_NB_ROUTE_HASH_SIZE; + +//*** max send pkts queued limit: over this limit the send pkts get discarded +ULONG MaxSendPktsQueued = MAX_SEND_PKTS_QUEUED; +INT WanPacketListId = -1; + +// Initial memory block allocated for the tables +CHAR *TableBlock = NULL; + +// Hash functions +#define InterfaceIndexHashFunc(Interface) (Interface%InterfaceHashSize) +#define ClientNodeHashFunc(Node64) ((UINT)(Node64%ClientHashSize)) +#define NetworkNumberHashFunc(Network) (Network%RouteHashSize) +#define NetbiosNameHashFunc(Name128) ((UINT)(Name128[0]+Name128[1])%NBRouteHashSize) + +/*++ +******************************************************************* + A l l o c a t e R o u t e + +Routine Description: + Allocates memory for route from memory zone reserved + for route storage. Extends zone if there are no + free blocks in currently allocated segements. +Arguments: + None +Return Value: + Pointer to allocated route + +******************************************************************* +--*/ +PFWD_ROUTE +AllocateRoute ( + void + ) { + PFWD_ROUTE fwRoute; + KIRQL oldIRQL; + + KeAcquireSpinLock (&RouteZoneLock, &oldIRQL); + // Check if there are free blocks in the zone + if (ExIsFullZone (&RouteZone)) { + // Try to allocate new segment if not + NTSTATUS status; + PVOID segment = ExAllocatePoolWithTag + (NonPagedPool, RouteSegmentSize, FWD_POOL_TAG); + if (segment==NULL) { + KeReleaseSpinLock (&RouteZoneLock, oldIRQL); + IpxFwdDbgPrint (DBG_ROUTE_TABLE, DBG_ERROR, + ("IpxFwd: Can't allocate route zone segment.\n")); + return NULL; + } + status = ExExtendZone (&RouteZone, segment, RouteSegmentSize); + ASSERTMSG ("Could not extend RouteZone ", NT_SUCCESS (status)); + } + fwRoute = (PFWD_ROUTE)ExAllocateFromZone (&RouteZone); + KeReleaseSpinLock (&RouteZoneLock, oldIRQL); + return fwRoute; +} + +/*++ +******************************************************************* + F r e e R o u t e + +Routine Description: + Releases memory allocated for route to route memory + zone. +Arguments: + fwRoute - route block to release +Return Value: + None +******************************************************************* +--*/ +VOID +FreeRoute ( + PFWD_ROUTE fwRoute + ) { + IpxFwdDbgPrint (DBG_ROUTE_TABLE, DBG_INFORMATION, + ("IpxFwd: Freeing route block %08lx.\n", fwRoute)); + ASSERT (fwRoute->FR_InterfaceReference==NULL); + ExInterlockedFreeToZone(&RouteZone,fwRoute,&RouteZoneLock); +} + + +/*++ +******************************************************************* + A l l o c a t e I n t e r f a c e + +Routine Description: + Allocates memory for interface from memory zone reserved + for interface storage. Extends zone if there are no + free blocks in currently allocated segements. +Arguments: + None +Return Value: + Pointer to allocated route + +******************************************************************* +--*/ +PINTERFACE_CB +AllocateInterface ( + void + ) { + PINTERFACE_CB ifCB; + KIRQL oldIRQL; + + KeAcquireSpinLock (&RouteZoneLock, &oldIRQL); + // Check if there are free blocks in the zone + if (ExIsFullZone (&InterfaceZone)) { + // Try to allocate new segment if not + NTSTATUS status; + PVOID segment = ExAllocatePoolWithTag + (NonPagedPool, InterfaceSegmentSize, FWD_POOL_TAG); + if (segment==NULL) { + KeReleaseSpinLock (&RouteZoneLock, oldIRQL); + IpxFwdDbgPrint (DBG_INTF_TABLE, DBG_ERROR, + ("IpxFwd: Can't allocate interface zone segment.\n")); + return NULL; + } + status = ExExtendZone (&InterfaceZone, segment, InterfaceSegmentSize); + ASSERTMSG ("Could not extend InterfaceZone ", NT_SUCCESS (status)); + } + ifCB = (PINTERFACE_CB)ExAllocateFromZone (&InterfaceZone); + KeReleaseSpinLock (&RouteZoneLock, oldIRQL); + return ifCB; +} + +/*++ +******************************************************************* + F r e e I n t e r f a c e + +Routine Description: + Releases memory allocated for interface to interface memory + zone. +Arguments: + fwRoute - route block to release +Return Value: + None +******************************************************************* +--*/ +VOID +FreeInterface ( + PINTERFACE_CB ifCB + ) { + KIRQL oldIRQL; + + IpxFwdDbgPrint (DBG_INTF_TABLE, DBG_WARNING, + ("IpxFwd: Freeing icb %08lx.\n", ifCB)); + + ASSERT(ifCB->ICB_Stats.OperationalState==FWD_OPER_STATE_DOWN); + KeAcquireSpinLock (&InterfaceZoneLock, &oldIRQL); + ExFreeToZone(&InterfaceZone, ifCB); + KeReleaseSpinLock (&InterfaceZoneLock, oldIRQL); +} + +/*++ +******************************************************************* + C r e a t e T a b l e s + +Routine Description: + Allocates and intializes all hash tables and related structures +Arguments: + None +Return Value: + STATUS_SUCCESS - tables were created ok + STATUS_INSUFFICIENT_RESOURCES - resource allocation failed +******************************************************************* +--*/ +NTSTATUS +CreateTables ( + void + ) { + UINT i; + CHAR *segment; + NTSTATUS status; + ULONG blockSize; + + ASSERT (TableBlock==NULL); + + blockSize = ROUND_TO_PAGES ( + InterfaceHashSize*sizeof(*InterfaceIndexHash) + +ClientHashSize*sizeof(*ClientNodeHash) + +RouteHashSize*sizeof(*RouteHash) + +NBRouteHashSize*sizeof(*NBRouteHash) + +InterfaceSegmentSize + +RouteSegmentSize + ); + + // Allocate first segment for route zone + TableBlock = segment = (CHAR *)ExAllocatePoolWithTag ( + NonPagedPool, blockSize, FWD_POOL_TAG); + if (segment!=NULL) { + InterfaceIndexHash = (LIST_ENTRY *)segment; + segment = (CHAR *)ALIGN_UP((ULONG)(InterfaceIndexHash+InterfaceHashSize),ULONGLONG); + + ClientNodeHash = (PINTERFACE_CB *)segment; + segment = (CHAR *)ALIGN_UP((ULONG)(ClientNodeHash+ClientHashSize),ULONGLONG); + + RouteHash = (PFWD_ROUTE *)segment; + segment = (CHAR *)ALIGN_UP((ULONG)(RouteHash + RouteHashSize),ULONGLONG); + + NBRouteHash = (PNB_ROUTE *)segment; + segment = (CHAR *)ALIGN_UP((ULONG)(NBRouteHash + NBRouteHashSize),ULONGLONG); + + status = ExInitializeZone (&InterfaceZone, + ALIGN_UP(sizeof (INTERFACE_CB),ULONGLONG), + segment, + InterfaceSegmentSize); + ASSERTMSG ("Could not initalize InterfaceZone ", + NT_SUCCESS (status)); + segment = (CHAR *)ALIGN_UP((ULONG)(segment+InterfaceSegmentSize),ULONGLONG); + + status = ExInitializeZone (&RouteZone, + ALIGN_UP(sizeof (FWD_ROUTE), ULONGLONG), + segment, + blockSize - (segment - TableBlock)); + + ASSERTMSG ("Could not initalize RouteZone ", NT_SUCCESS (status)); + + + // No global route yet + GlobalRoute = NULL; + GlobalNetwork = 0xFFFFFFFF; + + InternalInterface = &TheInternalInterface; + InitICB (InternalInterface, + FWD_INTERNAL_INTERFACE_INDEX, + FWD_IF_PERMANENT, + TRUE, + FWD_NB_DELIVER_ALL); +#if DBG + InitializeListHead (&InternalInterface->ICB_InSendQueue); +#endif + + KeInitializeSpinLock (&InterfaceTableLock); + KeInitializeSpinLock (&InterfaceZoneLock); + KeInitializeSpinLock (&RouteZoneLock); + InitializeRWLock (&RWLock); + ExInitializeFastMutex (&WriterMutex); + + // Initialize hash tables buckets + for (i=0; i<InterfaceHashSize; i++) + InitializeListHead (&InterfaceIndexHash[i]); + + for (i=0; i<ClientHashSize; i++) { + ClientNodeHash[i] = NULL; + } + + for (i=0; i<RouteHashSize; i++) { + RouteHash[i] = NULL; + } + + for (i=0; i<NBRouteHashSize; i++) { + NBRouteHash[i] = NULL; + } + return STATUS_SUCCESS; + } + else { + IpxFwdDbgPrint (DBG_INTF_TABLE, DBG_ERROR, + ("IpxFwd: Could not allocate table block!\n")); + } + + return STATUS_INSUFFICIENT_RESOURCES; +} + +/*++ +******************************************************************* + D e l e t e T a b l e s + +Routine Description: + Releases resources allocated for all hash tables +Arguments: + None +Return Value: + STATUS_SUCCESS - tables were freed ok +******************************************************************* +--*/ +NTSTATUS +DeleteTables ( + void + ) { + UINT i; + PVOID segment; + + + if (TableBlock==NULL) { + IpxFwdDbgPrint (DBG_INTF_TABLE, DBG_ERROR, ("Tables already deleted.\n")); + return STATUS_SUCCESS; + } + // First get rid of all routes + // (that should release all references to interface + // control blocks + for (i=0; i<RouteHashSize; i++) { + while (RouteHash[i]!=NULL) { + PFWD_ROUTE fwRoute = RouteHash[i]; + RouteHash[i] = fwRoute->FR_Next; + if (fwRoute->FR_InterfaceReference!=GLOBAL_INTERFACE_REFERENCE) { + ReleaseInterfaceReference (fwRoute->FR_InterfaceReference); + } + fwRoute->FR_InterfaceReference = NULL; + ReleaseRouteReference (fwRoute); + } + } + // Don't forget about global route + if (GlobalRoute!=NULL) { + GlobalRoute->FR_InterfaceReference = NULL; + ReleaseRouteReference (GlobalRoute); + GlobalRoute = NULL; + GlobalNetwork = 0xFFFFFFFF; + } + + // Now we should be able to release all interfaces + for (i=0; i<InterfaceHashSize; i++) { + while (!IsListEmpty (&InterfaceIndexHash[i])) { + PINTERFACE_CB ifCB = CONTAINING_RECORD (InterfaceIndexHash[i].Flink, + INTERFACE_CB, + ICB_IndexHashLink); + RemoveEntryList (&ifCB->ICB_IndexHashLink); + if (ifCB->ICB_Stats.OperationalState==FWD_OPER_STATE_UP) { + switch (ifCB->ICB_InterfaceType) { + case FWD_IF_PERMANENT: + DeregisterPacketConsumer (ifCB->ICB_PacketListId); + break; + case FWD_IF_DEMAND_DIAL: + case FWD_IF_LOCAL_WORKSTATION: + case FWD_IF_REMOTE_WORKSTATION: + break; + default: + ASSERTMSG ("Invalid interface type ", FALSE); + break; + } + if (ifCB->ICB_CashedInterface!=NULL) + ReleaseInterfaceReference (ifCB->ICB_CashedInterface); + ifCB->ICB_CashedInterface = NULL; + if (ifCB->ICB_CashedRoute!=NULL) + ReleaseRouteReference (ifCB->ICB_CashedRoute); + ifCB->ICB_CashedRoute = NULL; + if (ifCB->ICB_Network==GlobalNetwork) + DeleteGlobalNetClient (ifCB); + IPXCloseAdapterProc (ifCB->ICB_AdapterContext); + ReleaseInterfaceReference (ifCB); // Binding reference + } + + if (IS_IF_CONNECTING (ifCB)) { + SET_IF_NOT_CONNECTING (ifCB); + DequeueConnectionRequest (ifCB); + } + + while (!IsListEmpty (&ifCB->ICB_ExternalQueue)) { + PPACKET_TAG pktTag; + + pktTag = CONTAINING_RECORD (ifCB->ICB_ExternalQueue.Flink, + PACKET_TAG, PT_QueueLink); + RemoveEntryList (&pktTag->PT_QueueLink); + ReleaseInterfaceReference (pktTag->PT_InterfaceReference); + FreePacket (pktTag); + } + + while (!IsListEmpty (&ifCB->ICB_InternalQueue)) { + PINTERNAL_PACKET_TAG pktTag; + + pktTag = CONTAINING_RECORD (ifCB->ICB_InternalQueue.Flink, + INTERNAL_PACKET_TAG, IPT_QueueLink); + RemoveEntryList (&pktTag->IPT_QueueLink); + IPXInternalSendCompletProc (&pktTag->IPT_Target, + pktTag->IPT_Packet, + pktTag->IPT_Length, + STATUS_NETWORK_UNREACHABLE); + ReleaseInterfaceReference (pktTag->IPT_InterfaceReference); + ExFreePool (pktTag); + } + + ifCB->ICB_Stats.OperationalState = FWD_OPER_STATE_DOWN; + if (ifCB->ICB_NBRoutes!=NULL) { + DeleteNBRoutes (ifCB->ICB_NBRoutes, ifCB->ICB_NBRouteCount); + ifCB->ICB_NBRoutes = NULL; + } + ReleaseInterfaceReference (ifCB); + } + } + + if (InternalInterface->ICB_NBRoutes!=NULL) { + DeleteNBRoutes (InternalInterface->ICB_NBRoutes, + InternalInterface->ICB_NBRouteCount); + InternalInterface->ICB_NBRoutes = NULL; + } + if (InternalInterface->ICB_Stats.OperationalState==FWD_OPER_STATE_UP) { + InternalInterface->ICB_Stats.OperationalState = FWD_OPER_STATE_DOWN; + ReleaseInterfaceReference (InternalInterface); // Binding reference + } + ReleaseInterfaceReference (InternalInterface); + + + + // Release extra memory segments used for route table entries + segment = PopEntryList (&RouteZone.SegmentList); + while (RouteZone.SegmentList.Next!=NULL) { + ExFreePool (segment); + segment = PopEntryList (&RouteZone.SegmentList); + } + + // Release extra memory segments used for interface table entries + segment = PopEntryList (&InterfaceZone.SegmentList); + while (InterfaceZone.SegmentList.Next!=NULL) { + ExFreePool (segment); + segment = PopEntryList (&InterfaceZone.SegmentList); + } + + ExFreePool (TableBlock); + TableBlock = NULL; + return STATUS_SUCCESS; +} + +/*++ +******************************************************************* + L o c a t e I n t e r f a c e + +Routine Description: + Finds interface control block in interface + index hash table. Optionally returns the + insertion point pointer if interface block + with given index is not in the table. +Arguments: + InterfaceIndex - unique id of the interface + insertBefore - buffer to place the pointer to + hash table element where interface + block should be inserted if it is not + already in the table +Return Value: + Pointer to interface control block if found + NULL otherwise +******************************************************************* +--*/ +PINTERFACE_CB +LocateInterface ( + ULONG InterfaceIndex, + PLIST_ENTRY *insertBefore OPTIONAL + ) { + PLIST_ENTRY cur; + PINTERFACE_CB ifCB; + PLIST_ENTRY HashList; + + ASSERT (InterfaceIndex!=FWD_INTERNAL_INTERFACE_INDEX); + + // Find hash bucket + HashList = &InterfaceIndexHash[InterfaceIndexHashFunc(InterfaceIndex)]; + cur = HashList->Flink; + // Walk the list + while (cur!=HashList) { + ifCB = CONTAINING_RECORD(cur, INTERFACE_CB, ICB_IndexHashLink); + + if (ifCB->ICB_Index==InterfaceIndex) + // Found, return it (insertion point is irrelevant) + return ifCB; + else if (ifCB->ICB_Index>InterfaceIndex) + // No chance to find it + break; + cur = cur->Flink; + } + // Return insertion point if asked + if (ARGUMENT_PRESENT(insertBefore)) + *insertBefore = cur; + return NULL; +} + +/*++ +******************************************************************* + L o c a t e C l i e n t I n t e r f a c e + +Routine Description: + Finds interface control block in client + node hash bucket. Optionally returns the + insertion point pointer if interface block + with given node is not in the table +Arguments: + ClientNode - node address of the client on global network + insertBefore - buffer to place the pointer to + hash table element where interface + block should be inserted if it is not + already in the table +Return Value: + Pointer to interface control block if found + NULL otherwise +******************************************************************* +--*/ +PINTERFACE_CB +LocateClientInterface ( + ULONGLONG *NodeAddress64, + PINTERFACE_CB **prevLink OPTIONAL + ) { + PINTERFACE_CB cur, *prev; + + prev = &ClientNodeHash[ClientNodeHashFunc (*NodeAddress64)]; + cur = *prev; + while (cur!=NULL) { + if (*NodeAddress64==cur->ICB_ClientNode64[0]) + break; + else if (*NodeAddress64>cur->ICB_ClientNode64[0]) { + // No chance to find it + cur = NULL; + break; + } + prev = &cur->ICB_NodeHashLink; + cur = cur->ICB_NodeHashLink; + } + if (ARGUMENT_PRESENT(prevLink)) + *prevLink = prev; + return cur; +} + +/*++ +******************************************************************* + L o c a t e R o u t e + +Routine Description: + Finds route block in network number + hash table. Optionally returns the + insertion point pointer if route + for given destination netowrk is not in the table +Arguments: + Network - destination netowork number + insertBefore - buffer to place the pointer to + hash table element where route + block should be inserted if it is not + already in the table +Return Value: + Pointer to route block if found + NULL otherwise +******************************************************************* +--*/ +PFWD_ROUTE +LocateRoute ( + ULONG Network, + PFWD_ROUTE **prevLink OPTIONAL + ) { + PFWD_ROUTE cur, *prev; + + prev = &RouteHash[NetworkNumberHashFunc(Network)]; + cur = *prev; + + while (cur!=NULL) { + if (cur->FR_Network==Network) + break; + else if (cur->FR_Network>Network) { + cur = NULL; + // No chance to find it + break; + } + prev = &cur->FR_Next; + cur = *prev; + } + if (ARGUMENT_PRESENT(prevLink)) + *prevLink = prev; + + return cur; +} + +/*++ +******************************************************************* + L o c a t e N B R o u t e + +Routine Description: + Finds nb route block in nb name + hash table. Optionally returns the + insertion point pointer if nb route + for given name is not in the table +Arguments: + Name - netbios name + insertBefore - buffer to place the pointer to + hash table element where route + block should be inserted if it is not + already in the table +Return Value: + Pointer to nb route block if found + NULL otherwise +******************************************************************* +--*/ +PNB_ROUTE +LocateNBRoute ( + ULONGLONG *Name128, + PNB_ROUTE **prevLink OPTIONAL + ) { + PNB_ROUTE cur, *prev; + + prev = &NBRouteHash[NetbiosNameHashFunc(Name128)]; + cur = *prev; + + while (cur!=NULL) { + if ((cur->NBR_Name128[0]==Name128[0]) + && (cur->NBR_Name128[1]==Name128[1])) + break; + else if ((cur->NBR_Name128[0]>Name128[0]) + || ((cur->NBR_Name128[0]==Name128[0]) + && (cur->NBR_Name128[1]>Name128[1]))) { + cur = NULL; + // No chance to find it + break; + } + prev = &cur->NBR_Next; + cur = *prev; + } + if (ARGUMENT_PRESENT(prevLink)) + *prevLink = prev; + + return cur; +} + +/*++ +******************************************************************* + G e t I n t e r f a c e R e f e r e n c e + +Routine Description: + Returns reference interface based on its index +Arguments: + InterfaceIndex - unique id of the interface +Return Value: + Pointer to interface control block if there is one in the table + NULL otherwise +******************************************************************* +--*/ +PINTERFACE_CB +GetInterfaceReference ( + ULONG InterfaceIndex + ) { + KIRQL oldIRQL; + PINTERFACE_CB ifCB; + + KeAcquireSpinLock (&InterfaceTableLock, &oldIRQL); + if (InterfaceIndex!=FWD_INTERNAL_INTERFACE_INDEX) + ifCB = LocateInterface (InterfaceIndex, NULL); + else + ifCB = InternalInterface; + + if (ifCB!=NULL) + AcquireInterfaceReference (ifCB); + else { + IpxFwdDbgPrint (DBG_INTF_TABLE, DBG_ERROR, + ("IpxFwd: Could not get interface reference %ld.\n", InterfaceIndex)); + } + KeReleaseSpinLock (&InterfaceTableLock, oldIRQL); + return ifCB; +} + +/*++ +******************************************************************* + G e t N e x t I n t e r f a c e R e f e r e n c e + +Routine Description: + Returns reference to the next interface in the table + Reference to the provided interface is released +Arguments: + ifCB - interface to start with or NULL to start from the + beginning of the interface table +Return Value: + Pointer to interface control block if thare are any more interfaces + in the table + NULL otherwise +******************************************************************* +--*/ +PINTERFACE_CB +GetNextInterfaceReference ( + PINTERFACE_CB ifCB + ) { + PLIST_ENTRY cur; + PLIST_ENTRY HashList; + KIRQL oldIRQL; + + KeAcquireSpinLock (&InterfaceTableLock, &oldIRQL); + if (ifCB!=NULL) { + // Find hash bucket + ASSERT (ifCB->ICB_Index!=FWD_INTERNAL_INTERFACE_INDEX); + HashList = &InterfaceIndexHash[InterfaceIndexHashFunc(ifCB->ICB_Index)]; + if (LocateInterface (ifCB->ICB_Index, &cur)!=NULL) + cur = ifCB->ICB_IndexHashLink.Flink; + ReleaseInterfaceReference (ifCB); + ifCB = NULL; + } + else + cur = HashList = InterfaceIndexHash-1; + + if (cur==HashList) { + do { + HashList += 1; + if (HashList==&InterfaceIndexHash[InterfaceHashSize]) { + KeReleaseSpinLock (&InterfaceTableLock, oldIRQL); + return NULL; + } + } while (IsListEmpty (HashList)); + cur = HashList->Flink; + } + ifCB = CONTAINING_RECORD (cur, INTERFACE_CB, ICB_IndexHashLink); + AcquireInterfaceReference (ifCB); + KeReleaseSpinLock (&InterfaceTableLock, oldIRQL); + + return ifCB; +} + + +/*++ +******************************************************************* + A d d I n t e r f a c e + +Routine Description: + Adds interface control block to the table. +Arguments: + InterfaceIndex - unique if of the interface + Info - interface paramters +Return Value: + STATUS_SUCCESS - interface added ok + STATUS_UNSUCCESSFUL - interface is already in the table + STATUS_INSUFFICIENT_RESOURCES - can't allocate memory for + interface CB +******************************************************************* +--*/ +NTSTATUS +AddInterface ( + ULONG InterfaceIndex, + UCHAR InterfaceType, + BOOLEAN NetbiosAccept, + UCHAR NetbiosDeliver + ) { + PINTERFACE_CB ifCB; + PLIST_ENTRY cur; + KIRQL oldIRQL; + NTSTATUS status = STATUS_SUCCESS; + + KeAcquireSpinLock (&InterfaceTableLock, &oldIRQL); + if (InterfaceIndex!=FWD_INTERNAL_INTERFACE_INDEX) { + ifCB = LocateInterface (InterfaceIndex, &cur); + if (ifCB==NULL) { + ifCB = AllocateInterface (); + if (ifCB!=NULL) + NOTHING; + else { + status = STATUS_INSUFFICIENT_RESOURCES; + goto AddEnd; + } + } + else { + IpxFwdDbgPrint (DBG_INTF_TABLE, DBG_ERROR, + ("IpxFwd: Interface %ld is already in the table!\n", InterfaceIndex)); + status = STATUS_UNSUCCESSFUL; + goto AddEnd; + } + } + else + ifCB = InternalInterface; + + InitICB (ifCB, InterfaceIndex,InterfaceType,NetbiosAccept,NetbiosDeliver); +#if DBG + InitializeListHead (&ifCB->ICB_InSendQueue); +#endif + + switch (InterfaceType) { + case FWD_IF_PERMANENT: + break; + case FWD_IF_DEMAND_DIAL: + case FWD_IF_LOCAL_WORKSTATION: + case FWD_IF_REMOTE_WORKSTATION: + ASSERT (InterfaceIndex!=FWD_INTERNAL_INTERFACE_INDEX); + if (WanPacketListId==-1) { + status = RegisterPacketConsumer ( + WAN_PACKET_SIZE, + &WanPacketListId); + if (!NT_SUCCESS (status)) { + WanPacketListId = -1; + break; + } + } + ifCB->ICB_PacketListId = WanPacketListId; + break; + } + + if (NT_SUCCESS (status)) { + if (InterfaceIndex!=FWD_INTERNAL_INTERFACE_INDEX) { + InsertTailList (cur, &ifCB->ICB_IndexHashLink); + } + IpxFwdDbgPrint (DBG_INTF_TABLE, DBG_WARNING, + ("IpxFwd: Adding interface %d (icb: %08lx, plid: %d)\n", + InterfaceIndex, ifCB, ifCB->ICB_PacketListId)); + } + else + FreeInterface (ifCB); + +AddEnd: + KeReleaseSpinLock (&InterfaceTableLock, oldIRQL); + return status; +} + + +/*++ +******************************************************************* + A d d G l o b a l N e t C l i e n t + +Routine Description: + Adds interface control block to the table of + clients on the global network (should be done when + client connects) +Arguments: + ifCB - interface control block to add to the table +Return Value: + STATUS_SUCCESS - interface was added ok + STATUS_UNSUCCESSFUL - another interface with the same + node address is already in the table +******************************************************************* +--*/ +NTSTATUS +AddGlobalNetClient ( + PINTERFACE_CB ifCB + ) { + KIRQL oldIRQL; + RWCOOKIE cookie; + PINTERFACE_CB *prev; + NTSTATUS status = STATUS_SUCCESS; + + ASSERT (ifCB->ICB_Index!=FWD_INTERNAL_INTERFACE_INDEX); + + AcquireReaderAccess (&RWLock, cookie); + KeAcquireSpinLock (&InterfaceTableLock, &oldIRQL); + if (LocateClientInterface (ifCB->ICB_ClientNode64, &prev)==NULL) { + ifCB->ICB_NodeHashLink = *prev; + *prev = ifCB; + KeReleaseSpinLock (&InterfaceTableLock, oldIRQL); + ReleaseReaderAccess (&RWLock, cookie); + AcquireInterfaceReference (ifCB); // To make sure that + // interface block does not + // get deleted until it is + // removed from the node table + IpxFwdDbgPrint (DBG_INTF_TABLE, DBG_WARNING, + ("IpxFwd: Adding interface %ld (icb: %08lx)" + " to global client table.\n", ifCB->ICB_Index, ifCB)); + } + else { + KeReleaseSpinLock (&InterfaceTableLock, oldIRQL); + ReleaseReaderAccess (&RWLock, cookie); + IpxFwdDbgPrint (DBG_INTF_TABLE, DBG_ERROR, + ("IpxFwd: Interface %ld (icb: %08lx)" + " is already in the global client table.\n", + ifCB->ICB_Index, ifCB)); + status = STATUS_UNSUCCESSFUL; + } + + return status; +} + +/*++ +******************************************************************* + D e l e t e G l o b a l N e t C l i e n t + +Routine Description: + Removes interface control block from the table of + clients on the global network (should be done when + client disconnects) +Arguments: + ifCB - interface control block to remove from the table +Return Value: + STATUS_SUCCESS - interface was removed ok +******************************************************************* +--*/ +NTSTATUS +DeleteGlobalNetClient ( + PINTERFACE_CB ifCB + ) { + KIRQL oldIRQL; + RWCOOKIE cookie; + PINTERFACE_CB cur, *prev; + + IpxFwdDbgPrint (DBG_INTF_TABLE, DBG_WARNING, + ("IpxFwd: Deleting interface %ld (icb: %08lx)" + " from global client table.\n", ifCB->ICB_Index, ifCB)); + + ASSERT (ifCB->ICB_Index!=FWD_INTERNAL_INTERFACE_INDEX); + + AcquireReaderAccess (&RWLock, cookie); + KeAcquireSpinLock (&InterfaceTableLock, &oldIRQL); + cur = LocateClientInterface (ifCB->ICB_ClientNode64, &prev); + ASSERT (cur==ifCB); + *prev = ifCB->ICB_NodeHashLink; + KeReleaseSpinLock (&InterfaceTableLock, oldIRQL); + ReleaseReaderAccess (&RWLock, cookie); + + ReleaseInterfaceReference (ifCB); + return STATUS_SUCCESS; +} + + +/*++ +******************************************************************* + D e l e t e I n t e r f a c e + +Routine Description: + Deletes interface control block (the block is not actually + disposed of until all references to it are released). +Arguments: + InterfaceIndex - unique if of the interface +Return Value: + STATUS_SUCCESS - interface info retreived ok + STATUS_UNSUCCESSFUL - interface is not in the table +******************************************************************* +--*/ +NTSTATUS +DeleteInterface ( + ULONG InterfaceIndex + ) { + PINTERFACE_CB ifCB; + KIRQL oldIRQL; + NTSTATUS status = STATUS_SUCCESS; + + KeAcquireSpinLock (&InterfaceTableLock, &oldIRQL); + + if (InterfaceIndex!=FWD_INTERNAL_INTERFACE_INDEX) + ifCB = LocateInterface (InterfaceIndex, NULL); + else + ifCB = InternalInterface; + if (ifCB!=NULL) { + if (InterfaceIndex!=FWD_INTERNAL_INTERFACE_INDEX) { + RemoveEntryList (&ifCB->ICB_IndexHashLink); + } + KeReleaseSpinLock (&InterfaceTableLock, oldIRQL); + if (ifCB->ICB_Stats.OperationalState == FWD_OPER_STATE_UP) { + IpxFwdDbgPrint (DBG_INTF_TABLE, DBG_ERROR, + ("IpxFwd: Interface %ld (icb: %08lx) was still bound" + " when asked to delete it.\n", + ifCB->ICB_Index, ifCB)); + UnbindInterface (ifCB); + } + else if (IS_IF_CONNECTING (ifCB)) { + IpxFwdDbgPrint (DBG_INTF_TABLE, DBG_ERROR, + ("IpxFwd: Interface %ld (icb: %08lx) was still being connected" + " when asked to delete it.\n", + ifCB->ICB_Index, ifCB)); + SET_IF_NOT_CONNECTING (ifCB); + DequeueConnectionRequest (ifCB); + ProcessInternalQueue (ifCB); + ProcessExternalQueue (ifCB); + } + + ifCB->ICB_Stats.OperationalState = FWD_OPER_STATE_DOWN; + + IpxFwdDbgPrint (DBG_INTF_TABLE, DBG_WARNING, + ("IpxFwd: Deleting interface %ld (icb: %08lx).\n", + ifCB->ICB_Index, ifCB)); + + if (ifCB->ICB_NBRoutes!=NULL) { + DeleteNBRoutes (ifCB->ICB_NBRoutes, ifCB->ICB_NBRouteCount); + ifCB->ICB_NBRoutes = NULL; + } + + FltInterfaceDeleted (ifCB); + ReleaseInterfaceReference (ifCB); + } + else { + KeReleaseSpinLock (&InterfaceTableLock, oldIRQL); + IpxFwdDbgPrint (DBG_INTF_TABLE, DBG_ERROR, + ("IpxFwd: Could not delete interface %ld because it is not found.\n", + InterfaceIndex)); + status = STATUS_UNSUCCESSFUL; + } + return status; + +} + +/*++ +******************************************************************* + A d d R o u t e + +Routine Description: + Adds route to the hash table and finds and stores the reference + to the associated interface control block in the route. +Arguments: + Network - route's destination network + NextHopAddress - mac address of next hop router if network is not + directly connected + TickCount - ticks to reach the destination net + HopCount - hopss to reach the destination net + InterfaceIndex - index of the associated interface (through which + packets destined to the network are to be sent) +Return Value: + STATUS_SUCCESS - route was added ok + STATUS_UNSUCCESSFUL - route is already in the table + STATUS_INSUFFICIENT_RESOURCES - can't allocate memory for + route block +******************************************************************* +--*/ +NTSTATUS +AddRoute ( + ULONG Network, + UCHAR *NextHopAddress, + USHORT TickCount, + USHORT HopCount, + ULONG InterfaceIndex + ) { + PFWD_ROUTE fwRoute; + PFWD_ROUTE *prev; + NTSTATUS status = STATUS_SUCCESS; + KIRQL oldIRQL; + + // Assume success, allocate route and intialize it + // (the goal is to spend as little time as possible + // inside exclusive usage zone) + fwRoute = AllocateRoute (); + if (fwRoute!=NULL) { + fwRoute->FR_Network = Network; + IPX_NODE_CPY (fwRoute->FR_NextHopAddress, NextHopAddress); + fwRoute->FR_TickCount = TickCount; + fwRoute->FR_HopCount = HopCount; + fwRoute->FR_ReferenceCount = 0; + + if (InterfaceIndex!=0xFFFFFFFF) { + // See if interface is there + KeAcquireSpinLock (&InterfaceTableLock, &oldIRQL); + if (InterfaceIndex!=FWD_INTERNAL_INTERFACE_INDEX) + fwRoute->FR_InterfaceReference + = LocateInterface (InterfaceIndex, NULL); + else + fwRoute->FR_InterfaceReference = InternalInterface; + if (fwRoute->FR_InterfaceReference!=NULL) { + AcquireInterfaceReference (fwRoute->FR_InterfaceReference); + KeReleaseSpinLock (&InterfaceTableLock, oldIRQL); + + ExAcquireFastMutex (&WriterMutex); + // Check if route is already there + if (LocateRoute (Network, &prev)==NULL) { + fwRoute->FR_Next = *prev; + *prev = fwRoute; + } + else { + ReleaseInterfaceReference (fwRoute->FR_InterfaceReference); + fwRoute->FR_InterfaceReference = NULL; + IpxFwdDbgPrint (DBG_ROUTE_TABLE, DBG_ERROR, + ("IpxFwd: Route for net %08lx" + " is already in the table!\n", Network)); + status = STATUS_UNSUCCESSFUL; + } + + ExReleaseFastMutex (&WriterMutex); + } + else { + KeReleaseSpinLock (&InterfaceTableLock, oldIRQL); + IpxFwdDbgPrint (DBG_ROUTE_TABLE, DBG_ERROR, + ("IpxFwd: Interface %ld for route for net %08lx" + " is not in the table!\n", InterfaceIndex, Network)); + status = STATUS_UNSUCCESSFUL; + } + } + else { + ExAcquireFastMutex (&WriterMutex); + // Just check if we do not have it already + if (GlobalRoute==NULL) { + fwRoute->FR_InterfaceReference = GLOBAL_INTERFACE_REFERENCE; + GlobalNetwork = Network; + GlobalRoute = fwRoute; + } + else { + IpxFwdDbgPrint (DBG_ROUTE_TABLE, DBG_ERROR, + ("IpxFwd: Route for global net %08lx" + " is already in the table!\n", Network)); + status = STATUS_UNSUCCESSFUL; + } + ExReleaseFastMutex (&WriterMutex); + } + + if (NT_SUCCESS (status)) { + IpxFwdDbgPrint (DBG_ROUTE_TABLE, DBG_WARNING, + ("IpxFwd: Adding route for net %08lx" + " (rb: %08lx, NHA: %02x%02x%02x%02x%02x%02x," + " if: %ld, icb: %08lx).\n", + Network, fwRoute, + NextHopAddress[0], NextHopAddress[1], + NextHopAddress[2], NextHopAddress[3], + NextHopAddress[4], NextHopAddress[5], + InterfaceIndex, fwRoute->FR_InterfaceReference)); + } + else { + FreeRoute (fwRoute); + } + } + else + status = STATUS_INSUFFICIENT_RESOURCES; + + return status; +} + +/*++ +******************************************************************* + D e l e t e R o u t e + +Routine Description: + Deletes route from the hash table and releases the reference + to the interface control block associated with the route. +Arguments: + Network - route's destination network +Return Value: + STATUS_SUCCESS - route was deleted ok + STATUS_UNSUCCESSFUL - route is not in the table +******************************************************************* +--*/ +NTSTATUS +DeleteRoute ( + ULONG Network + ) { + PFWD_ROUTE fwRoute, *prev; + NTSTATUS status = STATUS_SUCCESS; + + ExAcquireFastMutex (&WriterMutex); + + if ((GlobalRoute!=NULL) + && (GlobalNetwork==Network)) { + fwRoute = GlobalRoute; + GlobalNetwork = 0xFFFFFFFF; + GlobalRoute = NULL; + } + else if ((fwRoute=LocateRoute (Network, &prev))!=NULL) { + *prev = fwRoute->FR_Next; + } + + if (fwRoute!=NULL) { + IpxFwdDbgPrint (DBG_ROUTE_TABLE, DBG_WARNING, + ("IpxFwd: Deleting route for net %08lx (rb: %08lx).\n", + Network, fwRoute)); + WaitForAllReaders (&RWLock); + if (fwRoute->FR_InterfaceReference!=GLOBAL_INTERFACE_REFERENCE) { + ReleaseInterfaceReference (fwRoute->FR_InterfaceReference); + } + fwRoute->FR_InterfaceReference = NULL; + ReleaseRouteReference (fwRoute); + } + else { + IpxFwdDbgPrint (DBG_ROUTE_TABLE, DBG_ERROR, + ("IpxFwd: Could not delete route for net %08lx because it is not in the table.\n", + Network)); + status = STATUS_UNSUCCESSFUL; + } + + ExReleaseFastMutex (&WriterMutex); + return status; +} + + +/*++ +******************************************************************* + U p d a t e R o u t e + +Routine Description: + Updates route in the hash table +Arguments: + Network - route's destination network + NextHopAddress - mac address of next hop router if network is not + directly connected + TickCount - ticks to reach the destination net + HopCount - hopss to reach the destination net + InterfaceIndex - index of the associated interface (through which + packets destined to the network are to be sent) +Return Value: + STATUS_SUCCESS - interface info retreived ok + STATUS_UNSUCCESSFUL - interface is not in the table +******************************************************************* +--*/ +NTSTATUS +UpdateRoute ( + ULONG Network, + UCHAR *NextHopAddress, + USHORT TickCount, + USHORT HopCount, + ULONG InterfaceIndex + ) { + PFWD_ROUTE fwRoute = NULL, newRoute, *prev; + PINTERFACE_CB ifCB = NULL; + KIRQL oldIRQL; + NTSTATUS status = STATUS_SUCCESS; + + + ExAcquireFastMutex (&WriterMutex); + + if ((GlobalRoute!=NULL) + && (GlobalNetwork==Network)) { + ASSERT (InterfaceIndex==0xFFFFFFFF); + fwRoute = GlobalRoute; + } + else { + ASSERT (InterfaceIndex!=0xFFFFFFFF); + fwRoute = LocateRoute (Network, &prev); + } + + if (fwRoute!=NULL) { + if (InterfaceIndex!=0xFFFFFFFF) { + if (fwRoute->FR_InterfaceReference->ICB_Index!=InterfaceIndex) { + // Get a reference to new interface + KeAcquireSpinLock (&InterfaceTableLock, &oldIRQL); + if (InterfaceIndex!=FWD_INTERNAL_INTERFACE_INDEX) + ifCB = LocateInterface (InterfaceIndex, NULL); + else + ifCB = InternalInterface; + if (ifCB!=NULL) { + AcquireInterfaceReference (ifCB); + } + else { + KeReleaseSpinLock (&InterfaceTableLock, oldIRQL); + status = STATUS_UNSUCCESSFUL; + goto ExitUpdate; + } + KeReleaseSpinLock (&InterfaceTableLock, oldIRQL); + } + else { + ifCB = fwRoute->FR_InterfaceReference; + AcquireInterfaceReference (ifCB); + } + } + else + ifCB = GLOBAL_INTERFACE_REFERENCE; + newRoute = AllocateRoute (); + if (newRoute!=NULL) { + newRoute->FR_Network = Network; + IPX_NODE_CPY (newRoute->FR_NextHopAddress, NextHopAddress); + newRoute->FR_TickCount = TickCount; + newRoute->FR_HopCount = HopCount; + newRoute->FR_ReferenceCount = 0; + newRoute->FR_InterfaceReference = ifCB; + // Lock the table only when updating it + if (InterfaceIndex!=0xFFFFFFFF) { + newRoute->FR_Next = fwRoute->FR_Next; + *prev = newRoute; + } + else + GlobalRoute = newRoute; + + WaitForAllReaders (&RWLock) + if (fwRoute->FR_InterfaceReference!=GLOBAL_INTERFACE_REFERENCE) { + ReleaseInterfaceReference (fwRoute->FR_InterfaceReference); + } + fwRoute->FR_InterfaceReference = NULL; + ReleaseRouteReference (fwRoute); + + } + else + status = STATUS_INSUFFICIENT_RESOURCES; + } + else + status = STATUS_UNSUCCESSFUL; + +ExitUpdate: + ExReleaseFastMutex (&WriterMutex); + return status; +} + + +/*++ +******************************************************************* + F i n d D e s t i n a t i o n + +Routine Description: + Finds destination interface for IPX address and + returns reference to its control block. +Arguments: + Network - destination network + Node - destination node (needed in case of global client) + Route - buffer to hold reference to route block +Return Value: + Reference to destination interface CB + NULL if route it not found +******************************************************************* +--*/ +PINTERFACE_CB +FindDestination ( + IN ULONG Network, + IN PUCHAR Node, + OUT PFWD_ROUTE *Route + ) { + PFWD_ROUTE fwRoute; + PINTERFACE_CB ifCB; + RWCOOKIE cookie; + + AcquireReaderAccess (&RWLock, cookie); + if ((GlobalRoute!=NULL) + && (GlobalNetwork==Network)) { + if (Node!=NULL) { // If caller did not specify node, + // we can't find the route + union { + ULONGLONG Node64[1]; + UCHAR Node[6]; + } u; + u.Node64[0] = 0; + IPX_NODE_CPY (u.Node, Node); + + ifCB = LocateClientInterface (u.Node64, NULL); + if (ifCB!=NULL) { + AcquireRouteReference (GlobalRoute); + *Route = GlobalRoute; + AcquireInterfaceReference (ifCB); + } + else + *Route = NULL; + } + else { + ifCB = NULL; + *Route = NULL; + } + } + else { + *Route = fwRoute = LocateRoute (Network, NULL); + if (fwRoute!=NULL) { + AcquireRouteReference (fwRoute); + ifCB = fwRoute->FR_InterfaceReference; + AcquireInterfaceReference (ifCB); + } + else + ifCB = NULL; + } + ReleaseReaderAccess (&RWLock, cookie); + return ifCB; +} + +/*++ +******************************************************************* + A d d N B R o u t e s + +Routine Description: + Adds netbios names associated with interface to netbios + route hash table +Arguments: + ifCB - interface with which names are associated + Names - array of names + Count - number of names in the array + routeArray - buffer to place allocated array of routes +Return Value: + STATUS_SUCCESS - names were added ok + STATUS_UNSUCCESSFUL - one of the names is already in the table + STATUS_INSUFFICIENT_RESOURCES - can't allocate memory for + route array +******************************************************************* +--*/ +NTSTATUS +AddNBRoutes ( + PINTERFACE_CB ifCB, + FWD_NB_NAME Names[], + ULONG Count, + PNB_ROUTE *routeArray + ) { + PNB_ROUTE nbRoutes, *prev; + NTSTATUS status = STATUS_SUCCESS; + + nbRoutes = (PNB_ROUTE)ExAllocatePoolWithTag ( + NonPagedPool, sizeof (NB_ROUTE)*Count, FWD_POOL_TAG); + if (nbRoutes!=NULL) { + ULONG i; + + ExAcquireFastMutex (&WriterMutex); + + for (i=0; i<Count; i++) { + nbRoutes[i].NBR_Name128[0] = nbRoutes[i].NBR_Name128[1] = 0; + NB_NAME_CPY (nbRoutes[i].NBR_Name, &Names[i]); + // Check if route is already there + if (LocateNBRoute (nbRoutes[i].NBR_Name128, &prev)==NULL) { + nbRoutes[i].NBR_Destination = ifCB; + nbRoutes[i].NBR_Next = *prev; + *prev = &nbRoutes[i]; + IpxFwdDbgPrint (DBG_NBROUTE_TABLE, DBG_WARNING, + ("IpxFwd: Adding nb route for name %16s.\n",Names[i])); + } + else { + IpxFwdDbgPrint (DBG_NBROUTE_TABLE, DBG_ERROR, + ("IpxFwd: Route for nb name %16s" + " is already in the table!\n", Names[i])); + break; + } + } + ExReleaseFastMutex (&WriterMutex); + if (i==Count) { + *routeArray = nbRoutes; + status = STATUS_SUCCESS; + + } + else { + status = STATUS_UNSUCCESSFUL; + DeleteNBRoutes (nbRoutes, i); + } + } + else { + IpxFwdDbgPrint (DBG_NBROUTE_TABLE, DBG_ERROR, + ("IpxFwd: Could allocate nb route array for if: %ld" + " (icb: %08lx).\n", ifCB->ICB_Index, ifCB)); + status = STATUS_INSUFFICIENT_RESOURCES; + } + return status; +} + +/*++ +******************************************************************* + D e l e t e N B R o u t e s + +Routine Description: + Deletes nb routes in the array from the route table and frees + the array +Arguments: + nbRoutes - array of routes + Count - number of routes in the array +Return Value: + STATUS_SUCCESS - route was deleted ok + STATUS_UNSUCCESSFUL - route is not in the table +******************************************************************* +--*/ +NTSTATUS +DeleteNBRoutes ( + PNB_ROUTE nbRoutes, + ULONG Count + ) { + PNB_ROUTE *prev; + NTSTATUS status = STATUS_SUCCESS; + ULONG i; + + ExAcquireFastMutex (&WriterMutex); + for (i=0; i<Count; i++) { + PNB_ROUTE cur = LocateNBRoute (nbRoutes[i].NBR_Name128, &prev); + ASSERT (cur==&nbRoutes[i]); + *prev = nbRoutes[i].NBR_Next; + IpxFwdDbgPrint (DBG_NBROUTE_TABLE, DBG_WARNING, + ("IpxFwd: Deleting nb route for name %16s.\n", + nbRoutes[i].NBR_Name)); + } + + WaitForAllReaders (&RWLock); + ExReleaseFastMutex (&WriterMutex); + + ExFreePool (nbRoutes); + + return STATUS_SUCCESS; +} + + +/*++ +******************************************************************* + F i n d N B D e s t i n a t i o n + +Routine Description: + Finds destination interface for nb name and + returns reference to its control block. +Arguments: + Name - name to look for +Return Value: + Reference to destination interface CB + NULL if route it not found +******************************************************************* +--*/ +PINTERFACE_CB +FindNBDestination ( + IN PUCHAR Name + ) { + PNB_ROUTE nbRoute; + PINTERFACE_CB ifCB; + RWCOOKIE cookie; + union { + ULONGLONG Name128[2]; + UCHAR Name[16]; + } u; + u.Name128[0] = u.Name128[1] = 0; + NB_NAME_CPY (u.Name, Name); + + AcquireReaderAccess (&RWLock, cookie); + nbRoute = LocateNBRoute (u.Name128, NULL); + if (nbRoute!=NULL) { + ifCB = nbRoute->NBR_Destination; + AcquireInterfaceReference (ifCB); + } + else + ifCB = NULL; + ReleaseReaderAccess (&RWLock, cookie); + return ifCB; +} + |