#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; ULONG InterfaceAllocCount = 0; ULONG InterfaceFreeCount = 0; // 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); InterlockedIncrement(&InterfaceAllocCount); 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); InterlockedIncrement(&InterfaceFreeCount); } /*++ ******************************************************************* 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 = (ULONG) 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_PTR)(InterfaceIndexHash+InterfaceHashSize),ULONGLONG); ClientNodeHash = (PINTERFACE_CB *)segment; segment = (CHAR *)ALIGN_UP((ULONG_PTR)(ClientNodeHash+ClientHashSize),ULONGLONG); RouteHash = (PFWD_ROUTE *)segment; segment = (CHAR *)ALIGN_UP((ULONG_PTR)(RouteHash + RouteHashSize),ULONGLONG); NBRouteHash = (PNB_ROUTE *)segment; segment = (CHAR *)ALIGN_UP((ULONG_PTR)(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_PTR)(segment+InterfaceSegmentSize),ULONGLONG); status = ExInitializeZone (&RouteZone, ALIGN_UP(sizeof (FWD_ROUTE), ULONGLONG), segment, blockSize - (ULONG)(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; iFR_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; iICB_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); //if (ifCB->ICB_Index > 1) //IpxFwdDbgPrint (DBG_INTF_TABLE, DBG_WARNING, // ("IpxFwd: GetInterfaceReference: Aquired if #%ld (%ld) \n", ifCB->ICB_Index, ifCB->ICB_ReferenceCount)); } else { IpxFwdDbgPrint (DBG_INTF_TABLE, DBG_ERROR, ("IpxFwd: Could not get interface reference %ld.\n", InterfaceIndex)); } KeReleaseSpinLock (&InterfaceTableLock, oldIRQL); return ifCB; } // // Function IncrementNicIds // // Increments the nic id of every nic in the interface table // whose id is greater than or equal to the given threshold. // NTSTATUS IncrementNicids (USHORT usThreshold) { KIRQL oldIRQL; PINTERFACE_CB ifCB; PLIST_ENTRY cur; PLIST_ENTRY HashList; ULONG i; IpxFwdDbgPrint (DBG_IPXBIND, DBG_INFORMATION, ("IpxFwd: Incrementing all nic id's >= %d", usThreshold)); KeAcquireSpinLock (&InterfaceTableLock, &oldIRQL); // Walk through all of the hash buckets for (i = 0; i < InterfaceHashSize; i++) { HashList = &InterfaceIndexHash[i]; cur = HashList->Flink; // Walk the list in this bucket updating as needed while (cur!=HashList) { ifCB = CONTAINING_RECORD(cur, INTERFACE_CB, ICB_IndexHashLink); if ((ifCB->ICB_NicId != INVALID_NIC_ID) && (ifCB->ICB_NicId >= usThreshold)) { IpxFwdDbgPrint (DBG_IPXBIND, DBG_INFORMATION, ("IpxFwd: Incrementing nic id %d", ifCB->ICB_NicId)); ifCB->ICB_NicId++; *((USHORT*)&ifCB->ICB_AdapterContext) = ifCB->ICB_NicId; } cur = cur->Flink; } } KeReleaseSpinLock (&InterfaceTableLock, oldIRQL); return STATUS_SUCCESS; } // // Function DecrementNicIds // // Decrements the nic id of every nic in the interface table // whose id is greater than the given threshold. // NTSTATUS DecrementNicids (USHORT usThreshold) { KIRQL oldIRQL; PINTERFACE_CB ifCB; PLIST_ENTRY cur; PLIST_ENTRY HashList; ULONG i; IpxFwdDbgPrint (DBG_IPXBIND, DBG_INFORMATION, ("IpxFwd: Decrementing all nic id's > %d", usThreshold)); KeAcquireSpinLock (&InterfaceTableLock, &oldIRQL); // Walk through all of the hash buckets for (i = 0; i < InterfaceHashSize; i++) { HashList = &InterfaceIndexHash[i]; cur = HashList->Flink; // Walk the list in this bucket updating as needed while (cur!=HashList) { ifCB = CONTAINING_RECORD(cur, INTERFACE_CB, ICB_IndexHashLink); // If this is a bound interface if (ifCB->ICB_NicId != INVALID_NIC_ID) { // If it's bound to a nic greater than the threshold, update // the nicid if (ifCB->ICB_NicId > usThreshold) { IpxFwdDbgPrint (DBG_IPXBIND, DBG_INFORMATION, ("IpxFwd: Decrementing nic id %d", ifCB->ICB_NicId)); ifCB->ICB_NicId--; } // The if with bound to the threshold is now unbound. else if (ifCB->ICB_NicId == usThreshold) { IpxFwdDbgPrint (DBG_IPXBIND, DBG_INFORMATION, ("IpxFwd: Marking interface %d as unbound", ifCB->ICB_Index)); ifCB->ICB_NicId = INVALID_NIC_ID; } *((USHORT*)&ifCB->ICB_AdapterContext) = ifCB->ICB_NicId; } cur = cur->Flink; } } KeReleaseSpinLock (&InterfaceTableLock, oldIRQL); return STATUS_SUCCESS; } // // Puts as much of the interface table into the buffer pointed to by // pRows as there is space. // NTSTATUS DoGetIfTable (FWD_INTERFACE_TABLE * pTable, ULONG dwRowBufferSize) { KIRQL oldIRQL; PINTERFACE_CB ifCB; PLIST_ENTRY cur; PLIST_ENTRY HashList; ULONG i, j = 0; KeAcquireSpinLock (&InterfaceTableLock, &oldIRQL); // Walk through all of the hash buckets for (i = 0; i < InterfaceHashSize; i++) { HashList = &InterfaceIndexHash[i]; cur = HashList->Flink; // Walk the list in this bucket updating as needed while (cur!=HashList) { ifCB = CONTAINING_RECORD(cur, INTERFACE_CB, ICB_IndexHashLink); // Validate the size of the return buffer if (dwRowBufferSize < (sizeof(FWD_INTERFACE_TABLE) + (sizeof(FWD_INTERFACE_TABLE_ROW) * (j + 1)))) { break; } // Validate the number of rows if (j >= pTable->dwNumRows) break; // Copy over the interface information pTable->pRows[j].dwIndex = ifCB->ICB_Index; pTable->pRows[j].dwNetwork = ifCB->ICB_Network; memcpy (pTable->pRows[j].uNode, ifCB->ICB_LocalNode, 6); memcpy (pTable->pRows[j].uRemoteNode, ifCB->ICB_RemoteNode, 6); pTable->pRows[j].usNicId = ifCB->ICB_NicId; pTable->pRows[j].ucType = ifCB->ICB_InterfaceType; j++; // Advance the current row and interface cur = cur->Flink; } } KeReleaseSpinLock (&InterfaceTableLock, oldIRQL); pTable->dwNumRows = j; return STATUS_SUCCESS; } /*++ ******************************************************************* 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, ref=%ld)" " to global client table.\n", ifCB->ICB_Index, ifCB->ICB_ReferenceCount, 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); //if (fwRoute->FR_InterfaceReference->ICB_Index > 1) //IpxFwdDbgPrint (DBG_INTF_TABLE, DBG_WARNING, // ("IpxFwd: AddRoute: Aquired if #%ld (%ld) \n", // fwRoute->FR_InterfaceReference->ICB_Index, // fwRoute->FR_InterfaceReference->ICB_ReferenceCount)); 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)) { InterfaceIndex = 0xFFFFFFFF; fwRoute = GlobalRoute; } else { ASSERT (InterfaceIndex!=0xFFFFFFFF); fwRoute = LocateRoute (Network, &prev); if ((fwRoute != NULL) && (fwRoute->FR_InterfaceReference == GLOBAL_INTERFACE_REFERENCE)) { status = STATUS_UNSUCCESSFUL; goto ExitUpdate; } } 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); //if (ifCB->ICB_Index > 1) //IpxFwdDbgPrint (DBG_INTF_TABLE, DBG_WARNING, // ("IpxFwd: UpdateRoute: Aquired if #%ld (%ld) \n", ifCB->ICB_Index, ifCB->ICB_ReferenceCount)); } else { KeReleaseSpinLock (&InterfaceTableLock, oldIRQL); status = STATUS_UNSUCCESSFUL; goto ExitUpdate; } KeReleaseSpinLock (&InterfaceTableLock, oldIRQL); } else { ifCB = fwRoute->FR_InterfaceReference; AcquireInterfaceReference (ifCB); //if (ifCB->ICB_Index > 1) //IpxFwdDbgPrint (DBG_INTF_TABLE, DBG_WARNING, // ("IpxFwd: UpdateRoute(2): Aquired if #%ld (%ld) \n", ifCB->ICB_Index, ifCB->ICB_ReferenceCount)); } } 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); //if (ifCB->ICB_Index > 1) //IpxFwdDbgPrint (DBG_INTF_TABLE, DBG_WARNING, // ("IpxFwd: FindDestination: Aquired if #%ld (%ld) \n", ifCB->ICB_Index, ifCB->ICB_ReferenceCount)); } 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); //if (ifCB->ICB_Index > 1) //IpxFwdDbgPrint (DBG_INTF_TABLE, DBG_WARNING, // ("IpxFwd: FindDestination(2): Aquired if #%ld (%ld) \n", ifCB->ICB_Index, ifCB->ICB_ReferenceCount)); } 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; iICB_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; iNBR_Destination; AcquireInterfaceReference (ifCB); //if (ifCB->ICB_Index > 1) //IpxFwdDbgPrint (DBG_INTF_TABLE, DBG_WARNING, // ("IpxFwd: FindNBDestination: Aquired if #%ld (%ld) \n", ifCB->ICB_Index, ifCB->ICB_ReferenceCount)); } else ifCB = NULL; ReleaseReaderAccess (&RWLock, cookie); return ifCB; }