Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

1272 lines
45 KiB

/* FullRtmp.c, /appletalk/source, Garth Conboy, 01/04/90 */
/* Copyright (c) 1988 by Pacer Software Inc., La Jolla, CA */
/* GC - Initial coding (from various other pieces: non-router AppleTalk
phase II RtmpStub and router AppleTalk phase I implementation).
GC - (08/18/90): New error logging mechanism.
GC - (10/08/90): Half port support.
GC - (03/13/91): Added the "recentRoutes" cache to improve large network
routing performance.
GC - (01/20/92): Removed usage of numberOfConfiguredPorts; portDescriptors
may now be sparse, we use the portActive flag instead.
GC - (01/21/92): Made RemoveFromRoutingTable externally visible.
GC - (03/31/92): Updated for BufferDescriptors.
GC - (06/15/92): Correct "notify neighbor" handling: don't allow route
upgrading.
GC - (11/28/92): Locks and reference counts come to town.
GC - (12/10/92): Half port fix from Eric Smith at Telebit.
*** Make the PVCS source control system happy:
$Header$
$Log$
***
Handle the RTMP protocol for a routing node.
*/
#define IncludeFullRtmpErrors 1
#include "atalk.h"
#if IamNot an AppleTalkRouter
/* Empty file */
#else
ExternForVisibleFunction TimerHandler RtmpSendTimerExpired;
ExternForVisibleFunction TimerHandler RtmpValidityTimerExpired;
ExternForVisibleFunction Boolean
GetOrSetNetworkNumber(int port,
unsigned int suggestedNetworkNumber);
ExternForVisibleFunction void SendRoutingData(int port,
AppleTalkAddress destination,
Boolean doSplitHorizon);
ExternForVisibleFunction void
EnterIntoRoutingTable(AppleTalkNetworkRange networkRange,
int port,
ExtendedAppleTalkNodeNumber nextRouter,
int numberOfHops);
static Boolean firstCall = True;
Boolean far StartRtmpProcessingOnPort(int port,
ExtendedAppleTalkNodeNumber routerNode)
{
/* For extended ports there is very little to do now; the process of getting
the node has done most of our work for us. */
if (PortDescriptor(port)->extendedNetwork)
{
/* If we've seen another router; does the real network agree with what
we were going to try to seed? No seeding for half ports. */
if (PortDescriptor(port)->portType isnt NonAppleTalkHalfPort and
PortDescriptor(port)->seenRouterRecently and
PortDescriptor(port)->initialNetworkRange.firstNetworkNumber isnt
UnknownNetworkNumber)
if (PortDescriptor(port)->initialNetworkRange.firstNetworkNumber isnt
PortDescriptor(port)->thisCableRange.firstNetworkNumber or
PortDescriptor(port)->initialNetworkRange.lastNetworkNumber isnt
PortDescriptor(port)->thisCableRange.lastNetworkNumber)
ErrorLog("StartRtmpProcessingOnPort", ISevWarning, __LINE__, port,
IErrFullRtmpIgnoredNetRange, IMsgFullRtmpIgnoredNetRange,
Insert0());
/* Otherwise, we're the first rotuer, so we'll seed (if we can). */
if (PortDescriptor(port)->portType is NonAppleTalkHalfPort)
{
PortDescriptor(port)->thisCableRange.firstNetworkNumber =
UnknownNetworkNumber;
PortDescriptor(port)->thisCableRange.lastNetworkNumber =
UnknownNetworkNumber;
}
else if (not PortDescriptor(port)->seedRouter)
{
ErrorLog("StartRtmpProcessingOnPort", ISevError, __LINE__, port,
IErrFullRtmpNoSeedCantStart, IMsgFullRtmpNoSeedCantStart,
Insert0());
return(False);
}
else if (not PortDescriptor(port)->seenRouterRecently)
PortDescriptor(port)->thisCableRange =
PortDescriptor(port)->initialNetworkRange;
}
else
{
/* For non-extended networks, we need to either find or seed our network
number. */
if (not GetOrSetNetworkNumber(port, (unsigned int)(
PortDescriptor(port)->seedRouter ?
PortDescriptor(port)->initialNetworkRange.
firstNetworkNumber :
UnknownNetworkNumber)))
return(False);
if (not PortDescriptor(port)->seenRouterRecently)
PortDescriptor(port)->thisCableRange.firstNetworkNumber =
PortDescriptor(port)->initialNetworkRange.firstNetworkNumber;
/* On non-extended networks, we would have "allocated" a node with network
zero, fix this up here! */
PortDescriptor(port)->activeNodes->extendedNode.networkNumber =
PortDescriptor(port)->aRouter.networkNumber =
routerNode.networkNumber =
PortDescriptor(port)->thisCableRange.firstNetworkNumber;
}
/* Well, we're a router now, so mark us as A-ROUTER. */
PortDescriptor(port)->seenRouterRecently = True;
PortDescriptor(port)->aRouter = routerNode;
PortDescriptor(port)->routerRunning = True;
/* Okay, make it so... */
if (PortDescriptor(port)->portType isnt NonAppleTalkHalfPort)
EnterIntoRoutingTable(PortDescriptor(port)->thisCableRange, port,
routerNode, 0);
/* Switch the incoming RTMP packet handler to be the router version. */
CloseSocketOnNodeIfOpen(port, routerNode, RtmpSocket);
if (OpenSocketOnNode(empty, port, &routerNode, RtmpSocket, RtmpPacketInRouter,
(long)0, False, empty, 0, empty) isnt ATnoError)
{
ErrorLog("StartRtmpProcessingOnPort", ISevError, __LINE__, port,
IErrFullRtmpBadSocketOpen, IMsgFullRtmpBadSocketOpen,
Insert0());
return(False);
}
/* Start the two RTMP timers. */
if (firstCall)
{
StartTimer(RtmpSendTimerExpired, RtmpSendTimerSeconds, 0, empty);
StartTimer(RtmpValidityTimerExpired, RtmpValidityTimerSeconds, 0, empty);
firstCall = False;
}
return(True);
} /* StartRtmpProcessingOnPort */
void far ShutdownFullRtmp(void)
{
firstCall = True;
} /* ShutdownFullRtmp */
long far RtmpPacketInRouter(AppleTalkErrorCode errorCode,
long unsigned userData,
int port,
AppleTalkAddress source,
long destinationSocket,
int protocolType,
char far *datagram,
int datagramLength,
AppleTalkAddress actualDestination)
{
AppleTalkNetworkRange cableRange;
int rtmpCommand, responseSize, numberOfHops, index, indexToo;
RoutingTableEntry routingTableEntry;
ExtendedAppleTalkNodeNumber nextRouter;
Boolean foundOverlap;
BufferDescriptor buffer;
/* "Use" unneeded formals. */
actualDestination, userData;
/* Do we care? */
if (errorCode is ATsocketClosed)
return((long)True);
else if (errorCode isnt ATnoError)
{
ErrorLog("RtmpPacketInRouter", ISevError, __LINE__, port,
IErrFullRtmpBadIncomingError, IMsgFullRtmpBadIncomingError,
Insert0());
return((long)True);
}
if (not appleTalkRunning or not PortDescriptor(port)->portActive)
return((long)True);
/* Is the packet long enough to have any interestng data? */
if (protocolType is DdpProtocolRtmpRequest)
{
if (datagramLength < RtmpRequestDatagramSize)
{
ErrorLog("RtmpPacketInRouter", ISevWarning, __LINE__, port,
IErrFullRtmpReqTooShort, IMsgFullRtmpReqTooShort,
Insert0());
return((long)True);
}
}
else if (protocolType is DdpProtocolRtmpResponseOrData)
{
if ((PortDescriptor(port)->extendedNetwork and
datagramLength < RtmpDataMinimumSizeExtended) or
(not PortDescriptor(port)->extendedNetwork and
datagramLength < RtmpDataMinimumSizeNonExtended))
{
ErrorLog("RtmpPacketInRouter", ISevWarning, __LINE__, port,
IErrFullRtmpDataTooShort, IMsgFullRtmpDataTooShort,
Insert0());
return((long)True);
}
}
else
return((long)True); /* Funny protocol type... */
/* We're going to be playing with the routing tables... */
DeferTimerChecking();
DeferIncomingPackets();
if (protocolType is DdpProtocolRtmpRequest)
{
rtmpCommand = (unsigned char)datagram[RtmpRequestCommandOffset];
switch (rtmpCommand)
{
case RtmpRequestCommand:
break;
case RtmpDataRequestCommand:
case RtmpEntireDataRequestCommand:
/* Send all or part of our routing tables back to the requesting
address... */
SendRoutingData(port, source,
(Boolean)(rtmpCommand is RtmpDataRequestCommand));
HandleIncomingPackets();
HandleDeferredTimerChecks();
return((long)True);
default:
ErrorLog("RtmpPacketInRouter", ISevWarning, __LINE__, port,
IErrFullRtmpBadReqCommand, IMsgFullRtmpBadReqCommand,
Insert0());
HandleIncomingPackets();
HandleDeferredTimerChecks();
return((long)True);
}
/* Okay, we're a standard RtmpRequest; do the right thing. Find our
port's entry in the routing table. */
if ((routingTableEntry =
FindInRoutingTable(PortDescriptor(port)->aRouter.
networkNumber)) is empty)
{
ErrorLog("RtmpPacketInRouter", ISevError, __LINE__, port,
IErrFullRtmpBadRoutingTables, IMsgFullRtmpBadRoutingTables,
Insert0());
HandleIncomingPackets();
HandleDeferredTimerChecks();
return((long)True);
}
UnlinkRoutingTableEntry(routingTableEntry);
/* This guy would like an RTMP response... */
if ((buffer = NewBufferDescriptor(RtmpResponseMaxSize)) is Empty)
{
ErrorLog("RtmpPacketInRouter", ISevError, __LINE__, port,
IErrFullRtmpOutOfMemory, IMsgFullRtmpOutOfMemory,
Insert0());
HandleIncomingPackets();
HandleDeferredTimerChecks();
return((long)True);
}
TakeLock(RoutingLock);
buffer->data[RtmpSendersNetworkOffset] =
(char)((PortDescriptor(port)->aRouter.networkNumber >> 8) & 0xFF);
buffer->data[RtmpSendersNetworkOffset + 1] =
(char)(PortDescriptor(port)->aRouter.networkNumber & 0xFF);
buffer->data[RtmpSendersIdLengthOffset] = 8; /* Bits */
buffer->data[RtmpSendersIdOffset] = PortDescriptor(port)->aRouter.nodeNumber;
/* On an extended port, we also want to add the initial network range
tuple. */
if (not PortDescriptor(port)->extendedNetwork)
responseSize = RtmpSendersIdOffset + sizeof(char);
else
{
buffer->data[RtmpRangeStartOffset] =
(char)((PortDescriptor(port)->thisCableRange.
firstNetworkNumber >> 8) & 0xFF);
buffer->data[RtmpRangeStartOffset + 1] =
(char)(PortDescriptor(port)->thisCableRange.
firstNetworkNumber & 0xFF);
buffer->data[RtmpRangeStartOffset + 2] = RtmpTupleWithRange;
buffer->data[RtmpRangeEndOffset] =
(char)((PortDescriptor(port)->thisCableRange.
lastNetworkNumber >> 8) & 0xFF);
buffer->data[RtmpRangeEndOffset + 1] =
(char)(PortDescriptor(port)->thisCableRange.
lastNetworkNumber & 0xFF);
responseSize = RtmpRangeEndOffset + sizeof(short unsigned);
}
ReleaseLock(RoutingLock);
/* Send the response. */
if (DeliverDdp(destinationSocket, source, DdpProtocolRtmpResponseOrData,
buffer, responseSize, Empty, Empty, 0) isnt ATnoError)
ErrorLog("RtmpPacketInRouter", ISevError, __LINE__, port,
IErrFullRtmpBadRespSend, IMsgFullRtmpBadRespSend,
Insert0());
}
else if (protocolType is DdpProtocolRtmpResponseOrData)
{
if ((unsigned char)datagram[RtmpSendersIdLengthOffset] isnt 8)
{
ErrorLog("RtmpPacketInRouter", ISevWarning, __LINE__, port,
IErrFullRtmpBadNodeIdLen, IMsgFullRtmpBadNodeIdLen,
Insert0());
HandleIncomingPackets();
HandleDeferredTimerChecks();
return((long)True);
}
/* For non extended networks, we should have a leading version stamp. */
if (PortDescriptor(port)->extendedNetwork)
{
/* Source could be bad (coming in from a half port) so in this case,
use the source in the Rtmp packet. */
if (source.networkNumber is UnknownNetworkNumber)
{
MoveShortWireToMachine(datagram + RtmpSendersNetworkOffset,
source.networkNumber);
source.nodeNumber = datagram[RtmpSendersIdOffset];
}
index = RtmpSendersIdOffset + 1;
}
else
if ((unsigned char)datagram[RtmpSendersIdOffset + 1] isnt 0 or
(unsigned char)datagram[RtmpSendersIdOffset + 2] isnt 0 or
(unsigned char)datagram[RtmpSendersIdOffset + 3] isnt
RtmpVersionNumber)
{
ErrorLog("RtmpPacketInRouter", ISevError, __LINE__, port,
IErrFullRtmpBadVersion, IMsgFullRtmpBadVersion,
Insert0());
HandleIncomingPackets();
HandleDeferredTimerChecks();
return((long)True);
}
else
index = RtmpSendersIdOffset + 4;
/* Walk through the routing tuples... The "+ 3" ensure we as least have
a non-extended tuple. */
while (index + 3 <= datagramLength)
{
/* Decode either a short or long tuple... */
cableRange.firstNetworkNumber = (unsigned short)
(((unsigned char)datagram[index++]) << 8);
cableRange.firstNetworkNumber += (unsigned char)datagram[index++];
numberOfHops = (unsigned char)datagram[index++];
if (numberOfHops & RtmpExtendedTupleMask)
{
if (index + 2 > datagramLength)
{
ErrorLog("RtmpPacketInRouter", ISevError, __LINE__, port,
IErrFullRtmpBadTuple, IMsgFullRtmpBadTuple,
Insert0());
HandleIncomingPackets();
HandleDeferredTimerChecks();
return((long)True);
}
cableRange.lastNetworkNumber = (unsigned short)
(((unsigned char)datagram[index++]) << 8);
cableRange.lastNetworkNumber += (unsigned char)datagram[index++];
if ((unsigned char)datagram[index++] isnt RtmpVersionNumber)
{
ErrorLog("RtmpPacketInRouter", ISevWarning, __LINE__, port,
IErrFullRtmpBadVersionExt, IMsgFullRtmpBadVersionExt,
Insert0());
HandleIncomingPackets();
HandleDeferredTimerChecks();
return((long)True);
}
}
else
cableRange.lastNetworkNumber = cableRange.firstNetworkNumber;
numberOfHops &= RtmpNumberOfHopsMask;
/* Skip dummy "this net" on half ports; the first tuple coming in
from a half port will fall into this catagory (having no network
range). */
if (PortDescriptor(port)->portType is NonAppleTalkHalfPort and
cableRange.firstNetworkNumber is UnknownNetworkNumber)
continue;
/* Validate what we have... */
if (not CheckNetworkRange(cableRange))
continue;
/* Okay, first check to see if this tuple concerns a network range
that we already know about. */
routingTableEntry = Empty;
if ((routingTableEntry =
FindInRoutingTable(cableRange.firstNetworkNumber)) isnt empty and
routingTableEntry->networkRange.firstNetworkNumber is
cableRange.firstNetworkNumber and
routingTableEntry->networkRange.lastNetworkNumber is
cableRange.lastNetworkNumber)
{
/* Check for "notify neighbor" telling us that an entry is bad. */
if (numberOfHops is 31 and
routingTableEntry->nextRouter.networkNumber is
source.networkNumber and
routingTableEntry->nextRouter.nodeNumber is source.nodeNumber)
{
/* Don't "upgrade" route to PrettyBad from Bad... */
if (routingTableEntry->entryState isnt Bad)
routingTableEntry->entryState = PrettyBad;
UnlinkRoutingTableEntry(routingTableEntry);
continue;
}
/* If we're hearing about one of our directly connected nets, we
know best... ignore the info. */
if (routingTableEntry->numberOfHops is 0)
{
UnlinkRoutingTableEntry(routingTableEntry);
continue;
}
/* Check for previously bad entry, and a short enough path
with this tuple... if so, replace the entry. */
if ((routingTableEntry->entryState is PrettyBad or
routingTableEntry->entryState is Bad) and
numberOfHops < 15)
{
TakeLock(RoutingLock);
routingTableEntry->numberOfHops = (short)(numberOfHops + 1);
routingTableEntry->nextRouter.networkNumber =
source.networkNumber;
routingTableEntry->nextRouter.nodeNumber =
source.nodeNumber;
routingTableEntry->port = (short)port;
routingTableEntry->entryState = Good;
ReleaseLock(RoutingLock);
UnlinkRoutingTableEntry(routingTableEntry);
continue;
}
/* Check fora shorter or equidistant path to the target network...
if so, replace the entry. */
if (routingTableEntry->numberOfHops >= numberOfHops + 1 and
numberOfHops < 15)
{
TakeLock(RoutingLock);
routingTableEntry->numberOfHops = (short)(numberOfHops + 1);
routingTableEntry->nextRouter.networkNumber =
source.networkNumber;
routingTableEntry->nextRouter.nodeNumber =
source.nodeNumber;
routingTableEntry->port = (short)port;
routingTableEntry->entryState = Good;
ReleaseLock(RoutingLock);
UnlinkRoutingTableEntry(routingTableEntry);
continue;
}
/* Check for the same router still thinking it has a path to the
network, but it's further away now... if so, update the entry. */
if (routingTableEntry->nextRouter.networkNumber is
source.networkNumber and
routingTableEntry->nextRouter.nodeNumber is
source.nodeNumber and
routingTableEntry->port is (short)port)
{
TakeLock(RoutingLock);
routingTableEntry->numberOfHops = (short)(numberOfHops + 1);
if (routingTableEntry->numberOfHops < 16)
{
routingTableEntry->entryState = Good;
ReleaseLock(RoutingLock);
UnlinkRoutingTableEntry(routingTableEntry);
}
else
{
ReleaseLock(RoutingLock);
UnlinkRoutingTableEntry(routingTableEntry);
RemoveFromRoutingTable(cableRange);
}
continue;
}
UnlinkRoutingTableEntry(routingTableEntry);
continue;
}
UnlinkRoutingTableEntry(routingTableEntry); /* Probably Empty... */
/* Otherwise, we need to walk through the entire routing table
making sure the current tuple doesn't overlap with anything
we already have (since it didn't exactly match) -- if we find
an overlap, ignore the tuple (a network configuration error,
no doubt), else add it as a new network range! */
TakeLock(RoutingLock);
for (foundOverlap = False, indexToo = 0;
not foundOverlap and indexToo < NumberOfRtmpHashBuckets;
indexToo += 1)
if (routingTable[indexToo] isnt Empty)
{
for (routingTableEntry = routingTable[indexToo];
not foundOverlap and routingTableEntry isnt empty;
routingTableEntry = routingTableEntry->next)
if (RangesOverlap(&cableRange,
&routingTableEntry->networkRange))
foundOverlap = True;
ReleaseLock(RoutingLock); /* Let somebody else play. */
TakeLock(RoutingLock);
}
ReleaseLock(RoutingLock);
if (foundOverlap)
{
ErrorLog("RtmpPacketInRouter", ISevWarning, __LINE__, port,
IErrFullRtmpOverlappingNets, IMsgFullRtmpOverlappingNets,
Insert0());
continue;
}
/* Okay, enter this new network range! */
nextRouter.networkNumber = source.networkNumber;
nextRouter.nodeNumber = source.nodeNumber;
if (numberOfHops < 15)
EnterIntoRoutingTable(cableRange, port, nextRouter,
numberOfHops + 1);
} /* Walk through all of the tuples loop. */
} /* Protocol type is DdpProtocolRtmpResponseOrData */
HandleIncomingPackets();
HandleDeferredTimerChecks();
return((long)True);
} /* RtmpPacketInRouter */
RoutingTableEntry far FindInRoutingTable(short unsigned networkNumber)
{
int index, recentRouteIndex;
RoutingTableEntry routingTableEntry, nextRoutingTableEntry;
/* First, try the "recent route cache". */
TakeLock(RoutingLock);
CheckMod(recentRouteIndex, networkNumber, NumberOfRecentRouteBuckets,
"FindInRoutingTable");
if (recentRoutes[recentRouteIndex] isnt empty and
IsWithinNetworkRange(networkNumber,
&recentRoutes[recentRouteIndex]->networkRange))
{
Link(recentRoutes[recentRouteIndex]);
ReleaseLock(RoutingLock);
return(recentRoutes[recentRouteIndex]);
}
/* Find a given network number in the RTMP routing table. We maintain the
routing table as a hashed lookup structure based upon the first network
of the given network range. First, check to see if we're lucky and the
target network number is the start of a range... */
CheckMod(index, networkNumber, NumberOfRtmpHashBuckets,
"FindInRoutingTable");
for (routingTableEntry = routingTable[index];
routingTableEntry isnt empty;
routingTableEntry = routingTableEntry->next)
if (routingTableEntry->networkRange.firstNetworkNumber is networkNumber)
break;
/* Cache and return our find! */
if (routingTableEntry isnt empty)
{
recentRoutes[recentRouteIndex] = routingTableEntry;
Link(routingTableEntry);
ReleaseLock(RoutingLock);
return(routingTableEntry);
}
/* Otherwise, we need to walk the entire routing table to see if this guy
is within any range! Sigh. If found, cache the find. */
for (index = 0; index < NumberOfRtmpHashBuckets; index += 1)
{
if (routingTable[index] isnt Empty)
{
for (routingTableEntry = routingTable[index];
routingTableEntry isnt empty;
routingTableEntry = routingTableEntry->next)
if (IsWithinNetworkRange(networkNumber,
&routingTableEntry->networkRange))
{
recentRoutes[recentRouteIndex] = routingTableEntry;
Link(routingTableEntry);
ReleaseLock(RoutingLock);
return(routingTableEntry);
}
ReleaseLock(RoutingLock); /* Let somebody else play... */
TakeLock(RoutingLock);
}
}
ReleaseLock(RoutingLock);
/* Oops, nobody home! */
return(empty);
} /* FindInRoutingTable */
Boolean far RemoveFromRoutingTable(AppleTalkNetworkRange networkRange)
{
int index;
RoutingTableEntry routingTableEntry, previousRoutingTableEntry = empty;
/* Remove a given network number from the RTMP routing table. Return True
if we can find/remove the network; False otherwise. */
TakeLock(RoutingLock);
CheckMod(index, networkRange.firstNetworkNumber, NumberOfRtmpHashBuckets,
"RemoveFromRoutingTable");
for (routingTableEntry = routingTable[index];
routingTableEntry isnt empty;
routingTableEntry = routingTableEntry->next)
if (routingTableEntry->networkRange.firstNetworkNumber is
networkRange.firstNetworkNumber and
routingTableEntry->networkRange.lastNetworkNumber is
networkRange.lastNetworkNumber)
{
/* Unthread the guy who's leaving... */
if (previousRoutingTableEntry is empty)
routingTable[index] = routingTableEntry->next;
else
previousRoutingTableEntry->next = routingTableEntry->next;
/* Do a pass through the "recent router cache", remove this guy if
he's there. */
for (index = 0; index < NumberOfRecentRouteBuckets; index += 1)
if (recentRoutes[index] is routingTableEntry)
{
recentRoutes[index] = Empty;
break;
}
/* Give him up. */
ReleaseLock(RoutingLock);
UnlinkRoutingTableEntry(routingTableEntry);
/* The deed is done. */
return(True);
}
else
previousRoutingTableEntry = routingTableEntry;
ReleaseLock(RoutingLock);
return(False);
} /* RemoveFromRoutingTable */
void far UnlinkRoutingTableEntry(RoutingTableEntry routingTableEntry)
{
/* If we're the last referant, free 'em. */
TakeLock(RoutingLock);
if (not UnlinkNoFree(routingTableEntry))
{
ReleaseLock(RoutingLock);
return;
}
ReleaseLock(RoutingLock);
FreeZones(routingTableEntry->zones);
Free(routingTableEntry);
return;
} /* UnlinkRoutingTableEntry */
ExternForVisibleFunction Boolean
GetOrSetNetworkNumber(int port,
unsigned int suggestedNetworkNumber)
{
/* We use this beast for non-extended networks only! */
/* The plan here is not to mess up a working Internet. So,
we'll send out a few RTMP request packets, if we can find the network
number of the network, we'll use that one and ignore the one that was
passed in. Otherwise, we'll use the one that was passed in, unless
that is zero, in which case we'll blow the user away. A zero value for
our argument indicates that the caller does not want to set the network
network-number and we should expect to find at least one bridge already
operating out there on the specified port. */
BufferDescriptor buffer;
AppleTalkAddress source, destination;
int numberOfRequests = 0;
if (PortDescriptor(port)->extendedNetwork)
{
ErrorLog("GetOrSetNetworkNumber", ISevError, __LINE__, port,
IErrFullRtmpSigh, IMsgFullRtmpSigh,
Insert0());
return(False);
}
/* Set up the source, destination, and our very simple request packet. */
source.networkNumber = destination.networkNumber = UnknownNetworkNumber;
source.nodeNumber =
PortDescriptor(port)->activeNodes->extendedNode.nodeNumber;
destination.nodeNumber = AppleTalkBroadcastNodeNumber;
source.socketNumber = destination.socketNumber = RtmpSocket;
/* Blast a number of requests out on the wire, see if anybody will let us in
on the network network-number. */
while (not PortDescriptor(port)->seenRouterRecently and
numberOfRequests < NumberOfRtmpRequests)
{
/* Build the request packet. */
if ((buffer = NewBufferDescriptor(RtmpRequestDatagramSize)) is Empty)
{
ErrorLog("GetOrSetNetworkNumber", ISevError, __LINE__, port,
IErrFullRtmpOutOfMemory, IMsgFullRtmpOutOfMemory,
Insert0());
return(False);
}
buffer->data[RtmpRequestCommandOffset] = RtmpRequestCommand;
if (not TransmitDdp(port, source, destination, DdpProtocolRtmpRequest,
buffer, RtmpRequestDatagramSize, 0, empty, empty,
Empty, 0))
{
ErrorLog("GetOrSetNetworkNumber", ISevError, __LINE__, port,
IErrFullRtmpBadReqSend, IMsgFullRtmpBadReqSend,
Insert0());
return(False);
}
numberOfRequests += 1;
WaitFor(RtmpRequestTimerInHundreths,
&PortDescriptor(port)->seenRouterRecently);
}
/* If we got an answer, we're set... */
TakeLock(RoutingLock);
if (PortDescriptor(port)->seenRouterRecently)
{
if (suggestedNetworkNumber isnt UnknownNetworkNumber and
PortDescriptor(port)->thisCableRange.firstNetworkNumber isnt
(short unsigned)suggestedNetworkNumber)
{
ReleaseLock(RoutingLock);
ErrorLog("GetOrSetNetworkNumber", ISevWarning, __LINE__, port,
IErrFullRtmpIgnoredNet, IMsgFullRtmpIgnoredNet,
Insert0());
}
return(True);
}
/* If we didn't get an answer, we had better have a suggested network number
passed in. */
if (suggestedNetworkNumber is UnknownNetworkNumber)
{
ReleaseLock(RoutingLock);
ErrorLog("GetOrSetNetworkNumber", ISevError, __LINE__, port,
IErrFullRtmpNoSeedCantStart, IMsgFullRtmpNoSeedCantStart,
Insert0());
return(False);
}
PortDescriptor(port)->thisCableRange.firstNetworkNumber =
(short unsigned)suggestedNetworkNumber;
ReleaseLock(RoutingLock);
return(True);
} /* GetOrSetNetworkNumber */
ExternForVisibleFunction void
EnterIntoRoutingTable(AppleTalkNetworkRange networkRange,
int port,
ExtendedAppleTalkNodeNumber nextRouter,
int numberOfHops)
{
RoutingTableEntry routingTableEntry;
int index;
/* Make a new entry in our RTMP routing table. We assume that we are passed
a legitimate new entry (i.e. all range overlaps have already been checked
for). */
CheckMod(index, networkRange.firstNetworkNumber, NumberOfRtmpHashBuckets,
"EnterIntoRoutingTable");
if (networkRange.lastNetworkNumber is UnknownNetworkNumber)
networkRange.lastNetworkNumber = networkRange.firstNetworkNumber;
routingTableEntry = (RoutingTableEntry)Calloc(sizeof(*routingTableEntry), 1);
routingTableEntry->networkRange = networkRange;
routingTableEntry->port = (short)port;
routingTableEntry->nextRouter = nextRouter;
routingTableEntry->numberOfHops = (short)numberOfHops;
routingTableEntry->entryState = Good;
/* Thread the new entry into the head of the hash list. */
TakeLock(RoutingLock);
routingTableEntry->next = routingTable[index];
routingTable[index] = Link(routingTableEntry);
ReleaseLock(RoutingLock);
} /* EnterIntoRoutingTable */
ExternForVisibleFunction void far RtmpSendTimerExpired(long unsigned timerId,
int additionalDataSize,
char far *additionalData)
{
AppleTalkAddress destination;
int port;
/* "Use" unneeded actual parameters. */
timerId, additionalDataSize, additionalData;
/* Broadcast out each port a split horizon view of our routing tables. */
destination.networkNumber = CableWideBroadcastNetworkNumber;
destination.nodeNumber = AppleTalkBroadcastNodeNumber;
destination.socketNumber = RtmpSocket;
for (port = 0; port < MaximumNumberOfPorts; port += 1)
if (PortDescriptor(port)->portActive and
PortDescriptor(port)->routerRunning)
{
DeferIncomingPackets();
SendRoutingData(port, destination, True);
HandleIncomingPackets();
}
/* Re-arm the send RTMP timer... */
StartTimer(RtmpSendTimerExpired, RtmpSendTimerSeconds, 0, empty);
/* We've done the deed... */
HandleDeferredTimerChecks();
return;
} /* RtmpSendTimerExpired */
ExternForVisibleFunction void far
RtmpValidityTimerExpired(long unsigned timerId,
int additionalDataSize,
char far *additionalData)
{
RoutingTableEntry routingTableEntry, nextRoutingTableEntry;
int index;
AppleTalkNetworkRange networkRange;
/* "Use" unneeded actual parameters. */
timerId, additionalDataSize, additionalData;
/* We're going to muck with the routing databases, so defer incoming
packets while we do the deed. */
DeferIncomingPackets();
/* Walk the routing table aging the entries... */
TakeLock(RoutingLock);
for (index = 0; index < NumberOfRtmpHashBuckets; index += 1)
if (routingTable[index] isnt Empty)
{
for (routingTableEntry = routingTable[index];
routingTableEntry isnt empty;
routingTableEntry = nextRoutingTableEntry)
{
/* Get the next entry first, we may delete the current entry... */
nextRoutingTableEntry = routingTableEntry->next;
switch (routingTableEntry->entryState)
{
case Good:
if (routingTableEntry->numberOfHops isnt 0)
routingTableEntry->entryState = Suspect;
break;
case Suspect:
routingTableEntry->entryState = PrettyBad;
break;
case PrettyBad:
routingTableEntry->entryState = Bad;
break;
case Bad:
networkRange = routingTableEntry->networkRange;
ReleaseLock(RoutingLock);
RemoveFromRoutingTable(networkRange);
TakeLock(RoutingLock);
nextRoutingTableEntry = routingTable[index];
break;
default:
networkRange = routingTableEntry->networkRange;
ReleaseLock(RoutingLock);
ErrorLog("RtmpValidityTimerExpired", ISevError, __LINE__,
routingTableEntry->port,
IErrFullRtmpBadEntryState, IMsgFullRtmpBadEntryState,
Insert0());
RemoveFromRoutingTable(networkRange);
TakeLock(RoutingLock);
nextRoutingTableEntry = routingTable[index];
break;
}
}
ReleaseLock(RoutingLock); /* Let somebody else play. */
TakeLock(RoutingLock);
}
ReleaseLock(RoutingLock);
/* Re-arm the Validity timer... */
StartTimer(RtmpValidityTimerExpired, RtmpValidityTimerSeconds, 0, empty);
/* We've done the deed... */
HandleIncomingPackets();
HandleDeferredTimerChecks();
return;
} /* RtmpValidityTimerExpired */
ExternForVisibleFunction void SendRoutingData(int port,
AppleTalkAddress destination,
Boolean doSplitHorizon)
{
RoutingTableEntry routingTableEntry, nextRoutingTableEntry;
BufferDescriptor datagram;
int index, hash, staticHeaderSize;
Boolean newPacket = True, unsentTuples = False;
AppleTalkAddress source;
long sourceSocket;
ExtendedAppleTalkNodeNumber sourceNode;
/* First, compute the source socket: Rtmp socket on our routers node. */
source.networkNumber = PortDescriptor(port)->aRouter.networkNumber;
source.nodeNumber = PortDescriptor(port)->aRouter.nodeNumber;
source.socketNumber = RtmpSocket;
if ((sourceSocket = MapAddressToSocket(port, source)) < ATnoError)
{
/* This may be okay during initialization... we're not started on all
ports yet. */
return;
}
/* Set up the static header to an RTMP data datagram (routers net/node
number). For half ports, since we really don't have such info fill in
the address of the first non-half-port routing port. */
if (PortDescriptor(port)->portType isnt NonAppleTalkHalfPort)
sourceNode = PortDescriptor(port)->aRouter;
else
{
for (index = 0; index < MaximumNumberOfPorts; index += 1)
if (PortDescriptor(index)->portActive and
PortDescriptor(index)->portType isnt NonAppleTalkHalfPort and
PortDescriptor(index)->routerRunning)
{
sourceNode = PortDescriptor(index)->aRouter;
break;
}
/* If we didn't find an address we like, just use the dummy one in
our current port (no doubt net/node = 0). */
if (index >= MaximumNumberOfPorts)
sourceNode = PortDescriptor(port)->aRouter;
}
/* Get the first buffer descriptor. */
if ((datagram = NewBufferDescriptor(MaximumDdpDatagramSize)) is Empty)
{
ErrorLog("SendRoutingData", ISevError, __LINE__, port,
IErrFullRtmpOutOfMemory, IMsgFullRtmpOutOfMemory,
Insert0());
return;
}
/* Walk through the routing table building a tuple for each network;
we may have to send multiple packets... */
for (hash = 0; hash < NumberOfRtmpHashBuckets; hash += 1)
{
TakeLock(RoutingLock);
routingTableEntry = Link(routingTable[hash]);
ReleaseLock(RoutingLock);
while (routingTableEntry isnt empty)
{
if (newPacket)
{
/* Build the static part of an RtmpData packet. */
MoveShortMachineToWire(datagram->data + RtmpSendersNetworkOffset,
sourceNode.networkNumber);
datagram->data[RtmpSendersIdLengthOffset] = 8; /* Bits */
datagram->data[RtmpSendersIdOffset] = sourceNode.nodeNumber;
/* For non-extended networks we need the version stamp, for exteneded
networks, we need to include a initial network range tuple as
part of the header. */
if (not PortDescriptor(port)->extendedNetwork)
{
datagram->data[RtmpSendersIdOffset + 1] = 0;
datagram->data[RtmpSendersIdOffset + 2] = 0;
datagram->data[RtmpSendersIdOffset + 3] = RtmpVersionNumber;
staticHeaderSize = RtmpSendersIdOffset + 1 + 2 + 1; /* + nodeID +
filler +
version */
}
else
{
datagram->data[RtmpRangeStartOffset] =
(char)((PortDescriptor(port)->thisCableRange.
firstNetworkNumber >> 8) & 0xFF);
datagram->data[RtmpRangeStartOffset + 1] =
(char)(PortDescriptor(port)->thisCableRange.
firstNetworkNumber & 0xFF);
datagram->data[RtmpRangeStartOffset + 2] = RtmpTupleWithRange;
datagram->data[RtmpRangeEndOffset] =
(char)((PortDescriptor(port)->thisCableRange.
lastNetworkNumber >> 8) & 0xFF);
datagram->data[RtmpRangeEndOffset + 1] =
(char)(PortDescriptor(port)->thisCableRange.
lastNetworkNumber & 0xFF);
datagram->data[RtmpRangeEndOffset + 2] = RtmpVersionNumber;
staticHeaderSize = RtmpRangeEndOffset + 2 + 1; /* + last net number +
version */
#if Verbose and (IamNot a WindowsNT)
printf(" RTMP Tuple: port %d; net \"%d:%d\"; distance %d.\n", port,
PortDescriptor(port)->thisCableRange.firstNetworkNumber,
PortDescriptor(port)->thisCableRange.lastNetworkNumber,
0);
#endif
}
/* Start of a new packet full of tuples; set the packet index to
the start up the tuples. */
index = staticHeaderSize;
newPacket = False;
}
/* Should the current tuple be omitted due to split horizon
processing? */
if (doSplitHorizon)
if (routingTableEntry->numberOfHops isnt 0)
if (routingTableEntry->port is port)
{
TakeLock(RoutingLock);
nextRoutingTableEntry = Link(routingTableEntry->next);
ReleaseLock(RoutingLock);
UnlinkRoutingTableEntry(routingTableEntry);
routingTableEntry = nextRoutingTableEntry;
continue;
}
/* Okay, place the tuple in the packet... */
datagram->data[index++] =
(char)(routingTableEntry->networkRange.firstNetworkNumber >> 8);
datagram->data[index++] =
(char)(routingTableEntry->networkRange.firstNetworkNumber & 0xFF);
/* Do "notify nieghbor" if our current state is bad. */
if (routingTableEntry->entryState is PrettyBad or
routingTableEntry->entryState is Bad)
datagram->data[index++] = RtmpNumberOfHopsMask;
else
datagram->data[index++] = (char)(routingTableEntry->numberOfHops &
RtmpNumberOfHopsMask);
/* Send an extended tuple if the network range isn't one or the target
port is an extended network. */
if (PortDescriptor(port)->extendedNetwork or
routingTableEntry->networkRange.firstNetworkNumber isnt
routingTableEntry->networkRange.lastNetworkNumber)
{
datagram->data[index - 1] |= RtmpExtendedTupleMask;
datagram->data[index++] =
(char)(routingTableEntry->networkRange.lastNetworkNumber >> 8);
datagram->data[index++] =
(char)(routingTableEntry->networkRange.lastNetworkNumber & 0xFF);
datagram->data[index++] = RtmpVersionNumber;
}
#if Verbose and (IamNot a WindowsNT)
printf(" RTMP Tuple: port %d; net \"%d:%d\"; distance %d.\n", port,
routingTableEntry->networkRange.firstNetworkNumber,
routingTableEntry->networkRange.lastNetworkNumber,
routingTableEntry->numberOfHops);
#endif
unsentTuples = True;
/* Send a data packet if we're full... */
if (index + RtmpExtendedTupleSize >= MaximumDdpDatagramSize)
{
/* Send the DDP packet... */
if (DeliverDdp(sourceSocket, destination,
DdpProtocolRtmpResponseOrData,
datagram, index, Empty, Empty, 0) isnt ATnoError)
ErrorLog("SendRoutingData", ISevError, __LINE__, port,
IErrFullRtmpBadDataSend, IMsgFullRtmpBadDataSend,
Insert0());
/* Get the next buffer descriptor. */
if ((datagram = NewBufferDescriptor(MaximumDdpDatagramSize)) is
Empty)
{
ErrorLog("SendRoutingData", ISevError, __LINE__, port,
IErrFullRtmpOutOfMemory, IMsgFullRtmpOutOfMemory,
Insert0());
UnlinkRoutingTableEntry(routingTableEntry);
return;
}
newPacket = True;
unsentTuples = False;
}
/* Move to next routing table Entry. */
TakeLock(RoutingLock);
nextRoutingTableEntry = Link(routingTableEntry->next);
ReleaseLock(RoutingLock);
UnlinkRoutingTableEntry(routingTableEntry);
routingTableEntry = nextRoutingTableEntry;
}
}
/* If "unsentTuples" is set then we have a partial packet that we need
to send. */
if (unsentTuples)
{
if (DeliverDdp(sourceSocket, destination,
DdpProtocolRtmpResponseOrData,
datagram, index, Empty, Empty, 0) isnt ATnoError)
ErrorLog("SendRoutingData", ISevError, __LINE__, port,
IErrFullRtmpBadDataSend, IMsgFullRtmpBadDataSend,
Insert0());
}
else
FreeBufferChain(datagram); /* Free unused buffer chain. */
/* The deed is done. */
return;
} /* SendRoutingData */
#if (Verbose and (IamNot a WindowsNT)) or (Iam a Primos)
void DumpRtmpRoutingTable(void)
{
RoutingTableEntry routingTableEntry;
int index;
Zones zone;
char *p;
DeferTimerChecking();
DeferIncomingPackets();
/* Dump the RTMP routing tables for debugging. */
printf("********** RTMP routing table start:\n");
for (index = 0; index < NumberOfRtmpHashBuckets; index += 1)
for (routingTableEntry = routingTable[index];
routingTableEntry isnt empty;
routingTableEntry = routingTableEntry->next)
{
printf("Range %05u:%05u [%d]; port %d; state %d; hops %02d; "
"next bridge %u.%u.\n",
routingTableEntry->networkRange.firstNetworkNumber,
routingTableEntry->networkRange.lastNetworkNumber,
routingTableEntry->refCount,
routingTableEntry->port,
routingTableEntry->entryState,
routingTableEntry->numberOfHops,
routingTableEntry->nextRouter.networkNumber,
routingTableEntry->nextRouter.nodeNumber);
p = " ZoneList = ";
for (zone = routingTableEntry->zones;
zone isnt empty;
zone = zone->next)
{
printf("%s%s\n", p, zone->zone->zone);
p = " ";
}
}
printf("********** RTMP routing table end.\n");
HandleIncomingPackets();
HandleDeferredTimerChecks();
return;
} /* DumpRtmpRoutingTable */
#endif
#endif