-- File: PupRouterIn.mesa, Last Edit: HGM February 25, 1981 3:17 PM -- Last edited by Andrew Birrell June 20, 1983 11:54 am -- Last Edited by: 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.