// 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)
]