2565 lines
71 KiB
C
2565 lines
71 KiB
C
/*++
|
||
|
||
|
||
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 (Device->RealAdapters && (RemoteAddress->NetworkAddress == 0)) {
|
||
FILL_LOCAL_TARGET(LocalTarget, FIRST_REAL_BINDING);
|
||
|
||
RtlCopyMemory (LocalTarget->MacAddress, RemoteAddress->NodeAddress, 6);
|
||
if (ARGUMENT_PRESENT(Counts)) {
|
||
Counts[0] = (USHORT)((839 + NIC_ID_TO_BINDING(Device, FIRST_REAL_BINDING)->MediumSpeed) /
|
||
NIC_ID_TO_BINDING(Device, FIRST_REAL_BINDING)->MediumSpeed); // tick count
|
||
Counts[1] = 1; // hop count
|
||
}
|
||
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.
|
||
// Do real loopback.
|
||
//
|
||
FILL_LOCAL_TARGET(LocalTarget, LOOPBACK_NIC_ID);
|
||
RtlCopyMemory (LocalTarget->MacAddress, NIC_ID_TO_BINDING(Device, LOOPBACK_NIC_ID)->LocalMacAddress.Address, 6);
|
||
|
||
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;
|
||
FILL_LOCAL_TARGET(LocalTarget, RouteEntry->NicId);
|
||
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 = FIRST_REAL_BINDING; i <= Index; i++) {
|
||
Binding = NIC_ID_TO_BINDING(Device, i);
|
||
if ((Binding != (PBINDING)NULL) &&
|
||
(Binding->Adapter->MacInfo.MediumAsync) &&
|
||
(RtlEqualMemory(
|
||
Binding->WanRemoteNode,
|
||
RemoteAddress->NodeAddress,
|
||
6))) {
|
||
FILL_LOCAL_TARGET(LocalTarget, MIN( Device->MaxBindings, Binding->NicId));
|
||
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 = FIRST_REAL_BINDING; i <= Index; i++) {
|
||
Binding = NIC_ID_TO_BINDING(Device, i);
|
||
//
|
||
// Self-directed - loopback
|
||
//
|
||
if ((Binding != (PBINDING)NULL) &&
|
||
(RtlEqualMemory(
|
||
Binding->LocalAddress.NodeAddress,
|
||
RemoteAddress->NodeAddress,
|
||
6))) {
|
||
FILL_LOCAL_TARGET(LocalTarget, LOOPBACK_NIC_ID);
|
||
|
||
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;
|
||
PSLIST_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.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSLIST_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;
|
||
|
||
IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS);
|
||
}
|
||
|
||
//
|
||
// 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);
|
||
}
|
||
|
||
if (Binding->BindingSetMember) {
|
||
IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS);
|
||
}
|
||
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;
|
||
IPX_DEFINE_LOCK_HANDLE(LockHandle1)
|
||
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;
|
||
|
||
//
|
||
// 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);
|
||
do {
|
||
|
||
Binding = NIC_ID_TO_BINDING(Device, NewNicId);
|
||
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;
|
||
}
|
||
}
|
||
//
|
||
// Why cycle thru the entire list?
|
||
//
|
||
NewNicId = (USHORT)((NewNicId % MIN (Device->MaxBindings, Device->ValidBindings)) + 1);
|
||
} 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.
|
||
//
|
||
RipCleanupPacket(Device, Reserved);
|
||
IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1);
|
||
IPX_GET_LOCK (&Device->Lock, &LockHandle);
|
||
|
||
IPX_PUSH_ENTRY_LIST (&Device->SendPacketList, &Reserved->PoolLinkage, &Device->SListsLock);
|
||
--Device->RipPacketCount;
|
||
IpxDereferenceDevice (Device, DREF_RIP_PACKET);
|
||
continue;
|
||
|
||
}
|
||
|
||
|
||
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);
|
||
|
||
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);
|
||
IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS);
|
||
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);
|
||
|
||
//
|
||
// 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);
|
||
}
|
||
|
||
continue;
|
||
|
||
}
|
||
//
|
||
// 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);
|
||
|
||
} else {
|
||
|
||
//
|
||
// Next time we need to advance the binding.
|
||
//
|
||
|
||
Reserved->u.SR_RIP.NoIdAdvance = FALSE;
|
||
NewNicId = OldNicId;
|
||
//
|
||
// 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);
|
||
|
||
}
|
||
//
|
||
// This packet should be sent on binding NewNicId; first
|
||
// move the data to the right location for the current
|
||
// binding.
|
||
//
|
||
CTEAssert (Binding == NIC_ID_TO_BINDING(Device, NewNicId)); // temp, just to make sure
|
||
// NewOffset = Binding->BcMcHeaderSize;
|
||
|
||
//
|
||
// Now submit the packet to NDIS.
|
||
//
|
||
|
||
Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]);
|
||
FILL_LOCAL_TARGET(&BroadcastTarget, NewNicId);
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
|
||
// 303606
|
||
// If we have three lan cards on the same lan with the same fram types,
|
||
// then the first two could be in the binding set, while auto detect rip
|
||
// packet is outstanding for the third card. So the assertion is not
|
||
// necessarily true.
|
||
|
||
// 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.
|
||
//
|
||
|
||
// 303606 not necessarily a master binding
|
||
MasterBinding = Binding->MasterBinding;
|
||
Binding = MasterBinding->CurrentSendBinding;
|
||
MasterBinding->CurrentSendBinding = Binding->NextBinding;
|
||
//
|
||
// We dont have a lock here - the masterbinding could be bogus
|
||
//
|
||
IpxDereferenceBinding1(MasterBinding, BREF_DEVICE_ACCESS);
|
||
IpxReferenceBinding1(Binding, BREF_DEVICE_ACCESS);
|
||
}
|
||
}
|
||
|
||
|
||
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);
|
||
}
|
||
IpxDereferenceBinding1(Binding, BREF_DEVICE_ACCESS);
|
||
|
||
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)
|
||
|
||
//
|
||
// [FW] TRUE if there are no more entries to age out.
|
||
//
|
||
BOOLEAN fMoreToAge=FALSE;
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
IPX_DEFINE_LOCK_HANDLE(LockHandle1)
|
||
IPX_GET_LOCK1(&Device->BindAccessLock, &LockHandle1);
|
||
{
|
||
ULONG Index = MIN (Device->MaxBindings, Device->HighestExternalNicId);
|
||
|
||
for (i = FIRST_REAL_BINDING; i <= Index; i++) {
|
||
|
||
Binding = NIC_ID_TO_BINDING(Device, i);
|
||
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;
|
||
IPX_DEBUG(RIP, (" %x set to FALSE\n", Binding));
|
||
if (Binding == Binding->NextBinding) {
|
||
DbgBreakPoint();
|
||
}
|
||
Binding->NextBinding->ReceiveBroadcast = TRUE;
|
||
IPX_DEBUG(RIP, (" %x set to TRUE\n", Binding->NextBinding));
|
||
|
||
break;
|
||
} else {
|
||
Binding = Binding->NextBinding;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
IPX_FREE_LOCK1(&Device->BindAccessLock, LockHandle1);
|
||
|
||
|
||
//
|
||
// If RIP is bound, we don't do any of this, and
|
||
// we stop the timer from running.
|
||
//
|
||
|
||
if (Device->UpperDriverBound[IDENTIFIER_RIP]) {
|
||
//
|
||
// [FW] For the case when the Forwarder appears after our table has
|
||
// been primed, we need to age out these entries....
|
||
//
|
||
if (Device->ForwarderBound) {
|
||
goto ageout;
|
||
}
|
||
|
||
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);
|
||
}
|
||
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
|
||
ageout:
|
||
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;
|
||
}
|
||
|
||
//
|
||
// [FW] There are more entries to age
|
||
//
|
||
fMoreToAge = TRUE;
|
||
|
||
++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) &&
|
||
!Device->ForwarderBound) {
|
||
|
||
//
|
||
// 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);
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// [FW] If RIP installed, restart the timer only if there was at least
|
||
// one entry which could be aged.
|
||
//
|
||
|
||
if (Device->ForwarderBound) {
|
||
|
||
if (fMoreToAge) {
|
||
|
||
IPX_DEBUG(RIP, ("More entries to age - restarting long timer\n"));
|
||
CTEStartTimer(
|
||
&Device->RipLongTimer,
|
||
60000, // one minute timeout
|
||
RipLongTimeout,
|
||
(PVOID)Device);
|
||
|
||
} else {
|
||
|
||
//
|
||
// Else, dont restart the timer and deref the device
|
||
//
|
||
|
||
IPX_DEBUG(RIP, ("No more entries to age - derefing the device\n"));
|
||
IpxDereferenceDevice (Device, DREF_LONG_TIMER);
|
||
}
|
||
} else {
|
||
//
|
||
// 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;
|
||
RouteEntry->NicId = NIC_FROM_LOCAL_TARGET(LocalTarget);
|
||
RouteEntry->NdisBindingContext = NIC_ID_TO_BINDING(Device, RouteEntry->NicId)->Adapter->NdisBindingHandle;
|
||
// What if this is NULL?? -> make sure not null before calling this routine.
|
||
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) {
|
||
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;
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
#ifdef SUNDOWN
|
||
if ((NdisStatus = IpxSendFrame(
|
||
LocalTarget,
|
||
Packet,
|
||
(ULONG) REQUEST_INFORMATION(WaitReserved->u.SR_DG.Request) + sizeof(IPX_HEADER),
|
||
sizeof(IPX_HEADER))) != NDIS_STATUS_PENDING) {
|
||
|
||
#else
|
||
if ((NdisStatus = IpxSendFrame(
|
||
LocalTarget,
|
||
Packet,
|
||
REQUEST_INFORMATION(WaitReserved->u.SR_DG.Request) + sizeof(IPX_HEADER),
|
||
sizeof(IPX_HEADER))) != NDIS_STATUS_PENDING) {
|
||
|
||
#endif
|
||
|
||
|
||
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) {
|
||
FILL_LOCAL_TARGET(LocalTarget, MIN( Device->MaxBindings, Binding->NicId));
|
||
}
|
||
|
||
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)
|
||
|
||
//
|
||
// 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;
|
||
|
||
}
|
||
//
|
||
// 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)) {
|
||
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 */
|
||
|