PupRouterIn.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
HGM February 25, 1981 3:17 PM
Birrell June 20, 1983 11:54 am
Levin, August 9, 1983 9:46 am
DIRECTORY
Basics USING [LongNumber],
ProcessorFace USING [GetClockPulses],
StatsDefs USING [StatIncr],
PupRouterDefs USING [bytesPerPupHeader, routerLock, routingTableUpdateTimeout, probeResponse, InThings, PupGateInfo, PupRouterSocket, RoutingTableEntry, RoutingTable, maxHop, TestPupChecksum, RejectPupWithBadChecksum, DontForwardPupBuffer, Reject],
CommFlags USING [doShow, doStats, doStorms],
DriverDefs USING [Network, Glitch],
PupDefs USING [EnqueuePup, ReturnFreePupBuffer, PupBuffer, incomingPup, zappedIncomingPup],
BufferDefs USING [BuffersLeft],
PupTypes USING [allHosts, gatewaySoc, PupAddress, PupHostID, PupNetID],
DriverTypes;
PupRouterIn:
MONITOR
LOCKS PupRouterDefs.routerLock
IMPORTS ProcessorFace, StatsDefs, PupRouterDefs, DriverDefs, PupDefs, BufferDefs
EXPORTS BufferDefs, PupRouterDefs
SHARES BufferDefs, DriverTypes =
BEGIN OPEN StatsDefs, PupRouterDefs, DriverDefs, PupDefs, PupTypes;
EXPORTed TYPEs
Network: PUBLIC TYPE = DriverDefs.Network;
SemiPublic things for PupRouterCold and friends
routerIsActive: PUBLIC BOOLEAN ← FALSE;
firstSocket: PUBLIC PupRouterSocket ← NIL;
routingTable: PUBLIC RoutingTable;
pupForwarder: PUBLIC PROCEDURE [PupBuffer] ← DontForwardPupBuffer;
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;
PupRouterNotActive: PUBLIC ERROR = CODE;
BeSurePupIsOn:
PUBLIC
PROCEDURE =
BEGIN IF ~routerIsActive THEN DriverDefs.Glitch[PupRouterNotActive]; END;
Only 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 ← b.network;
rte: RoutingTableEntry;
IF CommFlags.doStats THEN StatIncr[statPupReceived];
IF b.pupLength < bytesPerPupHeader
THEN
BEGIN
IF CommFlags.doStats THEN StatIncr[pupTooShort];
ReturnFreePupBuffer[b];
RETURN;
END;
IF ~TestPupChecksum[b]
THEN
BEGIN
IF CommFlags.doStats THEN StatIncr[statReceivedBadPupChecksum];
inThings.badChecksumProc[b];
RETURN;
END;
IF CommFlags.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
mumble: LONG CARDINAL ← ProcessorFace.GetClockPulses[];
Alto Pulses are short
lightning ← -INTEGER[LOOPHOLE[mumble, Basics.LongNumber].lowbits MOD 20B];
bolt ← 10;
END
ELSE BEGIN lightning ← 0; bolt ← bolt + 1; END;
END;
IF CommFlags.doShow
AND inThings.showIn
THEN
inThings.inShower[zappedIncomingPup, b];
ReturnFreePupBuffer[b];
IF CommFlags.doStats THEN StatIncr[statZappedP];
RETURN
END;
IF CommFlags.doShow
AND inThings.showIn
THEN
inThings.inShower[incomingPup, b];
Patch to recveive broadcasts on anonymous nets from old Gateways
(or replies to broadcasts)
IF network.netNumber.b > 377B
AND d.net = b.source.net
AND
(d.host = allHosts OR d.host = network.hostNumber) THEN
targetNet ← d.net ← b.dest.net ← [0];
rte ← GetRoutingTableEntry[targetNet];
IF targetNet # 0
AND network.netNumber.b # 0
THEN
BEGIN
Patch in case network.netNumber isn't reflected in routingTable. This can happen if the NS router finds the network number and doesn't tell us.
IF routingTable[network.netNumber.b].network # network
THEN routingTable[network.netNumber.b] ←
[net: [network.netNumber.b], hop: 0, time: 0, route: [0], network: network];
End patch --
IF rte =
NIL
OR rte.network =
NIL
OR rte.hop # 0
OR
(d.host = allHosts AND rte.network # network) THEN
BEGIN pupForwarder[b]; RETURN; END;
IF
FALSE
THEN
-- It is more complicated than that.....
b.pupTransportControl ← b.pupTransportControl + 20B;
Hack for Gateway init
network ← rte.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.b = 0
AND targetNet # 0
THEN
BEGIN -- packet for us, believe network number
network.netNumber ← [0, targetNet];
IF targetNet < routingTable.length
THEN
rte^ ←
[net: targetNet, hop: 0, time: 0, route: [0], network: network];
END;
IF so.input.length > 1
AND BufferDefs.BuffersLeft[] < 2
THEN
BEGIN
IF CommFlags.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 CommFlags.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];
EXITS
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 pupForwarder[b];
END;
Timeout:
PUBLIC
ENTRY
PROCEDURE =
BEGIN
WHILE routerIsActive
DO
There isn't any need to probe the Gateways if we don't know our network number, since they broadcast every 30 seconds or so.
EnumerateRoutingTable[FlushDeadNets];
WAIT routingTableUpdateTimeout; -- 30 seconds
ENDLOOP;
END;
FlushDeadNets:
PROCEDURE [rte: RoutingTableEntry] =
BEGIN
IF rte.hop = 0 THEN RETURN; -- directly connected
IF rte.network = NIL THEN RETURN; -- no way to get there
IF (rte.time ← rte.time + 30) > 180 THEN rte.network ← NIL
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
Patch for Anonymous networks
OR b.source.net ~IN[1..routingTable.length)
OR newRoute = 0
OR (length
MOD 2*
SIZE[PupGateInfo]) # 0
THEN
BEGIN IF CommFlags.doStats THEN StatIncr[statMouseTrap]; RETURN; END;
IF newRoute = network.hostNumber THEN RETURN; -- from self
IF CommFlags.doStats THEN StatIncr[statPupGatewayPacketsRecv];
IF network.netNumber = [0, 0]
THEN
BEGIN -- we don't know our network number on this device yet
net: PupNetID ← b.source.net;
network.netNumber ← [0, net];
IF net < routingTable.length
THEN
routingTable[net] ←
[net: net, 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 Ethernet 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 stored on: [MAXC]<Pup>GatewayInformation.bravo
BEGIN
data: LONG POINTER TO PupGateInfo ← LOOPHOLE[@b.pupWords[0]];
THROUGH [0..length/(2*
SIZE[PupGateInfo]))
DO
newHop, newTime: CARDINAL;
net: PupNetID ← [data.net];
rte: RoutingTableEntry;
newHop ← data.hop + 1;
data ← data + SIZE[PupGateInfo];
IF net >= routingTable.length THEN LOOP; -- too big, skip it
rte ← GetRoutingTableEntry[net];
IF rte.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 we don't want to learn new paths to nowhere.
IF ((rte.network = NIL OR rte.hop > maxHop) AND newHop > maxHop) THEN LOOP;
IF rte.network =
NIL
OR (rte.route = newRoute
AND rte.network = network)
OR
rte.time > 90 OR newHop < rte.hop OR
(newHop = rte.hop AND network.speed > rte.network.speed) THEN
BEGIN
IF newHop > maxHop
THEN
BEGIN
newHop ← maxHop + 1; -- dangling entry, don't rejuvenate timer
newTime ← rte.time;
END
ELSE newTime ← 0;
IF rte.hop # newHop THEN new ← TRUE;
rejuvenate timer if nothing else
rte^ ←
[net: net, hop: newHop, time: newTime, route: newRoute,
network: network];
END;
ENDLOOP;
END;
END;
GetRoutingTableEntry:
PUBLIC
PROCEDURE [net: PupNetID]
RETURNS [rte: RoutingTableEntry] =
BEGIN
IF net >= routingTable.length THEN RETURN[NIL];
RETURN[@routingTable[net]];
END;
EnumerateRoutingTable:
PUBLIC
PROCEDURE [proc:
PROCEDURE [RoutingTableEntry]] =
BEGIN
FOR n:
CARDINAL
IN [0..routingTable.length)
DO
proc[@routingTable[n]]; ENDLOOP;
END;
SetPupForwarder:
PUBLIC
PROCEDURE [proc:
PROCEDURE [PupBuffer]] =
BEGIN
pupForwarder ← proc;
END;
END.