-- File: PupRouterIn.mesa, Last Edit: HGM October 15, 1979 10:08 PM

-- Copyright Xerox Corporation 1979, 1980

DIRECTORY
StatsDefs: FROM "StatsDefs" USING [StatIncr],
CommUtilDefs: FROM "CommUtilDefs" USING [GetTicks],
PupRouterDefs: FROM "PupRouterDefs" USING [
routerLock, routingTableUpdateTimeout, probeResponse, InThings,
PupGateInfo, PupRouterSocket, PupRoutingTableEntry, maxHop,
ForwardThisPupBuffer, TestPupChecksum, RejectPupWithBadChecksum, Reject],
DriverDefs: FROM "DriverDefs" USING [doShow, doStats, doStorms, Network, Glitch],
PupDefs: FROM "PupDefs" USING [
EnqueuePup, ReturnFreePupBuffer, PupBuffer,
incomingPup, zappedIncomingPup],
BufferDefs: FROM "BufferDefs" USING [BuffersLeft],
PupTypes: FROM "PupTypes" USING [
allHosts, gatewaySoc,
PupAddress, PupHostID, PupNetID,
noProcessPupErrorCode, resourceLimitsPupErro
rCode],
D
riverTypes: FROM "DriverTypes";

PupRouterIn: MONITOR LOCKS PupRouterDefs.routerLock
IMPORTS StatsDefs, CommUtilDefs, PupRouterDefs, DriverDefs, PupDefs, BufferDefs
EXPORTS PupRouterDefs
SHARES BufferDefs, DriverTypes =
BEGIN OPEN StatsDefs, PupRouterDefs, DriverDefs, PupDefs, PupTypes;

-- SemiPublic things for PupRouterCold and friends
pupRouterIsActive: PUBLIC BOOLEAN ← FALSE;
firstSocket: PUBLIC PupRouterSocket ← NIL;
pupRoutingTable: PUBLIC DESCRIPTOR FOR ARRAY OF PupRoutingTableEntry;

inThings: PUBLIC InThings ← [
inStormy: FALSE,
watcherIsWatching: FALSE,
watcherSeesBroadcast: FALSE,
watcherCallsThis: ,
badChecksumProc: PupRouterDefs.RejectPupWithBadChecksum,
showIn: FALSE,
inShower: ];

-- parameters for killing packets
lightning: INTEGER ← 30;
bolt: INTEGER ← 10;


PupRouterNo
tActive: PUBLIC ERROR = CODE;

BeSurePupIsOn: PUBLIC PROCEDURE =
BEGIN
IF ~pupRouterIsActive
THEN DriverDefs.Glitch[PupRouterNotActive];
END;

-- On
ly called by dispatcher when a Pup arrives.
PupInputer: PUBLIC ENTRY PROCEDURE [b: PupBuffer] =
BEGIN
so: PupRouterSocket;
d: PupAddress ← b.dest;
targetNet: PupNetID ← d.net;
network: Network;
routing: POINTER TO PupRoutingTableEntry;
IF doStats THEN StatIncr[statPupReceived];

IF ~TestPupChecksum[b] THEN
BEGIN
IF doStats THEN StatIncr[statReceivedBadPupChecksum];
inThings.badChecksumProc[b];
RETURN;
END;

IF doStorms AND inThings.inStormy -- 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 inThings.showIn THEN inThings.inShower[zappedIncomingPup,b];
ReturnFreePupBuffer[b];
IF doStats THEN StatIncr[statZappedP];
RETURN
END;
IF doShow AND inThings.showIn THEN inThings.inShower[incomingPup,b];

