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.
1272 lines
45 KiB
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
|