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 BOOLEANFALSE;
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.