network ← b.network;
routing ← @pupRoutingTable[targetNet];
IF targetNet#0 AND network.netNumber#0 THEN
BEGIN
IF targetNet ~IN [0..LENGTH[pupRoutingTable])
OR routing.network=NIL
OR routing.hop#0
OR (d.host=allHosts AND routing.network#network) THEN
BEGIN
Forwar
dThisPupBuffer[b];
RETURN;
END;
IF FALSE THE
N -- It is more complicated than that.....
b.pupTransportControl ← b.pupTransportControl+20B; -- Hack for Gateway init
network ← routing.network; -- fixup backdoor problems
END;

IF (d.host=network.hostNumber OR d.host=allHosts) THEN
BEGIN -- packet for us - incomming or local
FOR so←firstSocket,so.next UNTIL so=NIL DO
IF so.local.socket=d.socket THEN
BEGIN
IF network.netNumber=0 AND targetNet#0 THEN
BEGIN -- packet for us, believe network number
network.netNumber ← targetNet;
IF targetNet<LENGTH[pupRoutingTable] THEN
routing↑ ← [hop: 0, time: 0, route: [0], network: network];
END;
IF so.input.length>1 AND BufferDefs.BuffersLeft[]<2 THEN
BEGIN
IF doStats THEN StatIncr[statPupInputQueueOverflow];
Reject[b,resourceLimitsPupErrorCode];
EXIT;
END;
EnqueuePup[@so.input,b];
NOTIFY so.ready;
EXIT;
END;
REPEAT FINISHED =>
BEGIN -- not in socket table
IF b.pupType=gatewayInfo AND d.socket=gatewaySoc THEN [] ← GatewaySee[b]
ELSE
BEGIN -- non gateway packet for unknown socket
IF doStats THEN
IF d.host#allHosts
THEN StatIncr[statJunkPupsForUsNoLocalSocket]
ELSE StatIncr[statJunkBroadcastPups];
IF ~inThings.watcherIsWatching THEN GOTO RejectThisPup;
IF (d.host#allHosts OR inThings.watcherSeesBroadcast)
AND inThings.watcherCallsThis[b] THEN
GOTO RejectThisPup;
END;
ReturnFreePupBuffer[b];
EXIT
S
RejectThisPup =>
BEGIN
-- Hack special
case check to avoid touching another module
IF b.dest.host=allHosts THEN ReturnFreePupBuffer[b]
ELSE
Reject[b,noProcessPupErrorCode];
END;
END;
ENDLOOP;
END
ELSE
ForwardThisPupBuffer[b];
END;

Timeout: PUBLIC ENTRY PROCEDURE =
BEGIN
n: CARDINAL;
rte: POINTER TO PupRout
ingTableEntry;
WHILE pupRouterIsActive DO
-- There isn’t any need to probe the Gateways if we don’t know our network number, s
ince they broadcast every 30 seconds or so.
FOR n IN [0..LENGTH[pupRoutingTable]) DO
rte ← @pupRoutingTable[n];
IF rte.hop=0 THEN LOOP; -- directly connected
IF rte.network=NIL THEN LOOP; -- no way to get there
IF (rte.time←rte.time+30)>180 THEN rte.network ← NIL
ENDLOOP;
WAIT routingTableUpdateT
imeout; -- 30 seconds
ENDLOOP;
END;

PupGatewaySee: PUBLIC ENTRY PROCEDURE [b: PupBuffer] RETURNS [BOOLEAN]
=
BEGIN

RETURN[GatewaySee[b]];
END;

GatewaySee: INTERNAL PROCEDURE [b: PupBuffer] RETURNS [new: BOOLEAN] =
BEGIN
newRoute: PupHostID = b.source.host;
network: Network = b.network;
length: CARDINAL = b.pupLength-22;
new ← FALSE; -- no changes yet

IF b.pupType#gatewayInfo
OR b.source.net ~IN[1..LENGTH[pupRoutingTable])
OR newRoute=0
OR (length MOD 2*SIZE[PupGateInfo])#0 THEN
BEGIN
IF doStats THEN StatIncr[statMouseTrap];
RETURN;
END;
IF newRoute=network.hostNumber THEN RETURN; -- from self
IF doStats THEN StatIncr[statPupGatewayPacketsRecv];
IF network.netNumber=0 THEN
BEGIN -- we don’t know our network number on this device yet
network.netNumber ← b.source.net;
IF network.netNumber<LENGTH[pupRoutingTable] THEN
pupRoutingTable[network.netNumber] ← [hop:
0, time: 0, route: [0], network: network];
-- Note: If we are not a gateway, we have probably just learned the network number of the standard Ethe
rnet device. The users process that called PupRouterOn will normally be waiting so kick him loose.
NOTIFY probeResponse;
END;
-- What should we do if the network number from this Pup doesn’t match the one we know in network.netNumber?

-- This is where we actually update the routing table. For all the details, see Taft’s memo sto
red on: [MAXC]<Pup>GatewayInformation.bravo
BEGIN
newHop, newTime, net: CARDINAL;
data: POINTER TO PupGateInfo ← LOOPHOLE[@b.pupWords[0]];
rt: POINTER TO PupRoutingTableEntry;
THROUGH [0..length/(2*SIZE[PupGateInfo])) DO
net ← data.net;
newHop ← data.hop+1;
data ← data+SIZE[PupGateInfo];
IF net>=LENGTH[pupRoutingTable] THEN LOOP; -- too big, skip it
rt ← @pupRoutingTable[net];
IF
rt.hop=0 THEN LOOP; -- directly connected
-- This is a bit tricky. We want to keep entrys with hop>maxHop until they timeout so that Gateways will do the right things about propagating changes, but w
e don’t want to learn new paths to nowhere.
IF ((rt.network=NIL OR rt.hop>maxHop) AND newHop>maxHop) THEN LOOP;
IF rt.network=NIL
OR (rt.route=newRoute AND rt.network=netw
ork)
OR rt.time>90
OR newHop<rt.hop
-- hack to give Larry’s PacketRadio preference over SLA lines
OR (ne
wHop=rt.hop AND network.device=packetradio)
THEN
BEGIN
IF newHop>maxHop THEN
BEGIN
newHop ← maxHop+1; -- dangling entry, don’t rejuvenate timer
newTime ← rt.time;
END
ELSE newTime ← 0;
IF rt.hop#newHop THEN new ← TRUE;
-- rejuvenate timer if nothing else
rt↑ ← [ hop: newHop, time: newTime, route: newRoute, network: network ];
END;
ENDLOOP;
END;
END;


-- initialization
END. -- PupRouterIn