mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1176 lines
41 KiB
1176 lines
41 KiB
/* aarp.c, /appletalk/ins, Garth Conboy, 10/04/88 */
|
|
/* Copyright (c) 1988 by Pacer Software Inc., La Jolla, CA */
|
|
|
|
/* GC - Initial coding.
|
|
GC - (12/02/89): AppleTalk phase II comes to town; AARP's a little bit
|
|
more fun now.
|
|
GC - (08/18/90): New error logging mechanism.
|
|
GC - (10/07/90): In TokenRing land, we may get our own broadcasts, so
|
|
ignore any AARPs from us.
|
|
GC - (01/19/92): Newest OS/2 integration: source routing info in the best
|
|
router cache; better random picking of nets and nodes
|
|
when AARPing in a range.
|
|
GC - (01/20/92): Removed usage of numberOfConfiguredPorts; portDescriptors
|
|
may now be sparse, we use the portActive flag instead.
|
|
GC - (02/10/92): Added support for PRAM selection of initial AppleTalk
|
|
net/node address.
|
|
GC - (03/30/92): Updated for BufferDescriptors.
|
|
GC - (04/05/92): Checking for net/node overlap with exsiting active nodes
|
|
should be done in AarpForNode... don't allow the PRAM
|
|
node to be re-used!
|
|
|
|
*** Make the PVCS source control system happy:
|
|
$Header$
|
|
$Log$
|
|
***
|
|
|
|
Most AARP handling.
|
|
|
|
Note that AarpForNodeOnPort() will leave the ZonesInformationSocket
|
|
open on the newly allocated node.
|
|
|
|
*/
|
|
|
|
#define IncludeAarpErrors 1
|
|
|
|
#include "atalk.h"
|
|
|
|
#define Debug 1
|
|
|
|
#define GetRandom(min, max) (((long)RandomNumber() % \
|
|
(long)(((max+1) - (min))) + (min)))
|
|
|
|
ExternForVisibleFunction Boolean AarpForNode(int port,
|
|
unsigned short networkNumber,
|
|
unsigned char nodeNumber);
|
|
|
|
ExternForVisibleFunction Boolean IsPrime(long step);
|
|
|
|
ExternForVisibleFunction void TuneRoutingInfo(int port, char far *routingInfo);
|
|
|
|
ExternForVisibleFunction void
|
|
EnterIntoAddressMappingTable(int port,
|
|
ExtendedAppleTalkNodeNumber sourceNode,
|
|
char far *sourceAddress,
|
|
int addressLength,
|
|
char far *routingInfo,
|
|
int routingInfoLength);
|
|
|
|
ExternForVisibleFunction BufferDescriptor
|
|
BuildAarpPacket(int port,
|
|
int type,
|
|
int hardwareLength,
|
|
char far *sourceHardwareAddress,
|
|
ExtendedAppleTalkNodeNumber
|
|
sourceLogicalAddress,
|
|
char far *destHardwareAddress,
|
|
ExtendedAppleTalkNodeNumber destLogicalAddress,
|
|
char far *trueDestination,
|
|
char far *routingInfo,
|
|
int routingInfoLength,
|
|
int far *packetLength);
|
|
|
|
ExternForVisibleFunction Boolean
|
|
AarpForNodeInRange(int port,
|
|
AppleTalkNetworkRange networkRange,
|
|
Boolean serverNode,
|
|
ExtendedAppleTalkNodeNumber far *node);
|
|
|
|
ExternForVisibleFunction TimerHandler AddressMappingAgingTimerExpired;
|
|
|
|
#if Iam a Primos
|
|
extern
|
|
#else
|
|
ExternForVisibleFunction
|
|
#endif
|
|
TimerHandler BestRouterAgingTimerExpired;
|
|
|
|
static Boolean firstGleanCall = True;
|
|
static Boolean firstAddressMappingCall = True;
|
|
|
|
void far ShutdownAarp(void)
|
|
{
|
|
|
|
firstGleanCall = True;
|
|
firstAddressMappingCall = True;
|
|
|
|
} /* ShutdownAarp */
|
|
|
|
void _near _fastcall AarpPacketIn(int port,
|
|
char far *routingInfo,
|
|
int routingInfoLength,
|
|
char far *packet,
|
|
int length)
|
|
{
|
|
int aarpCommand;
|
|
char far *sourceAddress, far *destinationAddress;
|
|
ExtendedAppleTalkNodeNumber sourceNode, destinationNode;
|
|
BufferDescriptor response;
|
|
short hardwareLength, logicalLength;
|
|
char far *startOfPacket;
|
|
int packetLength;
|
|
PortHandlers portHandlers;
|
|
ActiveNode activeNode;
|
|
|
|
/* Is it for us? */
|
|
|
|
#if (IamNot an OS2) and (IamNot a WindowsNT)
|
|
/* Checked before NDIS gives it to us. */
|
|
if (not Is802dot2headerGood(packet, aarpProtocolType))
|
|
{
|
|
ErrorLog("AarpPacketIn", ISevVerbose, __LINE__, port,
|
|
IErrAarpBad8022header, IMsgAarpBad8022header,
|
|
Insert0());
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
/* Do we even want AppleTalk packets now? */
|
|
|
|
if (not appleTalkRunning or not PortDescriptor(port)->portActive)
|
|
return;
|
|
|
|
packet += Ieee802dot2headerLength;
|
|
length -= Ieee802dot2headerLength;
|
|
startOfPacket = packet;
|
|
|
|
portHandlers = &portSpecificInfo[PortDescriptor(port)->portType];
|
|
|
|
/* Pull out the information we'll be playing with. All three valid AARP
|
|
commands use the same packet format. */
|
|
|
|
hardwareLength = (unsigned char)packet[AarpHardwareLengthOffset];
|
|
if (hardwareLength < 1 or
|
|
hardwareLength > MaximumHardwareAddressLength)
|
|
{
|
|
ErrorLog("AarpPacketIn", ISevWarning, __LINE__, port,
|
|
IErrAarpBadAddressLength, IMsgAarpBadAddressLength,
|
|
Insert0());
|
|
return;
|
|
}
|
|
logicalLength = (unsigned char)packet[AarpProtocolLengthOffset];
|
|
if (logicalLength isnt 4)
|
|
{
|
|
ErrorLog("AarpPacketIn", ISevWarning, __LINE__, port,
|
|
IErrAarpBadLogicalProt, IMsgAarpBadLogicalProt,
|
|
Insert0());
|
|
return;
|
|
}
|
|
|
|
aarpCommand = ((unsigned char)packet[AarpCommandOffset] << 8) +
|
|
(unsigned char)packet[AarpCommandOffset + 1];
|
|
packet += AarpSourceAddressOffset;
|
|
sourceAddress = packet;
|
|
packet += hardwareLength;
|
|
|
|
packet += 1; /* Skip over to leading null pad on logical address. */
|
|
sourceNode.networkNumber = (short unsigned)(*packet++ << 8);
|
|
sourceNode.networkNumber += (unsigned char)(*packet++);
|
|
sourceNode.nodeNumber = (unsigned char)(*packet++);
|
|
|
|
destinationAddress = packet;
|
|
packet += hardwareLength;
|
|
|
|
packet += 1; /* Skip over to leading null pad on logical address. */
|
|
destinationNode.networkNumber = (short unsigned)(*packet++ << 8);
|
|
destinationNode.networkNumber += (unsigned char)(*packet++);
|
|
destinationNode.nodeNumber = (unsigned char)(*packet++);
|
|
|
|
/* We should have eaten the whole packet... */
|
|
|
|
if (packet - startOfPacket isnt length)
|
|
{
|
|
ErrorLog("AarpPacketIn", ISevWarning, __LINE__, port,
|
|
IErrAarpPacketLenMismatch, IMsgAarpPacketLenMismatch,
|
|
Insert0());
|
|
return;
|
|
}
|
|
|
|
/* Ignore any AARPs from us. */
|
|
|
|
if (FixedCompareCaseSensitive(sourceAddress, TokenRingAddressLength,
|
|
PortDescriptor(port)->myAddress,
|
|
TokenRingAddressLength))
|
|
return;
|
|
|
|
/* Do the right thing... */
|
|
|
|
switch(aarpCommand)
|
|
{
|
|
case AarpRequest:
|
|
|
|
/* We can get valid mapping info from a request, use it! */
|
|
|
|
if (routingInfoLength isnt 0)
|
|
TuneRoutingInfo(port, routingInfo);
|
|
EnterIntoAddressMappingTable(port, sourceNode, sourceAddress,
|
|
hardwareLength, routingInfo,
|
|
routingInfoLength);
|
|
|
|
/* After that, we can ignore any request not destined for us. */
|
|
|
|
TakeLock(DdpLock);
|
|
for (activeNode = PortDescriptor(port)->activeNodes;
|
|
activeNode isnt empty;
|
|
activeNode = activeNode->next)
|
|
if (ExtendedAppleTalkNodesEqual(&destinationNode,
|
|
&activeNode->extendedNode))
|
|
break;
|
|
if (activeNode is empty)
|
|
{
|
|
ReleaseLock(DdpLock);
|
|
break; /* Not one of our nodes... */
|
|
}
|
|
ReleaseLock(DdpLock);
|
|
|
|
/* The're asking about us, speak the truth. */
|
|
|
|
response = BuildAarpResponseTo(port, hardwareLength, sourceAddress,
|
|
routingInfo, routingInfoLength,
|
|
destinationNode, sourceNode,
|
|
&packetLength);
|
|
if (not (*portHandlers->packetOut)(port, response, packetLength,
|
|
Empty, 0))
|
|
ErrorLog("AarpPacketIn", ISevError, __LINE__, port,
|
|
IErrAarpBadSendProbeResp, IMsgAarpBadSendProbeResp,
|
|
Insert0());
|
|
|
|
break;
|
|
|
|
case AarpResponse:
|
|
if (PortDescriptor(port)->tryingToFindNodeOnPort)
|
|
{
|
|
/* No doubt, this is a response to our probe, check to make sure
|
|
the address matches, if so set the "used" flag. */
|
|
|
|
if (ExtendedAppleTalkNodesEqual(&destinationNode,
|
|
&PortDescriptor(port)->
|
|
tentativeAppleTalkNode))
|
|
PortDescriptor(port)->tentativeNodeHasBeenUsed = True;
|
|
}
|
|
|
|
/* This must have been a response to a probe or request... update our
|
|
mapping table. */
|
|
|
|
if (routingInfoLength isnt 0)
|
|
TuneRoutingInfo(port, routingInfo);
|
|
EnterIntoAddressMappingTable(port, sourceNode, sourceAddress,
|
|
hardwareLength, routingInfo,
|
|
routingInfoLength);
|
|
break;
|
|
|
|
case AarpProbe:
|
|
if (PortDescriptor(port)->tryingToFindNodeOnPort)
|
|
{
|
|
/* If we get a probe for our current tentative address, set the
|
|
"used" flag. */
|
|
|
|
if (ExtendedAppleTalkNodesEqual(&destinationNode,
|
|
&PortDescriptor(port)->
|
|
tentativeAppleTalkNode))
|
|
PortDescriptor(port)->tentativeNodeHasBeenUsed = True;
|
|
}
|
|
|
|
/* If the probe isn't asking about one of our AppleTalk addresses,
|
|
drop it on the floor. */
|
|
|
|
TakeLock(DdpLock);
|
|
for (activeNode = PortDescriptor(port)->activeNodes;
|
|
activeNode isnt empty;
|
|
activeNode = activeNode->next)
|
|
if (ExtendedAppleTalkNodesEqual(&destinationNode,
|
|
&activeNode->extendedNode))
|
|
break;
|
|
if (activeNode is empty)
|
|
{
|
|
ReleaseLock(DdpLock);
|
|
break; /* Not one of our nodes... */
|
|
}
|
|
ReleaseLock(DdpLock);
|
|
|
|
/* The're talking to us! Build and send the response. */
|
|
|
|
if (routingInfoLength isnt 0)
|
|
TuneRoutingInfo(port, routingInfo);
|
|
response = BuildAarpResponseTo(port, hardwareLength, sourceAddress,
|
|
routingInfo, routingInfoLength,
|
|
destinationNode, sourceNode,
|
|
&packetLength);
|
|
if (not (*portHandlers->packetOut)(port, response, packetLength,
|
|
Empty, 0))
|
|
ErrorLog("AarpPacketIn", ISevError, __LINE__, port,
|
|
IErrAarpBadSendProbeResp, IMsgAarpBadSendProbeResp,
|
|
Insert0());
|
|
|
|
break;
|
|
|
|
default:
|
|
ErrorLog("AarpPacketIn", ISevWarning, __LINE__, port,
|
|
IErrAarpBadCommandType, IMsgAarpBadCommandType,
|
|
Insert0());
|
|
break;
|
|
}
|
|
|
|
} /* AarpPacketIn */
|
|
|
|
Boolean far GleanAarpInfo(int port,
|
|
char far *sourceAddress,
|
|
int addressLength,
|
|
char far *routingInfo,
|
|
int routingInfoLength,
|
|
char far *packet,
|
|
int length)
|
|
{
|
|
ExtendedAppleTalkNodeNumber sourceNode, destinationNode;
|
|
Boolean offCablePacket;
|
|
int index;
|
|
|
|
/* What we should have here is an AppleTalk packet (complete with 802.2
|
|
header)... we verify this and glean any AARP information that may be
|
|
available. */
|
|
|
|
/* Do we even want AppleTalk packets now? */
|
|
|
|
if (not appleTalkRunning or not PortDescriptor(port)->portActive)
|
|
return(False);
|
|
|
|
/* Is it for us? */
|
|
|
|
#if IamNot an OS2 /* Checked before NDIS gives it to us. */
|
|
if (not Is802dot2headerGood(packet, appleTalkProtocolType))
|
|
return(False);
|
|
#endif
|
|
|
|
packet += Ieee802dot2headerLength;
|
|
length -= Ieee802dot2headerLength;
|
|
|
|
/* Get the source and destination information. */
|
|
|
|
sourceNode.networkNumber =
|
|
(short unsigned)(packet[LongDdpSourceNetworkOffset] << 8);
|
|
sourceNode.networkNumber +=
|
|
(unsigned char)(packet[LongDdpSourceNetworkOffset + 1]);
|
|
sourceNode.nodeNumber =
|
|
(unsigned char)(packet[LongDdpSourceNodeOffset]);
|
|
destinationNode.networkNumber =
|
|
(short unsigned)(packet[LongDdpDestNetworkOffset] << 8);
|
|
destinationNode.networkNumber +=
|
|
(unsigned char)(packet[LongDdpDestNetworkOffset + 1]);
|
|
destinationNode.nodeNumber =
|
|
(unsigned char)(packet[LongDdpDestNodeOffset]);
|
|
|
|
/* Do a little verification. nnnn00 is legal for dest for NBP routing. */
|
|
|
|
if (sourceNode.nodeNumber < MinimumUsableAppleTalkNode or
|
|
sourceNode.nodeNumber > MaximumUsableAppleTalkNode or
|
|
sourceNode.networkNumber < FirstValidNetworkNumber or
|
|
sourceNode.networkNumber > LastValidNetworkNumber)
|
|
{
|
|
/* Only bother logging this if we are in some routing capacity,
|
|
otherwise, let A-ROUTER worry about it */
|
|
|
|
if (PortDescriptor(port)->routingPort)
|
|
ErrorLog("GleanAarpInfo", ISevWarning, __LINE__, port,
|
|
IErrAarpBadSource, IMsgAarpBadSource,
|
|
Insert0());
|
|
return(False);
|
|
}
|
|
if (destinationNode.networkNumber > LastValidNetworkNumber)
|
|
{
|
|
ErrorLog("GleanAarpInfo", ISevWarning, __LINE__, port,
|
|
IErrAarpBadDest, IMsgAarpBadDest,
|
|
Insert0());
|
|
return(False);
|
|
}
|
|
|
|
/* Did the packet come from off this cable? Look at the hop count. If so,
|
|
enter it into our best-router cache. */
|
|
|
|
if (routingInfoLength isnt 0)
|
|
TuneRoutingInfo(port, routingInfo);
|
|
offCablePacket = (((packet[LongDdpLengthOffset] >> 2) & 0x0F) isnt 0);
|
|
if (offCablePacket)
|
|
{
|
|
TakeLock(RoutingLock);
|
|
index = HashExtendedAppleTalkNode(sourceNode) % BestRouterHashBuckets;
|
|
PortDescriptor(port)->bestRouterCache[index].valid = True;
|
|
PortDescriptor(port)->bestRouterCache[index].agingCount = 0;
|
|
PortDescriptor(port)->bestRouterCache[index].target = sourceNode;
|
|
MoveMem(PortDescriptor(port)->bestRouterCache[index].routerAddress,
|
|
sourceAddress, addressLength);
|
|
PortDescriptor(port)->bestRouterCache[index].routingInfoLength =
|
|
(short)routingInfoLength;
|
|
if (routingInfoLength isnt 0)
|
|
MoveMem(PortDescriptor(port)->bestRouterCache[index].routingInfo,
|
|
routingInfo, routingInfoLength);
|
|
ReleaseLock(RoutingLock);
|
|
|
|
if (firstGleanCall)
|
|
{
|
|
/* Start the best router table maintenance timer. */
|
|
|
|
StartTimer(BestRouterAgingTimerExpired, BestRouterAgingSeconds,
|
|
0, empty);
|
|
firstGleanCall = False;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
/* "Glean" AARP information from on-cable packets. */
|
|
|
|
EnterIntoAddressMappingTable(port, sourceNode, sourceAddress,
|
|
addressLength, routingInfo,
|
|
routingInfoLength);
|
|
}
|
|
|
|
return(True);
|
|
|
|
} /* GleanAarpInfo */
|
|
|
|
Boolean far AarpForNodeOnPort(int port,
|
|
Boolean allowStartupRange,
|
|
Boolean serverNode,
|
|
ExtendedAppleTalkNodeNumber desiredNode,
|
|
ExtendedAppleTalkNodeNumber far *node)
|
|
{
|
|
Boolean foundNode = False, inStartupRange = False;
|
|
ActiveNode activeNode;
|
|
ExtendedAppleTalkNodeNumber newNode;
|
|
|
|
/* Try to find a new extended node on the given port; first try for the
|
|
requested address (if specified), else try in this port's cable range
|
|
(if it's known) or in the default cable range (if any), then try the
|
|
start-up range (if allowed). */
|
|
|
|
PortDescriptor(port)->tryingToFindNodeOnPort = True;
|
|
|
|
if (desiredNode.networkNumber isnt UnknownNetworkNumber)
|
|
foundNode = AarpForNode(port, desiredNode.networkNumber,
|
|
desiredNode.nodeNumber);
|
|
if (foundNode)
|
|
newNode = desiredNode;
|
|
else
|
|
{
|
|
if (not foundNode and PortDescriptor(port)->seenRouterRecently)
|
|
foundNode = AarpForNodeInRange(port, PortDescriptor(port)->thisCableRange,
|
|
serverNode, &newNode);
|
|
else if (PortDescriptor(port)->initialNetworkRange.firstNetworkNumber isnt
|
|
UnknownNetworkNumber)
|
|
foundNode = AarpForNodeInRange(port,
|
|
PortDescriptor(port)->initialNetworkRange,
|
|
serverNode, &newNode);
|
|
else
|
|
{
|
|
/* If no place else to try, try the start-up range. Do this even if
|
|
we don't want to end up there. */
|
|
|
|
foundNode = AarpForNodeInRange(port, startupNetworkRange,
|
|
serverNode, &newNode);
|
|
if (foundNode)
|
|
inStartupRange = True;
|
|
}
|
|
}
|
|
|
|
/* If we don't have a tentative node, flee. */
|
|
|
|
if (not foundNode)
|
|
{
|
|
PortDescriptor(port)->tryingToFindNodeOnPort = False;
|
|
return(False);
|
|
}
|
|
|
|
/* Build a new active node structure and tack the new node onto the port. */
|
|
|
|
if ((activeNode = (ActiveNode)Calloc(sizeof(*activeNode), 1)) is empty)
|
|
{
|
|
ErrorLog("AarpForNodeOnPort", ISevError, __LINE__, port,
|
|
IErrAarpOutOfMemory, IMsgAarpOutOfMemory,
|
|
Insert0());
|
|
PortDescriptor(port)->tryingToFindNodeOnPort = False;
|
|
return(False);
|
|
}
|
|
activeNode->port = port;
|
|
activeNode->extendedNode = newNode;
|
|
TakeLock(DdpLock);
|
|
activeNode->next = PortDescriptor(port)->activeNodes;
|
|
PortDescriptor(port)->activeNodes = Link(activeNode);
|
|
ReleaseLock(DdpLock);
|
|
|
|
/* See who's out there. We need to open the ZIP socket in order to be
|
|
able to hear replies. */
|
|
|
|
if (OpenSocketOnNode(empty, port, &newNode, ZonesInformationSocket,
|
|
ZipPacketIn, (long)0, False, empty, 0, empty) isnt
|
|
ATnoError)
|
|
{
|
|
ErrorLog("AarpForNodeOnPort", ISevError, __LINE__, port,
|
|
IErrAarpBadZipOpenSocket, IMsgAarpBadZipOpenSocket,
|
|
Insert0());
|
|
if (ReleaseNodeOnPort(port, activeNode->extendedNode) isnt ATnoError)
|
|
ErrorLog("AarpForNodeOnPort", ISevError, __LINE__, port,
|
|
IErrAarpCouldNotReleaseNode, IMsgAarpCouldNotReleaseNode,
|
|
Insert0());
|
|
PortDescriptor(port)->tryingToFindNodeOnPort = False;
|
|
return(False);
|
|
}
|
|
if (not PortDescriptor(port)->seenRouterRecently)
|
|
GetNetworkInfoForNode(port, activeNode->extendedNode, False);
|
|
|
|
/* If nobody was out there and our tentative node was in the startup range
|
|
and our caller doesn't want to be there, return an error now. */
|
|
|
|
if (inStartupRange and not PortDescriptor(port)->seenRouterRecently and
|
|
not allowStartupRange)
|
|
{
|
|
if (ReleaseNodeOnPort(port, activeNode->extendedNode) isnt ATnoError)
|
|
ErrorLog("AarpForNodeOnPort", ISevError, __LINE__, port,
|
|
IErrAarpCouldNotReleaseNode, IMsgAarpCouldNotReleaseNode,
|
|
Insert0());
|
|
PortDescriptor(port)->tryingToFindNodeOnPort = False;
|
|
return(False);
|
|
}
|
|
|
|
/* If we didn't find our our cable range or if we're already in the cable
|
|
range, then we're set. */
|
|
|
|
if (not PortDescriptor(port)->seenRouterRecently or
|
|
IsWithinNetworkRange(newNode.networkNumber,
|
|
&PortDescriptor(port)->thisCableRange))
|
|
{
|
|
PortDescriptor(port)->tryingToFindNodeOnPort = False;
|
|
if (node isnt empty)
|
|
*node = newNode;
|
|
EnterIntoAddressMappingTable(port, newNode,
|
|
PortDescriptor(port)->myAddress,
|
|
portSpecificInfo[PortDescriptor(port)->
|
|
portType].hardwareAddressLength,
|
|
empty, 0);
|
|
return(True);
|
|
}
|
|
|
|
/* Otherwise, we've come up in the wrong range... free our active node.
|
|
Then try to find a node in the correct range. */
|
|
|
|
if (ReleaseNodeOnPort(port, activeNode->extendedNode) isnt ATnoError)
|
|
ErrorLog("AarpForNodeOnPort", ISevError, __LINE__, port,
|
|
IErrAarpCouldNotReleaseNode, IMsgAarpCouldNotReleaseNode,
|
|
Insert0());
|
|
foundNode = AarpForNodeInRange(port, PortDescriptor(port)->thisCableRange,
|
|
serverNode, &newNode);
|
|
if (not foundNode)
|
|
{
|
|
ErrorLog("AarpForNodeOnPort", ISevError, __LINE__, port,
|
|
IErrAarpCouldNotFindNode, IMsgAarpCouldNotFindNode,
|
|
Insert0());
|
|
PortDescriptor(port)->tryingToFindNodeOnPort = False;
|
|
return(False);
|
|
}
|
|
|
|
/* Now build the correct active node! */
|
|
|
|
if ((activeNode = (ActiveNode)Calloc(sizeof(*activeNode), 1)) is empty)
|
|
{
|
|
ErrorLog("AarpForNodeOnPort", ISevError, __LINE__, port,
|
|
IErrAarpOutOfMemory, IMsgAarpOutOfMemory,
|
|
Insert0());
|
|
PortDescriptor(port)->tryingToFindNodeOnPort = False;
|
|
return(False);
|
|
}
|
|
activeNode->port = port;
|
|
activeNode->extendedNode = newNode;
|
|
TakeLock(DdpLock);
|
|
activeNode->next = PortDescriptor(port)->activeNodes;
|
|
PortDescriptor(port)->activeNodes = Link(activeNode);
|
|
ReleaseLock(DdpLock);
|
|
|
|
/* And, to be consistent, open the ZIP socket on our new node. */
|
|
|
|
if (OpenSocketOnNode(empty, port, &newNode, ZonesInformationSocket,
|
|
ZipPacketIn, (long)0, False, empty, 0, empty) isnt
|
|
ATnoError)
|
|
{
|
|
ErrorLog("AarpForNodeOnPort", ISevError, __LINE__, port,
|
|
IErrAarpBadZipOpenSocket, IMsgAarpBadZipOpenSocket,
|
|
Insert0());
|
|
if (ReleaseNodeOnPort(port, activeNode->extendedNode) isnt ATnoError)
|
|
ErrorLog("AarpForNodeOnPort", ISevError, __LINE__, port,
|
|
IErrAarpCouldNotReleaseNode, IMsgAarpCouldNotReleaseNode,
|
|
Insert0());
|
|
PortDescriptor(port)->tryingToFindNodeOnPort = False;
|
|
return(False);
|
|
}
|
|
|
|
/* All set! */
|
|
|
|
PortDescriptor(port)->tryingToFindNodeOnPort = False;
|
|
if (node isnt empty)
|
|
*node = newNode;
|
|
EnterIntoAddressMappingTable(port, newNode,
|
|
PortDescriptor(port)->myAddress,
|
|
portSpecificInfo[PortDescriptor(port)->portType].
|
|
hardwareAddressLength,
|
|
empty, 0);
|
|
return(True);
|
|
|
|
} /* AarpForNodeOnPort */
|
|
|
|
ExternForVisibleFunction void
|
|
EnterIntoAddressMappingTable(int port,
|
|
ExtendedAppleTalkNodeNumber sourceNode,
|
|
char far *sourceAddress,
|
|
int addressLength,
|
|
char far *routingInfo,
|
|
int routingInfoLength)
|
|
{
|
|
int index;
|
|
AddressMappingNode mapNode;
|
|
Boolean new = False;
|
|
|
|
#if Verbose
|
|
char buffer[100];
|
|
#endif
|
|
|
|
if (sourceNode.nodeNumber < MinimumUsableAppleTalkNode or
|
|
sourceNode.nodeNumber > MaximumUsableAppleTalkNode or
|
|
sourceNode.networkNumber < FirstValidNetworkNumber or
|
|
sourceNode.networkNumber > LastValidNetworkNumber)
|
|
{
|
|
ErrorLog("EnterIntoAddressMappingTable", ISevWarning, __LINE__, port,
|
|
IErrAarpBadSource, IMsgAarpBadSource,
|
|
Insert0());
|
|
return;
|
|
}
|
|
|
|
/* The address mapping tables need to be protected by critical sections! */
|
|
|
|
TakeLock(RoutingLock);
|
|
if (firstAddressMappingCall)
|
|
{
|
|
/* Start the address mapping table maintenance timer. */
|
|
|
|
firstAddressMappingCall = False;
|
|
ReleaseLock(RoutingLock);
|
|
StartTimer(AddressMappingAgingTimerExpired, AddressMappingAgingSeconds,
|
|
0, empty);
|
|
TakeLock(RoutingLock);
|
|
}
|
|
|
|
/* Do we already know about this mapping? */
|
|
|
|
index = HashExtendedAppleTalkNode(sourceNode) % NumberOfAddressMapHashBuckets;
|
|
for (mapNode = PortDescriptor(port)->addressMappingTable[index];
|
|
mapNode isnt empty;
|
|
mapNode = mapNode->next)
|
|
if (ExtendedAppleTalkNodesEqual(&sourceNode, &mapNode->target))
|
|
break;
|
|
|
|
/* If not, allocate a new mapping node. */
|
|
|
|
if (mapNode is empty)
|
|
{
|
|
ReleaseLock(RoutingLock);
|
|
if ((mapNode = (AddressMappingNode)Calloc(sizeof(*mapNode), 1)) is empty)
|
|
{
|
|
ErrorLog("EnterIntoAddressMappingTable", ISevError, __LINE__, port,
|
|
IErrAarpOutOfMemory, IMsgAarpOutOfMemory,
|
|
Insert0());
|
|
return;
|
|
}
|
|
|
|
/* Thread the new node into it's correct location. */
|
|
|
|
TakeLock(RoutingLock);
|
|
mapNode->next = PortDescriptor(port)->addressMappingTable[index];
|
|
PortDescriptor(port)->addressMappingTable[index] = mapNode;
|
|
mapNode->target = sourceNode;
|
|
new = True;
|
|
}
|
|
|
|
#if Verbose /* Assuming Ethernet/TokenRing here; only for debugging... */
|
|
if (new)
|
|
{
|
|
sprintf(buffer, "New #%d %u.%u, addr %04X%04X%04X", port,
|
|
sourceNode.networkNumber, sourceNode.nodeNumber,
|
|
*(short unsigned *)sourceAddress,
|
|
*(short unsigned *)(sourceAddress + sizeof(short)),
|
|
*(short unsigned *)(sourceAddress + sizeof(short) * 2));
|
|
printf("EnterIntoAddressMappingTable: %s.\n", buffer);
|
|
}
|
|
else if (not CompareSixBytes(sourceAddress, mapNode->hardwareAddress))
|
|
{
|
|
sprintf(buffer, "Rep #%d %u.%u, addr %04X%04X%04X", port,
|
|
sourceNode.networkNumber, sourceNode.nodeNumber,
|
|
*(short unsigned *)sourceAddress,
|
|
*(short unsigned *)(sourceAddress + sizeof(short)),
|
|
*(short unsigned *)(sourceAddress + sizeof(short) * 2));
|
|
printf("EnterIntoAddressMappingTable: %s.\n", buffer);
|
|
}
|
|
#endif
|
|
|
|
/* Update mapping table! */
|
|
|
|
MoveMem(mapNode->hardwareAddress, sourceAddress, addressLength);
|
|
if (routingInfoLength isnt 0)
|
|
MoveMem(mapNode->routingInfo, routingInfo, routingInfoLength);
|
|
mapNode->routingInfoLength = (short)routingInfoLength;
|
|
mapNode->agingCount = 0;
|
|
ReleaseLock(RoutingLock);
|
|
|
|
return;
|
|
|
|
} /* EnterIntoAddressMappingTable */
|
|
|
|
BufferDescriptor far BuildAarpProbeTo(int port,
|
|
int hardwareLength,
|
|
ExtendedAppleTalkNodeNumber node,
|
|
int far *packetLength)
|
|
{
|
|
|
|
return(BuildAarpPacket(port, AarpProbe, hardwareLength,
|
|
PortDescriptor(port)->myAddress,
|
|
node, zeros, node,
|
|
empty, empty, 0, packetLength));
|
|
|
|
} /* BuildAarpProbeTo */
|
|
|
|
BufferDescriptor far BuildAarpResponseTo(int port,
|
|
int hardwareLength,
|
|
char far *hardwareAddress,
|
|
char far *routingInfo,
|
|
int routingInfoLength,
|
|
ExtendedAppleTalkNodeNumber sourceNode,
|
|
ExtendedAppleTalkNodeNumber
|
|
destinationNode,
|
|
int far *packetLength)
|
|
{
|
|
|
|
return(BuildAarpPacket(port, AarpResponse, hardwareLength,
|
|
PortDescriptor(port)->myAddress,
|
|
sourceNode, hardwareAddress, destinationNode,
|
|
hardwareAddress, routingInfo, routingInfoLength,
|
|
packetLength));
|
|
|
|
} /* BuildAarpResponseTo */
|
|
|
|
BufferDescriptor far BuildAarpRequestTo(int port,
|
|
int hardwareLength,
|
|
ExtendedAppleTalkNodeNumber sourceNode,
|
|
ExtendedAppleTalkNodeNumber
|
|
destinationNode,
|
|
int far *packetLength)
|
|
{
|
|
|
|
return(BuildAarpPacket(port, AarpRequest, hardwareLength,
|
|
PortDescriptor(port)->myAddress,
|
|
sourceNode, zeros, destinationNode,
|
|
empty, empty, 0, packetLength));
|
|
|
|
} /* BuildAarpRequestTo */
|
|
|
|
ExternForVisibleFunction void far
|
|
AddressMappingAgingTimerExpired(long unsigned timerId,
|
|
int additionalDataSize,
|
|
char far *additionalData)
|
|
{
|
|
int port;
|
|
AddressMappingNode mapNode, nextMapNode, previousMapNode;
|
|
int index;
|
|
|
|
/* "Use" unused formals. */
|
|
|
|
timerId, additionalDataSize, additionalData;
|
|
|
|
/* Walk though all address mapping tables aging the entries. We need to
|
|
protect the mapping tables with critical sections, but don't stay in
|
|
a critical section too long. */
|
|
|
|
for (port = 0; port < MaximumNumberOfPorts; port += 1)
|
|
{
|
|
if (not PortDescriptor(port)->portActive or
|
|
not PortDescriptor(port)->extendedNetwork)
|
|
continue;
|
|
for (index = 0; index < NumberOfAddressMapHashBuckets; index += 1)
|
|
{
|
|
TakeLock(RoutingLock);
|
|
for (mapNode = PortDescriptor(port)->addressMappingTable[index],
|
|
previousMapNode = empty;
|
|
mapNode isnt empty;
|
|
previousMapNode = mapNode, mapNode = nextMapNode)
|
|
{
|
|
nextMapNode = mapNode->next;
|
|
if (mapNode->agingCount < AddressMappingAgingChances)
|
|
mapNode->agingCount += 1;
|
|
else
|
|
{
|
|
if (previousMapNode is empty)
|
|
PortDescriptor(port)->addressMappingTable[index] = nextMapNode;
|
|
else
|
|
previousMapNode->next = nextMapNode;
|
|
ReleaseLock(RoutingLock);
|
|
Free(mapNode);
|
|
TakeLock(RoutingLock);
|
|
|
|
/* Restart the scan on this index because we've been out of
|
|
a critical section. */
|
|
|
|
mapNode = empty;
|
|
nextMapNode = PortDescriptor(port)->addressMappingTable[index];
|
|
}
|
|
}
|
|
ReleaseLock(RoutingLock);
|
|
}
|
|
}
|
|
|
|
/* Try again in a little while... */
|
|
|
|
StartTimer(AddressMappingAgingTimerExpired, AddressMappingAgingSeconds,
|
|
0, empty);
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
|
|
} /* AddressMappingAgingTimerExpired */
|
|
|
|
ExternForVisibleFunction BufferDescriptor
|
|
BuildAarpPacket(int port,
|
|
int type,
|
|
int hardwareLength,
|
|
char far *sourceHardwareAddress,
|
|
ExtendedAppleTalkNodeNumber
|
|
sourceLogicalAddress,
|
|
char far *destHardwareAddress,
|
|
ExtendedAppleTalkNodeNumber destLogicalAddress,
|
|
char far *trueDestination,
|
|
char far *routingInfo,
|
|
int routingInfoLength,
|
|
int far *packetLength)
|
|
{
|
|
BufferDescriptor packet;
|
|
char far *startOfOnTheWirePacket;
|
|
char far *copyPoint;
|
|
PortHandlers portHandlers;
|
|
|
|
/* Get a buffer descriptor, and set the data to be beyond the longest
|
|
possible hardware and 802.2 header length. */
|
|
|
|
if ((packet = NewBufferDescriptor(MaximumAarpPacketSize)) is Empty)
|
|
{
|
|
ErrorLog("BuildAarpPacket", ISevError, __LINE__, port,
|
|
IErrAarpOutOfMemory, IMsgAarpOutOfMemory,
|
|
Insert0());
|
|
return(Empty);
|
|
}
|
|
AdjustBufferDescriptor(packet, -MaximumHeaderLength);
|
|
|
|
portHandlers = &portSpecificInfo[PortDescriptor(port)->portType];
|
|
|
|
/* Build the specified type of AARP packet with the specified information;
|
|
also, tack on the link specific header; return a pointer to the
|
|
true start of packet as well as the packet legnth. If this doesn't
|
|
make sense, reread the "Ethernet/AppleTalk AARP" spec. */
|
|
|
|
packet->data[AarpHardwareTypeOffset] =
|
|
(char)(portHandlers->aarpPhysicalType >> 8);
|
|
packet->data[AarpHardwareTypeOffset + 1] =
|
|
(char)(portHandlers->aarpPhysicalType & 0xFF);
|
|
|
|
packet->data[AarpProtocolTypeOffset] =
|
|
(char)(portHandlers->protocolTypeForAppleTalk >> 8);
|
|
packet->data[AarpProtocolTypeOffset + 1] =
|
|
(char)(portHandlers->protocolTypeForAppleTalk & 0xFF);
|
|
|
|
packet->data[AarpHardwareLengthOffset] = (char)hardwareLength;
|
|
packet->data[AarpProtocolLengthOffset] = 4;
|
|
|
|
packet->data[AarpCommandOffset] = (char)(type >> 8);
|
|
packet->data[AarpCommandOffset + 1] = (char)(type & 0xFF);
|
|
|
|
/* Source hardware address. */
|
|
|
|
copyPoint = packet->data + AarpSourceAddressOffset;
|
|
MoveMem(copyPoint, sourceHardwareAddress, hardwareLength);
|
|
copyPoint += hardwareLength;
|
|
|
|
/* Source logical address */
|
|
|
|
*copyPoint++ = 0;
|
|
*copyPoint++ = (char)(sourceLogicalAddress.networkNumber >> 8);
|
|
*copyPoint++ = (char)(sourceLogicalAddress.networkNumber & 0xFF);
|
|
*copyPoint++ = (char)(sourceLogicalAddress.nodeNumber);
|
|
|
|
/* Destination hardware address. */
|
|
|
|
MoveMem(copyPoint, destHardwareAddress, hardwareLength);
|
|
copyPoint += hardwareLength;
|
|
|
|
/* Destination logical address. */
|
|
|
|
*copyPoint++ = 0;
|
|
*copyPoint++ = (char)(destLogicalAddress.networkNumber >> 8);
|
|
*copyPoint++ = (char)(destLogicalAddress.networkNumber & 0xFF);
|
|
*copyPoint++ = (char)(destLogicalAddress.nodeNumber);
|
|
|
|
startOfOnTheWirePacket = (*portHandlers->buildHeader)(packet->data,
|
|
MaximumAarpDataSize,
|
|
port,
|
|
trueDestination,
|
|
routingInfo,
|
|
routingInfoLength,
|
|
AddressResolution);
|
|
*packetLength = copyPoint - startOfOnTheWirePacket;
|
|
AdjustBufferDescriptor(packet, packet->data - startOfOnTheWirePacket);
|
|
|
|
return(packet);
|
|
|
|
} /* BuildAarpPacket */
|
|
|
|
ExternForVisibleFunction Boolean
|
|
AarpForNodeInRange(int port,
|
|
AppleTalkNetworkRange networkRange,
|
|
Boolean serverNode,
|
|
ExtendedAppleTalkNodeNumber far *node)
|
|
{
|
|
unsigned char currentNodeNumber;
|
|
short unsigned currentNetworkNumber;
|
|
int firstNodeNumber, lastNodeNumber;
|
|
long netTry;
|
|
int nodeTry;
|
|
unsigned short nodeWidth, nodeChange, nodeIndex;
|
|
unsigned short netWidth, netChange, netIndex;
|
|
|
|
/* Do as our routine name implies. */
|
|
|
|
firstNodeNumber = MinimumUsableAppleTalkNode;
|
|
lastNodeNumber = MaximumExtendedAppleTalkNode;
|
|
|
|
/* Okay, now some fun starts. Plow through our options trying to find an
|
|
unused extended node number. */
|
|
|
|
/* Compute the width of our network range, and pick a random start point. */
|
|
|
|
netWidth = (short unsigned)((networkRange.lastNetworkNumber + 1) -
|
|
networkRange.firstNetworkNumber);
|
|
netTry = GetRandom(networkRange.firstNetworkNumber,
|
|
networkRange.lastNetworkNumber);
|
|
|
|
/* Come up with a random decrement, making sure it's odd (to avoid repeats)
|
|
and large enough to appear pretty random. I think this algorithm is a
|
|
little more trouble than its worth, but Microsoft likes it this way. */
|
|
|
|
netChange = (unsigned short)(GetRandom(1, netWidth) | 1);
|
|
while (netWidth % netChange is 0 or not IsPrime((long)netChange))
|
|
netChange += 2;
|
|
|
|
/* Now walk trough the range decrementing the starting network by the
|
|
choosen change (with wrap, of course) until we find an address or
|
|
we've processed every available network in the range. */
|
|
|
|
for (netIndex = 0; netIndex < netWidth; netIndex += 1)
|
|
{
|
|
currentNetworkNumber = (unsigned short) netTry;
|
|
|
|
/* Compute the width of our node range, and pick a random start point. */
|
|
|
|
nodeWidth = (short unsigned)((lastNodeNumber + 1) - firstNodeNumber);
|
|
nodeTry = (int)GetRandom(firstNodeNumber, lastNodeNumber);
|
|
|
|
/* Come up with a random decrement, making sure it's odd (to avoid repeats)
|
|
and large enough to appear pretty random. */
|
|
|
|
nodeChange = (short unsigned)(GetRandom(1, nodeWidth) | 1);
|
|
while (nodeWidth % nodeChange is 0 or not IsPrime((long)nodeChange))
|
|
nodeChange += 2;
|
|
|
|
/* Now walk trough the range decrementing the starting network by the
|
|
choosen change (with wrap, of course) until we find an address or
|
|
we've processed every available node in the range. */
|
|
|
|
for (nodeIndex = 0; nodeIndex < nodeWidth; nodeIndex += 1)
|
|
{
|
|
currentNodeNumber = (unsigned char)nodeTry;
|
|
|
|
/* Let AARP have a crack at it. */
|
|
|
|
if (AarpForNode(port, currentNetworkNumber, currentNodeNumber))
|
|
break;
|
|
|
|
/* Okay, try again, bump down with wrap. */
|
|
|
|
nodeTry -= nodeChange;
|
|
while (nodeTry < firstNodeNumber)
|
|
nodeTry += nodeWidth;
|
|
|
|
} /* Node number loop */
|
|
|
|
if (not PortDescriptor(port)->tentativeNodeHasBeenUsed)
|
|
break;
|
|
|
|
/* Okay, try again, bump down with wrap. */
|
|
|
|
netTry -= netChange;
|
|
while (netTry < (long)networkRange.firstNetworkNumber)
|
|
netTry += netWidth;
|
|
|
|
} /* Network number loop */
|
|
|
|
/* Okay if we found one return all's well, otherwise no luck. */
|
|
|
|
if (PortDescriptor(port)->tentativeNodeHasBeenUsed)
|
|
return(False);
|
|
if (node isnt empty)
|
|
*node = PortDescriptor(port)->tentativeAppleTalkNode;
|
|
return(True);
|
|
|
|
} /* AarpForNodeInRange */
|
|
|
|
ExternForVisibleFunction Boolean
|
|
AarpForNode(int port,
|
|
unsigned short networkNumber,
|
|
unsigned char nodeNumber)
|
|
{
|
|
int probeAttempt;
|
|
BufferDescriptor packet;
|
|
PortHandlers portHandlers;
|
|
int packetLength;
|
|
ActiveNode activeNode;
|
|
|
|
/* First make sure we don't own this node. */
|
|
|
|
TakeLock(DdpLock);
|
|
for (activeNode = PortDescriptor(port)->activeNodes;
|
|
activeNode isnt empty;
|
|
activeNode = activeNode->next)
|
|
if (activeNode->extendedNode.networkNumber is networkNumber and
|
|
activeNode->extendedNode.nodeNumber is nodeNumber)
|
|
{
|
|
ReleaseLock(DdpLock);
|
|
return(False);
|
|
}
|
|
ReleaseLock(DdpLock);
|
|
|
|
/* Use AARP to probe for a particular network/node address. */
|
|
|
|
portHandlers = &portSpecificInfo[PortDescriptor(port)->portType];
|
|
|
|
PortDescriptor(port)->tentativeNodeHasBeenUsed = False;
|
|
|
|
/* Build the packet and blast it out the specified number of times. */
|
|
|
|
for (probeAttempt = 0;
|
|
probeAttempt < PortDescriptor(port)->aarpProbes;
|
|
probeAttempt += 1)
|
|
{
|
|
PortDescriptor(port)->tentativeAppleTalkNode.networkNumber = networkNumber;
|
|
PortDescriptor(port)->tentativeAppleTalkNode.nodeNumber = nodeNumber;
|
|
packet = BuildAarpProbeTo(port, portHandlers->hardwareAddressLength,
|
|
PortDescriptor(port)->
|
|
tentativeAppleTalkNode,
|
|
&packetLength);
|
|
if (not (*portHandlers->packetOut)(port, packet, packetLength, Empty, 0))
|
|
{
|
|
ErrorLog("AarpForNode", ISevError, __LINE__, port,
|
|
IErrAarpBadProbeSend, IMsgAarpBadProbeSend,
|
|
Insert0());
|
|
return(False);
|
|
}
|
|
WaitFor(AarpProbeTimerInHundreths,
|
|
&PortDescriptor(port)->tentativeNodeHasBeenUsed);
|
|
if (PortDescriptor(port)->tentativeNodeHasBeenUsed)
|
|
break; /* Did we get a probe or a response? */
|
|
|
|
} /* Probe attempts loop */
|
|
|
|
/* We win if the current tentenative node has not been used! */
|
|
|
|
return(not PortDescriptor(port)->tentativeNodeHasBeenUsed);
|
|
|
|
} /* AarpForNode */
|
|
|
|
/* We assume "step" is odd. */
|
|
|
|
ExternForVisibleFunction Boolean IsPrime(long step)
|
|
{
|
|
long i, j;
|
|
|
|
/* All odds, seven and below, are prime. */
|
|
|
|
if (step <= 7)
|
|
return (True);
|
|
|
|
/* Do a little divisibility checking. The "/3" is a reasonably good
|
|
shot at sqrt() because the smallest odd to come through here will be
|
|
9. */
|
|
|
|
j = step/3;
|
|
for (i = 3; i <= j; i++)
|
|
if (step % i is 0)
|
|
return(False);
|
|
|
|
return(True);
|
|
|
|
} /* IsPrime */
|
|
|
|
#if Iam a Primos
|
|
extern
|
|
#else
|
|
ExternForVisibleFunction
|
|
#endif
|
|
void far BestRouterAgingTimerExpired(long unsigned timerId,
|
|
int additionalDataSize,
|
|
char far *additionalData)
|
|
{
|
|
int index, port;
|
|
|
|
/* "Use" unused formals. */
|
|
|
|
timerId, additionalDataSize, additionalData;
|
|
|
|
/* Loop through all of our active ports aging their best-router tables. */
|
|
|
|
for (port = 0; port < MaximumNumberOfPorts; port += 1)
|
|
{
|
|
if (not PortDescriptor(port)->portActive or
|
|
not PortDescriptor(port)->extendedNetwork)
|
|
continue;
|
|
TakeLock(RoutingLock);
|
|
for (index = 0; index < BestRouterHashBuckets; index += 1)
|
|
{
|
|
if (not PortDescriptor(port)->bestRouterCache[index].valid)
|
|
continue;
|
|
if (PortDescriptor(port)->bestRouterCache[index].agingCount <
|
|
BestRouterAgingChances)
|
|
PortDescriptor(port)->bestRouterCache[index].agingCount += 1;
|
|
else
|
|
PortDescriptor(port)->bestRouterCache[index].valid = False;
|
|
}
|
|
ReleaseLock(RoutingLock);
|
|
}
|
|
|
|
/* Restart the timer! */
|
|
|
|
StartTimer(BestRouterAgingTimerExpired, BestRouterAgingSeconds,
|
|
0, empty);
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
|
|
} /* BestRouterAgingTimerExpired */
|
|
|
|
ExternForVisibleFunction void TuneRoutingInfo(int port, char far *routingInfo)
|
|
{
|
|
/* Given an incoming TokenRing routing info, tune it to make it valid
|
|
for outing routing info. Do this in place! And, remember, you can
|
|
always tune a routing info, but you can't tune a fish. */
|
|
|
|
if (PortDescriptor(port)->portType isnt TokenRingNetwork)
|
|
return;
|
|
|
|
/* Set to "non-broadcast" and invert "direction". */
|
|
|
|
routingInfo[0] &= TokenRingNonBroadcastMask;
|
|
if (routingInfo[1] & TokenRingDirectionMask)
|
|
routingInfo[1] &= ~TokenRingDirectionMask;
|
|
else
|
|
routingInfo[1] |= TokenRingDirectionMask;
|
|
|
|
} /* TuneRoutingInfo */
|
|
|