diff options
Diffstat (limited to '')
-rw-r--r-- | private/ntos/tdi/isnp/ipx/rip.c | 2655 |
1 files changed, 2655 insertions, 0 deletions
diff --git a/private/ntos/tdi/isnp/ipx/rip.c b/private/ntos/tdi/isnp/ipx/rip.c new file mode 100644 index 000000000..d30770223 --- /dev/null +++ b/private/ntos/tdi/isnp/ipx/rip.c @@ -0,0 +1,2655 @@ +/*++ + + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + rip.c + +Abstract: + + This module contains code that implements the client-side + RIP support and simple router table support. + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + +UCHAR BroadcastAddress[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + +NTSTATUS +RipGetLocalTarget( + IN ULONG Segment, + IN TDI_ADDRESS_IPX UNALIGNED * RemoteAddress, + IN UCHAR Type, + OUT PIPX_LOCAL_TARGET LocalTarget, + OUT USHORT Counts[2] OPTIONAL + ) + +/*++ + +Routine Description: + + This routine looks up the proper route for the specified remote + address. If a RIP request needs to be generated it does so. + + NOTE: THIS REQUEST IS CALLED WITH THE SEGMENT LOCK HELD. + NOTE: IN THE CASE OF PnP, THIS COMES WITH THE BIND LOCK SHARED. + +Arguments: + + Segment - The segment associate with the remote address. + + RemoteAddress - The IPX address of the remote. + + Type - One of IPX_FIND_ROUTE_NO_RIP, IPX_FIND_ROUTE_RIP_IF_NEEDED, + or IPX_FIND_ROUTE_FORCE_RIP. + + LocalTarget - Returns the next router information. + + Counts - If specified, used to return the tick and hop count. + +Return Value: + + STATUS_SUCCESS if a route is found, STATUS_PENDING if a + RIP request needs to be generated, failure status if a + RIP request packet cannot be allocated. + +--*/ + +{ + PDEVICE Device = IpxDevice; + PIPX_ROUTE_ENTRY RouteEntry; + PBINDING Binding; + UINT i; + + + // + // Packets sent to network 0 go on the first adapter also. + // + + if (RemoteAddress->NetworkAddress == 0) { +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(LocalTarget, 1); + + RtlCopyMemory (LocalTarget->MacAddress, RemoteAddress->NodeAddress, 6); + if (ARGUMENT_PRESENT(Counts)) { + Counts[0] = (USHORT)((839 + NIC_ID_TO_BINDING(Device, 1)->MediumSpeed) / + NIC_ID_TO_BINDING(Device, 1)->MediumSpeed); // tick count + Counts[1] = 1; // hop count + } +#else + LocalTarget->NicId = 1; + + RtlCopyMemory (LocalTarget->MacAddress, RemoteAddress->NodeAddress, 6); + if (ARGUMENT_PRESENT(Counts)) { + Counts[0] = (USHORT)((839 + Device->Bindings[1]->MediumSpeed) / + Device->Bindings[1]->MediumSpeed); // tick count + Counts[1] = 1; // hop count + } +#endif + return STATUS_SUCCESS; + } + + // + // See if this is a packet sent to our virtual network. + // + + if (Device->VirtualNetwork && + (RemoteAddress->NetworkAddress == Device->SourceAddress.NetworkAddress)) { + + // + // Send it through adapter 1. + // BUGBUG: Do real loopback. + // +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(LocalTarget, 0); + RtlCopyMemory (LocalTarget->MacAddress, NIC_ID_TO_BINDING(Device, 1)->LocalMacAddress.Address, 6); +#else + // + // Loopback this packet + // + LocalTarget->NicId = 0; + RtlCopyMemory (LocalTarget->MacAddress, Device->Bindings[1]->LocalMacAddress.Address, 6); +#endif + + IPX_DEBUG (LOOPB, ("Loopback Nic returned for net: %lx\n", RemoteAddress->NetworkAddress)); + if (ARGUMENT_PRESENT(Counts)) { + Counts[0] = 1; // tick count + Counts[1] = 1; // hop count + } + return STATUS_SUCCESS; + + } + + // + // Look up the route in the table. If the net is one + // of the ones we are directly attached to, this will + // return an entry with the correct flag set. + // + + RouteEntry = RipGetRoute(Segment, (PUCHAR)&(RemoteAddress->NetworkAddress)); + + if (RouteEntry != NULL) { + + RouteEntry->Timer = 0; +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(LocalTarget, RouteEntry->NicId); +#else + LocalTarget->NicId = RouteEntry->NicId; +#endif + if (RouteEntry->Flags & IPX_ROUTER_LOCAL_NET) { + + // + // The machine is on the same net, so send it directly. + // + + RtlCopyMemory (LocalTarget->MacAddress, RemoteAddress->NodeAddress, 6); + + if (RouteEntry->Flags & IPX_ROUTER_GLOBAL_WAN_NET) { + + // + // The NicId here is bogus, we have to scan through + // our bindings until we find one whose indicated + // IPX remote node matches the destination node of + // this frame. We don't scan into the duplicate + // binding set members since they won't be WANs. + // + { + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (i = 1; i <= Index; i++) { +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, i); +#else + Binding = Device->Bindings[i]; +#endif + if ((Binding != (PBINDING)NULL) && + (Binding->Adapter->MacInfo.MediumAsync) && + (RtlEqualMemory( + Binding->WanRemoteNode, + RemoteAddress->NodeAddress, + 6))) { +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(LocalTarget, MIN( Device->MaxBindings, Binding->NicId)); +#else + LocalTarget->NicId = Binding->NicId; +#endif + break; + + } + } + } + + if (i > (UINT)MIN (Device->MaxBindings, Device->HighestExternalNicId)) { + // + // Bug #17273 return proper error message + // + + // return STATUS_DEVICE_DOES_NOT_EXIST; + return STATUS_NETWORK_UNREACHABLE; + } + + } else { + // + // Find out if this is a loopback packet. If so, return NicId 0 + // + { + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (i = 1; i <= Index; i++) { +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, i); +#else + Binding = Device->Bindings[i]; +#endif + // + // Self-directed - loopback + // + if ((Binding != (PBINDING)NULL) && + (RtlEqualMemory( + Binding->LocalAddress.NodeAddress, + RemoteAddress->NodeAddress, + 6))) { +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(LocalTarget, 0); +#else + LocalTarget->NicId = 0; +#endif + + IPX_DEBUG (LOOPB, ("2.Loopback Nic returned for net: %lx\n", RemoteAddress->NetworkAddress)); + break; + + } + } + } + } + + } else { + + CTEAssert ((RouteEntry->Flags & IPX_ROUTER_PERMANENT_ENTRY) == 0); + + // + // This is not a locally attached net, so if the caller + // is forcing a re-RIP then do that. + // + + if (Type == IPX_FIND_ROUTE_FORCE_RIP) { + goto QueueUpRequest; + } + + // + // Fill in the address of the next router in the route. + // + + RtlCopyMemory (LocalTarget->MacAddress, RouteEntry->NextRouter, 6); + + } + + if (ARGUMENT_PRESENT(Counts)) { + Counts[0] = RouteEntry->TickCount; + Counts[1] = RouteEntry->HopCount; + } + + return STATUS_SUCCESS; + + } + +QueueUpRequest: + + if (Type == IPX_FIND_ROUTE_NO_RIP) { + + // + // Bug #17273 return proper error message + // + + // return STATUS_DEVICE_DOES_NOT_EXIST; + return STATUS_NETWORK_UNREACHABLE; + + } else { + + return RipQueueRequest (RemoteAddress->NetworkAddress, RIP_REQUEST); + + } + +} /* RipGetLocalTarget */ + + +NTSTATUS +RipQueueRequest( + IN ULONG Network, + IN USHORT Operation + ) + +/*++ + +Routine Description: + + This routine queues up a request for a RIP route. It can be + used to find a specific route or to discover the locally + attached network (if Network is 0). It can also be used + to do a periodic announcement of the virtual net, which + we do once a minute if the router is not bound. + + NOTE: THIS REQUEST IS CALLED WITH THE SEGMENT LOCK HELD + IF IT IS A REQUEST AND THE NETWORK IS NOT 0xffffffff. + +Arguments: + + Network - The network to discover. + + Operation - One of RIP_REQUEST, RIP_RESPONSE, or RIP_DOWN. + +Return Value: + + STATUS_PENDING if the request is queued, failure status + if it could not be. + +--*/ + +{ + PDEVICE Device = IpxDevice; + PIPX_SEND_RESERVED Reserved; + PSINGLE_LIST_ENTRY s; + PLIST_ENTRY p; + PRIP_PACKET RipPacket; + TDI_ADDRESS_IPX RemoteAddress; + TDI_ADDRESS_IPX LocalAddress; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + PNDIS_BUFFER pNdisIpxBuff; + + + // + // Make sure we only queue a request for net 0xffffffff if we + // are auto-detecting, because we assume that in other places. + // + + if ((Network == 0xffffffff) && + (Device->AutoDetectState != AUTO_DETECT_STATE_RUNNING)) { + + return STATUS_BAD_NETWORK_PATH; + + } + + // + // Try to get a packet to use for the RIP request. We + // allocate this now, but check if it succeeded later, + // to make the locking work better (we need to keep + // the lock between when we check for an existing + // request on this network and when we queue this + // request). + // + + s = IpxPopSendPacket (Device); + + // + // There was no router table entry for this network, first see + // if there is already a pending request for this route. + // + + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + if (Operation == RIP_REQUEST) { + + for (p = Device->WaitingRipPackets.Flink; + p != &Device->WaitingRipPackets; + p = p->Flink) { + + Reserved = CONTAINING_RECORD (p, IPX_SEND_RESERVED, WaitLinkage); + + // + // Skip responses. + // + + if (Reserved->u.SR_RIP.RetryCount >= 0xfe) { + continue; + } + + if (Reserved->u.SR_RIP.Network == Network && + !Reserved->u.SR_RIP.RouteFound) { + + // + // There is already one pending, put back the packet if + // we got one (we hold the lock already). + // + + if (s != NULL) { + IPX_PUSH_ENTRY_LIST (&Device->SendPacketList, s, &Device->SListsLock); + } + IPX_FREE_LOCK (&Device->Lock, LockHandle); + return STATUS_PENDING; + } + } + + } + + + if (s == NULL) { + IPX_FREE_LOCK (&Device->Lock, LockHandle); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Reserved = CONTAINING_RECORD (s, IPX_SEND_RESERVED, PoolLinkage); + + // + // We have the packet, fill it in for this request. + // + + Reserved->Identifier = IDENTIFIER_RIP_INTERNAL; + Reserved->SendInProgress = FALSE; + Reserved->DestinationType = DESTINATION_BCAST; + Reserved->u.SR_RIP.CurrentNicId = 0; + Reserved->u.SR_RIP.NoIdAdvance = FALSE; + switch (Operation) { + case RIP_REQUEST: Reserved->u.SR_RIP.RetryCount = 0; break; + case RIP_RESPONSE: Reserved->u.SR_RIP.RetryCount = 0xfe; break; + case RIP_DOWN: Reserved->u.SR_RIP.RetryCount = 0xff; break; + } + Reserved->u.SR_RIP.RouteFound = FALSE; + Reserved->u.SR_RIP.Network = Network; + Reserved->u.SR_RIP.SendTime = Device->RipSendTime; + + // + // We aren't guaranteed that this is the case for packets + // on the free list. + // + + pNdisIpxBuff = NDIS_BUFFER_LINKAGE (Reserved->HeaderBuffer); + NDIS_BUFFER_LINKAGE (pNdisIpxBuff) = NULL; + + // + // Fill in the IPX header at the standard offset (for sending + // to actual bindings it will be moved around if needed). We + // have to construct the local and remote addresses so they + // are in the format that IpxConstructHeader expects. + // + + RemoteAddress.NetworkAddress = Network; + RtlCopyMemory (RemoteAddress.NodeAddress, BroadcastAddress, 6); + RemoteAddress.Socket = RIP_SOCKET; + + RtlCopyMemory (&LocalAddress, &Device->SourceAddress, FIELD_OFFSET(TDI_ADDRESS_IPX,Socket)); + LocalAddress.Socket = RIP_SOCKET; + + IpxConstructHeader( +// &Reserved->Header[Device->IncludedHeaderOffset], + &Reserved->Header[MAC_HEADER_SIZE], + sizeof(IPX_HEADER) + sizeof (RIP_PACKET), + RIP_PACKET_TYPE, + &RemoteAddress, + &LocalAddress); + + // + // Fill in the RIP request also. + // + +#if 0 + RipPacket = (PRIP_PACKET)(&Reserved->Header[Device->IncludedHeaderOffset + sizeof(IPX_HEADER)]); +#endif + RipPacket = (PRIP_PACKET)(&Reserved->Header[MAC_HEADER_SIZE + sizeof(IPX_HEADER)]); + RipPacket->Operation = Operation & 0x7fff; + RipPacket->NetworkEntry.NetworkNumber = Network; + + if (Operation == RIP_REQUEST) { + RipPacket->NetworkEntry.HopCount = REORDER_USHORT(0xffff); + RipPacket->NetworkEntry.TickCount = REORDER_USHORT(0xffff); + } else if (Operation == RIP_RESPONSE) { + RipPacket->NetworkEntry.HopCount = REORDER_USHORT(1); + RipPacket->NetworkEntry.TickCount = REORDER_USHORT(2); // will be modified when sent + } else { + RipPacket->NetworkEntry.HopCount = REORDER_USHORT(16); + RipPacket->NetworkEntry.TickCount = REORDER_USHORT(16); + } + + NdisAdjustBufferLength(pNdisIpxBuff, sizeof(IPX_HEADER) + sizeof(RIP_PACKET)); + // + // Now insert this packet in the queue of pending RIP + // requests and start the timer if needed (this is done + // to ensure the RIP_GRANULARITY milliseconds inter-RIP-packet + // delay). + // + + IPX_DEBUG (RIP, ("RIP %s for network %lx\n", + (Operation == RIP_REQUEST) ? "request" : ((Operation == RIP_RESPONSE) ? "announce" : "down"), + REORDER_ULONG(Network))); + + InsertHeadList( + &Device->WaitingRipPackets, + &Reserved->WaitLinkage); + + ++Device->RipPacketCount; + + if (!Device->RipShortTimerActive) { + + Device->RipShortTimerActive = TRUE; + IpxReferenceDevice (Device, DREF_RIP_TIMER); + + CTEStartTimer( + &Device->RipShortTimer, + 1, // 1 ms, i.e. expire immediately + RipShortTimeout, + (PVOID)Device); + } + + IpxReferenceDevice (Device, DREF_RIP_PACKET); + + IPX_FREE_LOCK (&Device->Lock, LockHandle); + return STATUS_PENDING; + +} /* RipQueueRequest */ + + +VOID +RipSendResponse( + IN PBINDING Binding, + IN TDI_ADDRESS_IPX UNALIGNED * RemoteAddress, + IN PIPX_LOCAL_TARGET LocalTarget + ) + +/*++ + +Routine Description: + + This routine sends a respond to a RIP request from a client -- + this is only used if we have a virtual network and the router + is not bound, and somebody queries on the virtual network. + +Arguments: + + Binding - The binding on which the request was received. + + RemoteAddress - The IPX source address of the request. + + LocalTarget - The local target of the received packet. + +Return Value: + + STATUS_PENDING if the request is queued, failure status + if it could not be. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PIPX_SEND_RESERVED Reserved; + TDI_ADDRESS_IPX LocalAddress; + PNDIS_PACKET Packet; + PIPX_HEADER IpxHeader; + PRIP_PACKET RipPacket; + PDEVICE Device = IpxDevice; + PBINDING MasterBinding; + NDIS_STATUS NdisStatus; + USHORT TickCount; + PNDIS_BUFFER pNdisIpxBuff; + + // + // Get a packet to use for the RIP response. + // + + s = IpxPopSendPacket (Device); + + if (s == NULL) { + return; + } + + IpxReferenceDevice (Device, DREF_RIP_PACKET); + + Reserved = CONTAINING_RECORD (s, IPX_SEND_RESERVED, PoolLinkage); + + // + // We have the packet, fill it in for this request. + // + + Reserved->Identifier = IDENTIFIER_RIP_RESPONSE; + Reserved->DestinationType = DESTINATION_DEF; + CTEAssert (!Reserved->SendInProgress); + Reserved->SendInProgress = TRUE; + + // + // We aren't guaranteed that this is the case for packets + // on the free list. + // + + pNdisIpxBuff = NDIS_BUFFER_LINKAGE (Reserved->HeaderBuffer); + NDIS_BUFFER_LINKAGE (pNdisIpxBuff) = NULL; + + // + // If this binding is a binding set member, round-robin through + // the various bindings when responding. We will get some natural + // round-robinning because broadcast requests are received on + // binding set members in turn, but they are only rotated once + // a second. + // + + if (Binding->BindingSetMember) { + + // + // It's a binding set member, we round-robin the + // responses across all the cards to distribute + // the traffic. + // + + MasterBinding = Binding->MasterBinding; + Binding = MasterBinding->CurrentSendBinding; + MasterBinding->CurrentSendBinding = Binding->NextBinding; + +#ifdef _PNP_POWER + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif + } + + // + // Fill in the IPX header at the correct offset. + // + + LocalAddress.NetworkAddress = Binding->LocalAddress.NetworkAddress; + RtlCopyMemory (LocalAddress.NodeAddress, Binding->LocalAddress.NodeAddress, 6); + LocalAddress.Socket = RIP_SOCKET; +#if 0 + IpxHeader = (PIPX_HEADER)(&Reserved->Header[Binding->DefHeaderSize]); +#endif + IpxHeader = (PIPX_HEADER)(&Reserved->Header[MAC_HEADER_SIZE]); + + IpxConstructHeader( + (PUCHAR)IpxHeader, + sizeof(IPX_HEADER) + sizeof (RIP_PACKET), + RIP_PACKET_TYPE, + RemoteAddress, + &LocalAddress); + + // + // In case the request comes from net 0, fill that in too. + // + + *(UNALIGNED ULONG *)IpxHeader->DestinationNetwork = Binding->LocalAddress.NetworkAddress; + + + // + // Fill in the RIP request. + // + + RipPacket = (PRIP_PACKET)(IpxHeader+1); + + RipPacket->Operation = RIP_RESPONSE; + RipPacket->NetworkEntry.NetworkNumber = Device->VirtualNetworkNumber; + + RipPacket->NetworkEntry.HopCount = REORDER_USHORT(1); + TickCount = (USHORT)(((839 + Binding->MediumSpeed) / Binding->MediumSpeed) + 1); + RipPacket->NetworkEntry.TickCount = REORDER_USHORT(TickCount); + + IPX_DEBUG (RIP, ("RIP response for virtual network %lx\n", + REORDER_ULONG(Device->VirtualNetworkNumber))); + + NdisAdjustBufferLength(pNdisIpxBuff, sizeof(IPX_HEADER) + sizeof(RIP_PACKET)); + // + // Now submit the packet to NDIS. + // + + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); + + if ((NdisStatus = IpxSendFrame( + LocalTarget, + Packet, + sizeof(RIP_PACKET) + sizeof(IPX_HEADER), + sizeof(RIP_PACKET) + sizeof(IPX_HEADER))) != NDIS_STATUS_PENDING) { + + IpxSendComplete( + (NDIS_HANDLE)Binding->Adapter, + Packet, + NdisStatus); + } + +#ifdef _PNP_POWER + if (Binding->BindingSetMember) { + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + } +#endif + return; + +} /* RipSendResponse */ + + +VOID +RipShortTimeout( + CTEEvent * Event, + PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called when the RIP short timer expires. + It is called every RIP_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; + PIPX_SEND_RESERVED Reserved; + PNDIS_PACKET Packet; + USHORT OldNicId, NewNicId; + ULONG OldOffset, NewOffset; + PIPX_HEADER IpxHeader; + PBINDING Binding, MasterBinding; + NDIS_STATUS NdisStatus; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + +#ifdef _PNP_LATER + static IPX_LOCAL_TARGET BroadcastTarget = { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, {0, 0, 0} }; +#else + static IPX_LOCAL_TARGET BroadcastTarget = { 0, { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }; +#endif + + static ULONG ZeroNetwork = 0; +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) +#endif + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + ++Device->RipSendTime; + + if (Device->RipPacketCount == 0) { + + Device->RipShortTimerActive = FALSE; + IPX_FREE_LOCK (&Device->Lock, LockHandle); + IpxDereferenceDevice (Device, DREF_RIP_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->WaitingRipPackets.Flink; + if (p == &Device->WaitingRipPackets) { + IPX_FREE_LOCK (&Device->Lock, LockHandle); + break; + } + + Reserved = CONTAINING_RECORD (p, IPX_SEND_RESERVED, WaitLinkage); + + if ((Reserved->u.SR_RIP.RouteFound) && (!Reserved->SendInProgress)) { + + (VOID)RemoveHeadList (&Device->WaitingRipPackets); + Reserved->Identifier = IDENTIFIER_IPX; + IPX_PUSH_ENTRY_LIST (&Device->SendPacketList, &Reserved->PoolLinkage, &Device->SListsLock); + --Device->RipPacketCount; + + // + // It is OK to do this with the lock held because + // it won't be the last one (we have the RIP_TIMER ref). + // + + IpxDereferenceDevice (Device, DREF_RIP_PACKET); + continue; + } + + if ((((SHORT)(Device->RipSendTime - Reserved->u.SR_RIP.SendTime)) < 0) || + Reserved->SendInProgress) { + IPX_FREE_LOCK (&Device->Lock, LockHandle); + break; + } + + (VOID)RemoveHeadList (&Device->WaitingRipPackets); + + // + // Find the right binding to send to. If NoIdAdvance + // is set, then the binding doesn't need to be changed + // this time (this means we wrapped last time). + // + + OldNicId = Reserved->u.SR_RIP.CurrentNicId; + + if (!Reserved->u.SR_RIP.NoIdAdvance) { + + BOOLEAN FoundNext = FALSE; + +#ifdef _PNP_POWER +// +// To maintain the lock order, release Device lock here and re-acquire later +// + USHORT StartId; + + if (Device->ValidBindings == 0) { + IPX_DEBUG(PNP, ("ValidBindings 0 in RipShortTimeOut\n")); + + Device->RipShortTimerActive = FALSE; + IPX_FREE_LOCK (&Device->Lock, LockHandle); + IpxDereferenceDevice (Device, DREF_RIP_TIMER); + return; + } + + StartId = (USHORT)((OldNicId % MIN (Device->MaxBindings, Device->ValidBindings)) + 1); + + NewNicId = StartId; + IPX_FREE_LOCK (&Device->Lock, LockHandle); + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#else + + USHORT StartId = (USHORT)((OldNicId % Device->BindingCount) + 1); + + NewNicId = StartId; +#endif + do { + +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, NewNicId); +#else + Binding = Device->Bindings[NewNicId]; +#endif + if (Reserved->u.SR_RIP.Network != 0xffffffff) { + + // + // We are looking for a real net; check that + // the next binding is valid. If it is a WAN + // binding, we don't send queries if the router + // is bound. If it is a LAN binding, we don't + // send queries if we are configured for + // SingleNetworkActive and the WAN is up. + // We also don't send queries on binding set + // members which aren't masters. + // + + if ((Binding != NULL) + && + ((!Binding->Adapter->MacInfo.MediumAsync) || + (!Device->UpperDriverBound[IDENTIFIER_RIP])) + && + ((Binding->Adapter->MacInfo.MediumAsync) || + (!Device->SingleNetworkActive) || + (!Device->ActiveNetworkWan)) + && + ((!Binding->BindingSetMember) || + (Binding->CurrentSendBinding))) { + + FoundNext = TRUE; + break; + } + + } else { + + // + // We are sending out the initial request to net + // 0xffffffff, to generate traffic so we can figure + // out our real network number. We don't do this + // to nets that already have a number and we don't + // do it on WAN links. We also don't do it on + // auto-detect nets if we have found the default. + // + + + if ((Binding != NULL) && + (Binding->TentativeNetworkAddress == 0) && + (!Binding->Adapter->MacInfo.MediumAsync) && + (!Binding->AutoDetect || !Binding->Adapter->DefaultAutoDetected)) { + FoundNext = TRUE; + break; + } + } +#ifdef _PNP_POWER + // + // [BUGBUGZZ] Why cycle thru the entire list? + // + NewNicId = (USHORT)((NewNicId % MIN (Device->MaxBindings, Device->ValidBindings)) + 1); +#else + NewNicId = (USHORT)((NewNicId % Device->BindingCount) + 1); +#endif + } while (NewNicId != StartId); + + if (!FoundNext) { + + // + // Nothing more needs to be done with this packet, + // leave it off the queue and since we didn't send + // a packet we can check for more. + // +#ifndef _PNP_POWER + // + // This was released above (before the BindAccessLock was taken + // + IPX_FREE_LOCK (&Device->Lock, LockHandle); +#endif + RipCleanupPacket(Device, Reserved); +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + IPX_PUSH_ENTRY_LIST (&Device->SendPacketList, &Reserved->PoolLinkage, &Device->SListsLock); + --Device->RipPacketCount; + IpxDereferenceDevice (Device, DREF_RIP_PACKET); + continue; + + } + +#ifdef _PNP_POWER + + IPX_DEBUG(RIP, ("RIP: FoundNext: %lx, StartId: %lx, OldNicId: %lx, NewNicId: %lx\n", FoundNext, StartId, OldNicId, NewNicId)); + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); + + // + // Re-acquire the Device lock + // + IPX_GET_LOCK (&Device->Lock, &LockHandle); +#endif + + Reserved->u.SR_RIP.CurrentNicId = NewNicId; + + // + // Move the data around if needed. + // + +#if 0 + if (OldNicId != NewNicId) { + + if (OldNicId == 0) { + OldOffset = Device->IncludedHeaderOffset; + } else { + OldOffset = Device->Bindings[OldNicId]->BcMcHeaderSize; + } + + NewOffset = Binding->BcMcHeaderSize; + + if (OldOffset != NewOffset) { + + RtlMoveMemory( + &Reserved->Header[NewOffset], + &Reserved->Header[OldOffset], + sizeof(IPX_HEADER) + sizeof(RIP_PACKET)); + + } + + } +#endif + + if (NewNicId <= OldNicId) { + + // + // We found a new binding but we wrapped, so increment + // the counter. If we have done all the resends, or + // this is a response (indicated by retry count of 0xff; + // they are only sent once) then clean up. + // + + if ((Reserved->u.SR_RIP.RetryCount >= 0xfe) || + ((++Reserved->u.SR_RIP.RetryCount) == Device->RipCount)) { + + // + // This packet is stale, clean it up and continue. + // + + IPX_FREE_LOCK (&Device->Lock, LockHandle); +#ifdef _PNP_POWER + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif + RipCleanupPacket(Device, Reserved); + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + IPX_PUSH_ENTRY_LIST (&Device->SendPacketList, &Reserved->PoolLinkage, &Device->SListsLock); + --Device->RipPacketCount; + IpxDereferenceDevice (Device, DREF_RIP_PACKET); + + } else { + + // + // We wrapped, so put ourselves back in the queue + // at the end. + // + + Reserved->u.SR_RIP.SendTime = (USHORT)(Device->RipSendTime + Device->RipTimeout - 1); + Reserved->u.SR_RIP.NoIdAdvance = TRUE; + InsertTailList (&Device->WaitingRipPackets, &Reserved->WaitLinkage); + +#ifdef _PNP_POWER + // + // Free the Device lock before deref'ing the Binding so we maintain + // the lock order: BindingAccess > GlobalInterLock > Device + // + IPX_FREE_LOCK (&Device->Lock, LockHandle); + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_GET_LOCK (&Device->Lock, &LockHandle); +#endif + } + + continue; + + } +#ifdef _PNP_POWER +// +// To prevent the re-acquire of the device lock, this is moved up... +// + // + // Send it again as soon as possible (it we just wrapped, then + // we will have put ourselves at the tail and won't get here). + // + + InsertHeadList (&Device->WaitingRipPackets, &Reserved->WaitLinkage); + + CTEAssert (Reserved->Identifier == IDENTIFIER_RIP_INTERNAL); + CTEAssert (!Reserved->SendInProgress); + Reserved->SendInProgress = TRUE; + + IPX_FREE_LOCK (&Device->Lock, LockHandle); +#endif + } else { + + // + // Next time we need to advance the binding. + // + + Reserved->u.SR_RIP.NoIdAdvance = FALSE; + NewNicId = OldNicId; +#ifdef _PNP_POWER + // + // Send it again as soon as possible (it we just wrapped, then + // we will have put ourselves at the tail and won't get here). + // + + InsertHeadList (&Device->WaitingRipPackets, &Reserved->WaitLinkage); + + CTEAssert (Reserved->Identifier == IDENTIFIER_RIP_INTERNAL); + CTEAssert (!Reserved->SendInProgress); + Reserved->SendInProgress = TRUE; + + IPX_FREE_LOCK (&Device->Lock, LockHandle); + + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); + Binding = NIC_ID_TO_BINDING(Device, NewNicId); + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#else + Binding = Device->Bindings[NewNicId]; +#endif + + } +#ifndef _PNP_POWER + // + // Send it again as soon as possible (it we just wrapped, then + // we will have put ourselves at the tail and won't get here). + // + + InsertHeadList (&Device->WaitingRipPackets, &Reserved->WaitLinkage); + + CTEAssert (Reserved->Identifier == IDENTIFIER_RIP_INTERNAL); + CTEAssert (!Reserved->SendInProgress); + Reserved->SendInProgress = TRUE; + + IPX_FREE_LOCK (&Device->Lock, LockHandle); +#endif + // + // This packet should be sent on binding NewNicId; first + // move the data to the right location for the current + // binding. + // +#ifdef _PNP_POWER + CTEAssert (Binding == NIC_ID_TO_BINDING(Device, NewNicId)); // temp, just to make sure +#else + CTEAssert (Binding == Device->Bindings[NewNicId]); // temp, just to make sure +#endif +// NewOffset = Binding->BcMcHeaderSize; + + // + // Now submit the packet to NDIS. + // + + Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]); +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(&BroadcastTarget, NewNicId); +#else + BroadcastTarget.NicId = NewNicId; +#endif + + // + // Modify the header so the packet comes from this + // specific adapter, not the virtual network. + // + + // IpxHeader = (PIPX_HEADER)(&Reserved->Header[NewOffset]); + IpxHeader = (PIPX_HEADER)(&Reserved->Header[MAC_HEADER_SIZE]); + + if (Reserved->u.SR_RIP.Network == 0xffffffff) { + *(UNALIGNED ULONG *)IpxHeader->SourceNetwork = 0; + } else { + *(UNALIGNED ULONG *)IpxHeader->SourceNetwork = Binding->LocalAddress.NetworkAddress; + } + + if (Reserved->u.SR_RIP.RetryCount < 0xfe) { + + // + // This is an outgoing query. We round-robin these through + // binding sets. + // + + if (Binding->BindingSetMember) { + + // + // Shouldn't have any binding sets during initial + // discovery. + // + + CTEAssert (Reserved->u.SR_RIP.Network != 0xffffffff); + + // + // If we are in a binding set, then use the current binding + // in the set for this send, and advance the current binding. + // The places we have used Binding before here will be fine + // since the binding set members all have the same media + // and frame type. + // + + CTEAssert (Binding->CurrentSendBinding); // should be a master. + MasterBinding = Binding; + Binding = MasterBinding->CurrentSendBinding; + MasterBinding->CurrentSendBinding = Binding->NextBinding; +#ifdef _PNP_POWER + // + // [BUGBUGZZ]: We dont have a lock here - the masterbinding could be bogus + // + IpxDereferenceBinding1(MasterBinding, BREF_DEVICE_ACCESS); + IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif + } + } + + + RtlCopyMemory (IpxHeader->SourceNode, Binding->LocalAddress.NodeAddress, 6); + + // + // Bug# 6485 + // Rip request, general or specific, is putting the network of the + // node to which the route has to be found in the ipx header remote + // network field. Some novell routers don't like that. This network + // field should be 0. + // + { + PRIP_PACKET RipPacket = (PRIP_PACKET)(&Reserved->Header[MAC_HEADER_SIZE + sizeof(IPX_HEADER)]); + + if (RipPacket->Operation != RIP_REQUEST) { + *(UNALIGNED ULONG *)IpxHeader->DestinationNetwork = Binding->LocalAddress.NetworkAddress; + } else { + *(UNALIGNED ULONG *)IpxHeader->DestinationNetwork = 0; + } + } + + // + // If this is a RIP_RESPONSE, set the tick count for this + // binding. + // + + if (Reserved->u.SR_RIP.RetryCount == 0xfe) { + + PRIP_PACKET RipPacket = (PRIP_PACKET)(IpxHeader+1); + USHORT TickCount = (USHORT) + (((839 + Binding->MediumSpeed) / Binding->MediumSpeed) + 1); + + RipPacket->NetworkEntry.TickCount = REORDER_USHORT(TickCount); + + } + + if ((NdisStatus = IpxSendFrame( + &BroadcastTarget, + Packet, + sizeof(RIP_PACKET) + sizeof(IPX_HEADER), + sizeof(RIP_PACKET) + sizeof(IPX_HEADER))) != NDIS_STATUS_PENDING) { + + IpxSendComplete( + (NDIS_HANDLE)Binding->Adapter, + Packet, + NdisStatus); + } +#ifdef _PNP_POWER + IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS); +#endif + + break; + + } + + CTEStartTimer( + &Device->RipShortTimer, + RIP_GRANULARITY, + RipShortTimeout, + (PVOID)Device); + +} /* RipShortTimeout */ + + +VOID +RipLongTimeout( + CTEEvent * Event, + PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called when the RIP long timer expires. + It is called every minute and handles periodic re-RIPping + to ensure that entries are accurate, as well as aging out + of entries if the rip router is not bound. + +Arguments: + + Event - The event used to queue the timer. + + Context - The context, which is the device pointer. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = (PDEVICE)Context; + PROUTER_SEGMENT RouterSegment; + PIPX_ROUTE_ENTRY RouteEntry; + UINT Segment; + UINT i; + PBINDING Binding; + IPX_DEFINE_LOCK_HANDLE(LockHandle) + + + // + // Rotate the broadcast receiver on all binding sets. + // We can loop up to HighestExternal only since we + // are only interested in finding binding set masters. + // +#ifdef _PNP_POWER + IPX_DEFINE_LOCK_HANDLE(LockHandle1) + IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1); +#endif + { + ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId); + + for (i = 1; i <= Index; i++) { + +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, i); +#else + Binding = Device->Bindings[i]; +#endif + if ((Binding != NULL) && + (Binding->CurrentSendBinding)) { + + // + // It is a master, so find the current broadcast + // receiver, then advance it. + // + + while (TRUE) { + if (Binding->ReceiveBroadcast) { + Binding->ReceiveBroadcast = FALSE; + Binding->NextBinding->ReceiveBroadcast = TRUE; + break; + } else { + Binding = Binding->NextBinding; + } + } + } + } + } +#ifdef _PNP_POWER + IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1); +#endif + + + // + // If RIP is bound, we don't do any of this, and + // we stop the timer from running. + // + + if (Device->UpperDriverBound[IDENTIFIER_RIP]) { + IpxDereferenceDevice (Device, DREF_LONG_TIMER); + return; + } + + + // + // If we have a virtual net, do our periodic broadcast. + // + + if (Device->RipResponder) { + (VOID)RipQueueRequest (Device->VirtualNetworkNumber, RIP_RESPONSE); + } + + + // + // Update the real counters from the temp ones. + // + + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DatagramBytesSent, + Device->TempDatagramBytesSent); + Device->Statistics.DatagramsSent += Device->TempDatagramsSent; + + Device->TempDatagramBytesSent = 0; + Device->TempDatagramsSent = 0; + + ADD_TO_LARGE_INTEGER( + &Device->Statistics.DatagramBytesReceived, + Device->TempDatagramBytesReceived); + Device->Statistics.DatagramsReceived += Device->TempDatagramsReceived; + + Device->TempDatagramBytesReceived = 0; + Device->TempDatagramsReceived = 0; + + + // + // We need to scan each hash bucket to see if there + // are any active entries which need to be re-RIPped + // for. We also scan for entries that should be timed + // out. + // + + for (Segment = 0; Segment < Device->SegmentCount; Segment++) { + + RouterSegment = &IpxDevice->Segments[Segment]; + + // + // Don't take the lock if the bucket is empty. + // + + if (RouterSegment->Entries.Flink == &RouterSegment->Entries) { + continue; + } + + IPX_GET_LOCK (&Device->SegmentLocks[Segment], &LockHandle); + + // + // Scan through each entry looking for ones to age. + // + + for (RouteEntry = RipGetFirstRoute (Segment); + RouteEntry != (PIPX_ROUTE_ENTRY)NULL; + RouteEntry = RipGetNextRoute (Segment)) { + + if (RouteEntry->Flags & IPX_ROUTER_PERMANENT_ENTRY) { + continue; + } + + ++RouteEntry->Timer; + if (RouteEntry->Timer >= Device->RipUsageTime) { + + RipDeleteRoute (Segment, RouteEntry); + IpxFreeMemory(RouteEntry, sizeof(IPX_ROUTE_ENTRY), MEMORY_RIP, "RouteEntry"); + continue; + + } + + // + // See if we should re-RIP for this segment. It has + // to have been around for RipAgeTime, and we also + // make sure that the Timer is not too high to + // prevent us from re-RIPping on unused routes. + // + + ++RouteEntry->PRIVATE.Reserved[0]; + + if ((RouteEntry->PRIVATE.Reserved[0] >= Device->RipAgeTime) && + (RouteEntry->Timer <= Device->RipAgeTime)) { + + // + // If we successfully queue a request, then reset + // Reserved[0] so we don't re-RIP for a while. + // + + if (RipQueueRequest (*(UNALIGNED ULONG *)RouteEntry->Network, RIP_REQUEST) == STATUS_PENDING) { + RouteEntry->PRIVATE.Reserved[0] = 0; + } + } + } + + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + + } + + + // + // Now restart the timer for the next timeout. + // + + if (Device->State == DEVICE_STATE_OPEN) { + + CTEStartTimer( + &Device->RipLongTimer, + 60000, // one minute timeout + RipLongTimeout, + (PVOID)Device); + + } else { + + // + // Send a DOWN packet if needed, then stop ourselves. + // + + if (Device->RipResponder) { + + if (RipQueueRequest (Device->VirtualNetworkNumber, RIP_DOWN) != STATUS_PENDING) { + + // + // We need to kick this event because the packet completion + // won't. + // + + KeSetEvent( + &Device->UnloadEvent, + 0L, + FALSE); + } + } + + IpxDereferenceDevice (Device, DREF_LONG_TIMER); + + } + +} /* RipLongTimeout */ + + +VOID +RipCleanupPacket( + IN PDEVICE Device, + IN PIPX_SEND_RESERVED RipReserved + ) + +/*++ + +Routine Description: + + This routine cleans up when a RIP packet times out. + +Arguments: + + Device - The device. + + RipReserved - The ProtocolReserved section of the RIP packet. + +Return Value: + + None. + +--*/ + +{ + ULONG Segment; + IPX_DEFINE_LOCK_HANDLE_PARAM (LockHandle) + + if (RipReserved->u.SR_RIP.RetryCount < 0xfe) { + + if (RipReserved->u.SR_RIP.Network != 0xffffffff) { + + IPX_DEBUG (RIP, ("Timing out RIP for network %lx\n", + REORDER_ULONG(RipReserved->u.SR_RIP.Network))); + + Segment = RipGetSegment ((PUCHAR)&RipReserved->u.SR_RIP.Network); + IPX_GET_LOCK (&Device->SegmentLocks[Segment], &LockHandle); + + // + // Fail all datagrams, etc. that were waiting for + // this route. This call releases the lock. + // + + RipHandleRoutePending( + Device, + (PUCHAR)&(RipReserved->u.SR_RIP.Network), + LockHandle, + FALSE, + NULL, + 0, + 0); + + } else { + + // + // This was the initial query looking for networks -- + // signal the init thread which is waiting. + // + + IPX_DEBUG (AUTO_DETECT, ("Signalling auto-detect event\n")); + KeSetEvent( + &Device->AutoDetectEvent, + 0L, + FALSE); + + } + + } else if (RipReserved->u.SR_RIP.RetryCount == 0xff) { + + // + // This is a DOWN message, set the device event that + // is waiting for it to complete. + // + + KeSetEvent( + &Device->UnloadEvent, + 0L, + FALSE); + } + + // + // Put the RIP packet back in the pool. + // + + RipReserved->Identifier = IDENTIFIER_IPX; + +} /* RipCleanupPacket */ + + +VOID +RipProcessResponse( + IN PDEVICE Device, + IN PIPX_LOCAL_TARGET LocalTarget, + IN RIP_PACKET UNALIGNED * RipPacket + ) + +/*++ + +Routine Description: + + This routine processes a RIP response from the specified + local target, indicating a route to the network in the RIP + header. + +Arguments: + + Device - The device. + + LocalTarget - The router that the frame was received from. + + RipPacket - The RIP response header. + +Return Value: + + None. + +--*/ + +{ + PIPX_SEND_RESERVED RipReserved; // ProtocolReserved of RIP packet + ULONG Segment; + PIPX_ROUTE_ENTRY RouteEntry, OldRouteEntry; + PLIST_ENTRY p; + IPX_DEFINE_LOCK_HANDLE_PARAM (LockHandle) + + // + // Since we have received a RIP response for this network. + // kill the waiting RIP packets for it if it exists. + // + + IPX_GET_LOCK (&Device->Lock, &LockHandle); + + for (p = Device->WaitingRipPackets.Flink; + p != &Device->WaitingRipPackets; + p = p->Flink) { + + RipReserved = CONTAINING_RECORD (p, IPX_SEND_RESERVED, WaitLinkage); + + if (RipReserved->u.SR_RIP.RetryCount >= 0xfe) { + continue; + } + + if (RipReserved->u.SR_RIP.Network == + RipPacket->NetworkEntry.NetworkNumber) { + break; + } + + } + + if (p == &Device->WaitingRipPackets) { + + // + // No packets pending on this, return. + // + + IPX_FREE_LOCK (&Device->Lock, LockHandle); + return; + } + + + // + // Put the RIP packet back in the pool. + // + + IPX_DEBUG (RIP, ("Got RIP response for network %lx\n", + REORDER_ULONG(RipPacket->NetworkEntry.NetworkNumber))); + + RipReserved->u.SR_RIP.RouteFound = TRUE; + if (!RipReserved->SendInProgress) { + + // + // If the send is done destroy it now, otherwise + // when it pops up in RipShortTimeout it will get + // destroyed because RouteFound is TRUE. + // + + RemoveEntryList (p); + RipReserved->Identifier = IDENTIFIER_IPX; + IPX_PUSH_ENTRY_LIST (&Device->SendPacketList, &RipReserved->PoolLinkage, &Device->SListsLock); + --Device->RipPacketCount; + IPX_FREE_LOCK (&Device->Lock, LockHandle); + + IpxDereferenceDevice (Device, DREF_RIP_PACKET); + + } else { + + IPX_FREE_LOCK (&Device->Lock, LockHandle); + } + + + // + // Try to allocate and add a router segment unless the + // RIP router is active...if we don't that is fine, we'll + // just re-RIP later. + // + + Segment = RipGetSegment ((PUCHAR)&RipPacket->NetworkEntry.NetworkNumber); + + if (!Device->UpperDriverBound[IDENTIFIER_RIP]) { + + RouteEntry = IpxAllocateMemory(sizeof(IPX_ROUTE_ENTRY), MEMORY_RIP, "RouteEntry"); + if (RouteEntry != (PIPX_ROUTE_ENTRY)NULL) { + + *(UNALIGNED LONG *)RouteEntry->Network = RipPacket->NetworkEntry.NetworkNumber; +#ifdef _PNP_POWER + RouteEntry->NicId = NIC_FROM_LOCAL_TARGET(LocalTarget); + RouteEntry->NdisBindingContext = NIC_ID_TO_BINDING(Device, RouteEntry->NicId)->Adapter->NdisBindingHandle; + // BUGBUG: What if this is NULL?? +#else + RouteEntry->NicId = LocalTarget->NicId; + RouteEntry->NdisBindingContext = Device->Bindings[LocalTarget->NicId]->Adapter->NdisBindingHandle; // BUGBUG: What if this is NULL?? +#endif + RouteEntry->Flags = 0; + RouteEntry->Timer = 0; + RouteEntry->PRIVATE.Reserved[0] = 0; + RouteEntry->Segment = Segment; + RouteEntry->HopCount = REORDER_USHORT(RipPacket->NetworkEntry.HopCount); + RouteEntry->TickCount = REORDER_USHORT(RipPacket->NetworkEntry.TickCount); + InitializeListHead (&RouteEntry->AlternateRoute); + InitializeListHead (&RouteEntry->NicLinkage); + RtlCopyMemory (RouteEntry->NextRouter, LocalTarget->MacAddress, 6); + + IPX_GET_LOCK (&Device->SegmentLocks[Segment], &LockHandle); + + // + // Replace any existing routes. This is OK because once + // we get the first response to a RIP packet on a given + // route, we will take the packet out of the queue and + // ignore further responses. We will only get a bad route + // if we do two requests really quickly and there + // are two routes, and the second response to the first + // request is picked up as the first response to the second + // request. + // + + if ((OldRouteEntry = RipGetRoute (Segment, (PUCHAR)&(RipPacket->NetworkEntry.NetworkNumber))) != NULL) { + + // + // These are saved so timeouts etc. happen right. + // + + RouteEntry->Flags = OldRouteEntry->Flags; + RouteEntry->Timer = OldRouteEntry->Timer; + + RipDeleteRoute (Segment, OldRouteEntry); + IpxFreeMemory(OldRouteEntry, sizeof(IPX_ROUTE_ENTRY), MEMORY_RIP, "RouteEntry"); + + } + + RipAddRoute (Segment, RouteEntry); + + } else { + + IPX_GET_LOCK (&Device->SegmentLocks[Segment], &LockHandle); + } + + } else { + + IPX_GET_LOCK (&Device->SegmentLocks[Segment], &LockHandle); + } + + // + // Complete all datagrams etc. that were waiting + // for this route. This call releases the lock. + // + + RipHandleRoutePending( + Device, + (PUCHAR)&(RipPacket->NetworkEntry.NetworkNumber), + LockHandle, + TRUE, + LocalTarget, + (USHORT)(REORDER_USHORT(RipPacket->NetworkEntry.HopCount)), + (USHORT)(REORDER_USHORT(RipPacket->NetworkEntry.TickCount)) + ); + +} /* RipProcessResponse */ + +VOID +RipHandleRoutePending( + IN PDEVICE Device, + IN UCHAR Network[4], + IN CTELockHandle LockHandle, + IN BOOLEAN Success, + IN OPTIONAL PIPX_LOCAL_TARGET LocalTarget, + IN OPTIONAL USHORT HopCount, + IN OPTIONAL USHORT TickCount + ) + +/*++ + +Routine Description: + + This routine cleans up pending datagrams, find route + requests, and GET_LOCAL_TARGET ioctls that were + waiting for a route to be found. + + THIS ROUTINE IS CALLED WITH THE SEGMENT LOCK HELD AND + RETURNS WITH IT RELEASED. + +Arguments: + + Device - The device. + + Network - The network in question. + + LockHandle - The handle used to acquire the lock. + + Success - TRUE if the route was successfully found. + + LocalTarget - If Success is TRUE, the local target for the route. + + HopCount - If Success is TRUE, the hop count for the route, + in machine order. + + TickCount - If Success is TRUE, the tick count for the route, + in machine order. + +Return Value: + + None. + +--*/ + +{ + + LIST_ENTRY DatagramList; + LIST_ENTRY FindRouteList; + LIST_ENTRY GetLocalTargetList; + LIST_ENTRY ReripNetnumList; + PIPX_SEND_RESERVED WaitReserved; // ProtocolReserved of waiting packet + PIPX_FIND_ROUTE_REQUEST FindRouteRequest; + PREQUEST GetLocalTargetRequest; + PREQUEST ReripNetnumRequest; + PISN_ACTION_GET_LOCAL_TARGET GetLocalTarget; + PIPX_NETNUM_DATA NetnumData; + ULONG Segment; + PBINDING Binding, SendBinding; + PLIST_ENTRY p; + PNDIS_PACKET Packet; + PIPX_HEADER IpxHeader; + ULONG HeaderSize; + NDIS_STATUS NdisStatus; + ULONG NetworkUlong = *(UNALIGNED ULONG *)Network; + + + InitializeListHead (&DatagramList); + InitializeListHead (&FindRouteList); + InitializeListHead (&GetLocalTargetList); + InitializeListHead (&ReripNetnumList); + + + // + // Put all packets that were waiting for a route to + // this network on DatagramList. They will be sent + // or failed later in the routine. + // + + Segment = RipGetSegment (Network); + + p = Device->Segments[Segment].WaitingForRoute.Flink; + + while (p != &Device->Segments[Segment].WaitingForRoute) { + + WaitReserved = CONTAINING_RECORD (p, IPX_SEND_RESERVED, WaitLinkage); + p = p->Flink; +#if 0 + if (*(UNALIGNED ULONG *)(((PIPX_HEADER)(&WaitReserved->Header[Device->IncludedHeaderOffset]))->DestinationNetwork) == + NetworkUlong) { +#endif + if (*(UNALIGNED ULONG *)(((PIPX_HEADER)(&WaitReserved->Header[MAC_HEADER_SIZE]))->DestinationNetwork) == + NetworkUlong) { + + RemoveEntryList (&WaitReserved->WaitLinkage); + InsertTailList (&DatagramList, &WaitReserved->WaitLinkage); + } + + } + + // + // Put all find route requests for this network on + // FindRouteList. They will be completed later in the + // routine. + // + + p = Device->Segments[Segment].FindWaitingForRoute.Flink; + + while (p != &Device->Segments[Segment].FindWaitingForRoute) { + + FindRouteRequest = CONTAINING_RECORD (p, IPX_FIND_ROUTE_REQUEST, Linkage); + p = p->Flink; + if (*(UNALIGNED ULONG *)(FindRouteRequest->Network) == + NetworkUlong) { + + RemoveEntryList (&FindRouteRequest->Linkage); + InsertTailList (&FindRouteList, &FindRouteRequest->Linkage); + } + + } + + // + // Put all get local target action requests for this + // network on GetLocalTargetList. They will be completed + // later in the routine. + // + + p = Device->Segments[Segment].WaitingLocalTarget.Flink; + + while (p != &Device->Segments[Segment].WaitingLocalTarget) { + + GetLocalTargetRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + GetLocalTarget = (PISN_ACTION_GET_LOCAL_TARGET)REQUEST_INFORMATION(GetLocalTargetRequest); + if (GetLocalTarget->IpxAddress.NetworkAddress == NetworkUlong) { + + RemoveEntryList (REQUEST_LINKAGE(GetLocalTargetRequest)); + InsertTailList (&GetLocalTargetList, REQUEST_LINKAGE(GetLocalTargetRequest)); + } + + } + + // + // Put all MIPX_RERIPNETNUM action requests for this + // network on ReripNetnumList. They will be completed + // later in the routine. + // + + p = Device->Segments[Segment].WaitingReripNetnum.Flink; + + while (p != &Device->Segments[Segment].WaitingReripNetnum) { + + ReripNetnumRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + NetnumData = (PIPX_NETNUM_DATA)REQUEST_INFORMATION(ReripNetnumRequest); + if (*(UNALIGNED ULONG *)NetnumData->netnum == NetworkUlong) { + + RemoveEntryList (REQUEST_LINKAGE(ReripNetnumRequest)); + InsertTailList (&ReripNetnumList, REQUEST_LINKAGE(ReripNetnumRequest)); + } + + } + + + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + + // + // For sends we will use the master binding of a binding + // set, but we'll return the real NicId for people who + // want that. + // + + if (Success) { +#ifdef _PNP_POWER + Binding = NIC_ID_TO_BINDING(Device, NIC_FROM_LOCAL_TARGET(LocalTarget)); + + if (Binding->BindingSetMember) { + SendBinding = Binding->MasterBinding; + FILL_LOCAL_TARGET(LocalTarget, MIN( Device->MaxBindings, SendBinding->NicId)); + } else { + SendBinding = Binding; + } +#else + Binding = Device->Bindings[LocalTarget->NicId]; + + if (Binding->BindingSetMember) { + SendBinding = Binding->MasterBinding; + LocalTarget->NicId = SendBinding->NicId; + } else { + SendBinding = Binding; + } +#endif + } + + + // + // Now that the lock is free, process all packets on + // DatagramList. + // + // NOTE: May misorder packets if they come in right now... + // + + for (p = DatagramList.Flink; p != &DatagramList ; ) { + + WaitReserved = CONTAINING_RECORD (p, IPX_SEND_RESERVED, WaitLinkage); + p = p->Flink; + Packet = CONTAINING_RECORD (WaitReserved, NDIS_PACKET, ProtocolReserved[0]); + +#if DBG + CTEAssert (!WaitReserved->SendInProgress); + WaitReserved->SendInProgress = TRUE; +#endif + + if (Success) { + + IPX_DEBUG (RIP, ("Found queued packet %lx\n", WaitReserved)); + + if (REQUEST_INFORMATION(WaitReserved->u.SR_DG.Request) > + SendBinding->RealMaxDatagramSize) { + + IPX_DEBUG (SEND, ("Queued send %d bytes too large (%d)\n", + REQUEST_INFORMATION(WaitReserved->u.SR_DG.Request), + SendBinding->RealMaxDatagramSize)); + + IpxSendComplete( + (NDIS_HANDLE)NULL, + Packet, + STATUS_INVALID_BUFFER_SIZE); + + } else { + +#if 0 + if (WaitReserved->DestinationType == DESTINATION_DEF) { + HeaderSize = SendBinding->DefHeaderSize; + } else { + HeaderSize = SendBinding->BcMcHeaderSize; + } + + IpxHeader = (PIPX_HEADER) + (&WaitReserved->Header[HeaderSize]); +#endif + IpxHeader = (PIPX_HEADER) + (&WaitReserved->Header[MAC_HEADER_SIZE]); + + // + // Move the header to the correct location now that + // we know the NIC ID to send to. + // +#if 0 + if (HeaderSize != Device->IncludedHeaderOffset) { + + RtlMoveMemory( + IpxHeader, + &WaitReserved->Header[Device->IncludedHeaderOffset], + sizeof(IPX_HEADER)); + + } +#endif + + if (Device->MultiCardZeroVirtual || + (IpxHeader->DestinationSocket == SAP_SOCKET)) { + + // + // These frames need to look like they come from the + // local network, not the virtual one. + // + + *(UNALIGNED ULONG *)IpxHeader->SourceNetwork = SendBinding->LocalAddress.NetworkAddress; + RtlCopyMemory (IpxHeader->SourceNode, SendBinding->LocalAddress.NodeAddress, 6); + } + + // + // Fill in the MAC header and submit the frame to NDIS. + // + + if ((NdisStatus = IpxSendFrame( + LocalTarget, + Packet, + REQUEST_INFORMATION(WaitReserved->u.SR_DG.Request) + sizeof(IPX_HEADER), + sizeof(IPX_HEADER))) != NDIS_STATUS_PENDING) { + + IpxSendComplete( + (NDIS_HANDLE)SendBinding->Adapter, + Packet, + NdisStatus); + } + + } + + } else { + + IPX_DEBUG (RIP, ("Timing out packet %lx\n", WaitReserved)); + + IpxSendComplete( + (NDIS_HANDLE)NULL, + Packet, + STATUS_BAD_NETWORK_PATH); + + } + + } + + + // + // Since we round-robin outgoing rip packets, we just use the + // real NicId here for find route and get local target requests. + // We changed LocalTarget->NicId to be the master above. + // + + if (Success) { +#ifdef _PNP_POWER + FILL_LOCAL_TARGET(LocalTarget, MIN( Device->MaxBindings, Binding->NicId)); +#else + LocalTarget->NicId = Binding->NicId; +#endif + } + + for (p = FindRouteList.Flink; p != &FindRouteList ; ) { + + FindRouteRequest = CONTAINING_RECORD (p, IPX_FIND_ROUTE_REQUEST, Linkage); + p = p->Flink; + + if (Success) { + + PUSHORT Counts; + + IPX_DEBUG (RIP, ("Found queued find route %lx\n", FindRouteRequest)); + FindRouteRequest->LocalTarget = *LocalTarget; + + Counts = (PUSHORT)&FindRouteRequest->Reserved2; + Counts[0] = TickCount; + Counts[1] = HopCount; + + } else { + + IPX_DEBUG (RIP, ("Timing out find route %lx\n", FindRouteRequest)); + + } + + (*Device->UpperDrivers[FindRouteRequest->Identifier].FindRouteCompleteHandler)( + FindRouteRequest, + Success); + + } + + for (p = GetLocalTargetList.Flink; p != &GetLocalTargetList ; ) { + + GetLocalTargetRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + GetLocalTarget = (PISN_ACTION_GET_LOCAL_TARGET)REQUEST_INFORMATION(GetLocalTargetRequest); + + if (Success) { + + IPX_DEBUG (RIP, ("Found queued LOCAL_TARGET action %lx\n", GetLocalTargetRequest)); + GetLocalTarget->LocalTarget = *LocalTarget; + REQUEST_INFORMATION(GetLocalTargetRequest) = sizeof(ISN_ACTION_GET_LOCAL_TARGET); + REQUEST_STATUS(GetLocalTargetRequest) = STATUS_SUCCESS; + + } else { + + IPX_DEBUG (RIP, ("Timing out LOCAL_TARGET action %lx\n", GetLocalTargetRequest)); + REQUEST_INFORMATION(GetLocalTargetRequest) = 0; + REQUEST_STATUS(GetLocalTargetRequest) = STATUS_BAD_NETWORK_PATH; + } + + IpxCompleteRequest(GetLocalTargetRequest); + IpxFreeRequest(Device, GetLocalTargetRequest); + + } + + // + // NOTE: LocalTarget->NicId now points to the real binding + // not the master, so we use SendBinding->NicId below. + // + + for (p = ReripNetnumList.Flink; p != &ReripNetnumList ; ) { + + ReripNetnumRequest = LIST_ENTRY_TO_REQUEST(p); + p = p->Flink; + NetnumData = (PIPX_NETNUM_DATA)REQUEST_INFORMATION(ReripNetnumRequest); + + if (Success) { + + IPX_DEBUG (RIP, ("Found queued MIPX_RERIPNETNUM action %lx\n", ReripNetnumRequest)); + NetnumData->hopcount = HopCount; + NetnumData->netdelay = TickCount; + NetnumData->cardnum = (INT)(MIN( Device->MaxBindings, SendBinding->NicId) - 1); + RtlMoveMemory (NetnumData->router, LocalTarget->MacAddress, 6); + + REQUEST_INFORMATION(ReripNetnumRequest) = + FIELD_OFFSET(NWLINK_ACTION, Data[0]) + sizeof(IPX_NETNUM_DATA); + REQUEST_STATUS(ReripNetnumRequest) = STATUS_SUCCESS; + + } else { + + IPX_DEBUG (RIP, ("Timing out MIPX_RERIPNETNUM action %lx\n", ReripNetnumRequest)); + REQUEST_INFORMATION(ReripNetnumRequest) = 0; + REQUEST_STATUS(ReripNetnumRequest) = STATUS_BAD_NETWORK_PATH; + } + + IpxCompleteRequest(ReripNetnumRequest); + IpxFreeRequest(Device, ReripNetnumRequest); + + } + +} /* RipHandleRoutePending */ + + +NTSTATUS +RipInsertLocalNetwork( + IN ULONG Network, + IN USHORT NicId, + IN NDIS_HANDLE NdisBindingContext, + IN USHORT Count + ) + +/*++ + +Routine Description: + + This routine creates a router entry for a local network + and inserts it in the table. + +Arguments: + + Network - The network. + + NicId - The NIC ID used to route packets + + NdisBindingHandle - The binding handle used for NdisSend + + Count - The tick and hop count for this network (will be + 0 for the virtual net and 1 for attached nets) + +Return Value: + + The status of the operation. + +--*/ + +{ + PIPX_ROUTE_ENTRY RouteEntry; + PDEVICE Device = IpxDevice; + ULONG Segment; + IPX_DEFINE_LOCK_HANDLE (LockHandle) + + // + // BUGBUG: We should allocate the memory in the binding/device + // structure itself. + // + + RouteEntry = IpxAllocateMemory(sizeof(IPX_ROUTE_ENTRY), MEMORY_RIP, "RouteEntry"); + if (RouteEntry == (PIPX_ROUTE_ENTRY)NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + Segment = RipGetSegment ((PUCHAR)&Network); + + *(UNALIGNED LONG *)RouteEntry->Network = Network; + RouteEntry->NicId = NicId; + RouteEntry->NdisBindingContext = NdisBindingContext; + + if (NicId == 0) { + RouteEntry->Flags = IPX_ROUTER_PERMANENT_ENTRY; + } else { + RouteEntry->Flags = IPX_ROUTER_PERMANENT_ENTRY | IPX_ROUTER_LOCAL_NET; + } + RouteEntry->Segment = Segment; + RouteEntry->TickCount = Count; + RouteEntry->HopCount = 1; + InitializeListHead (&RouteEntry->AlternateRoute); + InitializeListHead (&RouteEntry->NicLinkage); + + // + // RouteEntry->NextRouter is not used for the virtual net or + // when LOCAL_NET is set (i.e. every net that we will add here). + // + + RtlZeroMemory (RouteEntry->NextRouter, 6); + + IPX_GET_LOCK (&Device->SegmentLocks[Segment], &LockHandle); + + // + // Make sure one doesn't exist. + // + + if (RipGetRoute(Segment, (PUCHAR)&Network) != NULL) { + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + IpxFreeMemory (RouteEntry, sizeof(IPX_ROUTE_ENTRY), MEMORY_RIP, "RouteEntry"); + return STATUS_DUPLICATE_NAME; + } + + // + // Add this new entry. + // + + if (RipAddRoute (Segment, RouteEntry)) { + + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + return STATUS_SUCCESS; + + } else { + + IPX_FREE_LOCK (&Device->SegmentLocks[Segment], LockHandle); + IpxFreeMemory (RouteEntry, sizeof(IPX_ROUTE_ENTRY), MEMORY_RIP, "RouteEntry"); + return STATUS_INSUFFICIENT_RESOURCES; + + } + +} /* RipInsertLocalNetwork */ + + +VOID +RipAdjustForBindingChange( + IN USHORT NicId, + IN USHORT NewNicId, + IN IPX_BINDING_CHANGE_TYPE ChangeType + ) + +/*++ + +Routine Description: + + This routine is called when an auto-detect binding is + deleted or moved, or a WAN line goes down. + + It scans the RIP database for routes equal to this NIC ID + and modifies them appropriately. If ChangeType is + IpxBindingDeleted it will subract one from any NIC IDs + in the database that are higher than NicId. It is assumed + that other code is readjusting the Device->Bindings + array. + +Arguments: + + NicId - The NIC ID of the deleted binding. + + NewNicId - The new NIC ID, for IpxBindingMoved changes. + + ChangeType - Either IpxBindingDeleted, IpxBindingMoved, + or IpxBindingDown. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = IpxDevice; + PIPX_ROUTE_ENTRY RouteEntry; + UINT Segment; + CTELockHandle LockHandle; + + for (Segment = 0; Segment < Device->SegmentCount; Segment++) { + + CTEGetLock (&Device->SegmentLocks[Segment], &LockHandle); + + // + // Scan through each entry comparing the NIC ID. + // + + for (RouteEntry = RipGetFirstRoute (Segment); + RouteEntry != (PIPX_ROUTE_ENTRY)NULL; + RouteEntry = RipGetNextRoute (Segment)) { + + if (RouteEntry->NicId == NicId) { + + if (ChangeType != IpxBindingMoved) { + + IPX_DEBUG (AUTO_DETECT, ("Deleting route entry %lx, binding deleted\n", RouteEntry)); + RipDeleteRoute (Segment, RouteEntry); + + } else { + + IPX_DEBUG (AUTO_DETECT, ("Changing NIC ID for route entry %lx\n", RouteEntry)); + RouteEntry->NicId = NewNicId; + + } +#ifdef _PNP_POWER + // + // If the NicId is 0, we dont adjust the other entries' NicId's - this is to support the removal + // of the Virtual Net # which resides at NicId=0. + // + } else if (NicId && (ChangeType != IpxBindingDown) && (RouteEntry->NicId > NicId)) { +#else + } else if ((ChangeType != IpxBindingDown) && (RouteEntry->NicId > NicId)) { +#endif + IPX_DEBUG (AUTO_DETECT, ("Decrementing NIC ID for route entry %lx\n", RouteEntry)); + --RouteEntry->NicId; + + } + } + + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); + + } + +} /* RipAdjustForBindingChange */ + + +UINT +RipGetSegment( + IN UCHAR Network[4] + ) + +/*++ + +Routine Description: + + This routine returns the correct segment for the specified + network. + +Arguments: + + Network - The network. + +Return Value: + + The segment. + +--*/ + +{ + + ULONG Total; + + Total = Network[0] ^ Network[1] ^ Network[2] ^ Network[3]; + return (Total % IpxDevice->SegmentCount); + +} /* RipGetSegment */ + + +PIPX_ROUTE_ENTRY +RipGetRoute( + IN UINT Segment, + IN UCHAR Network[4] + ) + +/*++ + +Routine Description: + + This routine returns the router table entry for the given + network, which is in the specified segment of the table. + THE SEGMENT LOCK MUST BE HELD. The returned data is valid + until the segment lock is released or other operations + (add/delete) are performed on the segment. + +Arguments: + + Segment - The segment corresponding to the network. + + Network - The network. + +Return Value: + + The router table entry, or NULL if none exists for this network. + +--*/ + +{ + PLIST_ENTRY p; + PROUTER_SEGMENT RouterSegment; + PIPX_ROUTE_ENTRY RouteEntry; + + RouterSegment = &IpxDevice->Segments[Segment]; + + for (p = RouterSegment->Entries.Flink; + p != &RouterSegment->Entries; + p = p->Flink) { + + RouteEntry = CONTAINING_RECORD( + p, + IPX_ROUTE_ENTRY, + PRIVATE.Linkage); + + if ((*(UNALIGNED LONG *)RouteEntry->Network) == + (*(UNALIGNED LONG *)Network)) { + return RouteEntry; + } + } + + return NULL; + +} /* RipGetRoute */ + + +BOOLEAN +RipAddRoute( + IN UINT Segment, + IN PIPX_ROUTE_ENTRY RouteEntry + ) + +/*++ + +Routine Description: + + This routine stores a router table entry in the + table, which must belong in the specified segment. + THE SEGMENT LOCK MUST BE HELD. Storage for the entry + is allocated and filled in by the caller. + +Arguments: + + Segment - The segment corresponding to the network. + + RouteEntry - The router table entry. + +Return Value: + + TRUE if the entry was successfully inserted. + +--*/ + +{ + + IPX_DEBUG (RIP, ("Adding route for network %lx (%d)\n", + REORDER_ULONG(*(UNALIGNED ULONG *)RouteEntry->Network), Segment)); + InsertTailList( + &IpxDevice->Segments[Segment].Entries, + &RouteEntry->PRIVATE.Linkage); + + return TRUE; + +} /* RipAddRoute */ + + +BOOLEAN +RipDeleteRoute( + IN UINT Segment, + IN PIPX_ROUTE_ENTRY RouteEntry + ) + +/*++ + +Routine Description: + + This routine deletes a router table entry in the + table, which must belong in the specified segment. + THE SEGMENT LOCK MUST BE HELD. Storage for the entry + is freed by the caller. + +Arguments: + + Segment - The segment corresponding to the network. + + RouteEntry - The router table entry. + +Return Value: + + TRUE if the entry was successfully deleted. + +--*/ + +{ + + PROUTER_SEGMENT RouterSegment = &IpxDevice->Segments[Segment]; + + IPX_DEBUG (RIP, ("Deleting route for network %lx (%d)\n", + REORDER_ULONG(*(UNALIGNED ULONG *)RouteEntry->Network), Segment)); + + // + // If the current enumeration point for this segment is here, + // adjust the pointer before deleting the entry. We make it + // point to the previous entry so GetNextRoute will work. + // + + if (RouterSegment->EnumerateLocation == &RouteEntry->PRIVATE.Linkage) { + RouterSegment->EnumerateLocation = RouterSegment->EnumerateLocation->Blink; + } + + RemoveEntryList (&RouteEntry->PRIVATE.Linkage); + + return TRUE; + +} /* RipDeleteRoute */ + + +PIPX_ROUTE_ENTRY +RipGetFirstRoute( + IN UINT Segment + ) + +/*++ + +Routine Description: + + This routine returns the first router table entry in the + segment. THE SEGMENT LOCK MUST BE HELD. It is used in + conjunction with RipGetNextRoute to enumerate all the + entries in a segment. + +Arguments: + + Segment - The segment being enumerated. + +Return Value: + + The first router table entry, or NULL if the segment is empty. + +--*/ + +{ + PIPX_ROUTE_ENTRY FirstEntry; + PROUTER_SEGMENT RouterSegment = &IpxDevice->Segments[Segment]; + + RouterSegment->EnumerateLocation = RouterSegment->Entries.Flink; + + if (RouterSegment->EnumerateLocation == &RouterSegment->Entries) { + + return NULL; + + } else { + + FirstEntry = CONTAINING_RECORD( + RouterSegment->EnumerateLocation, + IPX_ROUTE_ENTRY, + PRIVATE.Linkage); + + return FirstEntry; + + } + +} /* RipGetFirstRoute */ + + +PIPX_ROUTE_ENTRY +RipGetNextRoute( + IN UINT Segment + ) + +/*++ + +Routine Description: + + This routine returns the next router table entry in the + segment. THE SEGMENT LOCK MUST BE HELD. It is used in + conjunction with RipGetFirstRoute to enumerate all the + entries in a segment. + + It is illegal to call RipGetNextRoute on a segment + without first calling RipGetFirstRoute. The segment + lock must be held for the duration of the enumeration + of a single segment. It is legal to stop enumerating + the segment in the middle. + +Arguments: + + Segment - The segment being enumerated. + +Return Value: + + The next router table entry, or NULL if the end of the + segment is reached. + +--*/ + +{ + PIPX_ROUTE_ENTRY NextEntry; + PROUTER_SEGMENT RouterSegment = &IpxDevice->Segments[Segment]; + + RouterSegment->EnumerateLocation = RouterSegment->EnumerateLocation->Flink; + + if (RouterSegment->EnumerateLocation == &RouterSegment->Entries) { + + return NULL; + + } else { + + NextEntry = CONTAINING_RECORD( + RouterSegment->EnumerateLocation, + IPX_ROUTE_ENTRY, + PRIVATE.Linkage); + + return NextEntry; + + } + +} /* RipGetNextRoute */ + + +VOID +RipDropRemoteEntries( + VOID + ) + +/*++ + +Routine Description: + + This routine deletes all non-local entries from the + RIP database. It is called when the WAN line goes up + or down and we want to remove all existing entries. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + PDEVICE Device = IpxDevice; + PIPX_ROUTE_ENTRY RouteEntry; + UINT Segment; + CTELockHandle LockHandle; + + for (Segment = 0; Segment < Device->SegmentCount; Segment++) { + + CTEGetLock (&Device->SegmentLocks[Segment], &LockHandle); + + // + // Scan through, deleting everything but local entries. + // + + for (RouteEntry = RipGetFirstRoute (Segment); + RouteEntry != (PIPX_ROUTE_ENTRY)NULL; + RouteEntry = RipGetNextRoute (Segment)) { + + if ((RouteEntry->Flags & IPX_ROUTER_PERMANENT_ENTRY) == 0) { + + IPX_DEBUG (AUTO_DETECT, ("Deleting route entry %lx, dropping remote entries\n", RouteEntry)); + RipDeleteRoute (Segment, RouteEntry); + + } + } + + CTEFreeLock (&Device->SegmentLocks[Segment], LockHandle); + + } + +} /* RipDropRemoteEntries */ + |