-- File: PupRouterIn.mesa, Last Edit: HGM February 25, 1981 3:17 PM DIRECTORY Inline USING [LowHalf], System 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 Inline, System, 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 _ System.GetClockPulses[]; -- Alto Pulses are short lightning _ -INTEGER[Inline.LowHalf[mumble] 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 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 < LENGTH[routingTable] 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..LENGTH[routingTable]) 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 < LENGTH[routingTable] 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]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 >= LENGTH[routingTable] 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 >= LENGTH[routingTable] THEN RETURN[NIL]; RETURN[@routingTable[net]]; END; EnumerateRoutingTable: PUBLIC PROCEDURE [proc: PROCEDURE [RoutingTableEntry]] = BEGIN FOR n: CARDINAL IN [0..LENGTH[routingTable]) DO proc[@routingTable[n]]; ENDLOOP; END; SetPupForwarder: PUBLIC PROCEDURE [proc: PROCEDURE [PupBuffer]] = BEGIN pupForwarder _ proc; END; END.