-- File: PupRouterOut.mesa, Last Edit:
-- HGM October 16, 1979 11:08 PM
-- Taft May 29, 1983 12:36 PM
-- Copyright Xerox Corporation 1979, 1980
DIRECTORY
StatsDefs: FROM "StatsDefs" USING [StatIncr],
CommUtilDefs: FROM "CommUtilDefs" USING [GetTicks],
PupRouterDefs: FROM "PupRouterDefs" USING [
routerLock, maxHop, emptyCacheEntry, OutThings, routingCacheHead, routingCacheTail,
pupRouterIsActive, probesLeftToDo, probeRetransmitTimeout, pleaseProbe,
BeSurePupIsOn, SetPupChecksum, RoutingCacheEntry, GetRoutingCacheEntry],
DriverDefs: FROM "DriverDefs" USING [
firstNetwork, doDebug, doShow, doStats, doStorms,
Glitch, Network, PutOnGlobalDoneQueue],
PupDefs: FROM "PupDefs" USING [
outgoingPup, zappedOutgoingPup, PupBuffer, GetFreePupBuffer,
SetPupContentsWords, PupRouterBroadcastThis],
BufferDefs: FROM "BufferDefs",
PupTypes: FROM "PupTypes" USING [
allNets, allHosts, gatewaySoc, PupType,
PupAddress, PupHostID, PupNetID],
DriverTypes: FROM "DriverTypes" USING [bufferSeal];
PupRouterOut: MONITOR LOCKS PupRouterDefs.routerLock
IMPORTS StatsDefs, CommUtilDefs, PupRouterDefs, DriverDefs, PupDefs
EXPORTS PupRouterDefs, PupDefs
SHARES BufferDefs, DriverTypes =
BEGIN OPEN StatsDefs, PupRouterDefs, DriverDefs, PupDefs, PupTypes;
dataWordsPerPup: PUBLIC CARDINAL;
outThings: PUBLIC OutThings ← [
outStormy: FALSE,
showOut: FALSE,
outShower: ];
-- parameters for killing packets
lightning: INTEGER ← 30;
bolt: INTEGER ← 10;
BufferSealBroken: PUBLIC ERROR = CODE;
IllegalPupLength: PUBLIC ERROR = CODE;
PupRouterSendThis: PUBLIC ENTRY PROCEDURE [b: PupBuffer] =
BEGIN
IF b.dest.net=allNets AND b.dest.host=allHosts THEN
BEGIN
PupRouterBroadcastThis[b];
RETURN;
END;
SendUnlocked[b];
END;
SneakySendUnlocked: PUBLIC PROCEDURE [b: PupBuffer] = JustSendUnlocked;
JustSendUnlocked: INTERNAL PROCEDURE [b: PupBuffer] =
BEGIN
SendUnlocked[b];
END;
SendUnlocked: INTERNAL PROCEDURE [b: PupBuffer] =
BEGIN
d: PupAddress ← b.dest;
n: PupNetID ← d.net;
routing: POINTER TO RoutingCacheEntry;
network: Network;
h: PupHostID;
IF doStats THEN StatIncr[statPupSent];
IF doDebug THEN BeSurePupIsOn[];
IF doDebug AND b.seal#DriverTypes.bufferSeal THEN Glitch[BufferSealBroken];
IF doStorms AND outThings.outStormy -- for debugging only
AND (lightning←lightning+1)>bolt OR lightning<0 THEN
BEGIN
IF lightning>bolt THEN
BEGIN
IF bolt>100 THEN
BEGIN
lightning←-INTEGER[CommUtilDefs.GetTicks[] MOD 20B];
bolt←10;
END
ELSE BEGIN lightning←0; bolt←bolt+1; END;
END;
IF doShow AND outThings.showOut THEN outThings.outShower[zappedOutgoingPup,b];
PutOnGlobalDoneQueue[b];
IF doStats THEN StatIncr[statZappedP];
RETURN;
END;
-- Maybe we should do something for the local case?
b.pupTransportControl ← 0;
routing ← GetRoutingCacheEntry[net: n, ifMissing: probeAndReturn];
IF routing=NIL OR routing.entry.hop>maxHop OR routing.entry.network=NIL THEN
BEGIN -- don't know where to send it
IF doStats THEN StatIncr[statPupsSentNowhere];
IF routing=NIL OR routing.entry.network=NIL THEN
BEGIN -- Send him an error Pup, but beware of RequeueProc-- END;
PutOnGlobalDoneQueue[b];
RETURN; -- wait to see if there is an alternate path
END;
network ← routing.entry.network;
b.source.host ← [network.hostNumber]; -- tell the truth
b.source.net ← [network.netNumber];
IF doShow AND outThings.showOut THEN outThings.outShower[outgoingPup,b];
h ← routing.entry.route;
IF h=0 THEN h ← b.dest.host; -- we are on the same net
IF doDebug AND ((b.pupLength+1)/2)>dataWordsPerPup+wordsPerPupHeader THEN
DriverDefs.Glitch[IllegalPupLength];
SetPupChecksum[b];
network.encapsulatePup[b,h];
network.sendBuffer[b];
END;
PupRouterBroadcastThis: PUBLIC PROCEDURE [b: PupBuffer] =
BEGIN
IF doStats THEN StatIncr[statPupBroadcast];
IF doDebug THEN BeSurePupIsOn[];
IF doDebug AND b.seal#DriverTypes.bufferSeal THEN Glitch[BufferSealBroken];
IF DriverDefs.firstNetwork=NIL THEN BEGIN PutOnGlobalDoneQueue[b]; RETURN; END;
b.allNets ← TRUE; -- this is where it gets turned on
b.network ← DriverDefs.firstNetwork;
b.dest.host ← allHosts;
PupBroadcaster[b];
END;
-- b.network is already set up to the desired network
PupBroadcaster: PUBLIC PROCEDURE [b: PupBuffer] =
BEGIN
network: Network ← b.network;
IF b.bypassZeroNet AND network.netNumber=0 THEN
BEGIN PutOnGlobalDoneQueue[b]; RETURN; END; -- goes (slowly) around in circles
b.pupTransportControl ← 0;
b.dest.net ← b.source.net ← [network.netNumber];
b.source.host ← [network.hostNumber];
network.encapsulatePup[b,PupTypes.allHosts];
SetPupChecksum[b];
network.sendBuffer[b];
END;
-- Pup is assumed to have arrived from somewhere else. We fixup defaults here.
SwapPupSourceAndDest: PUBLIC PROCEDURE [b: PupBuffer] =
BEGIN
network: DriverDefs.Network ← b.network;
temp: PupAddress;
IF doDebug AND b.seal#DriverTypes.bufferSeal THEN Glitch[BufferSealBroken];
temp ← b.source;
b.source ← b.dest;
b.dest ← temp;
-- in case we are returning a broadcast packet
IF b.dest.net=0 THEN b.dest.net ← [network.netNumber];
IF b.source.net=0 THEN b.source.net ← [network.netNumber];
IF b.source.host=PupTypes.allHosts THEN b.source.host ← [network.hostNumber];
END;
ReturnPup: PUBLIC PROCEDURE [b: PupBuffer, type: PupTypes.PupType, bytes: CARDINAL] =
BEGIN
SwapPupSourceAndDest[b];
SendPup[b,type,bytes];
END;
wordsPerPupHeader: CARDINAL = 11;
bytesPerPupHeader: CARDINAL = wordsPerPupHeader*2;
GetPupContentsBytes: PUBLIC PROCEDURE [b: PupBuffer] RETURNS [bytes: CARDINAL] =
BEGIN
RETURN[b.pupLength-bytesPerPupHeader];
END;
SetPupContentsBytes: PUBLIC PROCEDURE [b: PupBuffer, bytes: CARDINAL] =
BEGIN
IF bytes>dataWordsPerPup*2 THEN Glitch[IllegalPupLength];
b.pupLength ← bytes+bytesPerPupHeader;
END;
SetPupContentsWords: PUBLIC PROCEDURE [b: PupBuffer, words: CARDINAL] =
BEGIN
IF words>dataWordsPerPup THEN Glitch[IllegalPupLength];
b.pupLength ← words*2+bytesPerPupHeader;
END;
SendPup: PUBLIC PROCEDURE [b: PupBuffer, type: PupType, bytes: CARDINAL] =
BEGIN
b.pupType ← type;
SetPupContentsBytes[b,bytes];
PupRouterSendThis[b];
END;
InsertRoutingCacheEntry: PUBLIC PROCEDURE [net: PupNetID]
RETURNS [rte: POINTER TO RoutingCacheEntry] =
BEGIN
rte ← GetRoutingCacheEntry[net: net, ifMissing: return, promote: TRUE];
IF rte=NIL THEN
DO -- Repeatedly grab tail cache entry and put it at the head until we
-- find one that is NOT for a directly-connected network.
rte ← GetRoutingCacheEntry[
net: [routingCacheTail.net], ifMissing: return, promote: TRUE];
IF rte.net=emptyCacheEntry OR rte.entry.hop#0 THEN EXIT;
ENDLOOP;
rte.net ← net;
rte.entry ← [hop: maxHop+1, time: 90, route: [0], network: NIL];
END;
DeleteRoutingCacheEntry: PUBLIC PROCEDURE [net: PupNetID] =
BEGIN
rte: POINTER TO RoutingCacheEntry ← GetRoutingCacheEntry[
net: net, ifMissing: return, promote: TRUE];
IF rte#NIL THEN
BEGIN -- make entry empty and put it at the tail of the queue.
-- We know it is at the head now, because GetRoutingCacheEntry put it there!
rte.net ← emptyCacheEntry;
rte.entry.network ← NIL;
rte.entry.hop ← maxHop+1;
routingCacheHead ← rte.next;
rte.next ← NIL;
routingCacheTail.next ← rte;
routingCacheTail ← rte;
END;
END;
Prober: PUBLIC PROCEDURE =
BEGIN
-- This peculiar organization is to avoid calling GetFreePupBuffer from
-- within the monitor, which could cause a deadlock.
AwaitRequest: ENTRY PROCEDURE RETURNS [action: {probe, quit}] =
BEGIN
WHILE pupRouterIsActive DO
IF probesLeftToDo#0 THEN WAIT probeRetransmitTimeout ELSE WAIT pleaseProbe;
IF probesLeftToDo#0 THEN
{probesLeftToDo ← probesLeftToDo-1; RETURN [probe]};
ENDLOOP;
RETURN [quit];
END;
DO
SELECT AwaitRequest[] FROM
probe =>
BEGIN
b: BufferDefs.PupBuffer ← PupDefs.GetFreePupBuffer[];
b.bypassZeroNet ← FALSE;
b.pupType ← gatewayRequest;
b.pupID ← [0, probesLeftToDo];
b.dest.socket ← b.source.socket ← PupTypes.gatewaySoc;
PupDefs.SetPupContentsWords[b, 0];
PupDefs.PupRouterBroadcastThis[b];
END;
quit => RETURN;
ENDCASE;
ENDLOOP;
END;
END. -- PupRouterOut