// GateForward.bcpl -- Level 1 code implementing gateway functions // Last modified June 7, 1983 9:57 AM by Taft get "Pup0.decl" get "Pup1.decl" get "PupRoute.decl" get "GateForward.decl" external [ // outgoing procedures Forwarder; ForwarderCtx; TMLookup // incoming procedures GetPBI; ReleasePBI; PupError; ExchangePorts; CompletePup; RoutePup OnesComplementAdd; OnesComplementSubtract; LeftCycle; LocateNet SetTimer; TimerHasExpired; DoubleIncrement; MoveBlock; ReturnFrom CallSwat; Block; Enqueue; Dequeue; HEnumerate; HHash // outgoing statics @gf; gatewayGoingDown // incoming statics pupRT; pbiFreeQ ndbQ; gatewayListenerSoc maxPupDataBytes ] static [ @gf; gatewayGoingDown = false ] // The following must parallel the ID and Ports in a Pup structure IDAndPorts: [ id word 2; dPort @Port; sPort @Port ] manifest lenIDAndPorts = size IDAndPorts/16 //---------------------------------------------------------------------------- let ForwarderCtx() be //---------------------------------------------------------------------------- [ Block() while gf>>GF.tQ.head ne 0 do [ let pbi = Dequeue(lv gf>>GF.tQ) pbi>>PBI.ndb>>NDB.numGPBI = pbi>>PBI.ndb>>NDB.numGPBI +1 ReleasePBI(pbi) ] // time to broadcast routing information? if (TimerHasExpired(lv gf>>GF.routeBcstTimer) % pupRT>>RT.changed) & // only if my IQ is empty, else we may exceed its allocation! gatewayListenerSoc>>PupSoc.iQ.head eq 0 then [ let ok = SendRouteInfo(0) pupRT>>RT.changed = false // if failed to send (all of) route info because we ran out of // packet buffers, try again after a relatively short interval (10 sec). SetTimer(lv gf>>GF.routeBcstTimer, (ok? routeBcstInterval, 1000)) ] // are there any packets to forward? let pbi = Dequeue(lv gf>>GF.iQ); if pbi eq 0 return // Pup destination and source nets let dNet, sNet = pbi>>PBI.pup.dPort.net, pbi>>PBI.pup.sPort.net let psn = pbi>>PBI.ndb>>NDB.localNet //Physical source net test dNet eq 0 % sNet eq 0 % //dest or source net unspecified? (pbi>>PBI.pup.dPort.host eq 0 & dNet eq pbi>>PBI.ndb>>NDB.localNet) ifso ReleasePBI(pbi) //don't forward ifnot [ let v = pbi!(offset PBI.pup.transport/16) pbi>>PBI.pup.hopCnt = pbi>>PBI.pup.hopCnt+1 test pbi>>PBI.pup.hopCnt eq 0 ifso PupError(pbi, 1004b, "Discarded by 16th gateway") ifnot [ UpdatePupChecksum(lv pbi>>PBI.pup, offset Pup.transport/16, v) pbi>>PBI.queue = lv gf>>GF.tQ //where to queue when done let pdh = RoutePup(pbi) //try to route it let ndb = pbi>>PBI.ndb test ndb ne 0 //do we know how to route it? ifso test ndb>>NDB.numGPBI gr 0 //is there room? ifnot PupError(pbi, 1007b, "Gateway OQ overflow") ifso [ ndb>>NDB.numGPBI = ndb>>NDB.numGPBI -1 (ndb>>NDB.encapsulatePup)(pbi, pdh) (ndb>>NDB.level0Transmit)(pbi) TMIncrement(psn, ndb>>NDB.localNet) loop ] ifnot [ //don't know how to route to dNet LocateNet(dNet) //initiate a probe ReleasePBI(pbi) //discard ] ] ] TMIncrement(psn, 0) //increment discarded count for psn ] repeat //---------------------------------------------------------------------------- and Forwarder(pbi) be //---------------------------------------------------------------------------- // PupRoute passes on PBIs (from Socket 2) which it doesn't handle. [ ExchangePorts(pbi) // do this here rather than inside each case switchon pbi>>PBI.pup.type into [ case ptRouteRequest: [ //don't answer probes from networks whose identity we don't know if pbi>>PBI.pup.dPort.net ne 0 then [ let idAndPorts = vec lenIDAndPorts MoveBlock(idAndPorts, lv pbi>>PBI.pup.id, lenIDAndPorts) ReleasePBI(pbi) SendRouteInfo(idAndPorts) DoubleIncrement(lv gf>>GF.stats.routeReqs) return ] endcase ] case ptStatsRequest: [ let stats = lv pbi>>PBI.pup.words MoveBlock(lv pbi>>PBI.pup.words, lv gf>>GF.stats, lenStats) let ptr = lenStats +1 let ndb = ndbQ!0; while ndb ne 0 do [ //skip nets whose identity we don't know if ndb>>NDB.localNet ne 0 then [ stats>>Stats.numNets = stats>>Stats.numNets +1 pbi>>PBI.pup.words↑ptr = ndb>>NDB.localNet ptr = ptr +1 ] ndb = ndb>>NDB.link ] //if anybody else needs TMEnumerate, rewrite this procedure for i = 0 to (1 lshift gf>>GF.tm>>TM.logSize)-1 do if gf>>GF.tm>>TM.TME↑i.sdNet ne -1 then [ if (ptr+lenTME-1) gr (maxPupDataBytes rshift 1) break stats>>Stats.numTMEs = stats>>Stats.numTMEs +1 MoveBlock(lv pbi>>PBI.pup.words↑ptr, lv gf>>GF.tm>>TM.TME↑i.sdNet, lenTME) ptr = ptr + lenTME ] CompletePup(pbi, ptStatsReply, pupOvBytes + (ptr-1) lshift 1) return ] ] ReleasePBI(pbi) ] //---------------------------------------------------------------------------- and SendRouteInfo(idAndPorts) = valof //---------------------------------------------------------------------------- // Send routing info Pup(s) as specified by idAndPorts, or broadcast // to all nets if idAndPorts=0. // Returns true normally, false if failed due to running out of PBIs. [ // pbi must be the word after idAndPorts in the frame let pbi = InitRouteInfoPup(idAndPorts) if pbi ne 0 then [ HEnumerate(pupRT, PerRTE, lv idAndPorts) CompletePup(pbi) ] resultis pbi ne 0 ] //---------------------------------------------------------------------------- and PerRTE(rte, lvIDAndPorts) be //---------------------------------------------------------------------------- [ // repeat if rte>>RTE.net ne 0 then [ let pbi = lvIDAndPorts!1 let c = pbi>>PBI.pup.length - pupOvBytes if c gr maxRouteInfoDataBytes-4 then [ CompletePup(pbi) lvIDAndPorts!1 = InitRouteInfoPup(lvIDAndPorts!0) if lvIDAndPorts!1 eq 0 then ReturnFrom(SendRouteInfo, false) loop ] pbi>>PBI.pup.bytes↑(c+1) = rte>>RTE.net pbi>>PBI.pup.bytes↑(c+2) = rte>>RTE.ndb>>NDB.localNet pbi>>PBI.pup.bytes↑(c+3) = rte>>RTE.host // If we are about to go down, say that we are a terrible route to anywhere pbi>>PBI.pup.bytes↑(c+4) = gatewayGoingDown? maxHops+1, rte>>RTE.hops pbi>>PBI.pup.length = pbi>>PBI.pup.length+4 ] return ] repeat //---------------------------------------------------------------------------- and InitRouteInfoPup(idAndPorts) = valof //---------------------------------------------------------------------------- [ let pbi = GetPBI(gatewayListenerSoc, true) if pbi ne 0 then [ pbi>>PBI.pup.type = ptRouteReply pbi>>PBI.pup.length = pupOvBytes test idAndPorts eq 0 ifso [ pbi>>PBI.allNets = true; pbi>>PBI.bypassZeroNet = true ] ifnot MoveBlock(lv pbi>>PBI.pup.id, idAndPorts, lenIDAndPorts) ] resultis pbi ] //---------------------------------------------------------------------------- and UpdatePupChecksum(pup, index, oldValue) be //---------------------------------------------------------------------------- // Update Pup checksum to account for changing one word in the Pup // Typical call (changing Transport byte): // let oldWord = pup!(offset Pup.transport/16) // pup>>Pup.transport = newValue // UpdatePupChecksum(pup, offset Pup.transport/16, oldWord) [ let len = (pup>>Pup.length-1) rshift 1 if pup!len ne -1 then pup!len = OnesComplementAdd(pup!len, LeftCycle(OnesComplementSubtract(pup!index, oldValue), len-index)) ] //---------------------------------------------------------------------------- and TMLookup(tm, sNet, dNet, findFree; numargs na) = valof //---------------------------------------------------------------------------- [ let key = sNet lshift 8 + dNet let iProbe = nil let increment = HHash(tm>>TM.logSize, key, lv iProbe) let probe = iProbe [ let tme = lv tm>>TM.TME↑probe if tme>>TME.sdNet eq key then resultis tme if tme>>TME.sdNet eq -1 then resultis na ge 4 & findFree? tme, 0 //fail probe = (probe+increment) & ((1 lshift tm>>TM.logSize)-1) ] repeatuntil probe eq iProbe resultis 0 //fail. Searched the whole table ] //---------------------------------------------------------------------------- and TMIncrement(sNet, dNet) be //---------------------------------------------------------------------------- // Looks up sNet,,dNet in the transit matrix hash table. // Creates an entry if not found then increments the count. [ let tme = TMLookup(gf>>GF.tm, sNet, dNet, true) if tme eq 0 then return // table full! if tme>>TME.sdNet eq -1 then // empty tme tme>>TME.sdNet = sNet lshift 8 + dNet DoubleIncrement(lv tme>>TME.count) ]