-- Copyright (C) 1984, 1985 by Xerox Corporation. All rights reserved.
-- RealForwarder.mesa, HGM, 9-May-86 16:39:46
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,
maxDataWordsPerGatewayPup],
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 = maxDataWordsPerGatewayPup/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.