// Pup1b.bcpl -- Pup level 1 // This module contains 'main line' code. // Copyright Xerox Corporation 1979, 1981, 1982 // Last modified September 27, 1982 12:40 PM by Taft get "pup0.decl" get "pup1.decl" external [ // outgoing procedures PupLevel1; CompletePup; RoutePup; SocketNotFound GetPBI; ReleasePBI; PupError; ExchangePorts SetPupSPort; SetPupDPort; SetPupID // incoming procedures AppendStringToPup; BroadcastNextNet Zero; MoveBlock; CallSwat; SysErr Enqueue; Dequeue; Block; SetTimer; TimerHasExpired PupChecksum; MultEq; HInsert; HLookup; LocateNet // outgoing statics ndbQ; pbiFreeQ; pbiIQ; pbiTQ; socketQ; gatewayIQ; dPSIB; numNets pupZone; pupCtxQ; lenPBI; lenPup; maxPupDataBytes; pupLevel1Ctx // incoming statics pupRT ] static //level 1 statics [ ndbQ; pbiFreeQ; pbiIQ; pbiTQ; socketQ; gatewayIQ; dPSIB; numNets pupZone; pupCtxQ; lenPBI; lenPup; maxPupDataBytes; pupLevel1Ctx ] compileif pupDebug then [ static [ lastFreeQTail; freeQTimer ]] //---------------------------------------------------------------------------- let PupLevel1() be //---------------------------------------------------------------------------- // process to distribute input Pups and release used output pbis. [PupLevel1 let pbi = nil [ compileif pupDebug then [ //for debugging, test for lockup due to free queue empty test pbiFreeQ!0 eq 0 ifnot lastFreeQTail = 0 ifso test pbiFreeQ!1 eq lastFreeQTail ifnot [ SetTimer(lv freeQTimer, 2000) //20 seconds lastFreeQTail = pbiFreeQ!1 ] ifso if TimerHasExpired(lv freeQTimer) then CallSwat("pbiFreeQ empty for 20 sec. - deadlock?") ] while pbiTQ!0 ne 0 do [ pbi = Dequeue(pbiTQ) compileif multipleNets then [ if pbi>>PBI.allNets then [ //handle pbi going out on all nets BroadcastNextNet(pbi, pbi>>PBI.ndb>>NDB.link) loop ] ] ReleasePBI(pbi) ] pbi = Dequeue(pbiIQ) if pbi ne 0 then break Block() ] repeat // We now have an input pbi to process. pbi>>PBI.queue = pbiTQ //in case pbi is re-used for output pbi>>PBI.socket = 0 //pbi not yet assigned to a socket pbi>>PBI.status = 0 //in particular, allNets = false // Don't ever allow a Pup from host zero to be processed, because // if we turn it around it will become a broadcast! if pbi>>PBI.pup.sPort.host eq 0 then [ ReleasePBI(pbi); loop ] // still in PupLevel1 // PupLevel1 (cont'd) // If the destination net is different from that on which the pbi arrived then // if we know the identity of the net on which the pbi arrived // then if we have a valid RT entry for the Pup destination net // then if we are directly connected to that net & // (it isn't a broadcast % its immed. src net ne dest net) // then setup ndb so that we later compare the destination // host with our local host address on that net // else it can't be for us so forward it via gateway // else we don't know how it got here so discard // else assume it arrived on the correct destination net let dNet = pbi>>PBI.pup.dPort.net let dHost = pbi>>PBI.pup.dPort.host let ndb = pbi>>PBI.ndb //ndb for net it came in on if dNet ne ndb>>NDB.localNet then if dNet ne 0 & ndb>>NDB.localNet ne 0 then [ let rte = HLookup(pupRT, dNet) //rte for destination net test rte ne 0 ifso test rte>>RTE.hops eq 0 & (multipleNets? (dHost ne 0 % ndb eq rte>>RTE.ndb), true) ifso ndb = rte>>RTE.ndb ifnot [ Enqueue(gatewayIQ, pbi); loop ] ifnot //routing error. Don't know why it got to us. [ PupError(pbi, #1002, "Can't get there from here", true); loop ] ] // Now we know the dest net is one to which we are connected. // ndb points to the NDB for it. unless dHost eq ndb>>NDB.localHost % dHost eq 0 do [ Enqueue(gatewayIQ, pbi); loop ] // pbi is for us. Search local socket queue for recipient. let soc = socketQ!0 [ if soc eq 0 then [ SocketNotFound(pbi); break ] if pbi>>PBI.pup.dPort.socket↑1 eq soc>>PupSoc.lclPort.socket↑1 & pbi>>PBI.pup.dPort.socket↑2 eq soc>>PupSoc.lclPort.socket↑2 then [ // socket found if soc>>PupSoc.doChecksum then //check Pup checksum [ let c = pbi!(lenPBIOverhead+(pbi>>PBI.pup.length-1) rshift 1) unless c eq -1 % c eq PupChecksum(lv pbi>>PBI.pup) do [ PupError(pbi, 1, "Bad Checksum"); break ] ] // if we don't know the identity of the net on which the // packet arrived, but it was addressed to a specific // net, and to an existing socket in this host, // then believe the destination net number in the packet // and establish the identity of the connected net in the RT. if ndb>>NDB.localNet eq 0 & dNet ne 0 then [ pbi>>PBI.ndb>>NDB.localNet = dNet let rte = HInsert(pupRT, dNet) //zeroes hop count rte>>RTE.ndb = ndb rte>>RTE.host = ndb>>NDB.localHost ] FillInNets(pbi) // default zero source and dest net numbers if soc>>PupSoc.numIPBI eq 0 % soc>>PupSoc.numTPBI eq 0 then [ PupError(pbi, 3, "Port IQ full"); break ] soc>>PupSoc.iAll = soc>>PupSoc.iAll -1 // see comment in GetPBI soc>>PupSoc.tAll = soc>>PupSoc.tAll -1 pbi>>PBI.usage = true // this is an input packet pbi>>PBI.socket = soc Enqueue(lv soc>>PupSoc.iQ, pbi) break ] soc = soc!0 ] repeat ]PupLevel1 repeat //---------------------------------------------------------------------------- and SocketNotFound(pbi) be // default handling for unwanted pbi's //---------------------------------------------------------------------------- PupError(pbi, 2, "No such port") //---------------------------------------------------------------------------- and FillInNets(pbi) be //---------------------------------------------------------------------------- [ if pbi>>PBI.pup.dPort.net eq 0 then pbi>>PBI.pup.dPort.net = pbi>>PBI.ndb>>NDB.localNet if pbi>>PBI.pup.sPort.net eq 0 then pbi>>PBI.pup.sPort.net = pbi>>PBI.ndb>>NDB.localNet ] //---------------------------------------------------------------------------- and PupError(pbi, subtype, string, setSourcePort; numargs na) be //---------------------------------------------------------------------------- [ manifest numHeaderWords = (offset Pup.words)/16 test pbi>>PBI.pup.type eq typeError % // incoming type Error? pbi>>PBI.pup.dPort.host eq 0 % // broadcast? pbi>>PBI.pup.dPort.socket↑1 eq socTransient // to transient socket? ifso ReleasePBI(pbi) // yes, forget it ifnot [ MoveBlock(lv pbi>>PBI.pup.words↑1,lv pbi>>PBI.pup, numHeaderWords) FillInNets(pbi) pbi>>PBI.pup.words↑(numHeaderWords+1) = subtype pbi>>PBI.pup.words↑(numHeaderWords+2) = 0 AppendStringToPup(pbi, 2*(numHeaderWords+2)+1, string) ExchangePorts(pbi) if na ge 4 & setSourcePort then [ pbi>>PBI.pup.sPort.net = pbi>>PBI.ndb>>NDB.localNet pbi>>PBI.pup.sPort.host = pbi>>PBI.ndb>>NDB.localHost pbi>>PBI.pup.sPort.socket↑1 = 0 pbi>>PBI.pup.sPort.socket↑2 = 0 ] pbi>>PBI.queue = pbiTQ CompletePup(pbi, typeError) ] ] //---------------------------------------------------------------------------- and CompletePup(pbi, type, length; numargs na) be //---------------------------------------------------------------------------- [ if na ge 2 then pbi>>PBI.pup.type = type if na ge 3 then pbi>>PBI.pup.length = length pbi>>PBI.pup.transport = 0 let soc = pbi>>PBI.socket if soc ne 0 then // error and trace pups have no lcl socket [ // default any zero address fields in header DefaultPort(lv pbi>>PBI.pup.dPort, lv soc>>PupSoc.frnPort) DefaultPort(lv pbi>>PBI.pup.sPort, lv soc>>PupSoc.lclPort) compileif pupDebug then [ if pbi>>PBI.pup.sPort.socket↑1 eq 0 & pbi>>PBI.pup.sPort.socket↑2 eq 0 then CallSwat("[CompletePup] Zero source socket") ] ] // Check for request to broadcast on all directly-connected networks if pbi>>PBI.allNets then [ compiletest multipleNets ifso [ BroadcastNextNet(pbi, ndbQ!0); return ] ifnot [ // if bypassZeroNet, discard if net's identity is not known if (ndbQ!0)>>NDB.localNet eq 0 & pbi>>PBI.bypassZeroNet then [ ReleasePBI(pbi); return ] ] ] // Set Pup checksum pbi!(lenPBIOverhead + (pbi>>PBI.pup.length-1) rshift 1) = soc eq 0 % soc>>PupSoc.doChecksum? PupChecksum(lv pbi>>PBI.pup), -1 // Route, encapsulate, and transmit let pdh = RoutePup(pbi) test pbi>>PBI.ndb ne 0 ifso [ // destination net is in RT. Encapsulate and transmit (pbi>>PBI.ndb>>NDB.encapsulatePup)(pbi, pdh) (pbi>>PBI.ndb>>NDB.level0Transmit)(pbi) ] ifnot [ // destination net not in RT. Dispose of pbi as if it had // been transmitted, and initiate a probe to locate the net LocateNet(pbi>>PBI.pup.dPort.net) Enqueue(pbi>>PBI.queue, pbi) ] ] //---------------------------------------------------------------------------- and RoutePup(pbi) = valof //---------------------------------------------------------------------------- // Sets ndb pointer in pbi, 0 if can't route there // Returns physical destination host [ compileif pupDebug then [ if pbi>>PBI.pup.sPort.host eq 0 then CallSwat("[RoutePup] Zero source host") ] let rte = HLookup(pupRT, pbi>>PBI.pup.dPort.net) pbi>>PBI.ndb = rte ne 0 & rte>>RTE.hops le maxHops ? rte>>RTE.ndb, 0 resultis rte>>RTE.hops eq 0? pbi>>PBI.pup.dPort.host, rte>>RTE.host ] //---------------------------------------------------------------------------- and DefaultPort(pupPort, socPort) be //---------------------------------------------------------------------------- [ if pupPort>>Port.net eq 0 then [ // if port in socket refers to net zero but to a specific // host, then update the net number in the socket with // the actual net number of the default NDB, assuming that // is now known. if socPort>>Port.net eq 0 & socPort>>Port.host ne 0 then socPort>>Port.net = (ndbQ!0)>>NDB.localNet pupPort>>Port.net = socPort>>Port.net ] if pupPort>>Port.host eq 0 then pupPort>>Port.host = socPort>>Port.host if pupPort>>Port.socket↑1 eq 0 & pupPort>>Port.socket↑2 eq 0 then [ pupPort>>Port.socket↑1 = socPort>>Port.socket↑1 pupPort>>Port.socket↑2 = socPort>>Port.socket↑2 ] ] //---------------------------------------------------------------------------- and ExchangePorts(pbi) be //---------------------------------------------------------------------------- // Exchange source and destination ports. [ let d = lv pbi>>PBI.pup.dPort let s = lv pbi>>PBI.pup.sPort for i = 0 to lenPort-1 do [ let temp = d!i; d!i = s!i; s!i = temp ] // If the original pup had a destination net of zero, fill in the actual net // number (if we know it) so the reply will appear to arise from the // net the original pup arrived on. // If the original pup was broadcast, when the exchange is done // sPort.host will be zero. CompletePup will default this to // the lclPort.host in the level1 socket, which may not be the // correct host corresponding to sPort.net if this host has multiple // nets, so force it by setting sPort.host here. compileif multipleNets then [ if pbi>>PBI.pup.sPort.net eq 0 then pbi>>PBI.pup.sPort.net = pbi>>PBI.ndb>>NDB.localNet if pbi>>PBI.pup.sPort.host eq 0 then // the source net here was the destination before the swap, so // it is guaranteed to be in the routing table. pbi>>PBI.pup.sPort.host = HLookup(pupRT, pbi>>PBI.pup.sPort.net)>>RTE.ndb>>NDB.localHost ] ] //---------------------------------------------------------------------------- and SetPupDPort(pbi, port) be //---------------------------------------------------------------------------- MoveBlock(lv pbi>>PBI.pup.dPort, port, lenPort) //---------------------------------------------------------------------------- and SetPupSPort(pbi, port) be //---------------------------------------------------------------------------- MoveBlock(lv pbi>>PBI.pup.sPort, port, lenPort) //---------------------------------------------------------------------------- and SetPupID(pbi, pupID) be //---------------------------------------------------------------------------- [ pbi>>PBI.pup.id↑1 = pupID!0 pbi>>PBI.pup.id↑2 = pupID!1 ] //---------------------------------------------------------------------------- and GetPBI(soc, returnOnFail; numargs na) = valof //---------------------------------------------------------------------------- // Gets an OUTPUT pbi charged to the socket. The Pup header is cleared. // If returnOnFail is true, return zero on failure; // if false or omitted, block until a pbi is available. [ let pbi = nil [ if soc>>PupSoc.numTPBI gr 0 & soc>>PupSoc.numOPBI gr 0 then [ pbi = Dequeue(pbiFreeQ) if pbi ne 0 then break ] if na ge 2 & returnOnFail then resultis 0 Block() ] repeat // Adjust socket allocations. // Do full-word arithmetic because structure references are expensive. // Know that numTPBI and numOPBI are the right-hand bytes of the words // and that the computation will not underflow. soc>>PupSoc.tAll = soc>>PupSoc.tAll-1 soc>>PupSoc.oAll = soc>>PupSoc.oAll-1 Zero(pbi, lenPBIOverhead+pupOvWords) // clear pbi and pup header pbi>>PBI.socket = soc pbi>>PBI.queue = pbiTQ // default disposition after output done resultis pbi ] //---------------------------------------------------------------------------- and ReleasePBI(pbi) be //---------------------------------------------------------------------------- [ let soc = pbi>>PBI.socket if soc ne 0 then [ soc>>PupSoc.tAll = soc>>PupSoc.tAll+1 test pbi>>PBI.usage // input or output PBI? ifso soc>>PupSoc.iAll = soc>>PupSoc.iAll+1 ifnot soc>>PupSoc.oAll = soc>>PupSoc.oAll+1 ] Enqueue(pbiFreeQ, pbi) ]