-- File: PupRouterOut.mesa, Last Edit: -- HGM October 16, 1979 11:08 PM -- Taft May 29, 1983 12:36 PM -- Copyright Xerox Corporation 1979, 1980 DIRECTORY StatsDefs: FROM "StatsDefs" USING [StatIncr], CommUtilDefs: FROM "CommUtilDefs" USING [GetTicks], PupRouterDefs: FROM "PupRouterDefs" USING [ routerLock, maxHop, emptyCacheEntry, OutThings, routingCacheHead, routingCacheTail, pupRouterIsActive, probesLeftToDo, probeRetransmitTimeout, pleaseProbe, BeSurePupIsOn, SetPupChecksum, RoutingCacheEntry, GetRoutingCacheEntry], DriverDefs: FROM "DriverDefs" USING [ firstNetwork, doDebug, doShow, doStats, doStorms, Glitch, Network, PutOnGlobalDoneQueue], PupDefs: FROM "PupDefs" USING [ outgoingPup, zappedOutgoingPup, PupBuffer, GetFreePupBuffer, SetPupContentsWords, PupRouterBroadcastThis], BufferDefs: FROM "BufferDefs", PupTypes: FROM "PupTypes" USING [ allNets, allHosts, gatewaySoc, PupType, PupAddress, PupHostID, PupNetID], DriverTypes: FROM "DriverTypes" USING [bufferSeal]; PupRouterOut: MONITOR LOCKS PupRouterDefs.routerLock IMPORTS StatsDefs, CommUtilDefs, PupRouterDefs, DriverDefs, PupDefs EXPORTS PupRouterDefs, PupDefs SHARES BufferDefs, DriverTypes = BEGIN OPEN StatsDefs, PupRouterDefs, DriverDefs, PupDefs, PupTypes; dataWordsPerPup: PUBLIC CARDINAL; outThings: PUBLIC OutThings ← [ outStormy: FALSE, showOut: FALSE, outShower: ]; -- parameters for killing packets lightning: INTEGER ← 30; bolt: INTEGER ← 10; BufferSealBroken: PUBLIC ERROR = CODE; IllegalPupLength: PUBLIC ERROR = CODE; PupRouterSendThis: PUBLIC ENTRY PROCEDURE [b: PupBuffer] = BEGIN IF b.dest.net=allNets AND b.dest.host=allHosts THEN BEGIN PupRouterBroadcastThis[b]; RETURN; END; SendUnlocked[b]; END; SneakySendUnlocked: PUBLIC PROCEDURE [b: PupBuffer] = JustSendUnlocked; JustSendUnlocked: INTERNAL PROCEDURE [b: PupBuffer] = BEGIN SendUnlocked[b]; END; SendUnlocked: INTERNAL PROCEDURE [b: PupBuffer] = BEGIN d: PupAddress ← b.dest; n: PupNetID ← d.net; routing: POINTER TO RoutingCacheEntry; network: Network; h: PupHostID; IF doStats THEN StatIncr[statPupSent]; IF doDebug THEN BeSurePupIsOn[]; IF doDebug AND b.seal#DriverTypes.bufferSeal THEN Glitch[BufferSealBroken]; IF doStorms AND outThings.outStormy -- 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 outThings.showOut THEN outThings.outShower[zappedOutgoingPup,b]; PutOnGlobalDoneQueue[b]; IF doStats THEN StatIncr[statZappedP]; RETURN; END; -- Maybe we should do something for the local case? b.pupTransportControl ← 0; routing ← GetRoutingCacheEntry[net: n, ifMissing: probeAndReturn]; IF routing=NIL OR routing.entry.hop>maxHop OR routing.entry.network=NIL THEN BEGIN -- don't know where to send it IF doStats THEN StatIncr[statPupsSentNowhere]; IF routing=NIL OR routing.entry.network=NIL THEN BEGIN -- Send him an error Pup, but beware of RequeueProc-- END; PutOnGlobalDoneQueue[b]; RETURN; -- wait to see if there is an alternate path END; network ← routing.entry.network; b.source.host ← [network.hostNumber]; -- tell the truth b.source.net ← [network.netNumber]; IF doShow AND outThings.showOut THEN outThings.outShower[outgoingPup,b]; h ← routing.entry.route; IF h=0 THEN h ← b.dest.host; -- we are on the same net IF doDebug AND ((b.pupLength+1)/2)>dataWordsPerPup+wordsPerPupHeader THEN DriverDefs.Glitch[IllegalPupLength]; SetPupChecksum[b]; network.encapsulatePup[b,h]; network.sendBuffer[b]; END; PupRouterBroadcastThis: PUBLIC PROCEDURE [b: PupBuffer] = BEGIN IF doStats THEN StatIncr[statPupBroadcast]; IF doDebug THEN BeSurePupIsOn[]; IF doDebug AND b.seal#DriverTypes.bufferSeal THEN Glitch[BufferSealBroken]; IF DriverDefs.firstNetwork=NIL THEN BEGIN PutOnGlobalDoneQueue[b]; RETURN; END; b.allNets ← TRUE; -- this is where it gets turned on b.network ← DriverDefs.firstNetwork; b.dest.host ← allHosts; PupBroadcaster[b]; END; -- b.network is already set up to the desired network PupBroadcaster: PUBLIC PROCEDURE [b: PupBuffer] = BEGIN network: Network ← b.network; IF b.bypassZeroNet AND network.netNumber=0 THEN BEGIN PutOnGlobalDoneQueue[b]; RETURN; END; -- goes (slowly) around in circles b.pupTransportControl ← 0; b.dest.net ← b.source.net ← [network.netNumber]; b.source.host ← [network.hostNumber]; network.encapsulatePup[b,PupTypes.allHosts]; SetPupChecksum[b]; network.sendBuffer[b]; END; -- Pup is assumed to have arrived from somewhere else. We fixup defaults here. SwapPupSourceAndDest: PUBLIC PROCEDURE [b: PupBuffer] = BEGIN network: DriverDefs.Network ← b.network; temp: PupAddress; IF doDebug AND b.seal#DriverTypes.bufferSeal THEN Glitch[BufferSealBroken]; temp ← b.source; b.source ← b.dest; b.dest ← temp; -- in case we are returning a broadcast packet IF b.dest.net=0 THEN b.dest.net ← [network.netNumber]; IF b.source.net=0 THEN b.source.net ← [network.netNumber]; IF b.source.host=PupTypes.allHosts THEN b.source.host ← [network.hostNumber]; END; ReturnPup: PUBLIC PROCEDURE [b: PupBuffer, type: PupTypes.PupType, bytes: CARDINAL] = BEGIN SwapPupSourceAndDest[b]; SendPup[b,type,bytes]; END; wordsPerPupHeader: CARDINAL = 11; bytesPerPupHeader: CARDINAL = wordsPerPupHeader*2; GetPupContentsBytes: PUBLIC PROCEDURE [b: PupBuffer] RETURNS [bytes: CARDINAL] = BEGIN RETURN[b.pupLength-bytesPerPupHeader]; END; SetPupContentsBytes: PUBLIC PROCEDURE [b: PupBuffer, bytes: CARDINAL] = BEGIN IF bytes>dataWordsPerPup*2 THEN Glitch[IllegalPupLength]; b.pupLength ← bytes+bytesPerPupHeader; END; SetPupContentsWords: PUBLIC PROCEDURE [b: PupBuffer, words: CARDINAL] = BEGIN IF words>dataWordsPerPup THEN Glitch[IllegalPupLength]; b.pupLength ← words*2+bytesPerPupHeader; END; SendPup: PUBLIC PROCEDURE [b: PupBuffer, type: PupType, bytes: CARDINAL] = BEGIN b.pupType ← type; SetPupContentsBytes[b,bytes]; PupRouterSendThis[b]; END; InsertRoutingCacheEntry: PUBLIC PROCEDURE [net: PupNetID] RETURNS [rte: POINTER TO RoutingCacheEntry] = BEGIN rte ← GetRoutingCacheEntry[net: net, ifMissing: return, promote: TRUE]; IF rte=NIL THEN DO -- Repeatedly grab tail cache entry and put it at the head until we -- find one that is NOT for a directly-connected network. rte ← GetRoutingCacheEntry[ net: [routingCacheTail.net], ifMissing: return, promote: TRUE]; IF rte.net=emptyCacheEntry OR rte.entry.hop#0 THEN EXIT; ENDLOOP; rte.net ← net; rte.entry ← [hop: maxHop+1, time: 90, route: [0], network: NIL]; END; DeleteRoutingCacheEntry: PUBLIC PROCEDURE [net: PupNetID] = BEGIN rte: POINTER TO RoutingCacheEntry ← GetRoutingCacheEntry[ net: net, ifMissing: return, promote: TRUE]; IF rte#NIL THEN BEGIN -- make entry empty and put it at the tail of the queue. -- We know it is at the head now, because GetRoutingCacheEntry put it there! rte.net ← emptyCacheEntry; rte.entry.network ← NIL; rte.entry.hop ← maxHop+1; routingCacheHead ← rte.next; rte.next ← NIL; routingCacheTail.next ← rte; routingCacheTail ← rte; END; END; Prober: PUBLIC PROCEDURE = BEGIN -- This peculiar organization is to avoid calling GetFreePupBuffer from -- within the monitor, which could cause a deadlock. AwaitRequest: ENTRY PROCEDURE RETURNS [action: {probe, quit}] = BEGIN WHILE pupRouterIsActive DO IF probesLeftToDo#0 THEN WAIT probeRetransmitTimeout ELSE WAIT pleaseProbe; IF probesLeftToDo#0 THEN {probesLeftToDo ← probesLeftToDo-1; RETURN [probe]}; ENDLOOP; RETURN [quit]; END; DO SELECT AwaitRequest[] FROM probe => BEGIN b: BufferDefs.PupBuffer ← PupDefs.GetFreePupBuffer[]; b.bypassZeroNet ← FALSE; b.pupType ← gatewayRequest; b.pupID ← [0, probesLeftToDo]; b.dest.socket ← b.source.socket ← PupTypes.gatewaySoc; PupDefs.SetPupContentsWords[b, 0]; PupDefs.PupRouterBroadcastThis[b]; END; quit => RETURN; ENDCASE; ENDLOOP; END; END. -- PupRouterOut