perm filename TCPSER.MA1[IP,NET] blob
sn#702332 filedate 1983-03-05 generic text, type C, neo UTF8
COMMENT ⊗ VALID 00074 PAGES
C REC PAGE DESCRIPTION
C00001 00001
C00010 00002 title TCPSer VTCPSr
C00011 00003 \
C00012 00004 subttl TCP states S%Clos S%List S%SynS S%SyRP S%SyRA S%Estb S%Fin1 S%Fin2 S%Clsn S%TimW S%ClsW S%LAck
C00014 00005 subttl macro for dispatching on different states Dispat ...min ...max ...flg ...x ...flg ...x
C00018 00006 subttl defintions describing a TCP leader TcpLen TCPIBH TCPIBf TCPObf TCPPnt TCPFlg TC%Urg TC%Ack TC%Psh TC%Rst TC%Syn TC%Fin TC%Low TC%ALL TC%Onc
C00021 00007 subttl definitions TC$ACK WndSiz StartT TCPUTT TCPRTT RTMin RTMax
C00022 00008 subttl FMB
C00025 00009 subttl process incoming TCP message TCPIn TCPIn0 TCPNCk RedCnt NewLst
C00032 00010 subttl now parse options OptnLp OptDis OptMax OptSeg OptDun NoOptn
C00035 00011 here to process the message with DDB in tow. FuturL NoFutr
C00039 00012 subttl process a connection which has no DDB NewCon NotExc
C00043 00013 ROUTINE TO CHECK LEGALITY OF AN EXEC Well Known Port. WKPFND WKPFN1 WKPSRV WKPNUM WKPSKT WKPTFC
C00045 00014 here to process one message. this may be hot off the presses or it PrcMsg
C00048 00015 here to check for the segment starting in the window WndFit
C00051 00016 here to check for a segment finishing in the window WndEnd FlsLp FlsBad
C00054 00017 at this point we have a message which starts and ends within InWind IsNext
C00057 00018 deal with the urgent pointer, if there is one TCPUrg
C00059 00019 message chain is in P1. left half: first buffer, right half: last buffer. TCPTxt TCPTx1
C00061 00020 here to check for a FIN and handle it TCPFin TCPFi0 TCPFi1
C00064 00021 FINF2: movei t1,2*MSL load up twice maximum segment life FINF2 FINTW
C00066 00022 here if we received a segment for a connection that doesn't exist TryRst RstFls SndRst
C00068 00023 here if we received a segment while listening for one InLstn AckAck AckAc1
C00073 00024 here if received a segment for a connection in SYN-SENT state InSynS InSyn1 InSyn2
C00077 00025
C00078 00026 subttl returns NoLead NoMess BadChk FlsOpt NoPort NoDDB NoITY AckErr UrgErr FinErr SeqBad BufFls FlsRst RstSRP RstBTL RstSRA RstEst RstCls FlsSyn
C00083 00027 subttl routines to handle an ACK in various states ACKSyR ACKEst AckEs1 ACKF1 ACKCln ACKLAc ACKTW
C00089 00028 subttl AckUpd ACKUpd NoAllc RetrLp RetrD0 RetrDn RetrNo
C00095 00029 subttl deal with a message received before it should be NotNxt FtrOrd FtrOr1 FtrRpl FtrOr2 FtrNew FtrSav
C00099 00030 routine to get an FMB, return it in T1 GetFMB RelFMB FlsFMB FlsFM1
C00101 00031 subttl GetISS GetISS
C00102 00032 subttl SecChk SecChk
C00103 00033 subttl TCPMak TCPMak TCPMa1 MakCnt
C00110 00034 subttl TCPRsp TCPRsp
C00113 00035 subttl TCPIFn TCPIFn TCPIF1
C00116 00036 subttl TCPICK TCPICK
C00118 00037 subttl TCPOCK TCPOCK
C00120 00038 subttl TCPTCk TCPTCk
C00122 00039 subttl TCPWUp TCPWUp
C00124 00040 subttl SetUrg SetUrg
C00126 00041 subttl TcpChk TCPChk TCPCRT TCPRTR RtLoop RtNxt TCPCUT TCPUTO
C00132 00042 USER INTERFACE (IMPUUO) CALL FLAGS IF.NWT IF.PRV IF.ALS TIMEOUT E .UUDEV .UUSTT .UUSKT .UUHST .UURMT .UULST PUUTIM sk.lcl FreOvr FreLsh FrePrt FreMin FreMch
C00135 00043 NOWAITS< IMPUUO IMPUU1 ImpUU2 ImpUU3 ImpUU4
C00139 00044 MACRO FOR BUILDING THE DISPATCH TABLE ZZ ZZ UU.PVN UU.PVI UU.ASD UU.NDB UU.INT UU.DNU uu.NUp
C00141 00045 THE DISPATCH TABLE UUOTAB UUOLEN
C00143 00046 NOWAITS< It appears this code is never called. ERRCHK
C00144 00047 DEFINE ERRCOD(M,C) < ERRLST ERRXIT ErrSet
C00149 00048 NOWAITS< TRANS RAISS KILLS VERSS INISS IFRSTR
C00151 00049 NOWAITS<
C00153 00050 XSTSS: PUSHJ P,GETWDU## RETURN NUMBER OF USER ARGS XSTSS XSTSS0 XSTSS1 XSTSTB XSTBLN
C00156 00051 NOWAITS< STATS STATS0 STATS9 STATS1
C00158 00052 NOWAITS< Perhaps WAITS should have something like this ITTYS ITTYS1 ITTYS2
C00161 00053 HERE WITH DESIRED IMP DDB POINTED TO BY F ITTYS3
C00162 00054 repeat 0,< should be simple PIALS
C00164 00055 ROUTINE TO WAIT UNTIL THE CONNECTION BETWEEN A LOCAL TTY AND XPWTS PPARS RPARS
C00167 00056 ROUTINES TO SET AND READ THE VARIOUS QUOTE AND ESCAPE CHARACTERS PESCS RESCS ALLQUO ALLQU1
C00170 00057 SUBROUTINE FOR SETTING UP A SIMPLEX CONNECTION. CONNS GotArp
C00174 00058 SUBROUTINE TO DROP A CONNECTION. CLOSS PCLSSD ClsFls ClsEst
C00177 00059 SUBROUTINE TO DEASSIGN A DEVICE AFTER IT HAS HAD DEASS
C00178 00060 SUBROUTINE TO PUT A SOCKET IN THE LISTENING STATE LISTS Lists2 LISTS1
C00181 00061 SUBROUTINE TO GET A SOCKET REQUEST REQUS PW1PJ1
C00183 00062 SUBROUTINE TO CONNECT A DUPLEX IMP CONNECTION TO TALKS TalkOK
C00185 00063 repeat 0,< might be fun to do sometime..... TRACS TRCTAB PINTS AINTS PECOS
C00189 00064 SUBROUTINE TO RETURN THE LOCAL HOST AND IMP PARAMETERS PHSTS PWUPJ1
C00191 00065 REPEAT 0,< THESE FUNCTIONS WERE NEVER DEBUGGED PALLS PALLS1 PGVBS RSETS PNOPS PNOPS1 GETHS1
C00194 00066 NOWAITS< WAITS calls T10DDB to do some of this. SETDDB SETDD3 SETDD0 SETDD2 SETDD1 SetDD4
C00198 00067 subroutine to set up essential areas of a DDB PrpDDB
C00200 00068 ROUTINE TO MAKE A LOCAL SOCKET NUMBER FOR A USER'S IMPUUO. MAKMYS MAKMY0 MakM00 MakMy1 MakNxt MakFre
C00206 00069 ROUTINE TO ALLOCATE A FREE SOCKET RANGE FRESKT FRESK1
C00208 00070 subroutine to decide where to send a message on the ARPAnet to get Target
C00209 00071 subroutine to wait for state to arrive at an established state. EstbWt EstbCl EstbSF EstbTm EstbEr EstbFl EstbW1
C00213 00072 SUBROUTINE TO WAIT FOR NCP ACTIVITY. StWait StWai1 StWai2
C00215 00073 SUBROUTINE TO SET TCP state WAIT DONE. CALLED AT INTERRUPT LEVEL. TCPIOD
C00216 00074 $low SktNum
C00217 ENDMK
C⊗;
title TCPSer ; VTCPSr ;⊗
subttl provan
search f,s
search NetDef ; network definitions
search MacTen ; search only if symbol not found in NetDef
; MacTen needed for movx, txo, etc.
sall
$reloc
$high
XP VTCPSr,1 ; first TCP version
comment \
this module contains the support routines for the transmission
control protocol as defined in RFC-793
\
subttl TCP states ;⊗ S%Clos S%List S%SynS S%SyRP S%SyRA S%Estb S%Fin1 S%Fin2 S%Clsn S%TimW S%ClsW S%LAck
; first define the states we have for TCP
S%Clos==↑d0 ;; closed (sometimes convenient, although usually
;; detected by absense of DDB)
;; must ALWAYS be zero. "closed" type states are
;; less than or equal to zero.
S%List==↑d1 ;; listen
S%SynS==↑d2 ;; SYN sent
S%SyRP==↑d3 ;; SYN received, passive
S%SyRA==↑d4 ;; SYN received, active (from S%SynS)
S%Estb==↑d5 ;; established
S%Fin1==↑d6 ;; FIN wait 1
S%Fin2==↑d7 ;; FIN wait 2
S%Clsn==↑d8 ;; Closing
S%TimW==↑d9 ;; time wait
S%ClsW==↑d10 ;; Close wait
S%LAck==↑d11 ;; last ACK
subttl macro for dispatching on different states ; Dispat ;⊗ ...min ...max ...flg ...x ...flg ...x
; now define a macro to define a dispatch vector. there are three
; arguments. the first is the register containing the state code.
; the second is the location to jump to if a state comes which
; is not defined in this table. the third is a list of pairs of
; entries: the state code and the instruction to execute.
;warning: the state pairs MUST begin on the same line as the second
; argument. state pairs MUST be separated from each other with
; commas (between each pair) which MUST be on the same line as
; the macro which FOLLOWs.
define Dispat (AC,ErrLoc,StPair),
<
...min==777777 ;; a high starting point
...max==-1 ;; and a low one
define Pair (state,instr),
<
ifl <state>-...min,< ...min==<state> >
ifg <state>-...max,< ...max==<state> >
>
define $$help(bogus),< pair(bogus) > ;; your classic helper macro
irp StPair,< ;; for each pair
$$help(StPair) ;; expand the Pair macro with each pair
;; the as arguments.
>
;; code to check to see if the state is in the legal range
cail <AC>,...min ;; less than the lowest we know
caile <AC>,...max ;; or greater than the highest
jrst <ErrLoc> ;; go to the error handler
define Pair (state,instr),
<
ife ...x-<state>,< ;; is this our state?
instr ;; expand the instruction
...flg==1 ;; and tell that we did something
>
>
;; code for the actual dispatching
xct [
...x==...min ;; start with lowest state
repeat ...max-...min+1,< ;; do every state in the range
...flg==0 ;; nobody's claimed this spot yet
irp StPair,< ;; go through all the pairs
$$help(StPair) ;; expanding the Pair macro with each.
>
ife ...flg,< ;; if no one claimed to be this
jrst <ErrLoc>;; go to the error handler
>
...x==...x+1 ;; next place.
>
]-...min(<AC>) ;; now correctly index the XCT
purge ...min,...max,...x,...flg
> ;; end of Dispat macro definition
subttl defintions describing a TCP leader ;⊗ TcpLen TCPIBH TCPIBf TCPObf TCPPnt TCPFlg TC%Urg TC%Ack TC%Psh TC%Rst TC%Syn TC%Fin TC%Low TC%ALL TC%Onc
; see RFC-793 for details of this header.
TcpLen==:5 ; number ofwords in an TCP leader (not including options)
$low ; define the storage needed
TCPIBH: block NBHLen ; buffer header.
TCPIBf: block TCPLen ; words needed for header
; the following block is used to create a TCP leader for output.
; it is filled and then converted to 36 bit buffers all under ScnOff.
TCPObf: block NBHLen+TCPLen ; output buffer for forming leader
$high ; back to protected code
TCPPnt: point 8,TCPIBf ; pointer to start loading the
; header block from the stream.
; define the actual header fields. position is the bit position of the
; left most bit.
;
; name word position width
; TCP uses the standard ports, StdSP and StdDP.
;DefFd. TCPSP, 0, 0, 16 ; source port of message
;DefFd. TCPDP, 0, 16, 16 ; destination port
DefFd. TCPSeq, 1, 0, 32 ; sequence number
DefFd. TCPAck, 2, 0, 32 ; acknowledgement number
DefFd. TCPOff, 3, 0, 4 ; data offset from start of leader
; (length of total leader in words)
TCPFlg==3 ; flags are in the third word
TC%Urg==1b<↑d10> ; urgent flag
TC%Ack==1b<↑d11> ; acknowledge flag
TC%Psh==1b<↑d12> ; push flag
TC%Rst==1b<↑d13> ; reset flag
TC%Syn==1b<↑d14> ; syncronize sequence numbers
TC%Fin==1b<↑d15> ; finished
TC%Low==TC%Fin ; low order bit of group
TC%ALL==TC%Urg!TC%Ack!TC%Psh!TC%Rst!TC%Syn!TC%Fin
; bits which must be manually set each time they need to be sent.
TC%Onc==TC%Urg!TC%Psh!TC%Rst!TC%Syn!TC%Fin
DefFd. TCPWnd, 3, 16, 16 ; window allocated
DefFd. TCPChk, 4, 0, 16 ; checksum of message
DefFd. TCPUP, 4, 16, 16 ; urgent pointer
subttl definitions ;⊗ TC$ACK WndSiz StartT TCPUTT TCPRTT RTMin RTMax
; flags in S during input
TC$ACK==1b<↑d35> ; send an ACK at end of processing
; standard allocations and time-out times
WndSiz==20*NBfByt ; number of bytes is normal window
StartT==↑d60 ; time we'll wait for server
; process to start up.
TCPUTT==2*↑d60 ; time to wait before declaring
; a connection dead in the water.
TCPRTT==↑d5 ; retransmission time (in seconds)
RTMin==1 ; minimum retranmission time (1 sec.)
RTMax==↑d60 ; maximum retranmission time (1 min.)
subttl FMB ;⊗
; the FMB, Future Message Block, is a block of information about a
; message who's sequence number we are not ready to handle yet.
; this block contains all the information necessary to process
; the message, including the complete TCP header for this message
; and a pointer to buffers containing the message itself.
;;!------------------------------------|------------------------------------!
;;! !
;;! !
;;! TCP header for this message (5 words) !
;;! !
;;! !
;;!------------------------------------|------------------------------------!
;;! first buffer in message chain | last buffer in message chain !
;;!------------------------------------|------------------------------------!
;;! pointer to next FMB in chain !
;;!------------------------------------|------------------------------------!
;;! sequence number of first byte following this message !
;;!------------------------------------|------------------------------------!
bkini. ; have to start somewhere
bknxt. FMBTCL,TCPLen*ful.wd ; space TCP header
bkoff. FMBTCP ; grab offset into block for start
; of that field.
bkdef. FMBPnt ; buffer pointer (whole word)
bknxt. FMBFst,hlf.wd ; first buffer in message
bknxt. FMBLst,hlf.wd ; last buffer in message
bknxt. FMBNxt ; next buffer in chain
bkoff. FNxtOf ; grab offset, too
bknxt. FMBNBy ; sequence number of first byte
; of message which should follow.
bkend. FMBLen ; get the length
subttl process incoming TCP message ;⊗ TCPIn TCPIn0 TCPNCk RedCnt NewLst
entry TCPIn ; only load this module if IP calls this routine
TCPIn::
move p2,MsgLen(f) ; get length of message through IP
ifn FtChck,< ; doing checksum
setz p3, ; clear checksum
move t1,p2 ; make sure to checksum length
; of TCP message before we
; convert it to length of segment.
pushj p,CSmHWd## ; checksum the length.
>
subi p2,TCPLen*4 ; cut length by that amount
jumpl p2,NoLead ; not enough message to read in leader
movei t1,TCPIBH ; get pointer to input leader
move t2,ABfLst(f) ; get last buffer so far
stor. t1,NBHNxt,(t2) ; make us their next
movem t1,ABfLst(f) ; and make us last (for grins)
move t1,TCPPnt ; point at the storage block
movei t2,TCPLen*4 ; length of leader in bytes
stor. t2,NBHCnt,TCPIBH ; store in buffer header
pushj p,GetLed## ; get the leader and checksum
jrst NoLead ; not enough bytes for leader.
; now read in the options and hold for later
load. t1,TCPOff,TCPIBf ; get "offset to data"
subi t1,TCPLen ; get words left to be read in leader
jumple t1,TCPIn0 ; no options to read
lsh t1,wd2byt ; convert to bytes
sub p2,t1 ; cut down message length again
jumpl p2,NoLead ; not enough in IP message for
; TCP leader indicated.
pushj p,GetMes## ; read in the options
jrst NoLead ; message ended too soon.
aos TCPOpt## ; saw an option
TCPIn0:
exch t1,p2 ; position length of message
; and put options in a safe place.
movem t1,MsgLen(f) ; save length of TCP message
pushj p,GetMes## ; copy T1 bytes in.
jrst NoMess ; problem reading message
move p1,t1 ; save new stream pointer for later.
ifn FtChck,< ; doing checksumming
load. t1,TCPChk,TCPIBf ; get the checksum from the leader
jumpe t1,TCPNCk ; this guy doesn't do checksums
move t1,RmtAdr(f) ; get their address.
pushj p,CSmWrd## ; add in that checksum.
move t1,LclAdr(f) ; our address
pushj p,CSmWrd## ; checksum it.
move t1,Protcl(f) ; get the protocol
pushj p,CSmHWd## ; checksum that half a word
; bear in mind that the checksum we now have in P3 has, along with
; all the right stuff, its own one's complement. therefore, what
; we really have is <checksum> + -<checksum>, which is 0.
; further, since <checksum> has some bit on (otherwise the
; sender isn't checksuming and we wouldn't be here), it can be
; shown that the brand of one's complement 0 we must have is
; the version with all 1's. if that's what we have, we're ok.
; if not, the checksum failed.
hrrzs p3 ; get just the checksum
caie p3,<1←↑d16>-1 ; magic explained above
jrst BadChk ; checsum is bad.
TCPNCk: ; here to skip over the checksum checks because sender is not
; checksumming the messages.
>
; count all the bits in the flag word as message types to get some
; idea of what we're sending.
movx t1,TC%Low ; get lowest order bit
setz t2, ; and a count
RedCnt: tdne t1,TCPFlg+TCPIBf ; is that bit on in the flag word?
aos TCPITy##(t2) ; yes. count one more with
; that bit on.
lsh t1,1 ; shift bit over one
txne t1,TC%All ; bit no longer in field?
aoja t2,RedCnt ; still in flag field. count on.
move t1,RmtAdr(f) ; source (foreign host address)
load. t2,StdSP,TCPIBf ; get his port
load. t3,StdDP,TCPIBf ; get my port
move t4,Protcl(f) ; get protocol
move p3,MsgLen(f) ; put length of this message
; somewhere where we can get
; it for the new DDB.
push p,f ; save current DDB, in case we fail
pushj p,FndDDB## ; scan network DDBs for the one
; that matches.
jrst NewCon ; this is one we haven't heard of
pop p,(p) ; don't want that F any more.
NewLst: ; return here if we are now
; listening for it (exec port).
movem p3,MsgLen(f) ; remember the message length
; in the new DDB.
subttl now parse options ;⊗ OptnLp OptDis OptMax OptSeg OptDun NoOptn
jumple p2,NoOptn ; skip all this if no options
; were read in.
push p,p1 ; preserve our actual message
hlrz p1,p2 ; point at the first buffer of options
push p,p1 ; save that for later
setzb p3,s ; clear count register and flags
OptnLp: pushj p,NxtByt## ; get next option
jrst OptDun ; no more
caig t1,OptMax ; larger number than we know about?
jrst @OptDis(t1) ; no. handle it
aos TCEUOp## ; we don't understand this option.
pushj p,OptFls## ; flush the option
jrst OptDun ; all done.
jrst OptnLp ; and try the next option
; dispatch table for options
OptDis:
OptDun ; end of option list
OptnLp ; noop
OptSeg ; maximum segment size
OptMax==.-OptDis-1 ; get highest option number we know.
OptSeg: pushj p,NxtByt## ; get next byte.
jrst OptDun ; no next byte. all done.
move t4,t1 ; save count
pushj p,NxtByt## ; get first byte of length
jrst OptDun ; ran out
move t3,t1 ; save it
pushj p,NxtByt## ; get next byte
jrst OptDun ; ran out again
lsh t3,↑d8 ; shift first byte over to make room
ior t3,t1 ; or in the other byte
movem t3,SndMax(f) ; save it it the DDB
movei t1,-4(t4) ; get length back, minus parts
; we read.
pushj p,NxtFls## ; flush any that are more than
; we needed.
jrst OptDun ; nothing left in buffers
jrst OptnLp ; get next option
; here when all done reading options
OptDun:
pop p,t1 ; get the pointer to the first buffer
pushj p,RelBuf## ; release the entire stream
pop p,p1 ; recover message buffer pointer.
NoOptn: ; come here if there are no options to process
; here to process the message with DDB in tow. ;⊗ FuturL NoFutr
movei u,TCPIBf ; leader is still in the block.
setz s, ; clear flags word
pushj p,PrcMsg ; process this message
; scan the future queue for messages which can now be processed
FuturL: skipn t2,Future(f) ; get the start of the futures
jrst NoFutr ; no futures
load. t1,TCPSeq,FMBTCP(t2) ; get sequence number from header
camle t1,RcvNxt(f) ; are we ready for this one yet?
jrst NoFutr ; no. newest future is too late.
load. t1,FMBNxt,(t2) ; get this one's next pointer
movem t1,Future(f) ; now that's the next one
load. p1,FMBPnt,(t2) ; get buffer pointer back
load. t1,FMBNBy,(t2) ; get byte just past this message
caml t1,RcvNxt(f) ; did we pass the message altogether?
jrst [ ; yes. throw this one out.
pushj p,RelFMB ; get rid of the FMB
pushj p,BufFls ; release the buffers in the message
jrst FuturL ; try the next future
]
aos TCPFTU## ; count future message used
load. p2,FMBNBy,(t2) ; get the sequence number of the
; next message after this one.
load. t1,TCPSeq,FMBTCP(t2) ; get sequence number
sub p2,t1 ; compute the length of the message.
movem p2,MsgLen(f) ; remember that in the DDB
movei u,FMBTCP(t2) ; point at block with TCP leader.
push p,t2 ; save FMB so we can delete it
pushj p,PrcMsg ; process this message
pop p,t2 ; get back FMB
pushj p,RelFMB ; free FMB
jrst FuturL ; check for another future
NoFutr: txzn s,TC$ACK ; should we fire off an ACK?
popj p, ; no. return
scnoff ; shut down interrupts
skipg State(f) ; have we been closed while we
; weren't looking?
pjrst sonppj## ; yes. forget it
movx t2,TC%Ack ; get ACK bit
iorm t2,SndBts(f) ; make sure it's set.
pushj p,SndMsg## ; yes. tell IMPSER to get it sent or
; send it directly and return.
jfcl ; ignore error return
pjrst sonppj## ; interrupts back on and return
subttl process a connection which has no DDB ;⊗ NewCon NotExc
; handle a connection to a port which is not listening.
; port number is in T3. old DDB (at this writing, always the pseudo
; DDB) is on the stack.
NewCon:
pop p,f ; get back the DDB we've been given.
movem t2,RmtPrt(f) ; save his port
movem t3,LclPrt(f) ; and this port
trne t3,<-1←↑d8> ; anything on but the low 8 bits?
jrst NotExc ; yes. not an exec port.
move t1,t3 ; position our port number better
PUSHJ P,WKPFND ;IS THIS SOCKET'S SERVICE IMPLEMENTED?
jrst NoPort ; remember this "error"
move t4,t1 ; save service offset
MOVEI J,0 ;NO JOB NUMBER YET
PUSHJ P,DDBGET## ;TRY FOR FREE DDB
jrst NoDDB ; can't get one
PUSHJ P,ITYGET## ;GET A PORT
jrst NoITY ; can't get one
MOVSI u,TTYKBD!TTYPTR
IORb u,TTYLIN(F) ; SET TTY BITS, get ITY's LDB into U
PUSHJ P,TSETBI## ;CLEAR INPUT BUFFER
PUSHJ P,TSETBO## ;CLEAR OUTPUT BUFFER
move t1,t4 ; position pointer to service.
HRRO T2,WKPSRV(T1) ;FETCH POINTER TO LOGICAL NAME
POP T2,DEVLOG(F) ;SET LOGICAL NAME INTO DDB
LDB T1,WKPTFC ;FETCH TTY FORCED COMMAND INDEX
pushj p,TTFORC## ;FORCE THE APPROPRIATE COMMAND
pushj p,PrpDDB ; set essential DDB words
;now fill in the information we know
move t1,RmtAdr+PSDDDB## ; get the foreign host address.
movem t1,RmtAdr(f) ; and save it the real DDB
move t1,NetAdr+PSDDDB## ; get ARPA address
movem t1,NetAdr(f) ; save in the DDB
load. t1,StdSP,TCPIBf ; get the source port (his port)
movem t1,RmtPrt(f) ; save in DDB
load. t1,StdDP,TCPIBf ; get the destination port (my port)
movem t1,LclPrt(f) ; save in DDB
movei t1,S%List ; get state code "listen"
movem t1,State(f) ; make it this DDB's state
pushj p,NewLst ; go back a process this message
; as if nothing has happened.
move t2,State(f) ; now get the state
caie t2,S%List ; still listening?
popj p, ; no. just return.
pushj p,DDBFls## ; clear out DDB
pjrst DDBRel## ; and return it to free pool
NotExc: movei u,TCPIBf ; point at TCP leader
move p3,TCPFlg(u) ; get the flags from leader.
jumple p2,TryRst ; just reset if no options
hlrz t1,p2 ; get the first buffer of options
pushj p,RelBuf## ; free the options.
jrst TryRst ; try to send a reset and
; return the buffers and return.
;ROUTINE TO CHECK LEGALITY OF AN EXEC Well Known Port. ;⊗ WKPFND WKPFN1 WKPSRV WKPNUM WKPSKT WKPTFC
; MOVE t1,[local port NUMBER]
; PUSHJ P,WKPFND
; ERROR--SERVICE NOT IMPLEMENTED
; NORMAL--T1 CONTAINS INDEX INTO SERVER TABLE (WKPSRV)
WKPFND: pushj p,save2## ; get p1 and p2
move p2,t1 ; save port number
MOVSI t1,-WKPNUM ;NUMBER OF SERVICES IMPLEMENTED
WKPFN1: LDB p1,WKPSKT ;FETCH SOCKET NUMBER OF THIS SERVICE
CAMN p1,p2 ;MATCH?
JRST CPOPJ1 ;YES, GOOD RETURN, T1 is offset.
AOBJN t1,WKPFN1 ;NO, TRY NEXT
POPJ P, ;ERROR--SERVICE NOT IMPLEMENTED
;TABLE OF DEFINED SERVICES AVAILABLE THROUGH EXEC WKP.
; MACRO TO DEFINE A SERVICE:
; SERVER (PORT# , TTY FORCED COMMAND , LOGICAL NAME)
DEFINE SERVER(SKT,TFC,NAME) <
↑D<SKT>B26 + TFC## ,, [SIXBIT\NAME\]
>
WKPSRV: SERVER (3,TTFCXF,FTPSRV) ;FILE TRANSFER PROTOCOL SERVER
SERVER (23,TTFCXH,NETUSR) ;TELNET SERVER
server (79,ttfcxg,FngSrv) ;(241) finger service
IFN FTPATT,<
0 ;SPACE TO PATCH IN NEW SERVICES
0
>
WKPNUM==.-WKPSRV ;NUMBER OF DEFINED SERVICES
WKPSKT: POINT 9,WKPSRV(T1),8 ;POINTER TO SERVICE SOCKET NUMBER
WKPTFC: POINT 9,WKPSRV(T1),17 ;POINTER TO TTY FORCED COMMAND INDEX
; here to process one message. this may be hot off the presses or it ;⊗ PrcMsg
; may be a message that's was received out of order and can only now
; be processed, but it's ALWAYS called at IMP interrupt level.
; arguments:
; F - DDB
; U - pointer to block containing TCP leader for this message
; P1 - buffer descriptor: <LH> first buffer, <RH> last buffer
; length of message in bytes is in MsgLen(f)
; during this routine, P3 ALWAYS has the current flags from the TCP
; leader (we sometimes change them), and P2 ALWAYS has the current
; State, which should ALWAYS agree with State(f).
PrcMsg:
move p3,TCPFlg(u) ; get the flags from leader.
move p2,State(f) ; get state of this connection
cain p2,S%List ; waiting for anything?
jrst InLstn ; yes
cain p2,S%SynS ; waiting for SYN ACK?
jrst InSynS ; yes
; this is a segment arriving at a previously established connection.
move t1,RcvWnd(f) ; get the receive window size
move t2,RcvNxt(f) ; get the beginning of the rec window
load. t3,TCPSeq,(u) ; get the sequence number of it
move t4,MsgLen(f) ; load up message length
jumpn t1,WndFit ; receive window is non-zero, so
; try to fit this one in.
jumpn t4,SeqBad ; can't handle it, it's too big
came t3,t2 ; is it the one we are expecting?
jrst SeqBad ; no. sequence number out of range.
move t4,t3 ; last byte is the first byte.
jrst InWind ; this is it. process it.
; here to check for the segment starting in the window ;⊗ WndFit
WndFit: add t1,t2 ; compute the end of the window
add t4,t3 ; compute the end of the message
; note: now T4 points one beyond the end of the current message,
; T1 points one beyond the end of the current window.
camg t4,t1 ; does this message end within
; the window?
jrst WndEnd ; yes. do more checking.
caml t3,t1 ; does it start before the end?
jrst SeqBad ; no, it's way out of line.
aos TCPWET## ; count window end truncated
move t4,t1 ; the end of the message is
; going to agree with the end
; of the window when we get done.
sub t1,t3 ; compute the length we will accept:
; end of window less start of message.
; now scan through stream until we've seen as many bytes as we
; are going to allow, then throw away everything else.
hlrz t2,p1 ; get pointer to first buffer.
pushj p,SkpByt## ; skip past that many bytes.
; now pointing at unwanted bytes.
ifn debug,< ; is the code buggy?
skipn t2 ; is there a buffer with this byte?
stopcd CPOPJ##,DEBUG,NEB, ;++ not enough bytes.
>
hrr p1,t2 ; new last buffer in our pointer
stor. t1,NBHCnt,(p1) ; make this buffer have only as
; many bytes as we're prepared
; to see.
load. t1,NBHNxt,(p1) ; get pointer to next buffer
pushj p,RelBuf## ; release the rest of the stream
zero. t1,NBHNxt,(p1) ; zero out the link to the
; non-existent remains.
; restore these two badly clobbered values
move t2,RcvNxt(f) ; get the beginning of the rec window
load. t3,TCPSeq,(u) ; get the sequence number of it
; and charge on to check the end of the message.
; here to check for a segment finishing in the window ;⊗ WndEnd FlsLp FlsBad
WndEnd: caml t3,t2 ; starts after the start of window?
jrst InWind ; yes. this message is all in window
camg t4,t2 ; ends after start of window?
jrst SeqBad ; yes. we've already seen this.
; ACK may have been lost: make
; sure he KNOWS we saw this.
aos TCPWFT## ; count window front truncated
push p,f ; save real DDB
movei f,PSDDDB## ; get the pointer to the pseudo
; DDB for input.
hlrz t1,p1 ; get first buffer
hrrom t1,IBfThs+PSDDDB## ; save as current buffer, untouched.
setzm IBfBC+PSDDDB## ; clear count.
movei p4,InByte## ; input from buffers which are already
; in 32 bit words.
move p1,t2 ; get the start of window
sub p1,t3 ; subtract starting sequence
FlsLp: jsp p4,(p4) ; get next byte
jrst FlsBad ; someone miscounted.
sojg p1,FlsLp ; one more read. loop.
load. t4,TCPSeq,(u) ; get start sequence number again.
add t4,MsgLen(f) ; compute ending sequence again.
move t1,t4 ; position, now that T4 is restored.
sub t1,RcvNxt(f) ; subtract beginning of window
; to get number of bytes to read.
pushj p,GetMes## ; go read it into fresh buffers.
jrst FlsBad ; can't happen. someone miscounted.
move p1,t1 ; put message chain in proper place.
pop p,f ; get back real DDB address.
jrst IsNext ; this is the next message, so go.
; restore F
FlsBad: pop p,f ; clear stack
hrrz t1,IBfThs+PSDDDB## ; get next buffer to be input.
jrst RelBuf## ; release buffers and return
; at this point we have a message which starts and ends within ;⊗ InWind IsNext
; the receive window. now we must see if it is the next message to
; be used.
; T3 - sequence number of the first byte in message
; T4 - sequence number of the next byte after this message
InWind: came t3,RcvNxt(f) ; is this the byte we want next?
jrst NotNxt ; no. save it until its time.
; now we have the next entry we need to process
IsNext: txne p3,TC%Rst ; reset coming in?
jrst FlsRst ; yes. reset connection.
pushj p,SecChk ; check security for this packet.
jrst BufFls ; not good enough.
txne p3,TC%Syn ; incoming SYN?
jrst FlsSyn ; yes. can't be. reset connection.
txnn p3,TC%ACK ; an ACK?
jrst BufFls ; no. can't be for us. throw
; it away.
; deal with an ACK differently depending on state
Dispat (p2,ACKErr,<<S%SyRP,<pushj p,ACKSyR>>
,<S%SyRA,<pushj p,ACKSyR>>
,<S%Estb,<pushj p,ACKEst>>
,<S%Fin1,<pushj p,ACKF1>>
,<S%Fin2,<pushj p,ACKEst>>
,<S%ClsW,<pushj p,ACKEst>>
,<S%Clsn,<pushj p,ACKCln>>
,<S%LAck,<pushj p,ACKLAc>>
,<S%TimW,<pushj p,ACKTW>>
>)
jrst BufFls ; non-skip return from dispatch:
; discard message and return.
; fall through to next page.
; deal with the urgent pointer, if there is one ;⊗ TCPUrg
TCPUrg: ; SYN-Sent state processing for incoming may join us at this point.
; skip URG and text processing for states which can't have them.
Dispat(p2,UrgErr,<<S%Estb,<jfcl>>
,<S%Fin1,<jfcl>>
,<S%Fin2,<jfcl>>
,<S%ClsW,<jrst TCPFin>>
,<S%Clsn,<jrst TCPFin>>
,<S%LAck,<jrst TCPFin>>
,<S%TimW,<jrst TCPFin>>
>)
txnn p3,TC%Urg ; urgent bit set?
jrst TCPTxt ; no. process text
load. t1,TCPUP,(u) ; get the urgent pointer
add t1,t3 ; add offset to sequence number
; to get sequence number after
; urgentness
camg t1,RcvUrg(f) ; is this more urgent than previously?
jrst TCPTxt ; no. just ignore it.
movem t1,RcvUrg(f) ; yes. save the new urgent pointer.
pushj p,TTyUrg## ; do TTY urgent processing if
; necessary.
; message chain is in P1. left half: first buffer, right half: last buffer. ;⊗ TCPTxt TCPTx1
; note: can only get here in established or one of the FIN-wait states
TCPTxt:
camg t4,RcvNxt(f) ; it there any data here?
jrst TCPFin ; nope.
scnoff ; we are mucking with the
; stream, so protect our ass.
SKIPE T1,IBFLST(F) ;IS THERE ALREADY A STREAM?
jrst [ ; yes.
hlrz t2,p1 ; get first buffer of new message.
stor. t2,NBHNxt,(T1) ; join the new message to the end of
; the old stream.
jrst TCPTx1 ; and continue
]
HLROM p1,IBFTHS(F) ;NO, START ONE
TCPTx1: HRRZM P1,IBFLST(F) ;NEW END OF STREAM
ScnOn ; ok. let anyone have it.
pushj p,ImpNew## ; tell IO service about new data.
exch t4,RcvNxt(f) ; save the sequence number we
; expect next.
sub t4,RcvNxt(f) ; get negative number of words here
addm t4,RcvWnd(f) ; remove that many words from
; the window.
txo s,TC$ACK ; make sure to ACK this data
; here to check for a FIN and handle it ;⊗ TCPFin TCPFi0 TCPFi1
TCPFin: txnn p3,TC%Fin ; FIN set?
popj p, ; no. that's all for this message.
skipe RcvFin(f) ; have we received this FIN already?
jrst TCPFi1 ; yes. skip initial FIN processing.
aos RcvNxt(f) ; no. update next byte past FIN
setom RcvFin(f) ; remember we received a FIN
pushj p,ImpNew## ; tell input service about new
; informtaion.
movsi t1,ttyptr!ttykbd ; set up keyboard and printer bits
scnoff ; shut down interrupts for
; these checks.
cain p2,S%Estb ; are we established?
tdnn t1,ttylin(f) ; and are we dependent on the
; IMP for any TTY info? (actually,
; should check for KBD and JOB or
; PTR and not JOB, but since
; we always set both PTR and
; KBD together, we don't have to.)
jrst TCPFi0 ; no to one or the other.
movx t1,TC%Fin ; set FIN bit
iorm t1,SndBts(f) ; set it in bits to be sent
pushj p,SndMsg## ; try to send a FIN in response.
jfcl ; ignore errors
movei p2,S%LAck ; skip straight to last ACK
movem p2,State(f) ; save the new state
TCPFi0: scnon ; interrupts back on
TCPFi1: txo s,TC$ACK ; have to ACK this FIN.
; skip if we want to stay in the same state, else load P2 with
; the new state and non-skip
Dispat(p2,FinErr,<<S%Estb,<movei p2,S%ClsW>>
,<S%Fin1,<movei p2,S%Clsn>>
,<S%Fin2,<pushj p,FINF2>>
,<S%ClsW,<skipa>>
,<S%Clsn,<skipa>>
,<S%LAck,<skipa>>
,<S%TimW,<pushj p,FINTW>>
>)
movem p2,State(f) ; store a new state
popj p, ; all done.
FINF2: movei t1,2*MSL ; load up twice maximum segment life ;⊗ FINF2 FINTW
movem t1,GTimer(f) ; time wait timer is running
; RFC says "turn off other timers", but i see no timers here.
setzm DevLog(f) ; clear the logical name. this
; makes it easier to spot
; someone trying to reuse this
; connection in a legitimate way.
movei p2,S%TimW ; change to time wait state
popj p, ; return non-skip to set the
; new state.
FINTW: ; he must not know we're here yet. just restart timer.
movei t1,2*MSL ; two times the longest time a
; packet can live
movem t1,GTimer(f) ; set the timer.
pjrst cpopj1## ; and don't change state
; here if we received a segment for a connection that doesn't exist ;⊗ TryRst RstFls SndRst
TryRst: txnn p3,TC%Rst ; reset on?
RstFls: pushj p,SndRst ; no. reply with a reset
pjrst BufFls ; and flush the buffers
; send a reset
SndRst: movx t1,TC%Rst ; get reset bit
txnn p3,TC%Ack ; ACK set?
txo t1,TC%Ack ; no. set in response.
load. t2,TCPSeq,(u) ; get sequence number
add t2,MsgLen(f) ; add the length
txne p3,TC%Syn ; is SYN set?
aos t2 ; yes. length is one more
txne p3,TC%Fin ; is FIN set?
aos t2 ; yes. remember to count that, too.
movem t2,RcvNxt(f) ; use that as the ACK field.
setz t1, ; send sequence 0 back to him.
txne p3,TC%Ack ; ack set?
load. t1,TCPAck,(u) ; yes. use ACK field for sequence.
pushj p,TCPRsp ; send it off, T1 and T2 are args.
movx t1,TC%All ; get all the bits
andcam t1,SndBts(f) ; clear them ALL.
popj p, ; return
; here if we received a segment while listening for one ;⊗ InLstn AckAck AckAc1
InLstn: txne p3,TC%Rst ; is this a reset?
jrst BufFls ; can't be real. flush message
txnn p3,TC%ACK ; acknowleging?
txnn p3,TC%Syn ; or not SYNing?
jrst RstFls ; we didn't say anything.
; respond RESET.
; here when receiving a ligit incoming for our listen state.
load. t1,TCPSeq,(u) ; get sequence number
movem t1,RcvIRS(f) ; save in DDB
aos t1 ; compute next message expected
movem t1,RcvNxt(f) ; save that as what is expected
movem t1,RcvRed(f) ; save this as sequence number
; last time we updated RcvWnd.
; (we actually first "updated"
; it when we prepped the window.)
pushj p,GetISS ; decide on the initial send
; sequence number.
movem t1,SndISS(f) ; save ISS
aos t1 ; account for SYN
movem t1,SndNxt(f) ; and save it.
movem t1,SndLWd(f) ; and make that the first message
; number for window calculating
setzm SndWnd(f) ; we have no idea how much we
; can send until we hear.
; fill in defaults for passive open, just in case.
move t1,RmtAdr+PSDDDB## ; get the foreign host address.
movem t1,RmtAdr(f) ; and save it the real DDB
move t1,NetAdr+PSDDDB## ; get ARPA address
movem t1,NetAdr(f) ; save in the DDB
load. t1,StdSP,(u) ; get the source port (his port)
movem t1,RmtPrt(f) ; save in DDB
load. t1,StdDP,(u) ; get the destination port (my port)
movem t1,LclPrt(f) ; save in DDB
movei p2,S%SyRP ; change to syn-received, passive
jrst AckAc1 ; and continue
AckAck: ; here from Syn-sent code, to pretend to be a listen.
sos SndNxt(f) ; pretend we didn't send anything
movei p2,S%SyRA ; change state to syn-received, active
AckAc1: scnoff ; protect against unlikely race
skipg State(f) ; has this DDB been wiped while
; we were thinking?
pjrst sonppj## ; yes. just try to give up
movem p2,State(f) ; in DDB
movx t1,TC%Syn!TC%Ack ; get SYN bit and ACK the SYN we got
iorm t1,SndBts(f) ; set it in bits to be sent
setzm SndLst(f) ; force into retransmission queue.
pushj p,SndMsg## ; send message now.
jfcl ; ignore a error we can't help.
scnon ; ok to interrupt now.
skipn t4,MsgLen(f) ; any text in this message?
pjrst cpopj## ; no text. just return.
load. t3,TCPSeq,(u) ; get the starting sequence number
add t4,t3 ; compute the sequence number
; of the byte following this message.
txz p3,TC%Syn!TC%Ack ; don't reprocess SYN and ACK.
jrst NotNxt ; remember the text.
; here if received a segment for a connection in SYN-SENT state ;⊗ InSynS InSyn1 InSyn2
InSynS: txnn p3,TC%ACK ; is this an ACK?
jrst InSyn1 ; no
load. t1,TCPACK,(u) ; get the ACK number
came t1,SndNxt(f) ; is this the correct ACK?
jrst TryRst ; no. send a reset (unless reset)
InSyn1: txnn p3,TC%Rst ; is this a reset?
jrst InSyn2 ; no. still processable
txnn p3,TC%ACK ; was ACK on?
jrst BufFls ; no. this isn't for us.
; we flush this connection. at call, we can check for closed.
jrst RstCls ; delete DDB and message and return.
InSyn2: pushj p,SecChk ; security check. honk! honk!
jrst BufFls ; security isn't tight enough.
txnn p3,TC%Syn ; is this trying to get us together?
jrst BufFls ; no. must be from outer space
load. t4,TCPSeq,(u) ; get the sent sequence number
movem t4,RcvIRS(f) ; that's the first one we got
movem t4,RcvRed(f) ; save this as sequence number
; last time we updated RcvWnd.
; (we didn't really know it at
; the time.)
aos t4 ; we're expecting the next one
movem t4,RcvNxt(f) ; that's what we're expecting
; (after this SYN). (now T4 is
; loaded as it must be for TCPUrg)
txnn p3,TC%Ack ; is this ACKing our SYN?
jrst AckAck ; no. now we send another SYN as
; if we were coming from a listen
; with the RcvNxt we just got.
; if this beats our other SYN,
; then all will proceed as if
; we had been listening and
; the earlier SYN will be discarded
; (not in window). if the other
; SYN gets there first, this
; one will be discarded (not
; in window) and a proper ACK
; will be sent to us. this
; ACK will appear to us to
; "ACK our SYN", taking us
; from Syn-Rcvd to established.
; And vice versa.
pushj p,ACKUpd ; yes. go update the ACK stuff.
movei p2,S%Estb ; set state to ESTABLISHED
movem p2,State(f) ; in DDB
pushj p,TCPIOD ; wake up the job if needed.
txo s,TC$ACK ; remember to always ACK his ACK
jrst TCPUrg ; join ESTABLISHED processing
; at urgent pointer processing.
subttl returns ;⊗ NoLead NoMess BadChk FlsOpt NoPort NoDDB NoITY AckErr UrgErr FinErr SeqBad BufFls FlsRst RstSRP RstBTL RstSRA RstEst RstCls FlsSyn
; message ended before leader was read in
NoLead: aos TCELed## ; error with leader
popj p, ; return
; bytes ended before message or ran out of buffers while reading it
NoMess: aos TCEMes## ; count error reading message in
jumple p2,cpopj## ; return if no options
hlrz t1,p2 ; get first buffer of options
pjrst RelBuf## ; release the options, too.
BadChk: aos TCEChk## ; checksum wrong. count it
FlsOpt: jumple p2,BufFls ; just flush the buffers in no options
hlrz t1,p2 ; get first buffer of options
pushj p,RelBuf## ; free them
pjrst BufFls ; flush out buffers and return
NoPort: pushj p,SndNSP## ; call ICMP to tell him we
; don't do that.
aosa TCEPrt## ; incoming to a exec port we
; don't watch.
NoDDB: aos TCEDDB## ; couldn't get DDB when needed.
jrst FlsOpt ; go flush message and options
NoITY: aos TCEITY## ; couldn't get an ITY when i
; wanted one.
pushj p,DDBREL## ; RETURN THE DDB
jrst FlsOpt ; flush buffers and options and return
AckErr:
UrgErr:
FinErr: stopcd BufFls,DEBUG,SES, ;++ state error seen
; here to force an ACK if not handling a RESET and discard the message.
SeqBad: aos TCPMNW## ; count message not in window
txnn p3,TC%Rst ; a reset?
txo s,TC$ACK ; get an ACK sent back.
; subroutine to release all the buffers in our message.
BufFls: hlrz t1,p1 ; get first buffer of chain.
pjrst RelBuf## ; release the entire chain.
; here to flush the message and handle a reset
FlsRst: dispat (p2,RstCls,<<S%SyRP,<jrst RstSRP>>
,<S%SyRA,<jrst RstSRA>>
,<S%Estb,<jrst RstEst>>
,<S%Fin1,<jrst RstEst>>
,<S%Fin2,<jrst RstEst>>
,<S%ClsW,<jrst RstEst>>
>)
; incoming SYN to an almost established connection from a listen
RstSRP: pushj p,DDBFls## ; clear our all data buffers
pushj p,ImpDev## ; is this controlling a job?
jrst [ ; this device is NOT an IMP?
stopcd CPOPJ##,DEBUG,CNI ;++ connection not an IMP
]
jrst RstBTL ; not controlling a job: back
; to listen
pjrst DDBRel## ; this is an incoming
; connection to a server.
; flush it.
RstBTL: movsi p2,s%List ; get listen state
movem p2,State(f) ; back to listen state
setz s, ; no bits apply
popj p, ; try to get out of it
RstSRA: skipa s,[ xwd IOIMPM,0 ] ; set up S with improper mode
; incoming reset to an established connection
RstEst: movsi s,IODERR ; set device error
iorm s,DevIOS(f) ; in DDB
RstCls: setz s, ; no bits are operative
pushj p,BufFls ; get rid of the data.
pushj p,DDBFls## ; clear DDB queues
movsi t1,AllWat ; get wait flags
tdnn t1,ImpIOS(f) ; waiting for anything?
pjrst DDBRel## ; nope. nothing to tell him,
; so just make the DDB disappear.
pjrst ImpWak## ; wake up this user and fly
; incoming SYN where there can't be one. reset.
FlsSyn: pushj p,SndRst ; send a reset
jrst RstCls ; throw away DDB, queues and all.
subttl routines to handle an ACK in various states ;⊗ ACKSyR ACKEst AckEs1 ACKF1 ACKCln ACKLAc ACKTW
; all routines should skip return if this segment is still worthy
; of consideration, non-skip return if this segment should be
; discarded.
; ACK while in SYN-received state
ACKSyR: load. t2,TCPAck,(u) ; get ACK number for this message
caml t2,SndUna(f) ; has it been previously ACKed?
camle t2,SndNxt(f) ; or is it ACKing something
; not sent yet?
jrst [ ; yes. fucked up.
move t1,t2 ; get sequence number placed
movx t2,TC%Rst ; reset is the bit we want
pjrst TCPRsp ; queue it up to be sent and error
; return from AckSyR
]
movei p2,S%Estb ; change state to ESTABLISHED
movem p2,State(f) ; in the DDB
pushj p,TCPIOD ; try to wake job
jrst ACKEs1 ; now do established like processing
; ACK while in established state (also CLOSE-wait), as well as
; part of the processing for FIN-wait-1, FIN-wait-2, and Closing.
ACKEst: load. t2,TCPAck,(u) ; get the ACK number
camle t2,SndNxt(f) ; ACKing data not yet sent?
jrst [ ; yes. our friend seems confused.
txo s,TC$ACK ; send an ACK with the fields
; properly set.
popj p, ; perhaps that will straighten
; him out.
]
AckEs1: caml t2,SndUna(f) ; any chance of progress made here?
pushj p,ACKUpd ; yes. update ACK information.
pjrst cpopj1## ; and continue processing
; ACK while in FIN-wait-1
ACKF1: pushj p,ACKEst ; do the common established processing
popj p, ; this segment is no good
skipe RetrnQ(f) ; retransmission queue empty?
pjrst cpopj1## ; no. FIN hasn't been ACKed yet.
movei p2,S%Fin2 ; yes: our FIN's been ACKed
movem p2,State(f) ; enter FIN-wait-2 state
pjrst cpopj1## ; continue processing
; ACK while in closing state
ACKCln: pushj p,ACKEst ; common established processing
popj p, ; drop the segment
skipe RetrnQ(f) ; everything been ACKed
; (including our FIN)?
popj p, ; no: discard segment
movei t1,2*MSL ; load up twice maximum segment life
movem t1,GTimer(f) ; time wait timer is running
setzm DevLog(f) ; don't let the logical name be
; used anymore.
movei p2,S%TimW ; change state to Time-wait
movem p2,State(f) ; and remember in DDB
pjrst cpopj1## ; still going on this segment
; ACK while in Last-ACK
ACKLAc: pushj p,ACKEst ; normal ACK processing.
; (note: the specs indicate
; that this isn't necessary,
; but in last-ACK state, we
; can get ACKs of data which
; must be removed from the
; retransmission queue as always.)
popj p, ; flush segment
skipe RetrnQ(f) ; everything's been ACKed,
; including our FIN?
popj p, ; no. keep waiting
movx t1,S%Clos ; set state to closed
movem t1,State(f) ; in DDB.
; legally, the following two lines should be in, but experience shows that
; the user (well, me, anyway) expects the DDB to disappear at this point.
; skipe IBfThs(f) ; has everything been read?
; popj p, ; no. let input delete the DDB.
pushj p,DDBFls## ; yes. the end of the connection.
pjrst DDBRel## ; give up DDB.
; ACK while in time-wait
ACKTW: txnn p3,TC%Fin ; is this a FIN?
popj p, ; no. it can't be legal. ignore it.
txo s,TC$ACK ; ACK this FIN again: he didn't
; hear it last time.
movei t1,2*MSL ; time-wait max
movem t1,GTimer(f) ; set it
popj p, ; nothing more to do with this
subttl AckUpd ;⊗ ACKUpd NoAllc RetrLp RetrD0 RetrDn RetrNo
;++
; Functional description:
;
; update information about how much data has been acknowleged
; as received by the other host. this update includes
; remembering where the unacknowleged data now is, where the
; end of the receive window is, and deleting any packets in
; the retransmission queue that are entirely acknowleged.
;
;
; Calling sequence:
;
; move f,DDB
; pushj p,AckUpd
; <always returns here>
;
; Input parameters:
;
; F - DDB in question.
;
; Output parameters:
;
; none.
;
; Implicit inputs:
;
; TCP header, DDB
;
; Implicit outputs:
;
; outgoing window information in DDB.
;
; Routine value:
;
; none.
;
; Side effects:
;
; modified data in DDB. may delete some messages in the
; retransmission queue.
;--
ACKUpd: pushj p,save3## ; get P1 and p2 and p3
load. t1,TCPWnd,(u) ; get window length
move p2,t1 ; get copy
lsh p2,-2 ; get 25% of window length.
movem p2,SndThr(f) ; that's our threshold for sending
load. p1,TCPACK,(u) ; get ACK number.
add t1,p1 ; get highest sequence number
; we're allowed to send.
push p,t1 ; save that value
sub t1,SndLWd(f) ; subtract off sequence number
; of the end of the window
; from the last window update
; to get the change in the window
pop p,SndLWd(f) ; put the last window end in
; the DDB from stack.
addb t1,SndWnd(f) ; update the window
caml t1,p2 ; is the amount of window over
; the threshhold for sending?
pushj p,AlcNew## ; yes. wake job if waiting.
NoAllc: camg p1,SndUna(f) ; a real increase?
popj p, ; no.
movem p1,SndUna(f) ; remember how much has been ACKed.
move t1,UTTime(f) ; get user timeout time.
movem t1,UTTimr(f) ; and reset it.
setz p2, ; remember haven't seen a fully
; ACKed message yet.
scnoff ; protect BIB freeing code
hrrz t1,RetrnQ(f) ; get retransmission queue head.
jumpe t1,RetrD0 ; this shouldn't happen....
RetrLp:
ifn debug,< ; debugging
pushj p,BIBChk## ; consistency check
>
cam. p1,BIBSeq,(t1),ge ; is this fully ACKing this one?
jrst RetrDn ; no. that's the lowest we
; have. stop scanning.
load. p2,BIBTim,(t1) ; seconds since sent
load. p3,BIBRTQ,(t1) ; get next BIB in queue
pushj p,RelBIB## ; dump that BIB.
skipe t1,p3 ; position next BIB. is one?
jrst RetrLp ; yes. loop.
RetrD0: setzb t1,RetrnQ(f) ; nothing left in the queue
RetrDn:
hrrm t1,RetrnQ(f) ; update pointer to new first buffer.
scnon ; ok to interrupt us now.
jumple p2,RetrNo ; last BIB didn't have a
; real time in it.
; smooth retransmission timeout time by computing
; (7/8*<old retran time> + 1/8*<round trip time for this segment>),
; or (7*<old> + <round trip>)/8, in this case.
move p1,RTTime(f) ; get standard retransmission time
imuli p1,7 ; times 7
addb p2,p1 ; total them
addi p2,4 ; make sure to round up.
ash p2,-3 ; now divide both by 8
caige p2,RTMin ; is it too small?
movei p2,RTMin ; yes. least legal time
caile p2,RTMax ; is it too big?
movei p2,RTMax ; yes. most legal time
movem p2,RTTime(f) ; set new timeout time in ticks.
RetrNo:
; may need to send some information to user concerning the
; data we now know the other end received.
popj p, ; all done
subttl deal with a message received before it should be ;⊗ NotNxt FtrOrd FtrOr1 FtrRpl FtrOr2 FtrNew FtrSav
; P1 has a message pointer to message which cannot be accepted
; until other messages before it arrive. T4 has the sequence number
; just after this message.
NotNxt:
aos TCPFTS## ; count future message seen
load. t3,TCPSeq,(u) ; get sequence number (chain is
; ordered by initial sequence number)
movei p4,Future-FNxtOf(f) ; get the start of the FMB chain.
; such that using FMBNxt will
; point at future pointer word.
FtrOrd: load. p2,FMBNxt,(p4) ; get next FMB in queue
jumpe p2,FtrNew ; found the end of the futures chain.
; get an FMB and save this in it.
load. t1,TCPSeq,FMBTCP(p2) ; get sequence number of this one.
camg t3,t1 ; new starts after old?
jrst FtrOr1 ; no. could precede or be together.
cam. t4,FMBNBy,(p2),g ; does new extend beyond the old?
jrst BufFls ; no. new is duplicate. discard.
move p4,p2 ; grab copy of this pointer in
; case it's the last.
jrst FtrOrd ; try next FMB.
FtrOr1: came t3,t1 ; do they start at the same place?
jrst FtrOr2 ; no. new one definitely
; starts first.
cam. t4,FMBNBy,(p2),g ; does the new one end after
; the old one?
jrst BufFls ; nope. old has everything the
; new one does. kill new.
; replace old one: new one consumes it.
FtrRpl: load. t1,FMBFst,(p2) ; get first buffer in old message
pushj p,RelBuf## ; free all buffers
move t1,p2 ; position used but loved FMB
jrst FtrSav ; save all the data
FtrOr2: cam. t4,FMBNBy,(p2),l ; new one ends before old one?
jrst FtrRpl ; nope. completely consumes it.
; here to get an FMB and save the data in it
FtrNew: pushj p,GetFMB ; get a Future Message Block
jrst BufFls ; no big deal. flush buffer
; and go out normally.
stor. t1,FMBNxt,(p4) ; link to the rest of the stream
stor. p2,FMBNxt,(t1) ; whatever the next one was (may be
; zero), make sure it's our next.
; here to save the data in the FMB in t1
FtrSav: movem p3,TCPFlg(u) ; save the bits on this message.
; (we may have changed them)
movei t2,FMBTCP(t1) ; point at correct place in FMB
hrl t2,u ; BLT pointer to copy TCP header.
blt t2,FMBTCP+TCPLen-1(t1) ; copy the entire header into
; the FMB.
stor. p1,FMBPnt,(t1) ; save pointer to the buffer chain.
stor. t4,FMBNBy,(t1) ; save the sequence number of
; the next byte after this message.
popj p,
; routine to get an FMB, return it in T1 ;⊗ GetFMB RelFMB FlsFMB FlsFM1
GetFMB: syspif ; turn off PIE for this
movei t2,<FMBLen+3>/4 ; this many 4 word blocks in an FMB
push p,t4 ; save T4
pushj p,Get4Wd## ; go get it.
skipa ; failed
aos -1(p) ; skip return
pop p,t4 ; restore T4
pjrst onpopj## ; interrupts on and return
; routine to return an FMB in T2 to free core.
RelFMB: movei t1,<FMBLen+3>/4 ; this many 4 word blocks in an FMB
pjrst Giv4Wd## ; tell Core1 to take it back.
; routine to delete an FMB chain. first FMB is in T1
FlsFMB::
pushj p,save1## ; get p1
move p1,t1 ; start with buffer in correct place
FlsFM1: load. t1,FMBFst,(p1) ; get pointer to first buffer
; in message.
pushj p,RelBuf## ; release the buffer chain
move t2,p1 ; position this FMB for release
load. p1,FMBNxt,(p1) ; get pointer to next FMB in chain
pushj p,RelFMB ; release this FMB
jumpn p1,FlsFM1 ; loop if there's more
popj p, ; return
subttl GetISS ;⊗ GetISS
;++
; Functional description:
;
; decide on the Initial Send Sequence number whenever we need one.
;
;
; Calling sequence:
;
; pushj p,GetISS
; <always return here, ISS to use in T1>
;
; Input parameters:
;
; none.
;
; Output parameters:
;
; T1 - ISS
;
; Implicit inputs:
;
; none.
;
; Implicit outputs:
;
; none.
;
; Routine value:
;
; none.
;
; Side effects:
;
; none.
;--
GetISS: setz t1,0 ; just use zero for now.
popj p,
subttl SecChk ;⊗ SecChk
;++
; Functional description:
;
; Classified.
;
;
; Calling sequence:
;
; Classified.
;
; Input parameters:
;
; Classified.
;
; Output parameters:
;
; Classified.
;
; Implicit inputs:
;
; Classified.
;
; Implicit outputs:
;
; Classified.
;
; Routine value:
;
; Classified.
;
; Side effects:
;
; Classified.
;
;--
SecChk: pjrst cpopj1## ; security looks good.
subttl TCPMak ;⊗ TCPMak TCPMa1 MakCnt
;++
; Functional description:
;
; put TCP leader (in 32 bit format) into fixed TCP output leader
; buffer. then link the buffer to the beginning of the
; current output stream. then send the message down to the
; next level of protocol for further processing.
;
;
; Calling sequence:
;
; move f,DDB
; pushj p,TCPMak
; <always returns here>
;
; Input parameters:
;
; f - DDB for connection
;
; Output parameters:
;
; none.
;
; Implicit inputs:
;
; data in DDB
;
; Implicit outputs:
;
; data in DDB
;
; Routine value:
;
; returns non-skip if can't get a buffer
;
; Side effects:
;
; adds a buffer to the beginning of the current output stream.
;--
TCPMak::
setzm TCPOBf+NBHLen ; zero first word of leader.
move t2,[TCPOBf+NBHLen,,TCPOBf+NBHLen+1] ; set up blt
blt t2,TCPOBf+TCPLen+NBHLen-1 ; clear to end
move t2,SndBts(f) ; get bit field from DDB somewhere
movx t1,TC%Onc ; get bits which should only be
; sent once.
andcam t1,SndBts(f) ; clear bits which we should
; not send again.
andx t2,TC%All ; make sure not to get stray bits.
movsi t1,ttyptr!ttykbd ; some brand of crosspatch bits
tdne t1,TtyLin(f) ; some kind of crosspatch?
txo t2,TC%Psh ; yes. make sure it's shoved through.
move t1,SndNxt(f) ; no. get next sequence number
stor. t1,TCPSeq,NBHLen+TCPOBf ; save in leader
move t1,ObfByt(f) ; get byte count of this message
addm t1,SndNxt(f) ; update the current sequence
; that much.
txne t2,TC%Fin!TC%Syn ; FIN and SYN take up a sequence number.
aos SndNxt(f) ; add it.
; enter here for out of sequence sending. sequence number already set in
; TCP leader, bits to be sent now in T2.
TCPMa1:
; count all the bits in the flag word as message types to get some
; idea of what we're sending.
movx t1,TC%Low ; get lowest order bit
setz t3, ; and a count
MakCnt: tdne t2,t1 ; is that bit on?
aos TCPOTy##(t3) ; yes. count one more with
; that bit on.
lsh t1,1 ; shift bit over one
txne t1,TC%All ; bit no longer in field?
aoja t3,MakCnt ; still in flag field. count on.
movem t2,TCPFlg+NBHLen+TCPOBf ; set the bits wanted.
movei t1,TCPOBf ; point at the output leader space
exch t1,OBfFst(f) ; make us first, get old first
stor. t1,NBHNxt,TCPOBf ; link old first to us.
move t1,RmtPrt(f) ; get his port
stor. t1,StdDP,NBHLen+TCPOBf ; that's the destination port
move t1,LclPrt(f) ; get my port
stor. t1,StdSP,NBHLen+TCPOBf ; that's the source port
move t1,RcvNxt(f) ; get ACK number
stor. t1,TCPAck,NBHLen+TCPOBf ; into leader.
move t1,RcvWnd(f) ; current window
stor. t1,TCPWnd,NBHLen+TCPOBf ; in
move t1,SndUrg(f) ; current out going urgent pointer
stor. t1,TCPUP,NBHLen+TCPOBf ; save
movei t2,TCPLen ; get length (will need to
; compute when we perform options)
stor. t2,TCPOff,NBHLen+TCPOBf ; save that.
lsh t2,Wd2Byt ; convert from words to bytes
stor. t2,NBHCnt,TCPOBf ; save byte count for this buffer
addm t2,OBfByt(f) ; get a grand total in bytes.
; save T2 for checksumming
; one would add OPTIONS around here somewhere.
ifn FtChck,< ; doing checksums?
move t1,[point 16,NBHLen+TCPOBf]; starting pointer
; number of bytes is in t2
pushj p,CSmWds## ; and checksum it.
move t1,RmtAdr(f) ; get remote address
pushj p,CSmWrd## ; add it to checksum
move t1,LclAdr(f) ; local address, too
pushj p,CSmWrd## ; add it in.
move t1,Protcl(f) ; and get protocol
pushj p,CSmHWd## ; and add it in as well
move t1,OBfByt(f) ; get byte count of message
; plus leader
pushj p,CSmHWd## ; add that to checksum, too.
txc p3,msk.hw ; send one's complement of the sum
txnn p3,msk.hw ; if zero, make it...
movei p3,msk.hw ; ...the zero with all bits on
stor. p3,TCPChk,NBHLen+TCPOBf ; save the checksum in the leader.
>
ife FtChck,< ; not doing checksums
zero. t1,TCPChk,NBHLen+TCPOBf ; flag that we aren't checksumming
>
pjrst IpMake## ; call next level of protocol
subttl TCPRsp ;⊗ TCPRsp
;++
; Functional description:
;
; routine to send a TCP response which is out of sequence from
; the TCP stream. for example, it could be a RESET or an
; ACK to correct a bad sequence field.
;
;
; Calling sequence:
;
; move t1,<sequence to use>
; move t2,<bits>
; move f,<ddb>
; pushj p,TCPRsp
; <always returns here>
;
; Input parameters:
;
; T1 - sequence number to put on the message
; T2 - bits which should be set in message
; F - DDB
;
; Output parameters:
;
; none.
;
; Implicit inputs:
;
; DDB
;
; Implicit outputs:
;
; none.
;
; Routine value:
;
; none.
;
; Side effects:
;
; put a message in the output queue.
;--
TCPRsp:
scnoff ; STOP!
push p,p3 ; save lots of things
push p,ObfFst(f) ; save
push p,ObfThs(f) ; save
push p,ObfBC(f) ; save
push p,ObfByt(f) ; save
setzb p3,OBfFst(f) ; pretend no first message
setzm OBfByt(f) ; no bytes in message
stor. t1,TCPSeq,TCPObf+NBHLen ; set desired sequence number
pushj p,TCPMa1 ; call TCPMak properly
jfcl ; forget errors.
pop p,OBfByt(f) ; restore
pop p,OBfBC(f) ; restore
pop p,OBfThs(f) ; restore
pop p,OBfFst(f) ; restore
pop p,p3 ; restore
pjrst sonppj## ; return to caller
subttl TCPIFn ;⊗ TCPIFn TCPIF1
;++
; Functional description:
;
; check to see if this input stream has received a legitimate
; FIN. called after data is exhausted to see if there's any
; more data coming or if this is EOF. if we have received a FIN
; for this connection, close it now.
;
;
; Calling sequence:
;
; move f,DDB
; scnoff
; pushj p,TCPIFn
; <return here if EOF, FIN seen, connection closed,
; interrupts on>
; <return here if not EOF, FIN not yet seen,
; interrupts still off>
;
; Input parameters:
;
; F - DDB
;
; Output parameters:
;
; none.
;
; Implicit inputs:
;
; DDB
;
; Implicit outputs:
;
; none.
;
; Routine value:
;
; returns non-skip if this connection is done doing input
; (i.e., FIN received), else skip returns. in non-skip return,
; interrupts are enabled.
;
; Side effects:
;
; will close the connection if a FIN has been seen. turns
; on interrupts if return is non-skip, else leave them off.
;
;--
TCPIFn::
skipn RcvFin(f) ; seen a FIN
pjrst cpopj1## ; no. still open for action.
pushj p,save1## ; get a scratch
skiple p1,State(f) ; state some kind of closed?
jrst TCPIF1 ; no. check to see if we
; should release it, though.
scnon ; allow DDBFls to handle interrupts
pushj p,DDBFls## ; clear this DDB
pjrst DDBRel## ; and let someone else use it.
TCPIF1: cain p1,S%ClsW ; in close wait?
pjrst sonppj## ; yes. this state may still need the
; DDB, so keep it.
; no. any other state the has
; received a FIN is of no use
; to user. deassign the DDB.
scnon ; interrupts are ok again.
; detach IMP from terminal now.
pushj p,ItyRel## ; ditch ITY, if any.
pushj p,TTIDet## ; disconnect crosspatched IMP.
pjrst DDBDea## ; deassign DDB and return
subttl TCPICK ;⊗ TCPICK
;++
; Functional description:
;
; check a connection to see if it is in a state where input is legal.
;
;
; Calling sequence:
;
; move f,DDB
; pushj p,TCPICK
; <always returns here>
;
; Input parameters:
;
; f - ddb
;
; Output parameters:
;
; none.
;
; Implicit inputs:
;
; none.
;
; Implicit outputs:
;
; none.
;
; Routine value:
;
; returns non-skip if the connection is NOT open for input.
; returns skip if input is possible.
;
; Side effects:
;
; none.
;--
TCPICK::
pushj p,save1## ; get p1
move p1,state(f) ; get state from DDB
cain p1,S%Estb ; is it well established?
pjrst cpopj1## ; yes. that's legal
caie p1,S%Fin1 ; FIN wait 1?
cain p1,S%Fin2 ; or FIN wait 2?
aos (p) ; yes. he hasn't closed yet.
popj p, ; return.
subttl TCPOCK ;⊗ TCPOCK
;++
; Functional description:
;
; check a connection to see if it is in a state where output
; is legal.
;
;
; Calling sequence:
;
; move f,DDB
; pushj p,TCPOCK
; <always returns here>
;
; Input parameters:
;
; f - ddb
;
; Output parameters:
;
; none.
;
; Implicit inputs:
;
; none.
;
; Implicit outputs:
;
; none.
;
; Routine value:
;
; returns non-skip if the connection is NOT open for output.
; returns skip if output is possible.
;
; Side effects:
;
; none.
;--
TCPOCK::
pushj p,save1## ; get p1
move p1,state(f) ; get state from DDB
caie p1,S%Estb ; is it well established?
cain p1,S%ClsW ; or in close wait?
aos (p) ; yes. he hasn't closed yet.
popj p, ; return.
subttl TCPTCk ;⊗ TCPTCk
;++
; Functional description:
;
; check to see if the send threshhold has been exceeded. if
; there is enough real window available, it's ok to send more
; data, otherwise (non-skip), avoid sending data until more
; window appears.
;
;
; Calling sequence:
;
; move f,<ddb>
; pushj p,TCPTCk
; <returns here if not enough window>
; <returns here if enough window to warrent sending more>
;
; Input parameters:
;
; F - DDB
;
; Output parameters:
;
; none.
;
; Implicit inputs:
;
; DDB
;
; Implicit outputs:
;
; none.
;
; Routine value:
;
; returns skip if there is enough window to allow sending more
; data, else non-skip.
;
; Side effects:
;
; none.
;--
TCPTCk::
pushj p,save1## ; get p1
skiple p1,SndWnd(f) ; any window?
camge p1,SndThr(f) ; yes. are we over the threshhold?
popj p, ; no to either: avoid sending
move p1,State(f) ; get the connection state
cail p1,S%Estb ; at least established?
aos (p) ; yes. set for skip, is ok
popj p, ; no: pretend there's no window
; until we get into an
; established state.
subttl TCPWUp ;⊗ TCPWUp
;++
; Functional description:
;
; update a window if the user has read some of the data waiting.
;
;
; Calling sequence:
;
; move f,DDB
; scnoff
; pushj p,TCPWUp
; <always returns here>
;
; Input parameters:
;
; f - DDB
;
; Output parameters:
;
; none.
;
; Implicit inputs:
;
; DDB data
;
; Implicit outputs:
;
; DDB data
;
; Routine value:
;
; none.
;
; Side effects:
;
; none.
;--
TCPWUp::
skipn t1,IBfByt(f) ; get byte count read since
; last update
popj p, ; none: nothing to do.
setzm IBfByt(f) ; clear read byte count.
addm t1,RcvRed(f) ; update sequence number of
; bytes read.
addb t1,RcvHld(f) ; add up bytes we're holding
; back from window.
camge t1,RcvThr(f) ; are we over our threshhold?
popj p, ; nope. keep waiting
setzm RcvHld(f) ; not holding any now.
addm t1,RcvWnd(f) ; add freed bytes into window
pushj p,SndMsg## ; send the message
jfcl ; can't do much here
popj p, ; and return
subttl SetUrg ;⊗ SetUrg
;++
; Functional description:
;
; set up TCP data to send an URG message next time out.
; computes the current SndNxt (the value in DDB may be
; out of date) and store is in SndUrg, then sets the URG
; bit in the DDB. note that we NEVER want to send this
; now, because we want to add a data mark (for telnet) and
; have it in this message.
;
;
; Calling sequence:
;
; move f,ddb
; pushj p,SetUrg
; <always return here>
;
; Input parameters:
;
; f - ddb
;
; Output parameters:
;
; none.
;
; Implicit inputs:
;
; DDB data
;
; Implicit outputs:
;
; SndUrg in DDB
;
; Routine value:
;
; none.
;
; Side effects:
;
; none.
;--
SetUrg::
move t1,SndNxt(f) ; get next sequence number
add t1,OBfByt(f) ; find real current sequence number
movem t1,SndUrg(f) ; make this the urgent pointer
movx t1,TC%Urg ; set urgent bit
iorm t1,SndBts(f) ; in DDB
popj p, ; and let it be sent with next
; message out.
subttl TcpChk ;⊗ TCPChk TCPCRT TCPRTR RtLoop RtNxt TCPCUT TCPUTO
;++
; Functional description:
;
; subroutine to do various once a second checks to an IMP DDB.
;
;
; Calling sequence:
;
; move f,DDB
; pushj p,TCPChk##
; <always returns here>
;
; Input parameters:
;
; f - DDB of an IMP device.
;
; Output parameters:
;
; none.
;
; Implicit inputs:
;
; DDB and queues
;
; Implicit outputs:
;
; DDB and queues
;
; Routine value:
;
; none.
;
; Side effects:
;
; may didle with output queues if it finds it needs to retransmit.
; may delete DDB altogether, although DevSer will still have the
; link to the next DDB. (HINT: call this after doing everything else.)
;--
TCPChk::
skiple GTimer(f) ; general timer set to run?
sosle GTimer(f) ; yes. has it expired?
jrst TCPCRT ; no. don't worry about it
skiple t1,State(f) ; get the state: ok if closed or error
cain t1,S%TimW ; is it time-wait?
skipa ; either closed or time wait is ok.
jrst TCPCRT ; must have changed under our nose.
movx t1,S%Clos ; set close state
movem t1,State(f) ; in DDB
; legally, the following two lines should be in, but experience shows that
; the user (well, me, anyway) expects the DDB to disappear at this point.
; skipe IBfThs(f) ; anything left to input?
; popj p, ; yes. let input handle
; releasing DDB.
pushj p,DDBFls## ; clear out the DDB
pjrst DDBRel## ; return DDB to free pool
; here if not time-wait time-out
TCPCRT: scnoff ; no interrupts here
skipe RetrnQ(f) ; anything waiting to retranmit?
skipg t1,State(f) ; and is it some kind of active state?
pjrst sonppj## ; no. don't count if closed or idle.
; here if we need to retransmit for this DDB
TCPRTR:
pushj p,save2## ; get some scratches
hrrz p1,RetrnQ(f) ; get first BIB in
; retransmission queue.
RtLoop: jumpe p1,TCPCUT ; end of queue. check user timeout
ifn debug,< ; debugging
move t1,p1 ; position BIB
pushj p,BIBChk## ; consistency check
>
skip. t2,BIBTQ,(p1),n ; is this BIB already in the
; transmission queue?
skip. p2,BIBTim,(p1),g ; no. are we counting?
jrst RtNxt ; not counting or already in TQ
incr. p2,BibTim,(p1) ; incr time
camg p2,RTTime(f) ; time to retransmit this one?
jrst RtNxt ; no
aos TCPPRT## ; count a packet we had to retransmit.
move t1,p1 ; position BIB pointer
pushj p,Go1822## ; put it in the transmission
; queue again.
jfcl ; can't do nothin'
ifn debug,< ; debugging
move t1,p1 ; position BIB
pushj p,BIBChk## ; consistency check
>
RtNxt: load. p1,BIBRTQ,(p1) ; get next
jrst RtLoop ; and loop
TCPCUT: scnon ; interrupts safe now
sosg UTTimr(f) ; user time-out expired?
jrst TCPUTO ; yes. go delete all queues in
; DDB and flag error.
popj p, ; no. nothing timed out.
; here if user's timer time's out.
TCPUTO: movsi s,IODTER ; set data error
iorm s,DevIOS(f) ; set that in DDB
pushj p,DDBFls## ; strip all queues from this DDB
movsi t1,AllWat ; get wait flags?
tdnn t1,ImpIOS(f) ; waiting for anything?
pjrst DDBRel## ; no. flush DDB on the way out
pjrst ImpWak## ; yes. wake up user on the way out.
SUBTTL USER INTERFACE (IMPUUO) ;⊗ CALL FLAGS IF.NWT IF.PRV IF.ALS TIMEOUT E .UUDEV .UUSTT .UUSKT .UUHST .UURMT .UULST PUUTIM sk.lcl FreOvr FreLsh FrePrt FreMin FreMch
COMMENT \
PROVIDES ABILITY FOR THE USER TO INITIATE IMP CONNECTIONS
UNDER PROGRAM CONTROL.
CALL:
MOVE AC,[BYTE (8)FLAGS, (3)TIMEOUT, (7)CODE, (18)E ]
CALL AC,[SIXBIT /IMPUUO/]
ERROR RETURN -- CODE IN E+1
OK RETURN
;NOTE THE CORRESPONDING CALLI UUO IS -5 AT HARVARD, -17 AT CMU,
; AND -4 AT AFAL SO DON'T USE IT.
FLAGS: \
IF.NWT==1B0 ;IF SET, DON'T GO INTO IO WAIT FOR NCP ACTIVITY
IF.PRV==1B1 ;IF SET, ALLOW THE OPERATION EVEN IF THE USER
; DOESN'T OWN THE DEVICE (PRIVILEGED)
IF.ALS==1B2 ;IF SET, LOCAL SOCKET IS ABSOLUTE RATHER THAN
; JOB- OR USER-RELATIVE (PRIVILEGED)
COMMENT \
TIMEOUT:3 BIT CODE(T) STARTS A TIMEOUT OF M SECONDS
M = 4 * 2↑T
THUS, THE USER MAY SPECIFY A TIMEOUT FROM 8 TO 512 SECONDS.
IF T = 0, THEN THE DEFAULT IS 30 SECONDS.
FORMAT OF THE ARGUMENT LIST: (EXCEPT AS OTHERWISE NOTED)
E: SIXBIT /LOGICAL NAME/
EXP STATUS/ERROR CODES
EXP SOCKET NUMBER
exp Foreign network/host/imp number ;[96bit]
EXP FOREIGN SOCKET NUMBER
\
.UUDEV==0
.UUSTT==1
.UUSKT==2
.UUHST==3
.UURMT==4
.UULST==4 ; length of block
PUUTIM: POINT 3,P1,10 ;POINTER TO GET TIMEOUT FIELD
; socket constants:
; bottom 3 bits of a port are user controlled, that leaves 13 bits for
; the program controlled part. high bit is used to detect overflow.
sk.lcl==7 ; low 3 bits are user controlled
FreOvr==10000 ; how to detect wrap around
FreLsh==3 ; make room for low 3 bits
FrePrt==400 ; 0-377 are assigned.
FreMin==FrePrt ; add on the value of the last ARPA
; assigned port to avoid these.
FreMch==177770 ; what bits are important for
; detecting ports in the same
; group of 8.
NOWAITS< ;⊗ IMPUUO IMPUU1 ImpUU2 ImpUU3 ImpUU4
IMPUUO::PUSHJ P,SAVE4## ;SAVE P1, P2, P3, P4
MOVE P1,T1 ;PERMANENT COPY OF USER STUFF
HRR M,P1 ;REL ADDRESS OF ARG BLOCK
LDB T3,[POINT 7,P1,17] ;GET THE FUNCTION CODE
MOVSI T1,-UUOLEN ;SEARCH UUO TABLE
MOVE p4,UUOTAB(T1) ;GET THE TABLE ENTRY
LDB T2,[POINT 7,p4,17];GET THE CODE
CAME T2,T3 ;THIS IT?
AOBJN T1,.-3 ;NO
JUMPGE T1,ERRILU ;JUMP IF NOT THERE
MOVEI T1,JP.IMP ;TEST PRIVILEGES
PUSHJ P,PRVBIT## ;SUPER IMP?
JRST IMPUU1 ;YES
TLZ P1,(IF.PRV) ;NO--DISABLE PRIVILEGED IMPUUO FLAGS
TLNE p4,UU.PVI ;REQUIRED?
JRST ERRPRV ;YES--ERROR
MOVEI T1,JP.NET ;SETUP TO TEST NETWORK ACCESS PRIVILEGES
TLNE p4,UU.PVN ;NET PRIVILEGES REQUIRED?
PUSHJ P,PRVBIT## ;YES, GOT THEM?
JRST IMPUU1 ;YES OR NOT NEEDED
JRST ERRPRV ;NO
;HERE TO GO AHEAD WITH THE UUO DISPATCH
IMPUU1: HRRZ T1,P1 ;ADDRESS CHECK THE ARGUMENTS
CAIGE T1,↑D16-.UULST ;IN ACS?
JRST ImpUU2 ;YES, OK.
PUSHJ P,IADRCK##
JRST ERRADR ;ADDRESS CHECK
MOVEI T1,.UULST(P1)
PUSHJ P,IADRCK##
JRST ERRADR
ImpUU2: tlnn p4,uu.NUp ;(260) must have a working network?
jrst ImpUU3 ;(260) no. don't check.
skipe OKFlag## ;(260) is it working?
skipe StopFl## ;(260) yes. are we coming down?
jrst ErrNNU ;(260) either not up or going down
ImpUU3: TLNE p4,UU.DNU ;NEED TO SETUP DDB?
JRST ImpUU4 ;NO
PUSHJ P,SETDDB ;YES, DO IT
JRST cpopj## ;ERROR
ImpUU4: TLNN p4,UU.INT ;INTERRUPTS ALLOWED?
ScnOff ;NO. LET NOTHING INTERFERE
PUSHJ P,(p4) ;CALL THE ROUTINE
skipa ; non-skip return, please.
aos (p) ; pass back the good return.
tlnn p4,uu.int ; did we shut down dangerous interrupts?
ScnOn ; yes. allow them again.
popj p, ; return as set up
; register setup at the time of UUO dispatch:
; f - IMPDDb
; w - PDB
; p2 - local port, if any
; p4 - dispatch bits. these must be preserved.
>;NOWAITS
;MACRO FOR BUILDING THE DISPATCH TABLE ;⊗ ZZ ZZ UU.PVN UU.PVI UU.ASD UU.NDB UU.INT UU.DNU uu.NUp
NOWAITS<
DEFINE U(C,DD,F)<
ZZ==0
IRP F,<
ZZ==ZZ!UU.'F
>
.U'DD==↑D'C
ZZ+↑D<C> ,, DD'S
>
>;NOWAITS
IFWAITS<
;JJW - generate external symbols giving table words for each code.
DEFINE U(C,DD,F)<
ZZ==0
IRP F,<
ZZ==ZZ!UU.'F
>
.W'DD==:<ZZ+↑D<C> ,, DD'S>
>
>;IFWAITS
;THE DEFINITIONS OF THE VARIOUS BITS AND FIELDS
UU.PVN==(1B1) ;NETWORK PRIVILEGES REQUIRED
UU.PVI==(1B2) ;SUPER IMP PRIVILEGES REQUIRED
UU.ASD==(1B3) ;MUST CONSOLE ASSIGN AN IMP DEVICE
UU.NDB==(1B4) ;ALLOWED TO GET A FREE DDB
UU.INT==(1B5) ;INTERRUPTS NEED NOT BE DISABLED
UU.DNU==(1B6) ;DDB NOT USED (DON'T CALL SETDDB BEFOREHAND)
uu.NUp==(1b7) ;(260) network must be up to perform this UUO.
IFWAITS<INTERN UU.PVN,UU.PVI,UU.ASD,UU.NDB,UU.INT,UU.DNU,UU.NUP>
;THE DISPATCH TABLE ;⊗ UUOTAB UUOLEN
IFWAITS<INTERN UUOTAB>
UUOTAB:
NOWAITS<
U 00,STAT,<>
>;NOWAITS
; U 01,CONN,<PVN,ASD,NDB>
; U 02,CLOS,<PVN,ASD>
U 03,CONN,<PVN,ASD,NDB,NUp> ;(260)
U 04,CLOS,<PVN,ASD,Int,NUp> ;(260)
U 05,LIST,<PVN,ASD,NDB,NUp> ;(260)
U 06,REQU,<PVN,ASD,NDB,NUp> ;(260)
U 07,TALK,<PVN,ASD,NUp> ;(260)
; U 08,TRAN,<PVN,ASD>
;(temp) U 09,PINT,<PVN,ASD,NUp> ;(260)
;(temp) U 10,AINT,<PVN,ASD,NUp> ;(260)
U 11,VERS,<INT,DNU>
U 12,DEAS,<PVN,ASD,Int>
U 13,PHST,<INT,DNU>
; U 14,CDDB,<>
; U 15,PGVB,<PVN,ASD,NUp> ;(260)
NOWAITS< ;may change
U 16,ITTY,<DNU>
>;NOWAITS
U 17,XPWT,<PVN,ASD,INT,NUp> ;(260)
U 18,PESC,<INT,DNU>
U 19,RESC,<INT,DNU>
U 20,PPAR,<PVN,ASD>
U 21,RPAR,<PVN,ASD>
U 22,XSTS,<DNU,Int> ; we turn off interrupt when we want
;(temp) U 23,TRAC,<PVN,ASD>
;(temp) U 24,PIAL,<PVN,ASD>
;(temp) U 64,PNOP,<PVI,DNU,NUp> ;(260)
;(temp) U 65,RSET,<PVI,DNU,NUp> ;(260)
; U 66,PALL,<PVI,ASD,NUp> ;(260)
; U 69,PECO,<PVI,DNU,NUp> ;(260)
U 70,INIS,<PVI,Int,DNU>
U 71,KILL,<PVI,INT,DNU>
U 72,RAIS,<PVI,INT,DNU>
; U 73,ERRO,<PVN,DNU>
IFN FTAIMP,< ;DK/OCT 75
;DO IMP IACCOUNTING
U 81,IACT,<PVI,DNU>
>
UUOLEN==.-UUOTAB
NOWAITS< ;It appears this code is never called. ;⊗ ERRCHK
; ERROR CODES -- RETURNED IN E+1 ON NON-SKIP RETURN
;HERE TO TEST FOR REMOTE HOST DOWN BEFORE DECLARING SYSTEM
; FAILURE.
ERRCHK: movx t1,TrgDwn ; get target down flag
tdne t1,ImpIOS(f) ; is it set?
JRST ERRDWN ; yes. target was down
JRST ERRSOF ; no. connection was reset.
>;NOWAITS
DEFINE ERRCOD(M,C) < ;⊗ ERRLST ERRXIT ErrSet
E.'M== .-ERRLST
ERR'M: JSP T1,ERRXIT
>
NOWAITS<
ERRLST:
errcod ILU, ILLEGAL(UNIMPLEMENTED) UUO
errcod NSD, NO SUCH DEVICE
errcod DNA, DEVICE NOT AVAILABLE
errcod LNU, LOGICAL NAME ALREADY IN USE
errcod STT, STATE ERROR (WRONG STATE FOR THIS FUNCTION)
errcod SOF, SOCKET OPENING failure (RESET)
errcod SYS, SYSTEM ERROR
; errcod ABT, A RFC WAS ABORTED
ErrCod CGT, Can't get there from here
; errcod REQ, THE REQUEST DOESNT MATCH YOUR RFC
errcod NES, not enough internal buffer space
errcod SKT, SOCKET NUMBER IN USE
errcod HST, ILLEGAL HOST NUMBER
errcod DWN, REMOTE HOST DOWN OR NOT ON NET
errcod ADR, ADDRESS CHECK IN CALLI ARG LIST
ERRCOD TIM, TIMEOUT
ERRCOD PAR, PARAMETER SPECIFICATION ERROR
ERRCOD NCI, TTY NOT CONNECTED TO IMP
ERRCOD QUO, QUOTE OR ESCAPE ILLEGAL OR NOT DISTINCT
ERRCOD PRV, NOT PRIVILEGED TO DO OPERATION
ErrCod NAI, device is not an IMP
ErrCod NNU, ;(260) Network Not Up
ErrCod DUR, destination unreachable (code in <lh>)
>;NOWAITS
IFWAITS<
ERRLST:
errcod ILU, ILLEGAL(UNIMPLEMENTED) UUO
errcod SKT, SOCKET NUMBER IN USE (SIU in WAITS)
errcod CCS, ;Can't change socket numbers
errcod SYS, SYSTEM ERROR
errcod NLA, ;No link available (won't ever happen)
errcod ILB, ;Illegal byte size
ErrCod NNU, ;(260) Network Not Up (IDD in WAITS)
errcod GMM, ;Gender mismatch (won't happen)
errcod NSD, NO SUCH DEVICE
errcod DNA, DEVICE NOT AVAILABLE
errcod LNU, LOGICAL NAME ALREADY IN USE
errcod STT, STATE ERROR (WRONG STATE FOR THIS FUNCTION)
errcod SOF, SOCKET OPENING failure (RESET)
; errcod ABT, A RFC WAS ABORTED
ErrCod CGT, Can't get there from here
; errcod REQ, THE REQUEST DOESNT MATCH YOUR RFC
errcod NES, not enough internal buffer space
errcod HST, ILLEGAL HOST NUMBER
errcod DWN, REMOTE HOST DOWN OR NOT ON NET
errcod ADR, ADDRESS CHECK IN CALLI ARG LIST
ERRCOD TIM, TIMEOUT
ERRCOD PAR, PARAMETER SPECIFICATION ERROR
ERRCOD NCI, TTY NOT CONNECTED TO IMP
ERRCOD QUO, QUOTE OR ESCAPE ILLEGAL OR NOT DISTINCT
ERRCOD PRV, NOT PRIVILEGED TO DO OPERATION
ErrCod NAI, device is not an IMP
ErrCod DUR, destination unreachable (code in <lh>)
>;IFWAITS
ERRXIT: SUBI T1,ERRLST+1
ANDI T1,-1 ;GET RID OF LEFT HALF JUNK
; here to store an error code
ErrSet:
NOWAITS<
HRRI M,.UUSTT(P1) ;PUT ERROR CODE HERE
PUSHJ P,PUTWRD##
JRST ADRERR##
>;NOWAITS
IFWAITS<
DPB T1,[POINT 6,STB##(F),35] ;JJW - store error in DDB
>;IFWAITS
POPJ P,
NOWAITS< ;⊗ TRANS RAISS KILLS VERSS INISS IFRSTR
TRANS== ERRILU ;ILLEGAL CODE
;SUBROUTINE TO PUT THE TEN ON THE NETWORK (PRIVILEGED)
RAISS: TROA T1,-1 ;SET FLAG
;SUBROUTINE TO TAKE THE TEN OFF THE NETWORK SOFTLY. (PRIVILEGED)
KILLS: MOVEI T1,1 ;SET FLAG
HRREM T1,IMPUP##
IFN FTAIMP,<
JRST IFRSTR ;INDICATE RESTART IN ACCT DATA
>
IFE FTAIMP,<
JRST CPOPJ1##
>
;SUBROUTINE TO RETURN THE CURRENT SOFTWARE VERSION NUMBERS
VERSS: MOVE T1,[VIMPSR##,,VIPSer##] ; IMP (1822) and IP versions
pushj p,PutWdu## ; store for user
hrlzi t1,VTCPSr ; TCP version
pjrst pw1pj1 ; store that and skip return
;SUBROUTINE TO WIPE EVERYTHING (PRIVILEGED)
INISS: PUSHJ P,DINI+IMPDSP## ;DO 400 RESTART STUFF
IFN FTAIMP,<
IFRSTR: SETZ T1, ;PREPARE ENTRY FOR ACCTNG
MOVEI T2,17 ;IDNICATE RESTART
DPB T2,IFTCOD ;IN T1
PUSHJ P,IFENTR ;MAKE ENTRY
>
JRST CPOPJ1##
>;NOWAITS
IFWAITS<
RAISS:
KILLS:
VERSS:
INISS:
>;IFWAITS
NOWAITS<
;SUBROUTINE TO RETURN EXTENDED STATUS OF AN IMP DEVICE. MORE
; ARGUMENTS MAY BE ADDED WITHOUT INVALIDATING EXISTING PROGRAMS.
; MOVE P1,[REL ADR OF ARGUMENT BLOCK]
; PUSHJ P,XSTSS
; ERROR--CODE IN T1
; NORMAL RETURN--ARGUMENT BLOCK FILLED WITH STATUS INFO.
;BLOCK: N ;NUMBER OF LOCATIONS THAT FOLLOW IN ARG BLOCK
; (0 IS SAME AS ↑O12)
; SIXBIT /DEV/
; N-1 LOCATIONS FOR DATA TO BE RETURNED IN. (IF N IS GREATER THAN
; THE NUMBER OF WORDS PROVIDED BY THE MONITOR, THE REMAINDER
; OF THE BLOCK WILL BE ZEROED).
; note: this UUO was massively changed by TCP
;CURRENTLY-DEFINED INDICES ARE:
; 0 .XSNUM NUMBER OF WORDS THAT FOLLOW
; 1 .XSDEV DEVICE NAME
; 2 .XSJob owning job number
; 3 .XSIST STATE of connection
; 4 .XSILS LOCAL port NUMBER
; 5 .XSIHS HOST
; 6 .XSIRS REMOTE port NUMBER
; 7 .XSPrt protocol
; 7 .XSRWn INPUT window (how much we are giving him)
; 10 .XSSWn OUTPUT window (how much he is giving us)
; 11 .XSIOS RH I/O STATUS WORD (DEVIOS)
; 12 .XSRTT current retranmission timeout time.
XSTSS: PUSHJ P,GETWDU## ;RETURN NUMBER OF USER ARGS ;⊗ XSTSS XSTSS0 XSTSS1 XSTSTB XSTBLN
CAIGE T1,2*<.UULST+1> ;WANT MORE THAN MINIMUM BLOCK?
MOVEI T1,2*<.UULST+1> ;NO, SUPPLY MINIMUM INFO
ADDI T1,(M) ;COMPUTE USER ADR OF LAST WORD OF BLOCK
TRNN T1,777760 ;STILL IN AC'S?
JRST XSTSS0 ;YES, IT'S OK
TRNE M,777760 ;NO, ERROR IF STARTED IN AC'S
PUSHJ P,IADRCK## ; OR IF WENT OUT OF BOUNDS
AOJA P1,ERRADR
XSTSS0: PUSH P,T1 ;SAVE USER ADR OF LAST WORD
AOS M,P1 ;POINT TO DEVICE ARGUMENT
PUSHJ P,SETDDB ;SETUP IMP DDB
pjrst tpopj## ; restore T1 for failure
ScnOff ; make sure to get a consistent picture
PUSHJ P,STATS0 ;RETURN SHORT STATUS, INCL. DEVICE NAME
POP P,P1 ;GET BACK FINAL USER ADR
movei p2,1 ; start with first entry in block.
;LOOP TO PLACE EXTENDED VALUES IN USER BLOCK
XSTSS1: CAIG P1,(M) ;ANY MORE SPACE IN USER BLOCK?
JRST sonpj1## ;NO, SKIP RETURN TO USER
CAILE P2,XSTBLN ;YES, REACHED END OF STATUS INFO?
TDZA T1,T1 ;YES, RETURN ZERO FOR REST OF BLOCK
XCT XSTSTB-1(P2) ;NO, GET NEXT ITEM
PUSHJ P,PUTWD1## ;STORE IN NEXT CELL IN USER BLOCK
AOJA P2,XSTSS1 ;BACK FOR MORE
;TABLE FOR FETCHING EXTENDED STATUS INFORMATION. NOTE THAT IT MAY BE
; APPENDED TO, BUT MAY NOT BE REARRANGED OR ENTRIES DELETED WITHOUT
; INVALIDATING EXISTING PROGRAMS
XSTSTB: move t1,Protcl(f) ; .XSPrt protocol of this connection
MOVE T1,RcvWnd(F) ; .XSRWn receive window size
MOVE T1,SndWnd(F) ; .XSSWn send window size
HRRZ T1,DEVIOS(F) ; .XSIOS DEVICE STATUS BITS
move t1,RTTime(f) ; .XSRTT retransmission time
XSTBLN==.-XSTSTB ;NUMBER OF EXTENDED STATUS ENTRIES
>;NOWAITS
IFWAITS<
XSTSS:
>;IFWAITS
NOWAITS< ;⊗ STATS STATS0 STATS9 STATS1
;SUBROUTINE TO RETURN THE STATUS OF A SIMPLEX CONNECTION
; LOOKS AT IMPDEV(P1) AND LOW BIT OF IMPSKT(P1).
;CALL:
; MOVE P1,[REL ADDRESS OF ARGUMENT LIST
; PUSHJ P,STATS
; ERROR RETURN ...CODE IN T1
; OK RETURN
STATS: AOS (P) ;PRESET SKIP RETURN
;CALLED FROM XSTSS (EXTENDED STATUS) ALSO.
STATS0: HRRI M,.UUDEV(P1) ;ADDRESS OF DEVICE NAME
TLNE P1,(IF.PRV) ;IF IMPORTANT PERSON,
JRST STATS9 ; GIVE HIM LOGICAL NAME
LDB T1,PJOBN## ;GET OWNERS JOB NUMBER
MOVEI T2,ASSCON
TDNE T2,DEVMOD(F) ;OWNED?
CAME T1,.CPJOB## ;BY THIS USER?
JRST STATS1 ;NO
STATS9: SKIPE T1,DEVLOG(F) ;LOGICAL NAME ASSIGNED?
PUSHJ P,PUTWDU## ;YES, RETURN IT
STATS1: move t1,state(f) ; get state
LDB T2,PJOBN## ;GET JOB NUMBER
HRL T1,T2 ; put that in left half
PUSHJ P,PUTWD1## ;RETURN IT TOO
move t1,LclPrt(f) ; get local port
PUSHJ P,PUTWD1## ;RETURN THE port NUMBER
move t1,RmtAdr(f) ; get his address
pushj p,PutWd1## ; store it
move t1,RmtPrt(f) ; get his port
PJRST PUTWD1## ;GIVE IT TO THE USER AND RETURN
>;NOWAITS
NOWAITS< ;Perhaps WAITS should have something like this ;⊗ ITTYS ITTYS1 ITTYS2
;SUBROUTINE TO TRANSLATE BETWEEN IMPS AND CONTROLLING OR CONTROLLED TTYS.
; MOVE M,[REL ADR OF ARG BLOCK]
; MOVE P1,M
; PUSHJ P,ITTYS
; ERROR RETUR--CODE IN T1
; OK RETURN
;THE RESULTS DEPEND ON THE CONTENTS OF THE BLOCK, AS FOLLOWS:
; BEFORE AFTER
; ------ -----
;BLOCK: SIXBIT /IMPN/ BLOCK: SIXBIT /IMPN/
; 0 FLAGS,, TTY LINE #
;BLOCK: 0 BLOCK: SIXBIT /IMPN/
; 0,, TTY LINE # FLAGS,, LINE # OF TTY CROSSPATCHED
; TO IMPN.
;BLOCK: 0 BLOCK: SIXBIT /IMPN/
; -1,, TTY LINE # FLAGS,, LINE # OF TTY CONTROLLED
; BY IMPN.
;FLAGS ARE: BIT 0: IMP CONTROLS TTY (I.E. TTY IS AN ITY)
; BIT 1: TTY PRINTER CROSSPATCHED TO IMP
; BIT 2: TTY KEYBOARD CROSSPATCHED TO IMP
ITTYS: PUSHJ P,GETWDU## ;GET FIRST ARGUMENT FROM USER
JUMPE T1,ITTYS1 ;JUMP IF BLANK
ScnOn ; let DDB stuff do it's stuff
; without these problems
PUSHJ P,SETDDB ;SETUP FOR DDB WORK
jrst [ ; error, not an IMP DDB
ScnOff ; dispatch expects these off
jrst ErrNAI ; give the Not An Imp return
]
ScnOff ; shut down interrupts again
JRST ITTYS3 ;OK, GO PROCESS USING THIS IMP
;HERE IF DEVICE NAME IS BLANK. USE TTY NUMBER ARGUMENT.
ITTYS1: PUSHJ P,GETWD1## ;GET NEXT ARGUMENT
MOVEI T3,(T1) ;ISOLATE LINE NUMBER
CAIL T3,TTPLEN## ;LEGAL?
JRST ERRPAR ;NO
HRRZ U,LINTAB##(T1) ;YES, GET LDB POINTER FOR THAT LINE
JUMPGE T1,ITTYS2 ;JUMP IF USER ASKING FOR CROSSPATCHED IMP
CAIL T3,ITYFST## ;NO, WANT CONTROLLING IMP. IS THIS
CAIL T3,ITYFST##+ITYN## ; AN ITY?
JRST ERRNCI ;NO
SKIPA F,ITYOFS##(T1) ;YES, GET ADR OF IMP CONTROLLING ITY
ITTYS2: HRRZ F,LDBIMP##(U) ;HERE TO GET ADR OF CROSSPATCHED IMP
JUMPE F,ERRNCI ;ERROR IF NO IMP CONNECTION TO TTY
; fall into next page
;HERE WITH DESIRED IMP DDB POINTED TO BY F ;⊗ ITTYS3
ITTYS3: MOVSI U,TTYJOB+TTYPTR+TTYKBD ;BITS TO TEST FOR IMP CONNECTION
TDON U,TTYLIN(F) ;ARE ANY SET? IF SO, SET U TO LDB
JRST ERRNCI ;NO--ERROR
HRRI M,(P1) ;RESET TO START OF USER ARGLIST
MOVE T1,DEVNAM(F) ;FETCH PHYSICAL IMP NAME
PUSHJ P,PUTWDU## ;RETURN IT
LDB T1,LDPLNO## ;FETCH LINE NO OF CONNECTED TTY
HLL T1,TTYLIN(F) ;RETURN FLAGS
PJRST PW1PJ1 ;RETURN SECOND ARG AND SKIP
>;NOWAITS
repeat 0,< ; should be simple ;⊗ PIALS
;ROUTINE TO SET DESIRED ALLOCATION FOR AN OPEN INPUT CONNECTION
; MOVE P1,[ADDRESS OF ARGUMENT LIST]
; PUSHJ P,PIALS
; ERROR--CODE IN T1
; NORMAL RETURN
; THE .IBHST AND .IBRMT WORDS SPECIFY THE MESSAGE AND BIT ALLOCATIONS
; TO BE USED SUBSEQUENTLY ON THE CONNECTION. NOTE THAT THESE ARE
; RESET TO SMALL VALUES BY THE 'TALK' OPERATION, SO 'PIAL' SHOULD
; BE EXECUTED AFTER 'TALK'
PIALS: PUSHJ P,GETWD1## ;GET DESIRED MESSAGE ALLOCATION IN .IBHST
CAIGE T1,1 ;AT LEAST 1?
MOVEI T1,1 ;NO, MAKE IT 1
CAILE T1,.ALMSX ;WITHIN LIMIT?
MOVEI T1,.ALMSX ;NO, USE LIMIT
DPB T1,PIALMS ;STORE DESIRED ALLOCATION
PUSHJ P,GETWD1## ;NOW GET BIT ALLOCATION IN .IBRMT
LDB T2,PIBYTE ;GET CONNECTION BYTESIZE
CAIGE T1,(T2) ;AT LEAST ONE BYTE'S WORTH?
MOVEI T1,(T2) ;NO, MAKE IT SO
CAILE T1,.ALBTX ;WITHIN LIMIT?
MOVEI T1,.ALBTX ;NO, USE LIMIT
DPB T1,PIALBT ;STORE DESIRED BIT ALLOCATION
JRST CPOPJ1## ;OK RETURN
> ; end of repeat 0
;ROUTINE TO WAIT UNTIL THE CONNECTION BETWEEN A LOCAL TTY AND ;⊗ XPWTS PPARS RPARS
; A CROSSPATCHED IMP IS BROKEN, EITHER BY THE ESCAPE HAVING BEEN TYPED
; OR BY THE CONNECTION BEING CLOSED OR RESET.
; MOVE M,[REL ADR OF ARGUMENT BLOCK]
; PUSHJ P,XPTWS
; ERROR RETURN--CODE IN T1
; OK RETURN AFTER WAITING FOR CROSSPATCH TO BE BROKEN
;JJW - calls to SETACT, WSYNC, CLRACT will have to be fixed for WAITS
XPWTS: MOVSI P1,TTYXWT ;SETUP WAITING-FOR-CROSSPATCH BIT
IORM P1,TTYLIN(F) ;SET IN DDB
DPB P1,PDVTIM## ;SET TIMER TO INFINITY
MOVE S,DEVIOS(F) ;GET I/O STATUS
PUSHJ P,SETACT## ;SET IOACT SO WSYNC WILL WORK
MOVSI T1,TTYKBD!TTYPTR ;BITS THAT MARK TTY-IMP CROSSPATCH
TDNE T1,TTYLIN(F) ;IS THE IMP CROSSPATCHED?
PUSHJ P,WSYNC## ;YES, WAIT UNTIL CROSSPATCH BROKEN
ANDCAM P1,TTYLIN(F) ;CLEAR WAITING-FOR-CROSSPATCH BIT
PUSHJ P,CLRACT## ;MAKE SURE IOACT IS CLEAR
JRST CPOPJ1 ;OK RETURN
;ROUTINES TO SET AND READ THE USER-DEFINED CONNECTION PARAMETER WORD.
; THIS WORD IS INTENDED FOR USE BY IMPCOM TO SAVE AND RESTORE ECHOING
; CHARACTERISTICS, ETC.
; MOVE M,[REL ADR OF ARG BLOCK]
; PUSHJ P,PPARS (TO SET) OR RPARS (TO READ)
; ERROR--CODE IN T1
; OK
;BLOCK: SIXBIT \IMPN\
; EXP PARAMETER WORD
PPARS: HRRI M,1(P1) ;GET USER PARAMETER
PUSHJ P,GETWDU##
MOVEM T1,USRPAR(F) ;STORE IN DDB
JRST CPOPJ1 ;OK RETURN
RPARS: HRRI M,1(P1) ;POINT TO 2ND WORD OF PARAMETER BLOCK
MOVE T1,USRPAR(F) ;PICK UP PARAMETER WORD
PJRST PWUPJ1 ;RETURN IT TO THE USER AND SKIP
;ROUTINES TO SET AND READ THE VARIOUS QUOTE AND ESCAPE CHARACTERS ;⊗ PESCS RESCS ALLQUO ALLQU1
; FOR THE CONTROLLING TTY.
; MOVE M,[REL ADR OF ARG BLOCK]
; PUSHJ P,PESCS (TO SET) OR RESCS (TO READ)
; ERROR RETURN--CODE IN T1
; OK RETURN
;BLOCK: EXP QUOTE CHARACTER
; EXP SHIFT CHARACTER
; EXP LOCAL ESCAPE CHARACTER
; EXP NETWORK ESCAPE CHARACTER
PESCS: JSP P2,ALLQUO ;DO THE FOLLOWING FOR EACH ARGUMENT
PUSHJ P,GETWDU## ;GET THE NEXT USER ARGUMENT
HRRZ T3,T1 ;COPY THE CHARACTER
PJRST QUOCHK## ;CHECK IF LEGAL AND STORE IN LDB IF SO
RESCS: JSP P2,ALLQUO ;DO THE FOLLOWING FOR EACH ARGUMENT
LDB T1,LDPQTB##(T4) ;FETCH A QUOTE OR ESCAPE CHAR FROM THE LDB
PJRST PWUPJ1 ;GIVE IT TO THE USER AND SKIP RETURN
;AUXILIARY ROUTINE TO CALL ANOTHER ROUTINE FOR EACH QUOTE OR ESCAPE
; CHARACTER ARGUMENT
; MOVE P2,[ADDRESS OF ROUTINE TO CALL]
; PUSHJ P,ALLQUO
; ERROR RETURN--CODE IN T1
; OK RETURN--CALL SUCCESSFULLY ITERATED OVER ALL CHARACTERS
;THE CALLEE IS PROVIDED WITH THE FOLLOWING AC'S SETUP:
; U THE TTY LDB ADDRESS
; T4[RH] THE QUOTE INDEX (INTO THE QUOTE POINTER TABLE)
; M UPDATED TO POINT TO NEXT USER ARGUMENT
ALLQUO: SKIPE U,TTYTAB##(J) ;FETCH THIS USER'S TTY DDB ADDRESS
HRRZ U,DDBLDB##(U) ;FOLLOW LINK TO LDB
JUMPE U,ERRDNA ;ERROR IF DETACHED OR NONEXISTENT
MOVSI T4,MNQUPT## ;SETUP -# OF QUOTE POINTERS,,0
ALLQU1: PUSHJ P,(P2) ;CALL GIVEN ROUTINE
JRST ERRQUO ;ERROR RETURN--RETURN CODE
AOBJP T4,CPOPJ1 ;INCREMENT INDEX. DONE?
AOJA M,ALLQU1 ;NO, DO ANOTHER ARGUMENT
;SUBROUTINE FOR SETTING UP A SIMPLEX CONNECTION. ;⊗ CONNS GotArp
;CALL:
; MOVE P1,[CODE,,RELATIVE ADDRESS OF ARGUMENT BLOCK]
; MOVE M,[REL ADDRESS OF ARGS (R) ]
; PUSHJ P,CONNS
; ERROR RETURN ...CODE IN T1
; OK RETURN
CONNS: skiple t1,State(f) ; get the state. is it closed state?
cain t1,S%List ; or listen?
skipa ; yes.
jrst ErrStt ; wrong state for this.
; set up DDB
NOWAITS<
pushj p,GetWd1## ; get host number (can be 32 bits)
>;NOWAITS
IFWAITS<
EXCTUX <MOVE T1,HLOC##(M)>
>;IFWAITS
jumpe t1,ErrHst ; can't be zero
txnn t1,NetMsk ; is there a network set?
txo t1,ArpAdr ; no. set ARPAnet address
movem t1,RmtAdr(f) ; store remote address
skipe NetAdr(f) ; need an arpanet address?
jrst GotArp ; nope. already read one off incoming
pushj p,Target ; find an ARPAnet address to
; try to get this sent.
jrst ErrCGT ; can't get there from here:
; couldn't find a route.
movem t1,NetAdr(f) ; save that in DDB
GotArp:
NOWAITS<
pushj p,GetWd1## ; get remote port
>;NOWAITS
IFWAITS<
EXCTUX <MOVE T1,FSLOC##(M)>
>;IFWAITS
andx t1,<1←↑d16>-1 ; trim down to 16 bits
movem t1,RmtPrt(f) ; save it in DDB
PUSHJ P,MAKMYS ;MAKE SOCKET
JRST ERRSKT ;ILLEGAL
pushj p,prpDDB ; set required areas of DDB
pushj p,GetISS ; get an initial send sequence number
movem t1,SndISS(f) ; save it in the DDB
movem t1,SndNxt(f) ; and make it the current
; sequence number
aos t1 ; account for SYN
movem t1,SndLWd(f) ; and make that the first message
setzm SndWnd(f) ; we have no idea how much we
; can send until we hear.
setzm SndLst(f) ; no last message yet (force
; this into retransmission queue)
movx t1,TC%Syn ; get SYN bit and ACK the SYN we got
iorm t1,SndBts(f) ; set it in bits to be sent
pushj p,SndMsg## ; send message now.
jrst errNES ; give not enough space return
movei t1,S%SynS ; we've sent a SYN
movem t1,State(f) ; save our new state
pushj p,EstbWt ; wait for established (T1 is loaded)
popj p, ; failed. error already
; returned to user.
jrst cpopj1## ; we're established: skip return
;SUBROUTINE TO DROP A CONNECTION. ;⊗ CLOSS PCLSSD ClsFls ClsEst
;CALL:
; PUSHJ P,CLOSS
; ERROR RETURN -- CODE IN T1
; OK RETURN
CLOSS:
NOWAITS<
SKIPGE TTYLIN(F) ;JOB CONTROL?
TLNE P1,(IF.PRV) ;YES, ENABLED SUPER-IMP PRIVILEGES?
JRST PCLSSD ;NO JOB CONTROL OR CORRECT PRIVILEGES
PUSHJ P,PRVJ## ;TEST FOR LOGIN, LOGOUT
jrst PCLSsd ;OK TO SUICIDE
JRST ERRDNA ;NOT AVAILABLE TO CASUAL PROG.
>;NOWAITS
PCLSSD: move t1,State(f) ; get state of the connection
dispat (t1,cpopj1##,<<S%List,<jrst ClsFls>>
,<S%SynS,<jrst ClsFls>>
,<S%SyRP,<jrst ClsEst>>
,<S%SyRA,<jrst ClsEst>>
,<S%Estb,<jrst ClsEst>>
,<S%ClsW,<jrst ClsEst>>
>)
ClsFls: pushj p,DDBFls## ; flush DDB
pushj p,DDBRel## ; return it to free pool
pjrst cpopj1## ; and give a good return
ClsEst: movx t1,TC%Fin ; set FIN bit
iorm t1,SndBts(f) ; set it in bits to be sent
scnoff ; no interrupts
pushj p,SndMsg## ; send message now.
jrst [ ; failed
scnon ; enable interrupts
pjrst errNES ; not enough buffer space for message
]
scnon ; interrupts ok now.
move t1,State(f) ; get state again
movei t2,S%Fin1 ; assume it's establish, so
; we're going to FIN-wait-1
caie t1,S%Estb ; are we established?
movei t2,S%LAck ; no, close-wait. goto last-ACK
movem t2,State(f) ; save the new state
pjrst cpopj1## ; and give a good return
;SUBROUTINE TO DEASSIGN A DEVICE AFTER IT HAS HAD ;⊗ DEASS
; BOTH SIDES CLOSED.
;CALL:
; PUSHJ P,DEASS
; ERROR RETURN ...CODE IN T1
; OK RETURN... DEVICE DEASSIGNED
DEASS: skiple t1,State(f) ; get state
jrst ERRSTT ; not a closed state. not allowed.
PUSHJ P,DDBFls## ;NOW RELEASE IT
pushj p,DDBRel## ; back to free pool
JRST CPOPJ1## ;SKIP RETURN
;SUBROUTINE TO PUT A SOCKET IN THE LISTENING STATE ;⊗ LISTS Lists2 LISTS1
;THE SOCKET MUST BE CLOSED, LISTENING, OR IN RFC IN STATE.
;CALL:
; PUSHJ P,LISTS
; ERROR RETURN -- CODE IN T1
; OK RETURN
LISTS:
NOWAITS<
PUSHJ P,GETWD1## ;GET remote host
>;NOWAITS
IFWAITS<
EXCTUX <MOVE T1,HLOC##(M)>
>;IFWAITS
jumpe t1,Lists2 ; don't munge it if he wants default.
txnn t1,NetMsk ; does it have a network number?
txo t1,ArpAdr ; no. default to our network.
Lists2: movem t1,RmtAdr(f) ; and save it
NOWAITS<
PUSHJ P,GETWD1## ;GET REMOTE SOCKET NUMBER
>;NOWAITS
IFWAITS<
EXCTUX <MOVE T1,FSLOC##(M)>
>;IFWAITS
andx t1,<1←↑d16>-1 ; trim down to 16 bits
movem t1,RmtPrt(f) ; and save it
move t1,State(f) ; get current state
cain t1,S%List ; already listening?
JRST LISTS1 ; YES. don't clobber port we have.
caie t1,S%Clos ; closed?
jrst ErrStt ; nope. must have slipped to
; a more advanced state while
; he wasn't looking (that's
; what he gets for using
; Listen instead of Request).
PUSHJ P,MAKMYS ;MAKE A port
JRST ERRSKT ;ILLEGAL
LISTS1:
pushj p,PrpDDB ; prepare DDB for action
MOVEI T1,S%List ; this is Listen state now
movem t1,State(f) ; new state
JRST CPOPJ1##
;SUBROUTINE TO GET A SOCKET REQUEST ;⊗ REQUS PW1PJ1
;IF THERE IS NONE IN YET, THE JOB WAITS FOR ONE.
;CALL:
; PUSHJ P,REQUS
; ERROR RETURN -- CODE IN T1
; OK RETURN
REQUS: PUSHJ P,LISTS ;MAKE SURE LISTENING OR RFC IN
POPJ P, ;ERROR!!
JUMPL P1,CPOPJ1 ;NO WAIT IF FLAG ON
movei t1,S%List ; waiting to get out of listen state
PUSHJ P,EstbWt ; wait to get into established
; or better.
popj p, ; error return (error code
; already given to user)
NOWAITS<
hrri m,.uuhst(p1) ; point at host word
move t1,RmtAdr(f) ; get host we accepted
pushj p,PutWdu## ; store host
>;NOWAITS
IFWAITS<
move t1,RmtAdr(f) ; get host we accepted
EXCTUU <MOVEM T1,HLOC##(M)>
>;IFWAITS
move t1,RmtPrt(f) ; get remote port number
NOWAITS<
;HERE TO STORE IN THE NEXT WORD OF THE USER'S BLOCK, THEN SKIP RETURN
PW1PJ1: PUSHJ P,PUTWD1## ;RETURN IT
JRST CPOPJ1## ;OK RETURN
>;NOWAITS
IFWAITS<
EXCTUU <MOVEM T1,FSLOC##(M)>
JRST CPOPJ1##
>;IFWAITS
;SUBROUTINE TO CONNECT A DUPLEX IMP CONNECTION TO ;⊗ TALKS TalkOK
; THE USER'S LOCAL TELETYPE.
;CALL:
; MOVE P1,[ADDRESS OF ARGUMENT LIST]
; PUSHJ P,TALKS
; ERROR RETURN ...CODE IN T1
; OK RETURN... TELETYPE CONNECTED
TALKS:
skipe IBfThs(f) ; anything waiting to be read?
jrst TalkOK ; yes. always legal to crosspatch.
move t1,State(f) ; get state
CAIE T1,S%Estb ; established?
cain t1,S%ClsW ; or close wait?
jrst TalkOK ; yes. data can still flow
caie t1,S%Fin1 ; Fin-1?
cain t1,S%Fin2 ; or 2?
jrst TalkOK ; yes. he can still send to us
JRST ERRSTT ; bad state to crosspatch
TalkOK: SKIPGE TTYLIN(F) ;JOB-CONTROLLING IMP?
JRST ERRDNA ;YES, DON'T ALLOW, ELSE WIERD LOOP
MOVSI T1,(IECHO) ;SET UP FOR TWEAK
MOVE T2,[ANDCAM T1,TELOWD(F)];NORMALLY A CLEAR
skipGE P1 ;BUT SOMETIMES NOT IF /ECHO SWITCH USED
HRLI T2,(IORM T1,(F)); (ASSUMING HERE FROM IMPCOM)
XCT T2 ;DO IT
PUSHJ P,IMPTTY## ;SET UP THE CONNECTION
; PUSHJ P,TLNSET ;SPECIFY SMALL ALLOCATIONS FOR TTY'S
JRST CPOPJ1## ; AND RETURN
repeat 0,< ; might be fun to do sometime..... ;⊗ TRACS TRCTAB PINTS AINTS PECOS
;SUBROUTINE TO ENABLE/DISABLE SENDING THE TRACE BIT ON ALL OUTPUT
; MESSAGES THRU THIS SOCKET.
; MOVE P1,[ADDRESS OF USER ARGUMENT LIST]
; PUSHJ P,TRACS
; ERROR--CODE IN T1
; OK RETURN -- TRACE ENABLED OR DISABLED
;BLOCK: SIXBIT /DEV/
; EXP SWITCH (0 TO DISABLE, NONZERO TO ENABLE)
TRACS: TRNN P2,1 ;CAN ONLY DO THIS FOR OUTPUT CONNECTIONS
JRST ERRPAR ;OOP
HRRI M,.UUSTT(P1) ;OK, POINT TO TRACE SWITCH
PUSHJ P,GETWDU## ;GET IT FROM USER CORE
JUMPE T1,.+2 ;JUMP IF TURNING OFF
MOVEI T1,1 ;ON
MOVSI T2,(TRCENB) ;SET OR CLEAR TRACE ENABLE BIT IN DDB
XCT TRCTAB(T1) ;ANDCAM OR IORM
JRST CPOPJ1## ;SKIP RETURN TO USER
TRCTAB: ANDCAM T2,ostat(F) ;[96bit] DISABLE
IORM T2,ostat(F) ;[96bit] ENABLE
;still in repeat 0
;SUBROUTINE TO SEND AN INTERRUPT ON THE SPECIFIED SOCKET
;CALL:
; MOVE P1,[ADDRESS OF ARGUMENT LIST]
; PUSHJ P,PINTS
; ALWAYS RETURN HERE
PINTS: PUSHJ P,GETSTT ;GET THE STATE
CAIE T1,.ISOPN ;OPEN?
JRST ERRSTT ;NO
PUSHJ P,GETHST ;GET THE HOST NUMBER
PUSHJ P,NDBSTU ;SET UP NCP UUO DDB
TRNN P2,1 ;MY RECEIVE SOCKET?
PUSHJ P,PINR ;YES, SEND "INR"
TRNE P2,1
PUSHJ P,PINS ;NO, SEND "INS"
PUSHJ P,OUTXX ;SEND IT
JRST CPOPJ1## ;OK RETURN
> ; end of one repeat 0
REPEAT 0,<
;SUBROUTINE TO SPECIFY THE USERS TRAP ADDRESS FOR INCOMING
; INTERRUPTS.(NOT FULLY IMPLEMENTED)
;CALL:
; MOVE P1,[ADDRESS OF ARGUMENT LIST]
; PUSHJ P,AINTS
; ERROR RETURN ...CODE IN T1
; OK RETURN... ADDRESS DEPOSITED IN DDB
AINTS: PUSHJ P,GETSTT ;GET STATE
CAIE T1,.ISOPN ;BETTER BE OPEN
JRST ERRSTT ;IT ISNT
PUSHJ P,GETWD1## ;GET HOST NUMBER FIELD(DISPATCH ADDRESS)
HRRZS T1
PUSHJ P,SETINT ;SET IT IN THE DDB
JRST CPOPJ1## ;OK RETURN
;STILL IN REPEAT 0
;SUBROUTINE TO SEND A "ECO" MESSAGE AT UUO LEVEL(PRIVILEGED)
PECOS: PUSHJ P,GETHS1 ;GET, TEST HOST NUMBER
JRST ERRHST ;FOUL-UP
PUSHJ P,NDBSTU ;SET UP UUO DDB FOR NCP
PUSHJ P,PECO ;SEND IT
PUSHJ P,OUTXX
JRST CPOPJ1##
> ;END REPEAT 0
;SUBROUTINE TO RETURN THE LOCAL HOST AND IMP PARAMETERS ;⊗ PHSTS PWUPJ1
; PARAMETERS:
; In .IbDev (.UUDev):
; bits 1-8: # OF ITY'S IN SYSTEM
; bits 9-17: # OF IMPS
;(246) right half: tty number of first ITY.
; In .IbStt (.UUStt):
; BIT 0: 1 IF IMP IS NOT READY
; In .IbHst (.UUHst)
; bits 18-35: LOCAL HOST'S NETWORK ADDRESS
PHSTS:
hrlzi t1,itimpl## ;[96bit] get the ity/imp count
hrri t1,ityfst## ;(246) and the first ITY number.
pushj p,putwdu## ;[96bit] put in first word of block
setz t1, ;[96bit] (more imp status can go in
; around here somewhere.)
skipn okflag## ;[96bit] imp up?
tlo t1,400000 ;[96bit] no: set flag
pushj p,putwd1## ;[96bit] put that in the second word
move t1,IPAddr## ;[96bit] get my site number
hrri m,.uuhst(p1) ;[96bit] point to host word
;HERE TO RETURN A WORD TO THE USER'S BLOCK, THEN SKIP RETURN
PWUPJ1: PUSHJ P,PUTWDU## ;RETURN IT
JRST CPOPJ1 ;OK RETURN
REPEAT 0,< ;THESE FUNCTIONS WERE NEVER DEBUGGED ;⊗ PALLS PALLS1 PGVBS RSETS PNOPS PNOPS1 GETHS1
;HERE TO SEND AN "ALL" TYPE MESSAGE(PRIVILEGED)
PALLS: PUSHJ P,GETSTT ;GET STATE
CAIE T1,.ISOPN ;OPEN?
JRST ERRSTT ;NO
PUSHJ P,GETWD1## ;GET MESSAGES
MOVE P3,T1
PUSHJ P,GETWD1## ;GET BITS
MOVE T2,P3
TRNN P2,1 ;MY SEND?
JRST PALLS1 ;NO
ADDM T1,OALBIT(F)
ADDM T2,OALMES(F)
PUSHJ P,IMPALL## ;TELL IMP SERVICE
JRST CPOPJ1##
;HERE TO SEND "ALL" TO REMOTE HOST
PALLS1: MOVNS T1
ADDM T1,IALBIT(F) ;DECREMENT INPUT ALLOCATION COUNTERS
MOVNS T2 ; SO THEY WILL BE INCREASED AT CLOCK
ADDM T2,IALMES(F) ; OR INTERRUPT LEVEL.
JRST CPOPJ1##
;STILL IN REPEAT 0
;HERE TO SEND A "GVB" MESSAGE TO RE-INITIALIZE ALLOCATION.
PGVBS: LDB T1,PIHOST ;GET HOST NUMBER
PUSHJ P,NDBSTU ;SET UP AN NCP DDB
PUSHJ P,PGVB ;BUILD THE MESSAGE
PUSHJ P,OUTXX ;SEND IT
JRST CPOPJ1##
> ;END REPEAT 0
repeat 0,< ; can't do this in TCP
;ROUTINE TO RESET A SPECIFIED HOST (PRIVILEGED)
RSETS: PUSHJ P,GETHS1 ;GET AND TEST HOST NUMBER
JRST ERRHST ;NO GOOD
PUSH P,T1 ;SAVE IT
PUSHJ P,HSTCLR ;WIPE THE HOST LOCALLY
POP P,T1 ;GET BACK HOST NUMBER
PJRST PNOPS1 ;CAUSE 'RST' TO BE SENT BY QUEUEING A NOP
;HERE TO SEND A "NO-OP" TO THE SPECIFIED HOST
PNOPS: PUSHJ P,GETHS1 ;GET AND TEST HOST NUMBER
JRST ERRHST ;ERROR
PNOPS1: PUSHJ P,NDBSTU ;SET UP A DDB
PUSHJ P,PNOP ;FORM THE MESSAGE
PUSHJ P,OUTXX ;SEND IT
JRST CPOPJ1## ;RETURN
;SUBROUTINE TO GET THE HOST FIELD AND TEST IT.
GETHS1:
hrri m,.uuhst(p1) ;[96bit] set to host word
pushj p,g.uuht ;[96bit] get host word
jumpg t1,cpopj1## ;[96bit] greater than 0 is OK.
popj p, ;[96bit] 0 is not OK.
> ; end of repeat 0
NOWAITS< ;WAITS calls T10DDB to do some of this. ;⊗ SETDDB SETDD3 SETDD0 SETDD2 SETDD1 SetDD4
;SET UP A DDB FOR UUO WORK
;CALL:
; MOVE P1,[ XWD CODE, REL ADDRESS OF ARGUMENT LIST]
; MOVE M,[RELADR(R)]
; MOVE J,JOB NUMBER
; PUSHJ P,SETDDB
; ERROR RETURN -- CODE IN T1
; OK RETURN
SETDDB: PUSHJ P,GETWDU## ;GET UUO DEVICE NAME
JUMPE T1,SETDD1 ;JUMP IF NONE
PUSHJ P,DEVSRG## ;FIND DEVICE
JRST SETDD1 ;NO SUCH DEVICE
HLRZ T1,DEVNAM(F) ;PHYSICAL DEVICE NAME
CAIE T1,(SIXBIT -IMP-);AN IMP?
JRST SETDD2 ;NO
LDB T1,PJOBN## ;GET OWNER'S JOB NUMBER
CAMN T1,J ;SAME?
JRST SETDD3 ;YES
TLNE P1,(IF.PRV) ;NO, SPECIAL ACTION?
TLOA P1,(IF.NWT) ;YES, FORCE NOWAIT OPTION
SETDD3: TLNN p4,UU.ASD ;MUST ASSIGN DEVICE?
JRST SETDD0 ;NO. DON'T ASSIGN IT
PUSHJ P,GETWDU## ;GET DEVICE NAME FOR ASSASG DK/MAR 75
MOVEI T2,ASSCON ;ASSIGN BY CONSOLE
PUSHJ P,ASSASG##
JRST ERRDNA ;CANT HAVE IT
skipg State(f) ; is it a closed DDB?
PUSHJ P,CLRIMP## ; yes. CLEAR THE DDB
SETDD0: HRRI M,.UUSKT(P1) ;POINT AT LOCAL SOCKET NUMBER
PUSHJ P,GETWDU## ;GET IT
andx t1,<1←↑d16>-1 ; trim down to 16 bits
MOVE P2,T1 ;PUT IN PROPER AC
JRST CPOPJ1## ;RETURN
;HERE WHEN THE DEVICE IS NOT AN IMP
SETDD2: PUSHJ P,GETWDU## ;GET DEVICE NAME AGAIN
CAMN T1,DEVLOG(F) ;WAS IT THE LOGICAL NAME FOR THIS IMP?
JRST ERRLNU ;YES, CAN'T ALLOW IT.
;HERE WHEN CANT FIND THE SPECIFIED DEVICE
SETDD1: TLNE p4,UU.NDB ;ALLOWED TO GET FREE DDB?
PUSHJ P,DDBGET## ;GET A DDB
JRST ERRNSD ;NO OR NONE
PUSHJ P,GETWDU## ;GET DEVICE NAME AGAIN
JUMPE T1,SetDD4 ;SPECIFIED?
CAME T1,[SIXBIT\IMP\] ;AND NOT 'IMP'?
MOVEM T1,DEVLOG(F) ;YES, ASSIGN LOGICAL NAME
SetDD4: PUSHJ P,SETDVL## ;MARK DDB AS BELONGING TO JOB (J) DK/MAR 75
;AND ADD TO LOGICAL NAME TABLE DK/MAR 75
MOVE T1,DEVNAM(F) ;PICK UP PHYSICAL NAME
PUSHJ P,PUTWDU## ;GIVE HIM THE PHYSICAL NAME
JRST SETDD0 ;AND SET IT UP
>;NOWAITS
; subroutine to set up essential areas of a DDB ;⊗ PrpDDB
PrpDDB: movei t1,.iptcp ; get TCP protocol number for IP
movem t1,Protcl(f) ; save in DDB
move t1,IPAddr## ; get my site number
movem t1,LclAdr(f) ; that's the source address
movei t1,TCPRTT ; get standard retransmission time
movem t1,RTTime(f) ; save that in DDB
movei t1,TCPUTT ; get user timeout time (can't
; be set by user yet).
movem t1,UTTime(f) ; put that in DDB as timeout set.
movem t1,UTTimr(f) ; and set timeout time now.
movei t1,WndSiz ; get size of standard window.
; (this should be more flexible.)
movem t1,RcvWnd(f) ; initialize window size.
lsh t1,-1 ; get 1/2 of size
movem t1,RcvThr(f) ; that's our window treshhold
setzm RcvHld(f) ; we're not holding back any bytes yet
popj p, ; return
;ROUTINE TO MAKE A LOCAL SOCKET NUMBER FOR A USER'S IMPUUO. ;⊗ MAKMYS MAKMY0 MakM00 MakMy1 MakNxt MakFre
; MOVE P1,[IMPUUO ARGUMENT WORD]
; MOVE P2,[LOCAL SOCKET AS SUPPLIED BY USER]
; MOVE J,[JOB NUMBER]
; MOVE F,[IMP DDB ADDRESS]
; PUSHJ P,MAKMYS
; ERROR--DUPLICATE OR UNAVAILABLE LOCAL SOCKET NUMBER
; OK--FULL LOCAL SOCKET NUMBER IN LclPrt(f)
MAKMYS: TLNN P1,(IF.ALS) ;USER WANT ABSOLUTE LOCAL SOCKET?
jrst MakFre ; no. grab a free socket
;JJW - study the following code carefully. I don't trust it.
pushj p,save4## ; get lots of registers
move p3,RmtPrt(f) ; target port
move p4,RmtAdr(f) ; target host
PUSH P,F ;SAVE DDB POINTER
MOVEI T4,(F) ;MAKE A COPY AND CLEAR SOCKET USE FLAG
HRRZ F,TTYTAB##(j) ;GET TTY DDB FOR THIS JOB
PUSHJ P,CTLJBD## ;FIND CONTROLLING JOB
move t2,t1 ; save controlling job number
MOVEI F,IMPDDB## ;SEARCH ALL DDB'S
NOWAITS<
MOVEI T3,IMPN##
>;NOWAITS
MAKMY0: skiple State(f) ; ignore closed DDBs
CAIN F,(T4) ; mustn't be our DDB
jrst MakNxt ; try next one
came p4,RmtAdr(f) ; is this aimed at the target site?
jrst MakNxt ; this isn't very informative
came p2,LclPrt(f) ; does the local port match ours?
jrst MakM00 ; no. try remote match
move t1,RmtPrt(f) ; load remote port
cain t1,1(p3) ; is the remote port this our
; controlling port?
jrst MakMy1 ; yes. this is a related connection
came t1,p3 ; is the remote port the same?
jrst MakNxt ; no. examine next DDB
move t1,State(f) ; get the state
caie t1,S%TimW ; is it time wait?
jrst FPopj## ; no. this is a functioning
; connection, in use.
ldb t1,PJobN## ; get the owning job
caie t1,(j) ; do we own it?
jumpn t1,FPopj## ; no, someone else does
pushj p,DDBRel## ; flush the one which is
; waiting to time out. this
; isn't quite legal, but
; someone knows she wants to
; reuse this connection, so go
; ahead and let them. chances
; are they're reusing it
; because they know they can.
jrst MakNxt ; but we still need to check
; for a related socket before
; we approve this connection.
MakM00: MOVE T1,LclPrt(F) ; get local port
camn p3,RmtPrt(f) ; are the remote sockets the same?
caie t1,1(p2) ; and are the local sockets related?
JRST MakNxt ; no to one. not related
MakMy1: LDB T1,PJOBN## ; get owning job
CAIe T1,(J) ; is it ours?
cain t1,(t2) ; or our parent's?
tloa T4,-1 ; yes. mark we saw a related
; socket that belongs to us.
jrst FPopj## ; oops. someone else owns a
; related socket.
MakNxt: HLRZ F,DEVSER(F) ;CHAIN TO NEXT DDB
NOWAITS<
SOJG T3,MAKMY0 ;MORE?
>;NOWAITS
IFWAITS<
CAIE F,IMP.NX## ;MORE?
JRST MAKMY0
>;IFWAITS
TLNn P1,(IF.PRV) ; is he prived?
pjmpge t4,FPopj## ; no. does he own a relative?
; if not, give error return.
movem p2,LclPrt(t4) ; save this port in the DDB
pjrst FPopj1## ; skip return: either has privs
; to do anything, or knows a
; related socket.
;JJW - The following 2 comment lines appear to be out-of-date.
;HERE IF USER-SUPPLIED ARGUMENT IS NEGATIVE, MEANING WANT A FREE SOCKET
; RANGE ALLOCATED.
MakFre: PUSHJ P,FRESKT ;FIND A FREE SOCKET
ANDI P2,SK.LCL ;MASK USER-SPECIFIED PORTION
IORb P2,T1 ;BUILD COMPLETE SOCKET
movem p2,LclPrt(f) ; save this port in the DDB
NOWAITS< ;JJW - this is done in IMPUUO.FAI.
hrri m,.uuSkt(p1) ; point at local port word
pushj p,PutWdu## ; tell user what the local port
; we assigned is (it's in T1)
>;NOWAITS
JRST CPOPJ1## ;GIVE NORMAL RETURN
;ROUTINE TO ALLOCATE A FREE SOCKET RANGE ;⊗ FRESKT FRESK1
; PUSHJ P,FRESKT
; ALWAYS RETURN HERE, WITH FIRST SOCKET IN RANGE IN T1.
FRESKT: AOS T1,SKTNUM ;ADVANCE SOCKET NUMBER GENERATOR
txne t1,FreOvr ; overflowing out of field?
setzb t1,SktNum ; yes. zero it.
LSH T1,FRELSH ;POSITION THE BITS
ADDx T1,FREMIN ;OFFSET FROM START
MOVE T2,T1 ;MAKE A COPY
NOWAITS<
MOVEI T3,IMPN## ;START IMP COUNTER
>;NOWAITS
MOVEI T4,IMPDDB## ;SEARCH ALL IMP DDB'S
FRESK1: xor t2,LclPrt(t4) ; compare with local port
txnn t2,FreMch ; is it a match?
JRST FRESKT ;YES, DISCARD AND TRY AGAIN
HLRZ T4,DEVSER(T4) ;LOOP THRU ALL DDB'S
NOWAITS<
SOJG T3,FRESK1
>;NOWAITS
IFWAITS<
CAIE T4,IMP.NX##
JRST FRESK1
>;IFWAITS
POPJ P, ;HERE WHEN FOUND FREE SOCKET RANGE.
; subroutine to decide where to send a message on the ARPAnet to get ;⊗ Target
; it to some host in the internet.
; call:
; move t1,<IP network address>
; pushj p,Target
; <return here if we couldn't figure out a way>
; <return here with T1 = ARPAnet address>
Target: pushj p,save1## ; get p1
ldb p1,[pointr t1,NetMsk] ; get network address
caie p1,.ArpaN ; arpanet?
popj p, ; too bad, that's the only one
; i know.
txz t1,NetMsk ; flush the network number
pjumpn t1,cpopj1## ; and just return that as the target.
popj p, ; just the network number was
; on. not funny.
; subroutine to wait for state to arrive at an established state. ;⊗ EstbWt EstbCl EstbSF EstbTm EstbEr EstbFl EstbW1
; Established and Close-Wait are both considered established.
; call:
; move f,DDB
; move t1,<"current" wait state (what we're waiting to get out of)>
; pushj p,EstbWt
; <return here is failed to get to established state, T1 has state,
; DDB has been cleared out>
; <here if in Established or in Close-Wait, T1 has state>
IFWAITS<ENTRY ESTBWT>
EstbWt:
pushj p,StWait ; wait for a change in state.
caie t1,S%Estb ; made it to being established?
cain t1,S%ClsW ; or even further: incoming closed?
jrst cpopj1## ; yes. good return.
caie t1,S%SyRA ; are we in SYN received? (we've
; been diverted from SYN sent.)
cain t1,S%SyRP ; either version is ok.
jrst EstbW1 ; one or the other. check again.
; failed. decide why before junking the DDB
jumpl t1,EstbEr ; ICMP got an error indication.
MOVE T2,IMPIOS(F) ; get flags
jumpe t1,EstbCl ; closed can't be timeout
MOVEI T3,TIMFLG ; timeout flag
ANDCAM T3,IMPIOS(F) ; CLEAR TIMFLG
TRNe T2,TIMFLG ; CHECK FOR TIMEOUT...
JRST EstbTm ; timeout it is. return it to
; user and non-skip to caller.
movsi t3,IODTer ; IO data error?
tdne t3,DevIOS(f) ; is that set?
jrst EstbTm ; yes. this is a timeout
; detected by data level (user
; timeout).
EstbCl: txnn t2,TrgDwn ; target host down?
jrst EstbSF ; no. some bizarre system failure
pushj p,ErrDwn ; target down error
jrst EstbFl ; flush DDB, etc.
EstbSF: pushj p,ErrSof ; system failure error to user
jrst EstbFl ; ditch the DDB and return bad
; to caller.
EstbTm: pushj p,ErrTim ; report timeout error to user
jrst EstbFl ; flush DDB and return to caller
EstbEr: hrlzs t1 ; get unreachable type in left half
hrri t1,errDUR-errLst ; get the proper error code in
; the right half.
pushj p,ErrSet ; go put the code in place
; now flush the DDB and return to caller
EstbFl: scnon ; interrupts to DDBFls
pushj p,DDBFls## ; zap buffers
scnoff ; reset interrupts as expected
pjrst DDBRel## ; return DDB to free pool.
EstbW1:
MOVEI T3,TIMFLG ; get the time out flag
ANDCAM T3,IMPIOS(F) ; make sure it's cleared.
jrst EstbWt ; yes. wait to leave that state.
;SUBROUTINE TO WAIT FOR NCP ACTIVITY. ;⊗ StWait StWai1 StWai2
;CALL:
; WAITS FOR A CHANGE IN THE STATE. IT IS UP TO THE CALLING
; ROUTINE TO DETERMINE IF THE NEW CODE IS PROPER.
; MOVE T1,STATE CODE
; MOVE F,DDB ADDRESS
; PUSHJ P,StWait
; RETURN HERE WITH NEW STATE IN T1
StWait: HRLM T1,(P) ;SAVE THE CODE
StWai1: MOVSI S,StatWT ; waiting for a change of state
IORM S,IMPIOS(F) ;SET IO ACTIVE
IORB S,DEVIOS(F) ;COPY FOR DEVIOS
HLRZ T2,(P) ;GET TEST CODE
CAmE T2,State(f) ; correct state?
JRST StWai2 ;NO. we're done.
MOVEI T1,TIMFLG ;TIMED OUT?
TDNE T1,IMPIOS(F)
JRST StWai2 ;YES.
LDB T1,PUUTIM ;GET USER WAIT CODE
CAIGE T1,1 ;NULL?
MOVEI T1,3 ;YES--DEFAULT (30 SECONDS)
PUSHJ P,IMPWAT## ;WAIT
ScnOff
JRST StWai1 ;TRY AGAIN
;HERE IF WAIT SATISFIED
StWai2: PUSHJ P,IMPWK1## ;CLEAR FLAGS
TLNE p4,UU.INT ;INTERRUPTS ALLOWED?
ScnOn ;YES
move t1,State(f) ; get state
popj p, ; and return
;SUBROUTINE TO SET TCP state WAIT DONE. CALLED AT INTERRUPT LEVEL. ;⊗ TCPIOD
; CLOBBERS T1. SAVES ALL OTHER ACS.
;CALL:
; MOVE F,[DATA BLOCK ADDRESS]
; PUSHJ P,NCPIOD
; ALWAYS RETURNS HERE
TCPIOD: movsi t1,StatWt ; state wait bit.
TDNN T1,IMPIOS(F) ;WAITING?
POPJ P, ;NO
PJRST IMPWAK## ;WAKE THE JOB
$low ;⊗ SktNum
; storage i need
SktNum: 0 ; number of last free port assigned.
$high
$LIT
END