-- File: RealForwarder.mesa, Last Edit: HGM March 17, 1981 8:27 PM DIRECTORY Process USING [Detach, SetPriority, SetTimeout, MsecToTicks], Runtime USING [IsBound], Storage USING [Node], StatsDefs USING [StatIncr], CommFlags USING [doStats], ForwarderDefs USING [ SetupForwarderThings, ForwarderStats, PrintBadPup, statGateInfoReplies, statRoutingTableChanges, statGateInfoBC, statGateLowOnBuffers, statGarbageSourceOrDest, statNoRouteToNet, statTooManyHops, forwarderStatsRequest], PupRouterDefs USING [ BuildErrorPup, EnumerateRoutingTable, GetRoutingTableEntry, maxHop, PupGateInfo, PupGatewaySee, RejectPupWithBadChecksum, RoutingTableEntry, SetBadPupProc, SetPupChecksum], PupDefs, DriverDefs USING [Network, GetDeviceChain, MaybeGetFreePupBuffer], BufferDefs USING [PupBuffer, ReturnFreeBuffer, BuffersLeft, QueueInitialize], PupTypes USING [ PupNetID, PupHostID, PupErrorCode, allNets, allHosts, gatewaySoc, maxDataWordsPerRoutingPup]; RealForwarder: MONITOR IMPORTS Process, Runtime, Storage, StatsDefs, ForwarderDefs, PupRouterDefs, DriverDefs, PupDefs, BufferDefs EXPORTS BufferDefs, ForwarderDefs, PupRouterDefs SHARES BufferDefs = BEGIN OPEN StatsDefs, PupRouterDefs, PupDefs, BufferDefs, PupTypes; -- EXPORTed TYPEs Network: PUBLIC TYPE = DriverDefs.Network; tellEverybody: BOOLEAN ← TRUE; -- debugging flag to avoid poluting the world packets: POINTER TO ARRAY [0..0) OF LONG CARDINAL; bytes: POINTER TO ARRAY [0..0) OF LONG CARDINAL; nets: CARDINAL; stop: BOOLEAN ← FALSE; pupGateSoc: PupSocket; GetPointerToPupGateStats: PUBLIC PROCEDURE RETURNS [ POINTER TO ARRAY [0..0) OF LONG CARDINAL, POINTER TO ARRAY [0..0) OF LONG CARDINAL, CARDINAL] = BEGIN RETURN[packets, bytes, nets]; END; PupForwarderOn: PUBLIC PROCEDURE = BEGIN network: Network ← DriverDefs.GetDeviceChain[]; IF CommFlags.doStats THEN BEGIN finger: Network; size: CARDINAL; nets ← 1; -- discard FOR finger ← network, finger.next UNTIL finger = NIL DO nets ← nets + 1; ENDLOOP; size ← nets*nets; packets ← Storage.Node[2*size]; bytes ← Storage.Node[2*size]; FOR i: CARDINAL IN [0..size) DO packets[i] ← bytes[i] ← 0; ENDLOOP; END; Process.Detach[FORK PupForwarderOn2[]]; END; PupForwarderOn2: ENTRY PROCEDURE = BEGIN network: Network ← DriverDefs.GetDeviceChain[]; world: PupAddress ← [[0], [0], gatewaySoc]; spin: CONDITION; Process.SetTimeout[@spin, Process.MsecToTicks[100]]; -- As a hack, we can run without knowing our network number at startup time. -- Wait here to be sure we don't polute things if we don't know it yet. Process.Detach[FORK LookAtBadPups[]]; Process.Detach[FORK TransferForwarderStats[]]; UNTIL network.netNumber # [0, 0] DO IF stop THEN RETURN; WAIT spin; ENDLOOP; Process.Detach[FORK PupTalk[]]; pupGateSoc ← PupSocketMake[gatewaySoc, world, veryLongWait]; Process.Detach[FORK PupListen[]]; END; PupForwarderOff: PUBLIC ENTRY PROCEDURE = BEGIN spin: CONDITION; Process.SetTimeout[@spin, Process.MsecToTicks[100]]; stop ← TRUE; THROUGH [0..5) DO -- 5 cycles of 100 ms each NOTIFY talker; WAIT spin; ENDLOOP; END; PupListen: PUBLIC PROCEDURE = BEGIN b: PupBuffer; DO b ← pupGateSoc.get[]; IF b = NIL THEN LOOP; SELECT b.pupType FROM gatewayRequest => BEGIN IF ~tellEverybody AND b.dest.host = allHosts THEN BEGIN -- don't answer broadcast requests yet ReturnFreePupBuffer[b]; END ELSE BEGIN SwapPupSourceAndDest[b]; SendPupRoutingPacket[b]; IF CommFlags.doStats THEN StatIncr[ForwarderDefs.statGateInfoReplies]; END END; gatewayInfo => BEGIN -- RoutingTable packet from another Gateway IF PupGatewaySee[b] THEN BEGIN KickTalker[]; IF CommFlags.doStats THEN StatIncr[ForwarderDefs.statRoutingTableChanges]; END; ReturnFreePupBuffer[b]; END; ForwarderDefs.forwarderStatsRequest => PutStats[b]; ENDCASE => ReturnFreePupBuffer[b]; ENDLOOP; END; talker: CONDITION; KickTalker: PUBLIC ENTRY PROCEDURE = BEGIN NOTIFY talker; END; PupTalk: PUBLIC ENTRY PROCEDURE = BEGIN spin: CONDITION; b: PupBuffer; i: CARDINAL ← 0; Process.SetTimeout[@talker, Process.MsecToTicks[30000]]; Process.SetTimeout[@spin, Process.MsecToTicks[100]]; DO -- forever IF tellEverybody THEN BEGIN i ← i + 1; UNTIL (b ← DriverDefs.MaybeGetFreePupBuffer[]) # NIL DO WAIT spin; ENDLOOP; b.pupID ← [0, i]; b.dest ← [allNets, allHosts, gatewaySoc]; b.source ← [, , gatewaySoc]; SendPupRoutingPacket[b]; IF CommFlags.doStats THEN StatIncr[ForwarderDefs.statGateInfoBC]; END; WAIT talker; ENDLOOP; END; SendPupRoutingPacket: PROCEDURE [b: PupBuffer] = BEGIN data: LONG POINTER TO PupGateInfo ← LOOPHOLE[@b.pupWords[0]]; AddOne: PROCEDURE [rte: RoutingTableEntry] = BEGIN IF rte.net = 0 OR rte.network = NIL THEN RETURN; IF n = maxDataWordsPerRoutingPup/SIZE[PupGateInfo] THEN BEGIN b2: PupBuffer ← DriverDefs.MaybeGetFreePupBuffer[]; IF b2 # NIL THEN BEGIN b2.pupID ← b.pupID; b2.source ← b.source; b2.dest ← b.dest; b2.pupType ← b.pupType; SetPupContentsWords[b, n*SIZE[PupGateInfo]]; PupRouterSendThis[b]; b ← b2; END; data ← LOOPHOLE[@b.pupWords[0]]; n ← 0; END; IF rte.hop = 0 THEN data↑ ← [net: rte.net, viaNet: rte.net, viaHost: [rte.network.hostNumber], hop: 0] ELSE data↑ ← [net: rte.net, viaNet: [rte.network.netNumber.b], viaHost: rte.route, hop: rte.hop]; IF stop THEN data.hop ← maxHop + 1; data ← data + SIZE[PupGateInfo]; n ← n + 1; END; n: CARDINAL ← 0; b.pupType ← gatewayInfo; EnumerateRoutingTable[AddOne]; SetPupContentsWords[b, n*SIZE[PupGateInfo]]; PupRouterSendThis[b]; END; magicOne: INTEGER = -9; -- offset for pupTransportControl magicTwo: CARDINAL = 1; DoForwardPupBuffer: PUBLIC PROCEDURE [b: PupBuffer] = BEGIN fromNetwork: Network ← b.network; toNetwork: Network; sourceNet: PupNetID ← b.source.net; destNet: PupNetID ← b.dest.net; route: PupHostID; rte: RoutingTableEntry; old: WORD; errorCode: PupTypes.PupErrorCode; -- Allow directed broadcasts, but avoid nonsense and loops IF sourceNet = 0 OR destNet = 0 OR b.source.host = allHosts OR (destNet = fromNetwork.netNumber.b AND b.dest.host = allHosts) THEN BEGIN -- Don't forward this Pup ReturnFreePupBuffer[b]; IF CommFlags.doStats THEN StatIncr[statPupNotForwarded]; IF CommFlags.doStats THEN StatIncr[ForwarderDefs.statGarbageSourceOrDest]; RETURN; END; old ← (@b.pupWords[0] + magicOne)↑; IF b.pupTransportControl = maxHop*20B THEN BEGIN DoErrorPup[b, eightHopsPupErrorCode, "Discarded by 16th Gateway"]; IF CommFlags.doStats THEN BumpPupStats[fromNetwork.index, 0, 0]; IF CommFlags.doStats THEN StatIncr[ForwarderDefs.statTooManyHops]; RETURN; END; b.pupTransportControl ← b.pupTransportControl + 20B; rte ← GetRoutingTableEntry[destNet]; IF rte = NIL OR rte.hop > maxHop OR (toNetwork ← rte.network) = NIL THEN BEGIN -- don't know how to get there DoErrorPup[b, cantGetTherePupErrorCode, "No route to that Net"]; IF CommFlags.doStats THEN BumpPupStats[fromNetwork.index, 0, 0]; IF CommFlags.doStats THEN StatIncr[ForwarderDefs.statNoRouteToNet]; RETURN; END; IF BuffersLeft[] < 3 THEN BEGIN IF CommFlags.doStats THEN StatIncr[statPupNotForwarded]; ReturnFreeBuffer[b]; IF CommFlags.doStats THEN BumpPupStats[fromNetwork.index, 0, 0]; IF CommFlags.doStats THEN StatIncr[ForwarderDefs.statGateLowOnBuffers]; RETURN; END; b.network ← toNetwork; UpdatePupChecksum[b, magicTwo, old]; IF (route ← rte.route) = 0 THEN route ← b.dest.host; toNetwork.encapsulatePup[b, route]; errorCode ← toNetwork.forwardBuffer[b]; SELECT errorCode FROM noErrorPupErrorCode => BEGIN IF CommFlags.doStats THEN BEGIN StatIncr[statPupForwarded]; BumpPupStats[fromNetwork.index, toNetwork.index, b.pupLength]; END; END; ENDCASE => BEGIN IF errorCode = cantGetTherePupErrorCode AND rte.hop # 0 THEN rte.hop ← maxHop + 1; DoErrorPup[b, errorCode, NIL]; IF CommFlags.doStats THEN BumpPupStats[fromNetwork.index, 0, 0]; END; END; -- This is the routine that defines the layout of the statistics counters. -- Note that a dest of 0 is used for discard. BumpPupStats: PROCEDURE [sourceIndex, destIndex, length: CARDINAL] = BEGIN index: CARDINAL = sourceIndex + destIndex*nets; packets[index] ← packets[index] + 1; bytes[index] ← bytes[index] + length; END; DoErrorPup: PROCEDURE [ b: PupBuffer, code: PupTypes.PupErrorCode, text: STRING] = BEGIN destNet: PupNetID; toNetwork: Network; route: PupHostID; rte: RoutingTableEntry; IF CommFlags.doStats THEN StatIncr[statPupNotForwarded]; IF text = NIL THEN SELECT code FROM connectionLimitPupErrorCode => BEGIN text ← "Gateway Output Queue connection limit exceeded"; code ← gatewayResourceLimitsPupErrorCode; END; gatewayResourceLimitsPupErrorCode => text ← "Gateway Output Queue full"; cantGetTherePupErrorCode => text ← "No route to host"; ENDCASE; IF ~PupRouterDefs.BuildErrorPup[b, code, text] THEN RETURN; destNet ← b.dest.net; b.source.socket ← [0, 0]; rte ← GetRoutingTableEntry[destNet]; IF rte = NIL OR rte.hop > maxHop OR (toNetwork ← rte.network) = NIL THEN BEGIN ReturnFreePupBuffer[b]; RETURN; END; b.network ← toNetwork; IF (route ← rte.route) = 0 THEN route ← b.dest.host; SetPupChecksum[b]; toNetwork.encapsulatePup[b, route]; toNetwork.sendBuffer[b]; END; UpdatePupChecksum: PUBLIC PROCEDURE [ b: PupBuffer, offset: INTEGER, oldValue: WORD] = BEGIN len: CARDINAL ← (b.pupLength - 1)/2; checksumLoc: LONG POINTER ← @b.pupLength + len; diff: WORD; IF checksumLoc↑ = 177777B THEN RETURN; diff ← OnesSub[b.pupWords[offset + (magicOne - magicTwo)], oldValue]; checksumLoc↑ ← OnesAdd[checksumLoc↑, LeftCycle[diff, len - offset]]; END; OnesAdd: PROCEDURE [a, b: CARDINAL] RETURNS [c: CARDINAL] = INLINE BEGIN c ← a + b; IF c < a THEN c ← c + 1; IF c = 177777B THEN c ← 0; END; OnesSub: PROCEDURE [a, b: CARDINAL] RETURNS [c: CARDINAL] = INLINE BEGIN c ← a + (-b - 1); IF c < a THEN c ← c + 1; IF c = 177777B THEN c ← 0; END; LeftCycle: PROCEDURE [a, b: CARDINAL] RETURNS [c: CARDINAL] = INLINE BEGIN c ← a; THROUGH [0..(b MOD 16)) DO IF c < 100000B THEN c ← c*2 ELSE c ← c*2 + 1; ENDLOOP; END; badPupArrived: CONDITION; badPupQueue: QueueObject; PutBadPup: ENTRY PROCEDURE [b: PupBuffer] = BEGIN IF badPupQueue.length > 5 THEN BEGIN PupRouterDefs.RejectPupWithBadChecksum[b]; RETURN; END; EnqueuePup[@badPupQueue, b]; NOTIFY badPupArrived; END; GetBadPup: ENTRY PROCEDURE RETURNS [b: PupBuffer] = BEGIN IF badPupQueue.length = 0 THEN WAIT badPupArrived; b ← DequeuePup[@badPupQueue]; END; LookAtBadPups: PROCEDURE = BEGIN IF ~Runtime.IsBound[ForwarderDefs.PrintBadPup] THEN RETURN; Process.SetPriority[1]; PupRouterDefs.SetBadPupProc[PutBadPup]; DO ForwarderDefs.PrintBadPup[GetBadPup[]]; ENDLOOP; END; -- UGH, we can't just call somebody because we don't want to clutter up core with stuff that isn't normally needed, so we go through this horrible process switch to change priorities. statsArrived: CONDITION; statsQueue: QueueObject; PutStats: ENTRY PROCEDURE [b: PupBuffer] = BEGIN IF statsQueue.length > 5 THEN BEGIN ReturnFreePupBuffer[b]; RETURN; END; EnqueuePup[@statsQueue, b]; NOTIFY statsArrived; END; GetStats: ENTRY PROCEDURE RETURNS [b: PupBuffer] = BEGIN WHILE statsQueue.length = 0 DO WAIT statsArrived; ENDLOOP; b ← DequeuePup[@statsQueue]; END; TransferForwarderStats: PROCEDURE = BEGIN Process.SetPriority[1]; DO ForwarderDefs.ForwarderStats[GetStats[]]; ENDLOOP; END; -- Initialization ForwarderDefs.SetupForwarderThings[]; BufferDefs.QueueInitialize[@badPupQueue]; Process.SetTimeout[@badPupArrived, Process.MsecToTicks[60000]]; BufferDefs.QueueInitialize[@statsQueue]; Process.SetTimeout[@statsArrived, Process.MsecToTicks[60000]]; END.