-- Copyright (C) 1984, 1985 by Xerox Corporation. All rights reserved. -- RealForwarder.mesa, WIrish, 5-Jun-86 10:15:00 DIRECTORY Heap USING [systemZone], Inline USING [LowHalf], Process USING [Detach, MsecToTicks, SetPriority, SetTimeout, SecondsToTicks], Runtime USING [IsBound], System USING [ GetGreenwichMeanTime, GreenwichMeanTime, Pulses, GetClockPulses, PulsesToMicroseconds], Buffer USING [ AccessHandle, BuffersLeft, GetBuffer, MakePool, PupBuffer, ReturnBuffer, QueueInitialize], CommFlags USING [doStats], Driver USING [Network, GetDeviceChain, PutOnGlobalDoneQueue], ForwarderDefs USING [ Counters, SetupForwarderThings, ForwarderStats, PrintBadPup, statPupGatewayPacketMs, statGateInfoReplies, statRoutingTableChanges, statGateInfoBC, statGateLowOnBuffers, statGarbageSourceOrDest, statNoRouteToNet, statTooManyHops, forwarderStatsRequest], Protocol1 USING [GetContext, EncapsulateAndTransmit], PupDefs, PupRouterDefs USING [ BuildErrorPup, EnumerateRoutingTable, GetRoutingTableEntry, maxHop, NetworkContext, PupGateInfo, PupGatewaySee, RejectPupWithBadChecksum, RoutingTableEntry, SetBadPupProc, SetPupChecksum], PupTypes USING [ Pair, PupAddress, PupNetID, PupHostID, PupErrorCode, allNets, allHosts, gatewaySoc, maxDataWordsPerRoutingPup], RoutingFudges USING [], Stats USING [StatBump, StatIncr]; RealForwarder: MONITOR IMPORTS Heap, Inline, Process, Runtime, System, Stats, ForwarderDefs, PupRouterDefs, Driver, Protocol1, PupDefs, Buffer EXPORTS Buffer, ForwarderDefs, PupRouterDefs, RoutingFudges SHARES Buffer = BEGIN OPEN Stats, PupRouterDefs, PupDefs, Buffer, PupTypes; -- EXPORTed TYPEs Network: PUBLIC TYPE = Driver.Network; tellEverybody: BOOLEAN ¬ TRUE; -- debugging flag to avoid poluting the world doStats: BOOLEAN = TRUE; packets: LONG POINTER TO ForwarderDefs.Counters; bytes: LONG POINTER TO ForwarderDefs.Counters; bad: LONG POINTER TO ForwarderDefs.Counters; nets: CARDINAL; stop: BOOLEAN ¬ FALSE; pupGateSoc: PupSocket; lake: Buffer.AccessHandle ¬ Buffer.MakePool[send: 20, receive: 0]; pond: Buffer.AccessHandle ¬ Buffer.MakePool[send: 1, receive: 0]; extraHops: CARDINAL ¬ 0; GetPointerToPupGateStats: PUBLIC PROCEDURE RETURNS [ packets, bytes: LONG POINTER TO ForwarderDefs.Counters, nets: CARDINAL] = BEGIN RETURN[RealForwarder.packets, RealForwarder.bytes, RealForwarder.nets]; END; GetPointerToBadPupStats: PUBLIC PROCEDURE RETURNS [ bad: LONG POINTER TO ForwarderDefs.Counters] = BEGIN RETURN[RealForwarder.bad]; END; SetPupFudge: PUBLIC PROCEDURE [hops: CARDINAL] = BEGIN extraHops ¬ hops; END; PupForwarderOn: PUBLIC PROCEDURE = BEGIN network: Network ¬ Driver.GetDeviceChain[]; IF doStats THEN BEGIN finger: Network; size: CARDINAL; nets ¬ 1; -- discard FOR finger ¬ network, finger.next UNTIL finger = NIL DO nets ¬ nets + 1; ENDLOOP; nets ¬ nets + 4; -- Allocate room for more drivers. size ¬ nets*nets; packets ¬ Heap.systemZone.NEW[ForwarderDefs.Counters[size]]; bytes ¬ Heap.systemZone.NEW[ForwarderDefs.Counters[size]]; FOR i: CARDINAL IN [0..size) DO packets[i] ¬ bytes[i] ¬ 0; ENDLOOP; bad ¬ Heap.systemZone.NEW[ForwarderDefs.Counters[nets]]; FOR i: CARDINAL IN [0..nets) DO bad[i] ¬ 0; ENDLOOP; END; Process.Detach[FORK PupForwarderOn2[]]; END; PupForwarderOn2: ENTRY PROCEDURE = BEGIN network: Network ¬ Driver.GetDeviceChain[]; context: PupRouterDefs.NetworkContext ¬ Protocol1.GetContext[network, pup]; 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 context.pupNetNumber # 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.pup.pupType FROM gatewayRequest => BEGIN IF ~tellEverybody AND b.pup.dest.host = allHosts THEN BEGIN -- don't answer broadcast requests yet Buffer.ReturnBuffer[b]; END ELSE BEGIN SwapPupSourceAndDest[b]; SendPupRoutingPacket[b, FALSE, lake, FALSE]; IF doStats THEN StatIncr[ForwarderDefs.statGateInfoReplies]; END END; gatewayInfo => BEGIN -- RoutingTable packet from another Gateway pulses: System.Pulses ¬ System.GetClockPulses[]; ms: LONG CARDINAL; IF doStats AND ~CommFlags.doStats THEN StatIncr[statPupGatewayPacketsRecv]; IF b.pup.pupTransportControl = 0 AND PupGatewaySee[b] THEN BEGIN KickTalker[]; IF doStats THEN StatIncr[ForwarderDefs.statRoutingTableChanges]; END; Buffer.ReturnBuffer[b]; pulses ¬ System.Pulses[System.GetClockPulses[] - pulses]; ms ¬ System.PulsesToMicroseconds[pulses]/1000; IF doStats THEN Stats.StatBump[ForwarderDefs.statPupGatewayPacketMs, Inline.LowHalf[ms]]; END; ForwarderDefs.forwarderStatsRequest => PutStats[b]; ENDCASE => Buffer.ReturnBuffer[b]; ENDLOOP; END; talker, pause: CONDITION; KickTalker: PUBLIC ENTRY PROCEDURE = BEGIN NOTIFY talker; END; broadcastSeconds: CARDINAL = 30; minDallySeconds: CARDINAL = 2; -- pond has only 1 send buffer, so we must wait until it has been sent to all nets -- before sending another packet PupTalk: PUBLIC PROCEDURE = BEGIN Pause: ENTRY PROCEDURE [seconds: LONG CARDINAL] = -- Beware of ML if GetBuffer waits BEGIN IF seconds < (broadcastSeconds-minDallySeconds) THEN { sec: CARDINAL ¬ CARDINAL[seconds]; dally: CARDINAL ¬ broadcastSeconds-minDallySeconds-sec; Process.SetTimeout[@talker, Process.SecondsToTicks[dally]]; WAIT talker; }; WAIT pause; -- Don't broadcast too often END; i: CARDINAL ¬ 0; Process.SetTimeout[@pause, Process.SecondsToTicks[minDallySeconds]]; DO start: System.GreenwichMeanTime ¬ System.GetGreenwichMeanTime[]; IF tellEverybody THEN BEGIN b: PupBuffer ¬ Buffer.GetBuffer[pup, pond, send, fullBuffer, TRUE]; b.pup.pupID ¬ [0, i]; b.pup.dest ¬ [allNets, allHosts, gatewaySoc]; b.pup.source ¬ [, , gatewaySoc]; SendPupRoutingPacket[b, TRUE, pond, TRUE]; IF doStats THEN StatIncr[ForwarderDefs.statGateInfoBC]; i ¬ i + 1; END; Pause[System.GetGreenwichMeanTime[]-start]; ENDLOOP; END; SendPupRoutingPacket: PROCEDURE [ b: PupBuffer, kick: BOOLEAN, pool: Buffer.AccessHandle, wait: BOOLEAN] = BEGIN source: PupTypes.PupAddress ¬ b.pup.source; dest: PupTypes.PupAddress ¬ b.pup.dest; id: PupTypes.Pair ¬ b.pup.pupID; packetNumber: CARDINAL ¬ 1; data: LONG POINTER TO PupGateInfo ¬ LOOPHOLE[@b.pup.pupWords[0]]; AddOne: PROCEDURE [rte: RoutingTableEntry] = BEGIN context: PupRouterDefs.NetworkContext ¬ rte.context; -- rte.context might change IF rte.net = 0 OR context = NIL THEN RETURN; IF b = NIL THEN RETURN; IF n = maxDataWordsPerRoutingPup/SIZE[PupGateInfo] THEN BEGIN b.pup.pupType ¬ gatewayInfo; SetPupContentsWords[b, n*SIZE[PupGateInfo]]; PupRouterSendThis[b]; packetNumber ¬ packetNumber + 1; IF kick THEN id.a ¬ packetNumber; b ¬ Buffer.GetBuffer[pup, pool, send, fullBuffer, wait]; IF b = NIL THEN RETURN; b.pup.pupID ¬ id; b.pup.source ¬ source; b.pup.dest ¬ dest; data ¬ LOOPHOLE[@b.pup.pupWords[0]]; n ¬ 0; END; IF rte.hop = 0 THEN data­ ¬ [ net: rte.net, viaNet: rte.net, viaHost: [context.pupHostNumber], hop: 0 + extraHops] ELSE data­ ¬ [ net: rte.net, viaNet: [context.pupNetNumber], viaHost: rte.route, hop: rte.hop + extraHops]; IF stop THEN data.hop ¬ maxHop + 1; data ¬ data + SIZE[PupGateInfo]; n ¬ n + 1; END; n: CARDINAL ¬ 0; IF kick THEN b.pup.pupID.a ¬ packetNumber; EnumerateRoutingTable[AddOne]; IF b = NIL THEN RETURN; b.pup.pupType ¬ gatewayInfo; SetPupContentsWords[b, n*SIZE[PupGateInfo]]; PupRouterSendThis[b]; END; magicOne: INTEGER = -9; -- offset for pupTransportControl magicTwo: CARDINAL = 1; DoForwardPupBuffer: PUBLIC PROCEDURE [b: PupBuffer] = BEGIN fromContext: PupRouterDefs.NetworkContext ¬ b.context; fromNetwork: Network ¬ b.network; toContext: PupRouterDefs.NetworkContext; sourceNet: PupNetID ¬ b.pup.source.net; destNet: PupNetID ¬ b.pup.dest.net; length: CARDINAL = b.pup.pupLength; route: PupHostID; rte: RoutingTableEntry; old: WORD; errorCode: PupTypes.PupErrorCode; -- Allow directed broadcasts, but avoid nonsense and loops globalFromContext ¬ fromContext; globalFromNetwork ¬ fromNetwork; IF sourceNet = 0 OR destNet = 0 OR b.pup.source.host = allHosts OR (destNet = fromContext.pupNetNumber AND b.pup.dest.host = allHosts) THEN BEGIN -- Don't forward this Pup Buffer.ReturnBuffer[b]; IF doStats THEN StatIncr[statPupNotForwarded]; IF doStats THEN StatIncr[ForwarderDefs.statGarbageSourceOrDest]; RETURN; END; old ¬ (@b.pup.pupWords[0] + magicOne)­; IF b.pup.pupTransportControl = maxHop*20B THEN BEGIN DoErrorPup[b, eightHopsPupErrorCode, "Discarded by 16th Gateway"]; IF doStats THEN StatIncr[ForwarderDefs.statTooManyHops]; RETURN; END; b.pup.pupTransportControl ¬ b.pup.pupTransportControl + 20B; rte ¬ GetRoutingTableEntry[destNet]; IF rte = NIL OR rte.hop > maxHop OR (toContext ¬ rte.context) = NIL THEN BEGIN -- don't know how to get there DoErrorPup[b, cantGetTherePupErrorCode, "No route to that Net"]; IF doStats THEN StatIncr[ForwarderDefs.statNoRouteToNet]; RETURN; END; IF FALSE THEN IF BuffersLeft[NIL] < 3 THEN BEGIN IF doStats THEN StatIncr[statPupNotForwarded]; Buffer.ReturnBuffer[b]; IF doStats THEN StatIncr[ForwarderDefs.statGateLowOnBuffers]; RETURN; END; b.context ¬ toContext; b.network ¬ toContext.network; UpdatePupChecksum[b, magicTwo, old]; IF (route ¬ rte.route) = 0 THEN route ¬ b.pup.dest.host; errorCode ¬ noErrorPupErrorCode; -- *********************** Protocol1.EncapsulateAndTransmit[b, @route]; SELECT errorCode FROM noErrorPupErrorCode => BEGIN IF doStats THEN BEGIN StatIncr[statPupForwarded]; BumpPupStats[fromNetwork.index, toContext.network.index, length]; END; END; ENDCASE => BEGIN IF errorCode = cantGetTherePupErrorCode AND rte.hop # 0 THEN rte.hop ¬ maxHop + 1; DoErrorPup[b, errorCode, NIL]; 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; globalFromNetwork: Network; -- Krock because interfaces are screwy -- Krock: PhoneNetDriver doesn't copy it *********** globalFromContext: PupRouterDefs.NetworkContext ¬ NIL; DoErrorPup: PUBLIC PROCEDURE [ b: PupBuffer, code: PupTypes.PupErrorCode, text: LONG STRING] = BEGIN context: PupRouterDefs.NetworkContext ¬ b.context; rte: RoutingTableEntry; forwarding: BOOL ¬ b.pup.pupTransportControl > 0FH; -- 4 bits of hop, 4 spares destNet: PupNetID; route: PupHostID; IF b.requeueProcedure # Buffer.ReturnBuffer THEN -- Don't mash a packet we might retransmit Driver.PutOnGlobalDoneQueue[b]; IF forwarding THEN { -- Beware of address faults IF doStats THEN BumpPupStats[globalFromNetwork.index, 0, 0]; IF 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; IF globalFromContext # NIL THEN -- ARGH. NIL FAULT IF PROBLEMS FROM LOCAL TRAFFIC TOO EARLY b.pup.source ¬ [[globalFromContext.pupNetNumber], [globalFromContext.pupHostNumber], [0, 0]]; destNet ¬ b.pup.dest.net; rte ¬ GetRoutingTableEntry[destNet]; IF rte = NIL OR rte.hop > maxHop OR (context ¬ rte.context) = NIL THEN BEGIN Buffer.ReturnBuffer[b]; RETURN; END; b.context ¬ context; b.network ¬ context.network; IF (route ¬ rte.route) = 0 THEN route ¬ b.pup.dest.host; SetPupChecksum[b]; Protocol1.EncapsulateAndTransmit[b, @route]; END; UpdatePupChecksum: PUBLIC PROCEDURE [ b: PupBuffer, offset: INTEGER, oldValue: WORD] = BEGIN len: CARDINAL ¬ (b.pup.pupLength - 1)/2; checksumLoc: LONG POINTER ¬ @b.pup.pupLength + len; diff: WORD; IF checksumLoc­ = 177777B THEN RETURN; diff ¬ OnesSub[ (@b.pup.pupWords[0] + (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 network: Network ¬ b.network; index: CARDINAL ¬ network.index; bad[index] ¬ bad[index] + 1; 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[LOOPHOLE[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 Buffer.ReturnBuffer[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[]; Buffer.QueueInitialize[@badPupQueue]; Process.SetTimeout[@badPupArrived, Process.SecondsToTicks[60]]; Buffer.QueueInitialize[@statsQueue]; Process.SetTimeout[@statsArrived, Process.SecondsToTicks[60]]; END.