-- File: INRImpl.mesa - last edit: -- AOF 5-Feb-88 13:54:58 -- Leong 28-Sep-87 18:40:59 -- Kluger 22-Oct-85 8:01:39 -- Copyright (C) 1985, 1986, 1987, 1988 by Xerox Corporation. All rights reserved. -- Function: The implementation module for the NS Internetwork Router. << IMPLEMENTATION NOTES: (1) In an attempt to fix the flapping routing table problem, gratuitous Routing Information response packets are sent one at a time. This should help when the inr/phonenet is congested with other packets. >> DIRECTORY Buffer, --EXPORT Device NSBuffer USING [AccessHandle, Body, MakePool, DestroyPool, Buffer, GetBuffer], Checksums USING [IncrNSTransportControlAndUpdateChecksum, SetChecksum], CommFlags USING [doStats, doDebug], CommHeap USING [zone], CommunicationInternal USING [], Driver USING [ ChangeNumberOfInputBuffers, Device, GetDeviceChain, PutOnGlobalDoneQueue], Environment USING [bytesPerWord], Protocol1 USING [EncapsulateAndTransmit, GetContext], NSConstants USING [routingInformationSocket], NSTypes USING [bytesPerIDPHeader, RoutingInfoTuple, RoutingInfoType, TransportControl, maxIDPDataBytes, maxIDPBytesPerPacket], Process USING [MsecToTicks, Pause, SetTimeout, DisableTimeout, SecondsToTicks, Abort, Detach, EnableAborts], ProcessorFace USING [SetMP], RoutingTable USING [Object, Handle, Register, FlushCacheProc, NetworkContext], Router USING [FindMyHostID, NoTableEntryForNet, RoutersFunction], RouterInternal USING [BroadcastThisPacket, checkIt, SendErrorPacket, SendPacket], Runtime USING [CallDebugger], Inr USING [], InrDebug, InrFriends USING [RoutingTableObject, HopCount, RoutingTableEntry, MisnumberedNetTrap], InrInternal, InrStats USING [Stats, RoutingTableSearch], InrTypes USING [RoutingInfoType, ExtendedRoutingInfoTuple, ExtendedRoutingInformation], Inline USING [LowHalf, BITAND], Socket USING [ChannelHandle, Create, Delete, GetPacket, SetWaitTime, ReturnBuffer, infiniteWaitTime, GetBufferPool], Stats USING [StatIncr], SpecialSystem USING [HostNumber, NetworkNumber, SocketNumber], System USING [broadcastHostNumber, GetClockPulses, GetGreenwichMeanTime, gmtEpoch, GreenwichMeanTime, HostNumber, NetworkAddress, NetworkNumber, nullNetworkNumber, nullHostNumber, Pulses], RoutingFudges USING []; INRImpl: MONITOR LOCKS inrLock IMPORTS InrInternal, NSBuffer, Inline, Checksums, CommHeap, Driver, Process, ProcessorFace, Protocol1, Router, RouterInternal, Socket, Stats, System, RoutingTable, Runtime EXPORTS Inr, InrDebug, InrFriends, InrInternal, InrStats, Buffer, System, RoutingFudges = BEGIN inrLock: PUBLIC MONITORLOCK; --monitors proc accessing the routing table doNSStats: BOOLEAN = TRUE OR CommFlags.doStats; --tied to TRUE for now ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- -- TYPEs ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- NetworkNumber: PUBLIC TYPE = SpecialSystem.NetworkNumber; HostNumber: PUBLIC TYPE = SpecialSystem.HostNumber; SocketNumber: PUBLIC TYPE = SpecialSystem.SocketNumber; Device: PUBLIC TYPE = Driver.Device; --LOOPHOLEs into b.fo.context -- clear the opaque type in Buffer Context: TYPE = RoutingTable.NetworkContext; RoutingTableObject: TYPE = InrFriends.RoutingTableObject; RoutingTableEntry: TYPE = InrFriends.RoutingTableEntry; HopCount: TYPE = InrFriends.HopCount; ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- -- CONSTANTS that make it all happen ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- unknownHostID: HostNumber = System.nullHostNumber; extraBuffersForForwarding: CARDINAL = 40; singleGratuitousResponseBuffer: CARDINAL = 1; -- The single sent buffer count ensures that Routing Information -- response packets are sent one at a time. The inr cannot get a -- second sent buffer until the first one has gone through the system. mask: CARDINAL = 31; -- for masking out the high eleven bits of the incoming -- delay, as we don't use them (yet). bitsHashed: CARDINAL ¬ InrInternal.defaultNumberOfHashBits; -- number of bits used to index into the routing table markDead: BOOLEAN ¬ TRUE; -- flag indicating invalid entries -- are to be marked dead instead of deallocated allLocalInternetRouters: System.NetworkAddress = [ net: nullNetworkNumber, host: System.broadcastHostNumber, socket: NSConstants.routingInformationSocket]; anyNetNetworkNumber: NetworkNumber = [177777B, 177777B]; anyNetNetworkNumberFor860: NetworkNumber = [0AAFFH, 0FFFFH]; nullNetworkNumber: NetworkNumber = System.nullNetworkNumber; -- routing info request network number that indicates all networks nullEnum: NetworkNumber = nullNetworkNumber; -- used for the various enumerations initialTransportControl: NSTypes.TransportControl = [ trace: FALSE, filler: 0, hopCount: 0]; updateCycles: CARDINAL = InrInternal.updateCycles; -- timeUnits gets reset to this; number of routing table update cycles. alternatePathTimeUnitThreshHold: CARDINAL = 2; -- we look for alternate routing path if timeUnits fall BELOW this value. -- various hop counts localHop: HopCount = InrInternal.localHop; -- rte delay for attached network(s) is zero hops. maxInternetrouterHops: CARDINAL = InrInternal.maxInternetrouterHops; infinityHopCount: CARDINAL = InrInternal.infinityHopCount; -- sizes of the Routing Information protocol objects bytesPerRoutingInfoType: CARDINAL = (2*SIZE[NSTypes.RoutingInfoType]); bytesPerRoutingInfoTuple: CARDINAL = (2*SIZE[NSTypes.RoutingInfoTuple]); -- sizes of buffer smallBuffer: CARDINAL = 130; --Pre Pilot 13.0 small buffer size fullBuffer: CARDINAL = NSTypes.maxIDPBytesPerPacket; -- Environment stuff bpw: CARDINAL = Environment.bytesPerWord; -- sizes of the extended Routing Information protocol objects bytesPerExtendedRoutingInfoTuple: CARDINAL = (2*SIZE[InrTypes.ExtendedRoutingInfoTuple]); maxTuplesPerExtendedRoutingPacket: CARDINAL = (NSTypes.maxIDPDataBytes - bytesPerRoutingInfoType) / bytesPerExtendedRoutingInfoTuple; ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- -- VARIABLES or semi-Variables ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- bufferHandle: NSBuffer.AccessHandle ¬ NIL; -- this is a global variable, instead of local to InternetRouterServer proc, -- so that SendGratuitousResponse proc may use the buffer account to send -- routing information myHostID: HostNumber; -- host ID of this system element pleaseStop: PUBLIC BOOLEAN ¬ TRUE; -- controls the processes. << TRUE if we are OFF, FALSE if we are ON >> privateCH: PUBLIC Socket.ChannelHandle; -- Note: this is PUBLIC and in global frame as a hook for access -- to the Well Known routing information socket internetRouterTimer, -- times out to send gratuitous routing packets auxInternetRouterTimer: -- times out by a random amount so we don't all send the gratuitous packets at the same time. CONDITION; routingInformationSupplierFork, internetRouterServerFork, decrementRoutingTableEntriesFork, routingTableChangedFork: PROCESS ¬ NIL; stats: PUBLIC LONG POINTER TO InrStats.Stats ¬ NIL; misnumberedNetTrap: InrFriends.MisnumberedNetTrap ¬ DefaultMisnumberedNetTrap; lastGetStats: System.GreenwichMeanTime ¬ System.gmtEpoch; --mark the last time a snap of the statistics was taken --Routing Table Object rto: PUBLIC RoutingTable.Object ¬ [ type: interNetworkRouting, start: Start, stop: Stop, startEnumeration: nullEnum, endEnumeration: nullEnum, enumerate: InrInternal.EnumerateByDistance, fillTable: Fill, getDelay: GetDelay, transmit: Transmit, forward: Forward, findNetwork: InrInternal.FindNetID, addNetwork: InrInternal.AddNet, removeNetwork: InrInternal.RemoveNet, flushCache: FlushCacheNoOp, stateChanged: ChangedState]; ptrToRto:RoutingTable.Handle ¬ @rto; extraHops: CARDINAL ¬ 0; -- fudge factor on the hop count to trick other -- routers into using another path for forwarding packets ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- -- Development-debugging utilities ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- doMPCodes: BOOLEAN = TRUE; -- should we munge the MP Codes? MPCodes: TYPE = InrDebug.MPCodes; mpCode: MPCodes ¬ default; gratuitousStats: ARRAY MPCodes OF LONG CARDINAL ¬ ALL[0]; ResetMPCodes: PUBLIC PROCEDURE [new: MPCodes] RETURNS [old: MPCodes] = BEGIN mp, offset: CARDINAL; old ¬ mpCode; mpCode ¬ new; IF ~doMPCodes THEN RETURN; mp ¬ Inline.LowHalf[gratuitousStats[mpCode]]; offset ¬ SELECT mpCode FROM phonenetResponseSent => 9000, phonenetResponseRecv => 7000, ENDCASE => 8000; mp ¬ (mp MOD 1000) + offset; ProcessorFace.SetMP [mp]; END; --of ResetMPCodes ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- -- Routing Table (list) handling Routines ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- -- PROCESS FORKED IN RoutingInformationSupplier[] -- This routine broadcasts a routing information request on all attached -- networks. If is supplied, the request will be for info on that net -- only, else information will be requested for information on all networks -- the routing information suppliers know about. -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- BroadcastRoutingRequest: PROCEDURE [ cH: Socket.ChannelHandle, net: NetworkNumber ¬ anyNetNetworkNumber] = BEGIN b: NSBuffer.Buffer; body: NSBuffer.Body; FOR i:CARDINAL IN [1..3) DO IF (b ¬ NSBuffer.GetBuffer [aH: Socket.GetBufferPool[cH], function: send, size: smallBuffer, wait: TRUE]) # NIL THEN BEGIN body ¬ b.ns; --got to set it before using it b.fo.type ¬ ns; body.packetType ¬ routingInformation; body.transportControl ¬ initialTransportControl; body.source.host ¬ myHostID; body.destination.socket ¬ body.source.socket ¬ NSConstants.routingInformationSocket; body.pktLength ¬ NSTypes.bytesPerIDPHeader + bytesPerRoutingInfoType + bytesPerRoutingInfoTuple; body.routingType ¬ routingInfoRequest; body.routingTuple[0] ¬ [net, infinityHopCount]; RouterInternal.BroadcastThisPacket[b]; END; Process.Pause [Process.SecondsToTicks [1]]; ENDLOOP; END; -- of BroadcastRoutingRequest -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- -- ChangedState is passed in to the Pilot Router. -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- ChangedState: PROCEDURE [context: Context] = BEGIN --used by driver when a network changes. InrInternal.RemoveNetDeviceLocked[context]; InrInternal.AddNet[context]; END; --ChangedState -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- -- This routine just takes a trip to debugger with the given message. -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- DefaultMisnumberedNetTrap: InrFriends.MisnumberedNetTrap = { Runtime.CallDebugger ["Misnumbered net trap."L]}; -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- -- Client requests that we gather routing table information. This is passed -- to the Pilot Router. -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Fill: PROCEDURE [maxDelay: CARDINAL] = BEGIN << Since we are already an inr this proc. is a NOP, (we leave it in for the time being because its part of the routing object). >> END; -- of Fill -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- -- FlushCacheNoOp is a no operation routine and is passed to the Pilot Router. -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- FlushCacheNoOp: RoutingTable.FlushCacheProc = {}; -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- -- This routine forwards a packet onto the next host in the route after -- checking the packet has not been forwarded too many times and a route -- exists to the destination network. Forward is passed to the Pilot Router. -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Forward: PROCEDURE [b: NSBuffer.Buffer] = BEGIN ENABLE UNWIND => NULL; body: NSBuffer.Body = b.ns; nextHost: HostNumber; e: RoutingTableEntry; context: Context; NotFoundDestinationNetworkLocked: ENTRY PROCEDURE RETURNS [BOOLEAN] = INLINE BEGIN ENABLE UNWIND => NULL; RETURN[ ((e ¬ InrInternal.FindNetworkNumber[body.destination.net]) = NIL) OR ((context ¬ e.context) = NIL) OR (context.network = NIL)]; END; -- of NotFoundDestinationNetworkLocked --see if we have traversed max number of internet routers already SELECT TRUE FROM (maxInternetrouterHops <= body.transportControl.hopCount) => -- note that it is "<=" - this prevents a packet from being -- forwarded past the farthest net a source knows about (15 hops) BEGIN << *** AR#6979 Shouldn't munge packet before sending Error packet *** body.destination ¬ [InrInternal.FindNetIDInternal [nullNetworkNumber], myHostID, NSConstants.routingInformationSocket]; >> -- the destination will get flipped with the source by -- SendErrorPacket RouterInternal.SendErrorPacket[b, excessHops, 0]; IF doNSStats THEN Stats.StatIncr[statNSNotForwarded]; StatIncr [@stats.tooManyHops] END; (NotFoundDestinationNetworkLocked[]) => BEGIN --outgoing packet for unknown net IF (body.source.host # myHostID) THEN BEGIN << *** AR#6979 Shouldn't munge packet before sending Error packet *** body.destination ¬ [InrInternal.FindNetIDInternal [nullNetworkNumber], myHostID, NSConstants.routingInformationSocket]; >> -- the destination will get flipped with the source by -- SendErrorPacket RouterInternal.SendErrorPacket[b, cantGetThere, 0]; END ELSE BEGIN b.fo.status ¬ noRouteToNetwork; --return b to the system buffer pool Driver.PutOnGlobalDoneQueue[LOOPHOLE[b]]; END; IF doNSStats THEN Stats.StatIncr[statNSNotForwarded]; StatIncr [@stats.unknownNet]; END; ENDCASE => BEGIN --outgoing packet Checksums.IncrNSTransportControlAndUpdateChecksum[body]; --now transmit it over the correct network IF ((nextHost ¬ e.route) = unknownHostID) THEN --already on dest. net nextHost ¬ body.destination.host; b.fo.network ¬ context.network; -- mark the network being used b.fo.context ¬ context; b.fo.type ¬ ns; b.fo.status ¬ goodCompletion; --maintain the crude counters of packets and bytes forwarded StatIncr [@stats.statNSForwarded]; StatBump [@stats.bytesForwarded, body.pktLength]; Protocol1.EncapsulateAndTransmit[LOOPHOLE[b], @nextHost]; --same net IF doNSStats THEN Stats.StatIncr[statNSForwarded]; END; END; --Forward -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- -- Provide the crude counters of packets forwarded by the router -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- ForwardingStats: PUBLIC PROCEDURE RETURNS [packets, bytes: LONG CARDINAL] = BEGIN IF (stats = NIL) THEN RETURN [0, 0]; --forwarding hasn't been activated packets ¬ stats.statNSForwarded; bytes ¬ stats.bytesForwarded; END; --ForwardingStats -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- -- GetDelay returns the number of hops away from the given network number. -- This routine is passed to the Pilot Router. -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- GetDelay: PROCEDURE [net: NetworkNumber] RETURNS [delay: CARDINAL] = BEGIN LockedFindNet: ENTRY PROC = INLINE {ENABLE UNWIND => NULL; e ¬ InrInternal.FindNetworkNumber[net, FALSE]}; e: RoutingTableEntry; LockedFindNet[]; --try searching first time IF (e = NIL) OR (e.context = NIL) THEN ERROR Router.NoTableEntryForNet; -- a net is not accessible unless -- we have a rte AND a network pointer delay ¬ e.delay; --value of interest END; --GetDelay -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- -- GetFarNet returns a non-null network number. The monitor lock is required -- here because of the possible table search. -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- -- can be a network driver that is anonyomous. In other words, it -- isn't assigned its own network number. Point to Point connections -- between INRs are anonymous E.g. a phoneline between two INRs. -- An anonyomous network invokes a routing table search to find the entry -- that uses the given driver and has the lowest delay to a destination -- net of all the rtes that use . That entry's destNetwork should be -- a viable network. GetFarNet: INTERNAL PROCEDURE [net: Context] RETURNS [farNet: NetworkNumber] = BEGIN farNet ¬ IF (net.netNumber = nullNetworkNumber) THEN InrInternal.FindFarNet[net] ELSE net.netNumber; END; --of GetFarNet -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- -- FORKED INTERNETWORK ROUTER PROCESS -- This process wakes up every 30 seconds and sends out Routing Information -- Protocol Response packets gratuitously, if this system element is an -- internet router. -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- InternetRouterServer: PROCEDURE = BEGIN Wait: ENTRY PROCEDURE [condition: CONDITION] = INLINE { ENABLE UNWIND => NULL; WAIT condition; }; --end of Wait -- start of procedure, when this process is first started it waits -- a while to allow the routing table to be built by the incoming -- routing response packets. Process.Pause[Process.MsecToTicks[100]]; Driver.ChangeNumberOfInputBuffers[TRUE]; -- create extra buffers because we are an inr bufferHandle ¬ NSBuffer.MakePool [ send: singleGratuitousResponseBuffer, --slows down rate of sending -- The single sent buffer count ensures that Routing Information -- response packets are sent one at a time. The inr cannot get a -- second sent buffer until the first one has gone through the system. receive: extraBuffersForForwarding]; -- inr's buffer account established (system pool is allowed to grow) UNTIL pleaseStop DO ENABLE ABORTED => EXIT; Wait[internetRouterTimer]; IF pleaseStop THEN EXIT; -- wait a random amount of time [0..7] seconds before sending out the -- gratuitous response packet. This will help prevent all inrs -- from sending at the same time. Process.SetTimeout[@auxInternetRouterTimer, Process.SecondsToTicks[Inline.LowHalf[System.GetClockPulses[]] MOD 8]]; Wait[auxInternetRouterTimer]; IF pleaseStop THEN EXIT; SendGratuitousResponse [to: allLocalInternetRouters]; StatIncr [@stats.gratuitousRoutingResponseSent]; ENDLOOP; -- At this point, the Inr has been stopped and it must inform everyone -- that it should no longer use it as a route anymore. SendGratuitousResponse [to: allLocalInternetRouters, b: NIL, allAreUnreachable: TRUE]; --tell everyone not to use as a route anymore StatIncr [@stats.gratuitousRoutingResponseSent]; -- delete the buffers allocated for the INR NSBuffer.DestroyPool [bufferHandle]; bufferHandle ¬ NIL; Driver.ChangeNumberOfInputBuffers[FALSE]; END; --InternetRouterServer -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- -- This routine routs a routing inforamtion response packet for its -- destination and sends it. A few of the packet's field should already be -- set coming into here. Note the two special case: sending to self and -- sending to everyone. If returned value is TRUE then the buffer -- was returned by this routine, otherwise the caller must return the buffer. -- -- Return buffer by making a call to either Socket.ReturnBuffer or -- Driver.PutOnGlobalDoneQueue -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- RoutAndSendRoutingInfoResponse: INTERNAL PROCEDURE [ b: NSBuffer.Buffer, extended: BOOLEAN ¬ FALSE] RETURNS [success: BOOLEAN ¬ TRUE] = BEGIN -- Routs and sends a routing information response. -- Should be called from inside the monitor. -- The incoming b.network, b.destination, and body.pktLength should -- be already set. Source net is set to be the network's, unless = 0 and -- destination socket not #1 - then it's set to a more informative number. -- -- Note the special case handling when the destination is us! body: NSBuffer.Body = b.ns; context: Context ¬ b.fo.context; immediateHost: HostNumber ¬ body.destination.host; e: RoutingTableEntry; destinationIsMe: BOOLEAN ¬ body.destination.host = myHostID; -- fill packet type information b.fo.type ¬ ns; body.packetType ¬ routingInformation; body.transportControl ¬ initialTransportControl; body.routingType ¬ IF ~extended THEN routingInfoResponse ELSE LOOPHOLE[InrTypes.RoutingInfoType[routingInfoExtendedResponse]]; body.source ¬ [IF (context # NIL) THEN context.netNumber ELSE nullNetworkNumber, myHostID, NSConstants.routingInformationSocket]; IF (body.destination.socket # NSConstants.routingInformationSocket) AND (body.source.net = nullNetworkNumber) THEN -- give them better information (network management) body.source.net ¬ InrInternal.FindNetIDInternal [nullNetworkNumber]; << "Valid" net numbers now can consume two words. Ex: 132-132, 132-001..132-009 IF CommFlags.doDebug AND (LOOPHOLE[body.source.net, NetworkNumber].a#0 OR LOOPHOLE[body.destination.net, NetworkNumber].a#0) THEN ERROR; -- BogusNetNumber! (high order bits non-zero) >> IF destinationIsMe THEN BEGIN b.fo.context ¬ LOOPHOLE[b.fo.network, Device] ¬ NIL; -- packet is locally sent RouterInternal.SendPacket [b]; -- hey, special treatment RETURN; END; -- destination is someone else, determine correct network IF body.destination.host = System.broadcastHostNumber THEN BEGIN RouterInternal.BroadcastThisPacket [b]; -- router sets b.fo.context -- broadcast on all attached networks IF doMPCodes THEN BEGIN -- for gratuitous response on phonenet accounting FOR n: Driver.Device ¬ Driver.GetDeviceChain[], n.next UNTIL (n = NIL) DO IF NOT ((n.device = phonenet) AND n.alive) THEN LOOP; StatIncr[@gratuitousStats[phonenetResponseSent]]; IF (mpCode = phonenetResponseSent) THEN BEGIN mp: CARDINAL ¬ Inline.LowHalf[gratuitousStats[phonenetResponseSent]]; mp ¬ (mp MOD 1000) + 9000; ProcessorFace.SetMP [mp]; -- show 9XXX for sent END; EXIT; ENDLOOP; END; --of accounting work RETURN; END; IF (body.destination.net # nullNetworkNumber) THEN -- if the dest. is net 0, use immediateHost's default (set in declaration) BEGIN -- find the route to this guy e ¬ InrInternal.FindNetworkNumber [body.destination.net, FALSE]; IF (e = NIL) OR (e.context = NIL) THEN RETURN [FALSE]; -- no route to destination (assume we already know about all routes -- since we are an internetwork router) context ¬ e.context; b.fo.context ¬ e.context; b.fo.network ¬ e.context.network; immediateHost ¬ IF (e.route = unknownHostID) THEN body.destination.host -- we are attached to destination net -- ELSE e.route -- send to next inr on path to destination net; END; IF RouterInternal.checkIt THEN Checksums.SetChecksum[body] ELSE body.checksum ¬ 177777B; -- now call proc to encapsulate and send the buffer, after which -- the proc will put the buffer on the done queue b.fo.status ¬ goodCompletion; Protocol1.EncapsulateAndTransmit[LOOPHOLE[b], @immediateHost]; END; -- of RoutAndSendRoutingInfoResponse -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- -- RoutingInformationPacket either sends or examine the packet depending -- on if the packet is a routingInfoRequest or routingInfoResponse. As part -- of an examination of a routingInfoResponse packet, the routing table is -- updated. -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- RoutingInformationPacket: PROCEDURE [b: NSBuffer.Buffer] RETURNS [bufferWasReturned: BOOLEAN] = BEGIN body: NSBuffer.Body = b.ns; newRoute: HostNumber = body.source.host; routingPacketType: NSTypes.RoutingInfoType = body.routingType; incomingNetwork: Context ¬ b.fo.context; -- check before using since -- we aren't always holding the monitor ExamineResponsePacket: ENTRY PROCEDURE = BEGIN ENABLE UNWIND => NULL; IF (body.source.socket # NSConstants.routingInformationSocket) THEN -- why isn't the response pkt's source socket 1? IF CommFlags.doDebug THEN ERROR ELSE --don't use-- RETURN; IF (body.transportControl.hopCount > 0) THEN BEGIN -- routing response packets should never be forwarded StatIncr [@stats.forwardedRoutingResponsesRecv]; RETURN; -- don't use END; IF NOT incomingNetwork.network.alive THEN RETURN; -- don't use -- for gratuitous response on phonenet accounting IF doMPCodes AND (body.destination.host = System.broadcastHostNumber) AND (incomingNetwork.network.device = phonenet) THEN BEGIN StatIncr[@gratuitousStats[phonenetResponseRecv]]; IF (mpCode = phonenetResponseRecv) THEN BEGIN mp: CARDINAL ¬ Inline.LowHalf[gratuitousStats[phonenetResponseRecv]]; mp ¬ (mp MOD 1000) + 7000; ProcessorFace.SetMP [mp]; -- show 7XXX for receive END; END; IF UpdateUnnumberedNetTable[].use THEN UpdateRoutingTable[b]; END; -- of ExamineResponsePacket UpdateUnnumberedNetTable: INTERNAL PROCEDURE RETURNS [use: BOOLEAN] = BEGIN << Sets a null network number. body.source.net is the field to use for setting the incomingNetwork.netNumber because we know this is a routing response which came from the INR on this net! >> use ¬ TRUE; SELECT TRUE FROM (incomingNetwork.netNumber = body.source.net) => NULL; -- we know our -- net number and it matches with the routing response (incomingNetwork.netNumber = nullNetworkNumber) => BEGIN -- our driver wants the new net number incomingNetwork.netNumber ¬ body.source.net; -- update InrInternal.AddNetDeviceInternal[incomingNetwork]; END; (body.source.net # nullNetworkNumber) => -- since the incoming net -- number is interesting ('cause it isn't zero) and it didn't match, -- then the network has two different, non-zero net numbers. Log -- the occurance and tell someone. BEGIN StatIncr [@stats.misnumberedNetTrapHits]; misnumberedNetTrap[from: body.source, net: incomingNetwork.netNumber]; -- tell someone END; ENDCASE => NULL; END; --UpdateUnnumberedNetTable -- START of procedure's main body SELECT TRUE FROM -- notice that no check is done for the socket number at this point (body.packetType # routingInformation), --TBD: Error protocolViolation (body.source.host = myHostID AND routingPacketType = routingInfoResponse), -- don't need to listen to myself (newRoute = unknownHostID), -- would be unhelpful (incomingNetwork = NIL AND body.source.host # myHostID) -- driver didn't note the network in the object? => RETURN [FALSE]; -- NO, don't use this packet ENDCASE; --can continue IF CommFlags.doDebug AND (body.source.host # myHostID) AND (incomingNetwork = NIL) THEN ERROR; -- driver didn't note the incoming network in the object? -- do we handle this packet? -- YES, a routingInfo packet for us (Yeah!) IF doNSStats THEN Stats.StatIncr[statNSGatewayPacketsRecv]; -- be aware that this count is incremented also for extended RIP SELECT routingPacketType FROM routingInfoRequest => BEGIN SendRoutingInfoResponse[body.source, b]; -- we respond to request -- note that body.source.net can be non null bufferWasReturned ¬ TRUE; -- SendRoutingInfoResponse proc -- always returns the buffer that it is given StatIncr [@stats.routingRequestRecv]; END; routingInfoResponse => BEGIN pulses: System.Pulses ¬ System.GetClockPulses[]; ExamineResponsePacket[]; bufferWasReturned ¬ FALSE; -- we merely examined it pulses ¬ System.Pulses[System.GetClockPulses[] - pulses]; PulsesBump [@stats.pulsesProcessingRoutingResponse, pulses]; StatIncr [@stats.routingResponseRecv]; END; -- Is this an extended Routing Information Protocol operation? LOOPHOLE[InrTypes.RoutingInfoType[routingInfoExtendedRequest]] => BEGIN SendRoutingInfoExtendedResponse[body.source, b]; bufferWasReturned ¬ TRUE; StatIncr [@stats.routingRequestRecv]; --do we want to do this? END; LOOPHOLE[InrTypes.RoutingInfoType[routingInfoExtendedResponse]] => BEGIN -- no support for handling extended response! bufferWasReturned ¬ FALSE; StatIncr [@stats.routingResponseRecv]; --do we want to do this? END; ENDCASE => bufferWasReturned ¬ FALSE; -- we should never get to this arm END; --RoutingInformationPacket -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- -- FORKED INTERNETWORK ROUTER PROCESS -- This process handles packets that come in over Socket # 1 -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- RoutingInformationSupplier: PROCEDURE = BEGIN cH: Socket.ChannelHandle; b: NSBuffer.Buffer; privateCH ¬ cH ¬ Socket.Create [socket: NSConstants.routingInformationSocket, send: 1, receive: 5, type: routingInformation]; Socket.SetWaitTime [cH, Socket.infiniteWaitTime]; Process.Detach [FORK BroadcastRoutingRequest[cH]]; --to build routing table. UNTIL pleaseStop DO ENABLE ABORTED => EXIT; -- the condition variable that we would -- get aborted on is in the Socket.Get routine b ¬ Socket.GetPacket [cH]; IF NOT RoutingInformationPacket[b].bufferWasReturned THEN Socket.ReturnBuffer [b]; -- if the buffer wasn't returned, then we -- return it here. Driver.PutOnGlobalDoneQueue would be overkill since -- we know that we got the buffer through the Socket.Get procedure. ENDLOOP; Socket.Delete [cH]; END; -- of RoutingInformationSupplier << *** Begin comment out CopyBuffer: PROCEDURE[b: NSBuffer.Buffer, aH: NSBuffer.AccessHandle] RETURNS [cb: NSBuffer.Buffer] = INLINE BEGIN cb ¬ NSBuffer.GetBuffer [aH: aH, function: receive, size: NSBuffer.DataBytesPerRawBuffer[b], wait: FALSE]; IF (cb = NIL) THEN RETURN; Inline.LongCOPY[ from: b.linkLayer.blockPointer, --@buffer.encapsulation nwords: (b.fo.driver.length + (bpw-1))/bpw, --length is in bytes to: cb.linkLayer.blockPointer]; -- finish up the link layer info cb.linkLayer.startIndex ¬ b.linkLayer.startIndex; cb.linkLayer.stopIndexPlusOne ¬ b.linkLayer.stopIndexPlusOne; -- do the high layer info cb.ns ¬ LOOPHOLE[ cb + LOOPHOLE[b.ns, LONG CARDINAL] - LOOPHOLE[b, LONG CARDINAL]]; cb.filler1 ¬ b.filler1; cb.filler2 ¬ b.filler2; -- set the fixed overhead info cb.fo.network ¬ b.fo.network; cb.fo.context ¬ b.fo.context; cb.fo.status ¬ b.fo.status; cb.fo.type ¬ b.fo.type; cb.fo.driver.length ¬ b.fo.driver.length; END; --of CopyBuffer *** End comment out >> -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- -- RoutingTableDeactivate aborts all forked process. -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- RoutingTableDeactivate: ENTRY PROCEDURE = INLINE BEGIN ENABLE UNWIND => NULL; pleaseStop ¬ TRUE; << TRUE shows that we are OFF >> Process.Abort [routingInformationSupplierFork]; -- poke him out of the Socket.Get on socket # 1 Process.Abort [internetRouterServerFork]; -- poke him out of WAITing to send a gratuitous pkt Process.Abort [decrementRoutingTableEntriesFork]; -- poke him out of the WAIT timer statement Process.Abort [routingTableChangedFork]; -- poke him out of the WAIT routingTableChanged statement END; --RoutingTableDeactivate -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- -- This routine will send a gratuitous type of routing response (information -- will be sent on all of the entries we know about). -- (1) Most often, this proc is called to send a broadcast gratuitous -- reponse to everyone every 30 seconds or so. -- (2) But it is also called when a request for information comes in that -- asks for info on all/some of the nets we know about. -- (3) And it is called when the we want to announce to everyone, via a -- broadcast, recent routing table changes. -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- -- Buffers for sending routing information response packets are acquired -- one at a time. A large routing table may require more than one buffer. -- Thus sending gratuitous responses can take a long time. With this being -- the case, this procedure should not be called with the monitor. EntryChanged: ENTRY PROC [net: NetworkNumber] RETURNS [changed: BOOLEAN] = INLINE { ENABLE UNWIND => NULL; e: RoutingTableEntry ¬ InrInternal.FindNetworkNumber[net, FALSE]; changed ¬ IF (e = NIL) THEN FALSE ELSE e.changed; }; --end of EntryChanged ResetEntry: ENTRY PROC [net: NetworkNumber] = INLINE { ENABLE UNWIND => NULL; e: RoutingTableEntry ¬ InrInternal.FindNetworkNumber[net, FALSE]; IF (e # NIL) THEN e.changed ¬ FALSE; }; --end of ResetEntry RoutAndSendRoutingInfoResponseLocked: ENTRY PROC [ b: NSBuffer.Buffer, extended: BOOLEAN ¬ FALSE] RETURNS [success: BOOLEAN ¬ TRUE] = INLINE { ENABLE UNWIND => NULL; success ¬ RoutAndSendRoutingInfoResponse[b, extended]; }; --end of RoutAndSendRoutingInfoResponseLocked SendGratuitousResponse: PUBLIC PROCEDURE [ to: System.NetworkAddress, b: NSBuffer.Buffer ¬ NIL, allAreUnreachable, onlyChangedEntries: BOOLEAN ¬ FALSE] = BEGIN -- If we are supplied with a buffer, then we'll use it and use its b.network -- if the destination net address is the nullNetNumber. (b.network is the -- network from which the packet came.) This is for sending on the same -- network the packet was received. -- If allAreUnreachable is TRUE, then the delay we'll use in all the tuples -- will be . This tells all listeners that we -- are not a good route for any destinatination nets -- This proc will send two or more pkts if there are too many tuples for one -- packet. body: NSBuffer.Body ¬ NIL; maxTuplesPerRoutingPacket: CARDINAL = (NSTypes.maxIDPDataBytes - bytesPerRoutingInfoType) / bytesPerRoutingInfoTuple; packetsSent: CARDINAL ¬ 0; --for debugging! tupleNumber: CARDINAL ¬ 0; destContext: RoutingTable.NetworkContext ¬ NIL; destNetwork: Driver.Device ¬ NIL; DoSendResponse: PROC [] RETURNS [nil: NSBuffer.Buffer ¬ NIL] = BEGIN sent: BOOLEAN ¬ RoutAndSendRoutingInfoResponseLocked[b]; IF NOT sent THEN Driver.PutOnGlobalDoneQueue[LOOPHOLE[b]]; packetsSent ¬ packetsSent + 1; END; --of DoSendResponse IF (b # NIL) THEN --we are supplied with a buffer BEGIN body ¬ b.ns; destContext ¬ b.fo.context; destNetwork ¬ b.fo.network; body.destination ¬ to; body.pktLength ¬ NSTypes.bytesPerIDPHeader + bytesPerRoutingInfoType; END; FOR hops: HopCount IN [localHop..infinityHopCount) DO net: NetworkNumber ¬ nullEnum; net ¬ InrInternal.EnumerateByDistance[net, hops]; UNTIL (net = nullEnum) DO --get entries with this hop count --* fill in buffer with (net, hops) pair SELECT TRUE FROM (net = nullNetworkNumber), --no ultimate dest (onlyChangedEntries AND ~EntryChanged[net]) => NULL; ENDCASE => BEGIN -- handle this valid rte; Do we have a buffer? IF (b = NIL) THEN BEGIN -- get and set a new buffer. Other fields of buffer are set by -- RoutAndSendRoutingInfoResponse proc b ¬ NSBuffer.GetBuffer [aH: bufferHandle, function: send, size: fullBuffer, wait: TRUE ! ABORTED => {b ¬ NIL; CONTINUE}]; -- The buffer was charged it to the inr's account. inr's sent -- buffer account (from ) is only one. -- By having a single send buffer, the rate of sending gratuitous -- response packet is slowed down since a second send buffer -- cannot be acquired until the first one has been through the -- system (and thus out the door). IF (b = NIL) THEN RETURN; -- give up! body ¬ b.ns; body.destination ¬ to; b.fo.context ¬ destContext; b.fo.network ¬ destNetwork; -- RoutAndSendRoutingInfoResponse proc -- will check that the b.fo.network is valid, and set it if we are -- broadcasting this response. body.pktLength ¬ NSTypes.bytesPerIDPHeader + bytesPerRoutingInfoType; tupleNumber ¬ 0; END; -- of getting a new buffer body.routingTuple[tupleNumber] ¬ [net, IF allAreUnreachable THEN infinityHopCount ELSE -- we increment the hop count here! MIN [(hops + 1 + extraHops), infinityHopCount] ]; ResetEntry[net]; tupleNumber ¬ tupleNumber + 1; body.pktLength ¬ body.pktLength + bytesPerRoutingInfoTuple; END; -- of handling a valid rte --* time to get next routing table entry net ¬ InrInternal.EnumerateByDistance[net, hops]; --next rte --* Is it time to send this buffer? IF (b # NIL) AND (tupleNumber >= maxTuplesPerRoutingPacket) THEN -- packet is full, go ahead and send the packet b ¬ DoSendResponse[]; ENDLOOP; --end of getting entries with this hop count ENDLOOP; --end of enumeration -- Now all the routing table entries have been enumerated. If a sparse -- buffer exists, sent it on its way. IF (b # NIL) THEN b ¬ DoSendResponse[]; END; --SendGratuitousResponse -- This is a special case! Send extended routing information. SendGratuitousExtendedResponse: PROCEDURE [ to: System.NetworkAddress, b: NSBuffer.Buffer ¬ NIL, allAreUnreachable, onlyChangedEntries: BOOLEAN ¬ FALSE, alwaysDetermineViaNet: BOOLEAN ¬ TRUE] = BEGIN body: NSBuffer.Body ¬ NIL; tupleNumber: CARDINAL ¬ 0; destContext: RoutingTable.NetworkContext ¬ NIL; destNetwork: Driver.Device ¬ NIL; e: RoutingTableEntry ¬ NIL; extended: LONG POINTER TO InrTypes.ExtendedRoutingInformation; DoSendResponse: PROC [] RETURNS [nil: NSBuffer.Buffer ¬ NIL] = BEGIN sent: BOOLEAN ¬ RoutAndSendRoutingInfoResponseLocked[b, --extended--TRUE]; IF NOT sent THEN Driver.PutOnGlobalDoneQueue[LOOPHOLE[b]]; END; --of DoSendResponse LockedGetFarNet: ENTRY PROC [net: Context] RETURNS [farNet: NetworkNumber] = BEGIN ENABLE UNWIND => NULL; farNet ¬ GetFarNet [net]; END; -- Mainline of the procedure ... IF (b # NIL) THEN --we are supplied with a buffer BEGIN destContext ¬ b.fo.context; destNetwork ¬ b.fo.network; body.destination ¬ to; body.pktLength ¬ NSTypes.bytesPerIDPHeader + bytesPerRoutingInfoType; extended ¬ LOOPHOLE[@body.routingType]; END; FOR hops: HopCount IN [localHop..infinityHopCount) DO net: NetworkNumber ¬ nullEnum; net ¬ InrInternal.EnumerateByDistance[net, hops]; e ¬ InrInternal.FindNetworkNumber[net, FALSE]; UNTIL (net = nullEnum) DO --get entries with this hop count --* fill in buffer with [net, hops, immediate inr] SELECT TRUE FROM (net = nullNetworkNumber), --no ultimate dest (onlyChangedEntries AND ~EntryChanged[net]) => NULL; ENDCASE => BEGIN IF (b = NIL) THEN BEGIN b ¬ NSBuffer.GetBuffer [aH: bufferHandle, function: send, size: fullBuffer, wait: TRUE ! ABORTED => {b ¬ NIL; CONTINUE}]; IF (b = NIL) THEN RETURN; -- give up! body ¬ b.ns; --set before using body.destination ¬ to; b.fo.context ¬ destContext; b.fo.network ¬ destNetwork; body.pktLength ¬ NSTypes.bytesPerIDPHeader + bytesPerRoutingInfoType; extended ¬ LOOPHOLE[@body.routingType]; tupleNumber ¬ 0; END; -- of getting a new buffer IF allAreUnreachable OR (e = NIL) OR (e.context = NIL) THEN BEGIN extended.routingTuple[tupleNumber] ¬ [ objectNetID: net, interrouterDelay: infinityHopCount, viaNet: nullNetworkNumber, viaHost: System.nullHostNumber]; END ELSE BEGIN farNet: NetworkNumber ¬ IF alwaysDetermineViaNet AND (e.context.netNumber = nullNetworkNumber) THEN LockedGetFarNet [e.context] ELSE e.context.netNumber; extended.routingTuple[tupleNumber] ¬ [ objectNetID: net, interrouterDelay: MIN [(hops + 1 + extraHops), infinityHopCount], viaNet: farNet, viaHost: e.route]; END; << ResetEntry[net]; --don't do for Extended Response >> tupleNumber ¬ tupleNumber + 1; body.pktLength ¬ body.pktLength + bytesPerExtendedRoutingInfoTuple; END; -- of handling a valid rte net ¬ InrInternal.EnumerateByDistance[net, hops]; --next rte e ¬ InrInternal.FindNetworkNumber[net, FALSE]; IF (b # NIL) AND (tupleNumber >= maxTuplesPerExtendedRoutingPacket) THEN b ¬ DoSendResponse[]; ENDLOOP; --end of getting entries with this hop count ENDLOOP; --end of enumeration -- Now all the routing table entries have been enumerated. If a sparse -- buffer exists, sent it on its way. IF (b # NIL) THEN b ¬ DoSendResponse[]; END; --SendGratuitousExtendedResponse -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- -- This proc will send out a response packet. It is given the incoming -- routing request packet, which it will properly handle. It will set the -- destination to be . (i.e. will use the request to send the response) -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- SendRoutingInfoResponse: PROCEDURE [ to: System.NetworkAddress, b: NSBuffer.Buffer] = BEGIN body: NSBuffer.Body = b.ns; maxTuplesPerRoutingPacket: CARDINAL = (NSTypes.maxIDPDataBytes - bytesPerRoutingInfoType) / bytesPerRoutingInfoTuple; numberOfTuples: CARDINAL = (body.pktLength - NSTypes.bytesPerIDPHeader - bytesPerRoutingInfoType) / bytesPerRoutingInfoTuple; SendRoutingInfoResponseLocked: ENTRY PROCEDURE RETURNS [sent: BOOLEAN ¬ TRUE] = BEGIN ENABLE UNWIND => NULL; e: RoutingTableEntry; FOR i: CARDINAL IN [0..numberOfTuples) DO IF (body.routingTuple[i].objectNetID = anyNetNetworkNumber) OR (body.routingTuple[i].objectNetID = anyNetNetworkNumberFor860) -- Hack for the dumb 860! THEN RETURN [FALSE]; -- we'll send him a gratitous packet with all the rte's e ¬ InrInternal.FindNetworkNumber [body.routingTuple[i].objectNetID, FALSE]; body.routingTuple[i].interrouterDelay ¬ IF (e = NIL) OR (e.context = NIL) THEN infinityHopCount -- no route -- we increment the hop count here! ELSE MIN [(e.delay + 1 + extraHops), infinityHopCount]; ENDLOOP; body.pktLength ¬ NSTypes.bytesPerIDPHeader + bytesPerRoutingInfoType + (numberOfTuples*bytesPerRoutingInfoTuple); body.destination ¬ to; IF NOT RoutAndSendRoutingInfoResponse[b] THEN Socket.ReturnBuffer[b]; << Driver.PutOnGlobalDoneQueue would be overkill since we know that we got the buffer through the Socket.Get procedure. We don't return[False] since the source would then be sent a gratuitous type of packet. >> END; -- of SendRoutingInfoResponseLocked -- start of procedure IF (numberOfTuples = 0) OR (numberOfTuples > maxTuplesPerRoutingPacket) OR NOT SendRoutingInfoResponseLocked[] THEN SendGratuitousResponse [to, b]; StatIncr [@stats.specificRoutingResponseSent]; END; -- of SendRoutingInfoResponse SendRoutingInfoExtendedResponse: PROCEDURE [ to: System.NetworkAddress, b: NSBuffer.Buffer, alwaysDetermineViaNet: BOOLEAN ¬ TRUE] = BEGIN body: NSBuffer.Body = b.ns; maxTuplesPerRoutingPacket: CARDINAL = (NSTypes.maxIDPDataBytes - bytesPerRoutingInfoType) / bytesPerExtendedRoutingInfoTuple; numberOfTuples: CARDINAL = (body.pktLength - NSTypes.bytesPerIDPHeader - bytesPerRoutingInfoType) / bytesPerExtendedRoutingInfoTuple; SendRoutingInfoExtendedResponseLocked: ENTRY PROCEDURE RETURNS [sent: BOOLEAN ¬ TRUE] = BEGIN ENABLE UNWIND => NULL; e: RoutingTableEntry; extended: LONG POINTER TO InrTypes.ExtendedRoutingInformation = LOOPHOLE[@body.routingType]; dest: NetworkNumber ¬ nullNetworkNumber; FOR i: CARDINAL IN [0..numberOfTuples) DO dest ¬ extended.routingTuple[i].objectNetID; IF (dest = anyNetNetworkNumber) OR (dest = anyNetNetworkNumberFor860) THEN RETURN [FALSE]; e ¬ InrInternal.FindNetworkNumber [dest, FALSE]; IF (e = NIL) OR (e.context = NIL) THEN BEGIN extended.routingTuple[i] ¬ [ objectNetID: dest, interrouterDelay: infinityHopCount, viaNet: nullNetworkNumber, viaHost: System.nullHostNumber]; END ELSE BEGIN farNet: NetworkNumber ¬ IF alwaysDetermineViaNet AND (e.context.netNumber = nullNetworkNumber) THEN GetFarNet [e.context] ELSE e.context.netNumber; extended.routingTuple[i] ¬ [ objectNetID: dest, interrouterDelay: MIN [(e.delay + 1 + extraHops), infinityHopCount], viaNet: farNet, viaHost: e.route]; END; ENDLOOP; body.pktLength ¬ NSTypes.bytesPerIDPHeader + bytesPerRoutingInfoType + (numberOfTuples*bytesPerExtendedRoutingInfoTuple); body.destination ¬ to; IF NOT RoutAndSendRoutingInfoResponse[b: b, extended: TRUE] THEN Socket.ReturnBuffer[b]; END; -- of SendRoutingInfoExtendedResponseLocked -- start of procedure IF (numberOfTuples = 0) OR (numberOfTuples > maxTuplesPerRoutingPacket) OR NOT SendRoutingInfoExtendedResponseLocked[] THEN SendGratuitousExtendedResponse [to, b]; StatIncr [@stats.specificRoutingResponseSent]; END; -- of SendRoutingInfoExtendedResponse -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- -- This proc sets the misnumbered net trap. Allows the client to learn about -- bogus routers on the network -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- SetMisnumberedNetTrap: PUBLIC -- InrFriends-- PROCEDURE [proc: InrFriends.MisnumberedNetTrap] = BEGIN misnumberedNetTrap ¬ IF proc = NIL THEN DefaultMisnumberedNetTrap ELSE proc; END; -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- -- This hot procedure fills in some routing information and sends the buffer -- out on the appropriate network. The send is asynchronous; the caller -- owns the buffer and receives its back from the the dispatcher via -- b.requeueProcedure (when the send is completed). Transmit is passed to -- the Pilot Router. -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Transmit: PROCEDURE[b: NSBuffer.Buffer] = BEGIN body: NSBuffer.Body = b.ns; destHost: HostNumber ¬ body.destination.host; nextHost: HostNumber; destNetNumber: NetworkNumber ¬ body.destination.net; context: Context; e: RoutingTableEntry; FindNetworkNumberLocked: ENTRY PROCEDURE [n: NetworkNumber] RETURNS [e: RoutingTableEntry] = INLINE BEGIN ENABLE UNWIND => NULL; e ¬ InrInternal.FindNetworkNumber[n]; END; -- of FindNetworkNumberLocked -- start of procedure e ¬ FindNetworkNumberLocked[destNetNumber]; --first find in routing entry IF (e = NIL) OR ((context ¬ e.context) = NIL) THEN BEGIN --outgoing packet for unknown net --return b to the system buffer pool b.fo.status ¬ noRouteToNetwork; Driver.PutOnGlobalDoneQueue[LOOPHOLE[b]]; IF doNSStats THEN Stats.StatIncr[statNSSentNowhere]; RETURN; --goodbye! END; --outgoing packet to be transmitted over the correct network nextHost ¬ IF (e.route # unknownHostID) THEN e.route -- next inr's address ELSE destHost; -- we are attached to the destination net b.fo.status ¬ goodCompletion; b.fo.network ¬ context.network; -- mark the network being used b.fo.context ¬ context; b.fo.type ¬ ns; --synchronous buffer send Protocol1.EncapsulateAndTransmit[LOOPHOLE[b], @nextHost]; --start packet on its way StatIncr [@stats.pktsTransmitted]; END; --Transmit -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- -- This routine examines the routing tuples in the passed in Routing -- Information Response packet and uses them to update its routing table. -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- UpdateRoutingTable: INTERNAL PROCEDURE [b: NSBuffer.Buffer] = BEGIN body: NSBuffer.Body = b.ns; i: CARDINAL; e: RoutingTableEntry ¬ NIL; newDelay: CARDINAL; changed: BOOLEAN ¬ FALSE; t: NSTypes.RoutingInfoTuple; tuples: CARDINAL = -- number of tuples in the packet. (body.pktLength - NSTypes.bytesPerIDPHeader - bytesPerRoutingInfoType) / bytesPerRoutingInfoTuple; FOR i IN [0..tuples) DO << this is all reasonably hairy. See page 28 of Yogen's Internet Transport Protocols book for further information >> t ¬ body.routingTuple[i]; newDelay ¬ Inline.BITAND[mask, t.interrouterDelay]; -- ignore high bits. -- must be greater than local hop (0), otherwise error IF (newDelay = localHop) THEN IF CommFlags.doDebug THEN ERROR ELSE LOOP; -- misleading tuple IF ((e ¬ InrInternal.FindNetworkNumber[t.objectNetID, FALSE]) # NIL) THEN BEGIN -- update an already present tuple SELECT TRUE FROM ((e.route = body.source.host) AND (e.context = b.fo.context)), -- new info from our supplier (case 2 from the spec) (e.timeUnits < alternatePathTimeUnitThreshHold), -- time to get better info (case 3) (e.context = NIL), --need better info (newDelay < e.delay) --better route (case 4)-- => BEGIN e.changed ¬ (e.delay # newDelay); changed ¬ (changed OR e.changed); -- to the outside world (who hear via routing info packets), -- there has been a change only if the has changed. -- Or if a destination net has been added e.delay ¬ newDelay; e.route ¬ body.source.host; e.context ¬ b.fo.context; IF (e.delay < infinityHopCount) THEN e.timeUnits ¬ updateCycles ELSE BEGIN e.delay ¬ infinityHopCount; -- this ensures that the delay -- isn't > infinityHopCount e.context ¬ NIL; e.route ¬ System.nullHostNumber; END; END; ENDCASE; END -- End of update of existing tuple. ELSE -- no tuple so far -- collect new info because, as an INR, we know about every net IF (newDelay < infinityHopCount) -- is it accessable? AND (t.objectNetID # anyNetNetworkNumber) -- Paranoia AND (t.objectNetID # System.nullNetworkNumber) -- Paranoia THEN BEGIN e ¬ CommHeap.zone.NEW[RoutingTableObject]; e­ ¬ [ nextRTE:, nextNSE:, destNetwork: t.objectNetID, delay: newDelay, timeUnits: updateCycles, route: body.source.host, context: b.fo.context, changed: TRUE]; InrInternal.AddEntry[e]; --it will set linkage changed ¬ TRUE; END; ENDLOOP; --FOR i IN [0..tuples) do --if something has changed then propagate the information IF changed THEN NOTIFY InrInternal.routingTableChanged; END; --UpdateRoutingTable -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- -- Start-up/ Shut-down routines. When a new INR is register with Pilot -- with the routine RoutingTable.Register[], the old INR is automatically -- unregistered. Upon the call to register, Pilot calls the stop routine -- for the previously registered INR and then subsequently calls the start -- routine of the newly registered INR. -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- ActivateINR: PUBLIC PROCEDURE = BEGIN --will be called by IRS to start INR IF pleaseStop THEN RoutingTable.Register[ptrToRto]; --made call only if we are not already started END; DeactivateINR: PUBLIC PROCEDURE = BEGIN --will be called by IRS to stop INR. IF NOT pleaseStop THEN RoutingTable.Register[NIL]; END; SetNSFudge: PUBLIC PROCEDURE [hops: CARDINAL] = BEGIN extraHops ¬ hops; END; -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- -- Stop the Internetwork Router. This routine is passed to the Pilot Router. -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Stop: PROCEDURE = BEGIN RoutingTableDeactivate[]; -- inline to abort all forked process JOIN routingInformationSupplierFork; JOIN internetRouterServerFork; JOIN decrementRoutingTableEntriesFork; InrInternal.CleanUpRoutingTable[]; StopStats[]; END; --Stop -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- -- Start the Internetwork Router. This routine is passed to the Pilot Router. -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Start: PROCEDURE = BEGIN pleaseStop ¬ FALSE; << FALSE shows that we are ON >> StartStats[]; myHostID ¬ Router.FindMyHostID[]; InrInternal.RoutingTableActivate[bitsHashed, markDead]; FOR n: Driver.Device ¬ Driver.GetDeviceChain[], n.next UNTIL (n = NIL) DO InrInternal.AddNet[Protocol1.GetContext[n, ns]]; ENDLOOP; routingInformationSupplierFork ¬ FORK RoutingInformationSupplier []; internetRouterServerFork ¬ FORK InternetRouterServer []; decrementRoutingTableEntriesFork ¬ FORK InrInternal.DecrementRoutingTableEntries []; routingTableChangedFork ¬ FORK InrInternal.RoutingTableChanged []; END; --Start --**************** Statistics **************** GetStats: PUBLIC PROCEDURE RETURNS [ LONG POINTER TO READONLY InrStats.Stats, System.GreenwichMeanTime] = {RETURN [stats, lastGetStats ¬ System.GetGreenwichMeanTime[]]}; StartStats: PROCEDURE = BEGIN stats ¬ CommHeap.zone.NEW [InrStats.Stats ¬ [System.GetGreenwichMeanTime[]]]; END; StopStats: PROCEDURE = BEGIN CommHeap.zone.FREE [@stats]; IF doMPCodes THEN BEGIN FOR i: MPCodes IN [MPCodes.FIRST..MPCodes.LAST] DO gratuitousStats[i] ¬ 0; ENDLOOP; ProcessorFace.SetMP[8000]; END; END; --of StopStats StatIncr: PROCEDURE [counter: LONG POINTER TO LONG CARDINAL] = INLINE --add one to counter BEGIN --locals counter­ ¬ (counter­ + 1) MOD (LAST[LONG CARDINAL] - 1); END; StatBump: PROCEDURE [ counter: LONG POINTER TO LONG CARDINAL, bumpAmount: CARDINAL] = INLINE BEGIN --locals << --add bumpAmount to counter; if bumpAmount < 10000, there will never --be overflow counter­ ¬ (counter­ + bumpAmount) MOD (LAST[LONG CARDINAL] - 10000); >> counter­ ¬ MIN[(counter­ + bumpAmount), (LAST[LONG CARDINAL] - 10000)]; END; PulsesBump: PROCEDURE [ counter: LONG POINTER TO System.Pulses, bumpAmount: System.Pulses] = INLINE BEGIN overflow: System.Pulses = LOOPHOLE[LAST[LONG CARDINAL]]; counter­ ¬ System.Pulses[MIN[(counter­ + bumpAmount), overflow]]; END; --**************** Main Program **************** --initialization (Cold) Process.SetTimeout[@internetRouterTimer, Process.MsecToTicks[25500]]; -- set for 26.5 seconds so that when the extra random wait [0..7] seconds -- is added, the average time between gratuitous pkts is 30 seconds Process.SetTimeout[@auxInternetRouterTimer, Process.MsecToTicks[500]]; Process.DisableTimeout [@InrInternal.routingTableChanged]; Process.EnableAborts [@internetRouterTimer]; Process.EnableAborts [@auxInternetRouterTimer]; Process.EnableAborts [@InrInternal.routingTableChanged]; END. --RoutingTableImpl module. << LOG 5-Feb-88 13:43:52 AOF Trimmed for PupGateway >>