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.
1197 lines
37 KiB
1197 lines
37 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
fullrtmp.c
|
|
|
|
Abstract:
|
|
|
|
Handle the RTMP protocol for a routing node.
|
|
|
|
Author:
|
|
|
|
Garth Conboy (Pacer Software)
|
|
Nikhil Kamkolkar (NikhilK)
|
|
|
|
Revision History:
|
|
|
|
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.
|
|
|
|
--*/
|
|
|
|
|
|
#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 (GET_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 (GET_PORTDESCRIPTOR(port)->PortType isnt NonAppleTalkHalfPort and
|
|
GET_PORTDESCRIPTOR(port)->SeenRouterRecently and
|
|
GET_PORTDESCRIPTOR(port)->InitialNetworkRange.FirstNetworkNumber isnt
|
|
UnknownNetworkNumber)
|
|
if (GET_PORTDESCRIPTOR(port)->InitialNetworkRange.FirstNetworkNumber isnt
|
|
GET_PORTDESCRIPTOR(port)->ThisCableRange.FirstNetworkNumber or
|
|
GET_PORTDESCRIPTOR(port)->InitialNetworkRange.LastNetworkNumber isnt
|
|
GET_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 (GET_PORTDESCRIPTOR(port)->PortType is NonAppleTalkHalfPort) {
|
|
GET_PORTDESCRIPTOR(port)->ThisCableRange.FirstNetworkNumber =
|
|
UnknownNetworkNumber;
|
|
GET_PORTDESCRIPTOR(port)->ThisCableRange.LastNetworkNumber =
|
|
UnknownNetworkNumber;
|
|
}
|
|
else if (not GET_PORTDESCRIPTOR(port)->SeedRouter) {
|
|
ErrorLog("StartRtmpProcessingOnPort", ISevError, __LINE__, port,
|
|
IErrFullRtmpNoSeedCantStart, IMsgFullRtmpNoSeedCantStart,
|
|
Insert0());
|
|
return(False);
|
|
}
|
|
else if (not GET_PORTDESCRIPTOR(port)->SeenRouterRecently)
|
|
GET_PORTDESCRIPTOR(port)->ThisCableRange =
|
|
GET_PORTDESCRIPTOR(port)->InitialNetworkRange;
|
|
}
|
|
else {
|
|
//
|
|
// For non-extended networks, we need to either find or seed our network
|
|
// number.
|
|
//
|
|
|
|
if (not GetOrSetNetworkNumber(port, (unsigned int)(
|
|
GET_PORTDESCRIPTOR(port)->SeedRouter ?
|
|
GET_PORTDESCRIPTOR(port)->InitialNetworkRange.
|
|
FirstNetworkNumber :
|
|
UnknownNetworkNumber)))
|
|
return(False);
|
|
|
|
if (not GET_PORTDESCRIPTOR(port)->SeenRouterRecently)
|
|
GET_PORTDESCRIPTOR(port)->ThisCableRange.FirstNetworkNumber =
|
|
GET_PORTDESCRIPTOR(port)->InitialNetworkRange.FirstNetworkNumber;
|
|
|
|
//
|
|
// On non-extended networks, we would have "allocated" a node with network
|
|
// zero, fix this up here!
|
|
//
|
|
|
|
GET_PORTDESCRIPTOR(port)->ActiveNodes->ExtendedNode.NetworkNumber =
|
|
GET_PORTDESCRIPTOR(port)->ARouter.NetworkNumber =
|
|
routerNode.NetworkNumber =
|
|
GET_PORTDESCRIPTOR(port)->ThisCableRange.FirstNetworkNumber;
|
|
}
|
|
|
|
// Well, we're a router now, so mark us as A-ROUTER.
|
|
|
|
GET_PORTDESCRIPTOR(port)->SeenRouterRecently = True;
|
|
GET_PORTDESCRIPTOR(port)->ARouter = routerNode;
|
|
GET_PORTDESCRIPTOR(port)->RouterRunning = True;
|
|
|
|
// Okay, make it so...
|
|
|
|
if (GET_PORTDESCRIPTOR(port)->PortType isnt NonAppleTalkHalfPort)
|
|
EnterIntoRoutingTable(GET_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);
|
|
}
|
|
|
|
// 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 ((GET_PORTDESCRIPTOR(port)->ExtendedNetwork and
|
|
datagramLength < RtmpDataMinimumSizeExtended) or
|
|
(not GET_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(GET_PORTDESCRIPTOR(port)->ARouter.
|
|
networkNumber)) is empty) {
|
|
ErrorLog("RtmpPacketInRouter", ISevError, __LINE__, port,
|
|
IErrFullRtmpBadRoutingTables, IMsgFullRtmpBadRoutingTables,
|
|
Insert0());
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return((long)True);
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
buffer->data[RtmpSendersNetworkOffset] =
|
|
(char)((GET_PORTDESCRIPTOR(port)->ARouter.NetworkNumber >> 8) & 0xFF);
|
|
buffer->data[RtmpSendersNetworkOffset + 1] =
|
|
(char)(GET_PORTDESCRIPTOR(port)->ARouter.NetworkNumber & 0xFF);
|
|
buffer->data[RtmpSendersIdLengthOffset] = 8; // Bits
|
|
buffer->data[RtmpSendersIdOffset] = GET_PORTDESCRIPTOR(port)->ARouter.NodeNumber;
|
|
|
|
//
|
|
// On an extended port, we also want to add the initial network range
|
|
// tuple.
|
|
//
|
|
|
|
if (not GET_PORTDESCRIPTOR(port)->ExtendedNetwork)
|
|
responseSize = RtmpSendersIdOffset + sizeof(char);
|
|
else {
|
|
buffer->data[RtmpRangeStartOffset] =
|
|
(char)((GET_PORTDESCRIPTOR(port)->ThisCableRange.
|
|
FirstNetworkNumber >> 8) & 0xFF);
|
|
buffer->data[RtmpRangeStartOffset + 1] =
|
|
(char)(GET_PORTDESCRIPTOR(port)->ThisCableRange.
|
|
FirstNetworkNumber & 0xFF);
|
|
buffer->data[RtmpRangeStartOffset + 2] = RtmpTupleWithRange;
|
|
buffer->data[RtmpRangeEndOffset] =
|
|
(char)((GET_PORTDESCRIPTOR(port)->ThisCableRange.
|
|
LastNetworkNumber >> 8) & 0xFF);
|
|
buffer->data[RtmpRangeEndOffset + 1] =
|
|
(char)(GET_PORTDESCRIPTOR(port)->ThisCableRange.
|
|
LastNetworkNumber & 0xFF);
|
|
responseSize = RtmpRangeEndOffset + sizeof(short unsigned);
|
|
}
|
|
|
|
// 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 (GET_PORTDESCRIPTOR(port)->ExtendedNetwork)
|
|
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 (GET_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.
|
|
//
|
|
|
|
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;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If we're hearing about one of our directly connected nets, we
|
|
// know best... ignore the info.
|
|
//
|
|
|
|
if (routingTableEntry->numberOfHops is 0)
|
|
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) {
|
|
routingTableEntry->numberOfHops = (short)(numberOfHops + 1);
|
|
routingTableEntry->nextRouter.networkNumber =
|
|
source.networkNumber;
|
|
routingTableEntry->nextRouter.nodeNumber =
|
|
source.nodeNumber;
|
|
routingTableEntry->port = (short)port;
|
|
routingTableEntry->entryState = Good;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check fora shorter or equidistant path to the target network...
|
|
// if so, replace the entry.
|
|
//
|
|
|
|
if (routingTableEntry->numberOfHops >= numberOfHops + 1 and
|
|
numberOfHops < 15) {
|
|
routingTableEntry->numberOfHops = (short)(numberOfHops + 1);
|
|
routingTableEntry->nextRouter.networkNumber =
|
|
source.networkNumber;
|
|
routingTableEntry->nextRouter.nodeNumber =
|
|
source.nodeNumber;
|
|
routingTableEntry->port = (short)port;
|
|
routingTableEntry->entryState = Good;
|
|
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) {
|
|
routingTableEntry->numberOfHops = (short)(numberOfHops + 1);
|
|
if (routingTableEntry->numberOfHops < 16)
|
|
routingTableEntry->entryState = Good;
|
|
else
|
|
RemoveFromRoutingTable(cableRange);
|
|
continue;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// 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!
|
|
//
|
|
|
|
for (foundOverlap = False, indexToo = 0;
|
|
not foundOverlap and indexToo < NumberOfRtmpHashBuckets;
|
|
indexToo += 1)
|
|
for (routingTableEntry = routingTable[indexToo];
|
|
not foundOverlap and routingTableEntry isnt empty;
|
|
routingTableEntry = routingTableEntry->next)
|
|
if (RangesOverlap(&cableRange, &routingTableEntry->networkRange))
|
|
foundOverlap = True;
|
|
|
|
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;
|
|
|
|
// First, try the "recent route cache".
|
|
|
|
CheckMod(recentRouteIndex, networkNumber, NumberOfRecentRouteBuckets,
|
|
"FindInRoutingTable");
|
|
if (recentRoutes[recentRouteIndex] isnt empty and
|
|
IsWithinNetworkRange(networkNumber,
|
|
&recentRoutes[recentRouteIndex]->networkRange))
|
|
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;
|
|
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)
|
|
for (routingTableEntry = routingTable[index];
|
|
routingTableEntry isnt empty;
|
|
routingTableEntry = routingTableEntry->next)
|
|
if (IsWithinNetworkRange(networkNumber,
|
|
&routingTableEntry->networkRange)) {
|
|
recentRoutes[recentRouteIndex] = routingTableEntry;
|
|
return(routingTableEntry);
|
|
}
|
|
|
|
// 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.
|
|
//
|
|
|
|
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;
|
|
}
|
|
|
|
// Free the zone list and the actual routing table node.
|
|
|
|
FreeZoneList(routingTableEntry->zoneList);
|
|
Free(routingTableEntry);
|
|
|
|
// The deed is done.
|
|
|
|
return(True);
|
|
}
|
|
else
|
|
previousRoutingTableEntry = routingTableEntry;
|
|
|
|
return(False);
|
|
|
|
} // RemoveFromRoutingTable
|
|
|
|
|
|
|
|
|
|
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 (GET_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 =
|
|
GET_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 GET_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,
|
|
&GET_PORTDESCRIPTOR(port)->SeenRouterRecently);
|
|
}
|
|
|
|
// If we got an answer, we're set...
|
|
|
|
if (GET_PORTDESCRIPTOR(port)->SeenRouterRecently) {
|
|
if (suggestedNetworkNumber isnt UnknownNetworkNumber and
|
|
GET_PORTDESCRIPTOR(port)->ThisCableRange.FirstNetworkNumber isnt
|
|
(short unsigned)suggestedNetworkNumber)
|
|
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) {
|
|
ErrorLog("GetOrSetNetworkNumber", ISevError, __LINE__, port,
|
|
IErrFullRtmpNoSeedCantStart, IMsgFullRtmpNoSeedCantStart,
|
|
Insert0());
|
|
return(False);
|
|
}
|
|
|
|
GET_PORTDESCRIPTOR(port)->ThisCableRange.FirstNetworkNumber =
|
|
(short unsigned)suggestedNetworkNumber;
|
|
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.
|
|
|
|
routingTableEntry->next = routingTable[index];
|
|
routingTable[index] = routingTableEntry;
|
|
|
|
} // 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 (GET_PORTDESCRIPTOR(port)->Flags & PD_ACTIVE and
|
|
GET_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;
|
|
|
|
// "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...
|
|
|
|
for (index = 0; index < NumberOfRtmpHashBuckets; index += 1)
|
|
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:
|
|
RemoveFromRoutingTable(routingTableEntry->networkRange);
|
|
break;
|
|
default:
|
|
ErrorLog("RtmpValidityTimerExpired", ISevError, __LINE__,
|
|
routingTableEntry->port,
|
|
IErrFullRtmpBadEntryState, IMsgFullRtmpBadEntryState,
|
|
Insert0());
|
|
RemoveFromRoutingTable(routingTableEntry->networkRange);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
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 = GET_PORTDESCRIPTOR(port)->ARouter.NetworkNumber;
|
|
source.nodeNumber = GET_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 (GET_PORTDESCRIPTOR(port)->PortType isnt NonAppleTalkHalfPort)
|
|
sourceNode = GET_PORTDESCRIPTOR(port)->ARouter;
|
|
else {
|
|
for (index = 0; index < MaximumNumberOfPorts; index += 1)
|
|
if (GET_PORTDESCRIPTOR(index)->Flags & PD_ACTIVE and
|
|
GET_PORTDESCRIPTOR(index)->PortType isnt NonAppleTalkHalfPort and
|
|
GET_PORTDESCRIPTOR(index)->RouterRunning) {
|
|
sourceNode = GET_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 = GET_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)
|
|
for (routingTableEntry = routingTable[hash];
|
|
routingTableEntry isnt empty;
|
|
routingTableEntry = routingTableEntry->next) {
|
|
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 GET_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)((GET_PORTDESCRIPTOR(port)->ThisCableRange.
|
|
FirstNetworkNumber >> 8) & 0xFF);
|
|
datagram->data[RtmpRangeStartOffset + 1] =
|
|
(char)(GET_PORTDESCRIPTOR(port)->ThisCableRange.
|
|
FirstNetworkNumber & 0xFF);
|
|
datagram->data[RtmpRangeStartOffset + 2] = RtmpTupleWithRange;
|
|
datagram->data[RtmpRangeEndOffset] =
|
|
(char)((GET_PORTDESCRIPTOR(port)->ThisCableRange.
|
|
LastNetworkNumber >> 8) & 0xFF);
|
|
datagram->data[RtmpRangeEndOffset + 1] =
|
|
(char)(GET_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,
|
|
GET_PORTDESCRIPTOR(port)->ThisCableRange.FirstNetworkNumber,
|
|
GET_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)
|
|
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 (GET_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());
|
|
return;
|
|
}
|
|
|
|
newPacket = True;
|
|
unsentTuples = False;
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
ZoneList 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; port %d; state %d; hops %02d; "
|
|
"next bridge %u.%u.\n",
|
|
routingTableEntry->networkRange.firstNetworkNumber,
|
|
routingTableEntry->networkRange.lastNetworkNumber,
|
|
routingTableEntry->port,
|
|
routingTableEntry->entryState,
|
|
routingTableEntry->numberOfHops,
|
|
routingTableEntry->nextRouter.networkNumber,
|
|
routingTableEntry->nextRouter.nodeNumber);
|
|
p = " ZoneList = ";
|
|
for (zone = routingTableEntry->zoneList;
|
|
zone isnt empty;
|
|
zone = zone->next) {
|
|
printf("%s%s\n", p, zone->zone);
|
|
p = " ";
|
|
}
|
|
}
|
|
printf("********** RTMP routing table end.\n");
|
|
|
|
HandleIncomingPackets();
|
|
HandleDeferredTimerChecks();
|
|
return;
|
|
|
|
} // DumpRtmpRoutingTable
|
|
#endif
|
|
|
|
#endif
|