1
0
mirror of https://github.com/PDP-10/its.git synced 2026-01-11 23:53:12 +00:00
PDP-10.its/src/system/inet.139

1485 lines
48 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;:;; -*- Mode:MIDAS -*-
;;; Copyright (c) 1999 Massachusetts Institute of Technology
;;; See the COPYING file at the top-level directory of this project.
; Insert new buffer stuff...
$INSRT TCPBUF
IP%VER==740000,, ; 0 IP Version # (= 4)
IP%IHL==036000,, ; 0 IP Header Length in 32-bit wds - at least 5
IP%TOS==001774,, ; 0 Type Of Service
IP%TOL==000003,,777760 ; 0 Total Length in octets (including header)
IP%ID== 777774,, ; 1 Identification
IP%FLG== 3,,400000 ; 1 Flags
IP%FDF== 1,,0 ; Don't-Fragment flag
IP%FMF== 400000 ; More-Fragments flag
IP%FRG== 0,,377760 ; 1 Fragment Offset
IP%TTL==776000,, ; 2 Time To Live
IP%PTC== 1774,, ; 2 Protocol
IP%CKS== 3,,777760 ; 2 Header Checksum
IP%SRC==777777,,777760 ; 3 Source Address
IP%DST==777777,,777760 ; 4 Destination Address
; 5 Start of options
IP$VER==<.BP IP%VER,0>
IP$IHL==<.BP IP%IHL,0>
IP$TOS==<.BP IP%TOS,0>
IP$TOL==<.BP IP%TOL,0>
IP$ID== <.BP IP%ID, 1>
IP$FLG==<.BP IP%FLG,1>
IP$FRG==<.BP IP%FRG,1>
IP$TTL==<.BP IP%TTL,2>
IP$PTC==<.BP IP%PTC,2>
%PTCIC==:1 ; Protocol ICMP
%PTCTC==:6. ; Protocol TCP
%PTCUD==:17. ; Protocol UDP
IP$CKS==<.BP IP%CKS,2>
IP$SRC==<.BP IP%SRC,3>
IP$DST==<.BP IP%DST,4>
; UDP fields
UD$SRC==<242000,,0> ; 0 wd 1 Source port
UD$DST==<042000,,0> ; 0 wd 2 Dest port
UD$LEN==<242000,,1> ; 1 wd 1 # octets in data
UD$CKS==<042000,,1> ; 1 wd 2 UDP checksum
UD$DAT==<441000,,2> ; 2 Data - actually an ILDB pointer!
; ICMP fields
IC$TYP==<341000,,0> ; 0 Type of message
IC$COD==<241000,,0> ; 0 Code (subtype)
IC$CKS==<042000,,0> ; 0 ICMP Checksum
IC$GWA==<044000,,1> ; 1 Random arg, usually Gateway Addr
IC$IPH==2 ; 2 Random data, usually an IP header
; Hack metering macro, since we'll want lots of 'em to start with.
; You know, MR% used to be MTR%, except that when %NMTRS is over 100
; you get duplicated symbols, and since they used to be defined with
; == instead of ==: you didn't hear about it, either.
%NMTRS==:140 ; Allow this many meters
DEFINE MTRCOD ; Put this macro someplace after last meter.
EBLK
MTRCNT: BLOCK %NMTRS ; Holds actual meter AOS'd
BBLK
MTRNAM: BLOCK %NMTRS ; Holds <instr loc>,,<addr of ASCIZ meter name>
LOC MTRNAM
REPEAT %%%MTR,CONC MR%,\.RPCNT
IF2, REPEAT %%%MTR,CONC EXPUNGE MR%,\.RPCNT
LOC MTRNAM+%NMTRS
TERMIN
; METER - Must be used as in following example:
; METER("IP: # of bad cksums")
%%%MTR==0
DEFINE METER &(NAME)
IFGE %%%MTR-%NMTRS,.ERR Too many meters!
AOS MTRCNT+%%%MTR
CONC MR%,\%%%MTR,==:<.,,[ASCIZ NAME]>
;CONC MR%,\%%%MTR,==:<.,,>
;IF1 SHOMTR %%%MTR,NAME
%%%MTR==%%%MTR+1
TERMIN
DEFINE SHOMTR #OFF#,&STR&
PRINTX /;;;;;;;; METER :::: MTRCNT+!OFF! => /
PRINTX STR
PRINTX /
/
TERMIN
EBLK
IPMDFL: 0 ; # of flushed input datagrams
IPMCKF: 0 ; # of input datagrams with bad checksum
IPMFRG: 0 ; # of fragments received
IPMFRD: 0 ; # of sucessfully reassembled datagrams
BBLK
SUBTTL IP Input Interrupt Level
; IPGIPT - Get datagram input buffer
; Clobbers Q,T
; A/ Max size of buffer in words
; Returns .+1 if failure (error message already printed)
; Returns .+2
; A/ Pointer to datagram structure associated with buffer
; B/ Input BLKI pointer to buffer, -<# wds>,,<addr-1>
IPGIPT: CAILE A,PKBSIZ ; Make sure size needed will fit in a packet buffer
JRST IPGIP9
CALL PKTGFI ; Get a packet at int level
RET ; Failed, none available.
TRCPKT A,"IPGIPT Net input alloc"
MOVE T,PK.BUF(A) ; Get addr of buffer
HRLOI B,-PKBSIZ ; -<#wds>,,-1
ADDI B,(T) ; Now get BLKI pointer into buffer
JRST POPJ1 ; Win!
IPGIP9: BUG CHECK,[IP: Too-big buff reqd =],OCT,A
RET ; Fail.
; IPRDGM - Process a received datagram at PI level
; Must put datagram into one of the following lists:
; User IP input queue (IPQ)
; IP reassembly table
; ICMP processing
; TCP connection queue
; A/ Pointer to datagram structure
; B/ # words read in datagram
; C/ # wds offset to start of IP header
;;; J is not used, and not supplied by all callers
;;; J/ host-table index of address datagram came from
; Returns .+1 always
; Can clobber all ACs except P
; Sets up
; R/ addr of packet entry
; W/ addr of IP header
; H/ addr of IP data
IPRDGM: METER("IP: IDs rcvd")
MOVEI R,(A) ; Set up packet entry ptr in canonical place
TRCPKT R,"IPRDGM Input from net"
CAIGE B,5(C) ; Make sure it's big enough
JRST IPRDG9
HRLM B,PK.BUF(R) ; Store # words read
ADD C,PK.BUF(R) ; Find addr of start of IP header
HRLZM C,PK.IP(R) ; and set it.
MOVEI W,(C)
LDB H,[IP$IHL (W)] ; Find claimed length of IP header
ADDI H,(W) ; Get addr of start of IP data
HRLZM H,PK.TCP(R) ; Set that too.
; Do initial vectoring test.
SKIPE IPUQUS ; Check Queue 0 (SysIn)
JRST IPRDG2 ; It exists!! Always vector for it.
; Perform initial checking for address, checksum, and so forth
; to verify datagram is good; also dispatch to handle fragments.
; This is entry point for re-vectors from SysIn IP queue.
IPRDGV: CALL IPCKSM ; Compute checksum for IP header
LDB B,[IP$CKS (W)] ; and get what the datagram had,
CAIE A,(B) ; in order to compare them...
JRST [ METER("IP: Ifl bad cksm")
AOS IPMCKF ; Bump two meters
JRST IPRD90] ; Go flush it forthwith.
LDB B,[IP$DST(W)] ; Get destination host, should be us
REPEAT NNIFS,[
CAMN B,NIFIPA+.RPCNT ; Is it us?
JRST IPRD10 ; Yep!
]
; Not for us, should we forward packet?
SKIPG IPFORW
JRST [ METER("IP: Packets not forwarded")
JRST IPRD90]
METER("IP: Packets forwarded")
LDB B,[IP$TTL(W)] ; Decrement time to live
SOJLE B,[ METER("IP: Packets expired")
JRST IPRD90 ]
ICMEK1: DPB B,[IP$TTL(W)]
CALL IPCKSM ; Update the checksum
DPB A,[IP$CKS(W)]
MOVEI A,(R) ; Transmit it
CALRET IPKSNQ
IPRD10: HRRE B,IP$FRG(W) ; HACK! Get both IP%FMF and IP%FRG!
JUMPN B,IPRD50 ; Jump if this is a fragment.
; Do datagram vectoring. This code is temporarily (?) crude,
; it just scans the whole Internet Queue table.
; This is entry point for re-vectoring. W must point to IP
; header, and H to IP data. I should point at 1st queue entry
; to start checking at.
IPRD20: MOVEI I,2 ; If drop in, start at 2 (leave 0+1 alone)
LDB B,[IP$PTC (W)] ; Get protocol number
CAIN B,%PTCTC ; Is it TCP?
JRST TCPIS ; Yes, go process TCP input segment.
CAIN B,%PTCUD ; Well, is it UDP?
JRST IPRD30 ; Yeah, can handle that one.
CAIN B,%PTCIC ; Maybe ICMP?
JRST ICMP ; Yup, hack it.
IPRD90: MOVEI A,(R)
CALL PKTRT ; Bah, nothing we handle, flush it.
AOS IPMDFL ; Bump count of flushed dgms.
RET
; Here to dispatch a UDP datagram
IPRD30: LDB A,[IP$TOL (W)] ; Make sure it's long enough! Find dgm length
LDB B,[IP$IHL (W)] ; and get IP header length
IMULI B,4 ; in octets
SUBI A,(B) ; to subtract from dgm length.
CAIGE A,2*4 ; Must have enough data for UDP header!
JRST [ METER("IP: Ifl bad UDP len")
JRST IPRD90] ; Flush this dgm.
IPRD31: CAIL I,NIPUQ
JRST [ METER("IP: Ifl no UDP port") ; Didn't find any queues,
JRST IPRD90] ; so flush it.
SKIPN IPUQUS(I) ; Check each active UDP queue
AOJA I,IPRD31
LDB B,[UD$DST (H)] ; Get UDP dest port number
HRRZ T,IPUQCT(I) ; and port # we're watching for
CAIE B,(T)
AOJA I,IPRD31 ; No match, try another.
METER("IP: # UDP dgms queued")
CAIA
IPRDG2: SETZ I, ; Entry point for SysIn queueing
MOVEI Q,IPUQHD(I) ; Hurray, got it! Add to queue
MOVE B,(Q) ; Save prev contents of header
MOVEI A,(R)
CALL PKQPL(PK.IP) ; Put at end of input IP queue
JUMPE B,IPQUSI ; If nothing previously there, give user int.
RET
IPRDG9: BUG INFO,[IP: Netin dgm too small, size ],OCT,B,[ offset ],OCT,C
JRST IPRD90 ; Try flushing the packet buffer.
; IP Datagram Reassembly - Handle received fragment.
IPRD50: AOS IPMFRG ; Bump count of fragments received
LDB D,[IP$ID (W)] ; Get datagram ID field
LDB C,[IP$PTC (W)] ; Then protocol field
HRLI D,(C) ; Make <ptcl>,,<ID>
MOVE E,IP$SRC(W) ; Then source address
MOVEI I,NIPF-1
IPRD51: CAME D,IPFDID(I)
IPRD52: SOJGE I,.-1
JUMPL I,IPRD70 ; If no more, must add to table.
MOVE B,IPFDPE(I) ; Matching ID! Get buffer ptr
HLRZ T,PK.IP(B) ; Get IP header ptr for existing fragment
CAME E,IP$SRC(T) ; Ensure same source host
JRST IPRD52 ; Nope, go check next entry.
HLRZ H,PK.TCP(B) ; Get ptr to start of data in reassembly buff
; OK, we matched up a fragment! Now start reassembly procedure.
; If fragment is first one (offset 0) then must copy IP header,
; unless already done. Safe to BLT since we always reserve
; enough room for a full 15-word IP header.
; If fragment is last one (IP%FMF 0) then must set IP$TOL to
; the total # octets in full datagram. This gets fixed
; to include the IP header length when datagram is complete.
; I/ idx of reassembly entry
; T/ ptr to IP header in reassembly buff
; H/ ptr to data in reassembly buff
; R, W as for entry to IPRD50
IPRD55: LDB A,[IP$IHL (W)] ; Get IP header length in 4-octet wds
LDB E,[IP$TOL (W)] ; Get total length of this dgm in octets
HRRE D,IP$FRG(W) ; Hack - get frag offset and more-frag flag
TRNN D,IP%FRG ; Is frag offset 0 - 1st part of dgm?
JRST [ LDB C,[IP$FRG (T)] ; Yeah. Already copied header?
JUMPE C,.+1 ; Jump if so, don't do again.
MOVEI B,(T)
HRLI B,(W) ; Set up BLT from,,to
MOVEI C,(T)
ADDI C,(A) ; Get to+IHL
MOVE Q,IP$CKS(T) ; Save ptr to hole list
LDB TT,[IP$TOL (T)] ; Save TOL, might already be set.
BLT B,-1(C) ; Copy the IP header
HRRM Q,IP$CKS(T) ; Restore hole list head
DPB TT,[IP$TOL (T)]
JRST .+1]
ASH D,-3 ; Get frag.first in terms of 4-octet words
JUMPGE D,[ ; Jump for special processing if last frag
MOVNI B,(A)
ASH B,2 ; Get -<# octets in header>
ADDI B,(E) ; Find # octets of data in this fragment
MOVEI C,(D)
LSH C,2 ; Get # octets data is offset
ADDI B,(C) ; Finally get total # data octets of full dgm
DPB B,[IP$TOL (T)]
ADDI E,3 ; Okay, round UP to full word
LSH E,-2 ; Get rounded-up length in terms of 4-octet wds
SUBI E,1(A) ; Get # whole wds of data (minus 1)
JRST IPRD56] ; Go rejoin normal processing
; Not last frag. Only special check is to ensure length of data
; is rounded down to a fragment boundary (frags are 8-octet chunks).
TRZ D,-1#<IP%FRG_-3> ; Not last frag, clean up RH of frag.first
LSH E,-2 ; Get rounded length in terms of 4-octet words
SUBI E,1(A) ; Get # whole words of data, minus 1
TRNN E,1 ; Paranoia: ensure # wds of data was EVEN
SUBI E,1 ; If not, round DOWN to ensure 8-octet boundary
IPRD56: JUMPL E,IPRD80 ; Flush if bad length
ADDI E,(D) ; Get frag.last
CAIL E,<PKBSIZ-16.> ; Make sure datagram won't be too big.
JRST [ METER("IP: Ifl huge dgm")
CALL IPFDFL ; Ugh, must flush whole datagram entry!
JRST IPRD90] ; Would it be better instead to just
; truncate it, and accept anyway since TCP
; can ACK up to that much? Probably not.
; Each hole descriptor is 1 word of format
; hole.first: <hole.last>,,<hole.next (hole.first of next hole)>
;
; During re-configuration of the hole descriptor list, following
; ACs are used
; A/ scratch
; B/ hole.first (wd offset)
; C/ hole.last
; D/ <lastflg>,,frag.first ; lastflg is 0 if last fragment.
; E/ frag.last
; Q/ ptr to current hole descriptor
; TT/ ptr to previous hole descriptor
; H/ ptr to start of data in reassembly buffer (base for offsets)
; W/ ptr to IP header of just-arrived fragment
; T/ ptr to IP header of reassembly buffer
; R/ ptr to packet entry of just-arrived fragment
MOVEI Q,IP$CKS(T) ; Get ptr to 1st hole descriptor
IPRD61: MOVEI TT,(Q) ; Save old ptr
HRRE Q,(Q) ; Get next descriptor
JUMPL Q,IPRD68 ; Jump if end of list
MOVEI B,(Q) ; Set hole.first
ADDI Q,(H) ; Make ptr to hole descriptor
HLRZ C,(Q) ; Get hole.last
CAIGE C,(D) ; If hole.last < frag.first,
JRST IPRD61 ; back to try next hole farther on.
CAIGE E,(B) ; If frag.last < hole.first,
JRST IPRD68 ; passed affected area, so can stop now.
; New fragment interacts with current hole in some way!
; Remove current hole from the list, but keep Q pointing to
; start of hole. TT points to the last valid hole descriptor.
MOVE A,(Q) ; Get hole.first of next hole
HRRM A,(TT) ; Store in prev hole, so current is skipped.
CAIL B,(D) ; If hole.first < frag.first, skip.
JRST IPRD66
; Create new hole descriptor at start of old hole
; with new.first = hole.first and new.last = frag.first-1
; i.e. hole.first: <frag.first-1>,,<hole.next>
; First get ptr to new hole and put it on list.
HRRM B,(TT) ; Point prev hole to new hole.
HRLI A,-1(D) ; Make <frag.first-1>,,<hole.first of next>
MOVEM A,(Q) ; Store new hole descriptor.
MOVEI TT,(Q) ; Make prev be current, in case test below wins
; Drop thru to check high bound of old hole
IPRD66: CAIL E,(C) ; If frag.last < hole.last then hole not all filled
JRST IPRD61 ; (hole all filled, so go check further holes)
CAIL D, ; Some hole left; is this the last fragment?
JRST [ HLLOS (TT) ; Yes! Zap prev hole to ensure list ends.
JRST IPRD68] ; and get out of loop now.
; Fragment didn't fill last part of hole, so need to create
; new hole descriptor for it,
; with new.first = frag.last+1 and new.last = hole.last
; i.e. frag.last+1: <hole.last>,,<hole.next>
MOVEI Q,1(E) ; Get frag.last+1
HRRM Q,(TT) ; Point previous to new hole
ADDI Q,(H) ; Make abs ptr to new hole
HRLI A,(C) ; Make <hole.last>,,<hole.next>
MOVEM A,(Q) ; Store new hole descriptor.
; Can drop through to end loop, since no further holes
; are affected.
; No more holes on list, we can copy the data now!
IPRD68: HLL D,PK.TCP(R) ; Get <ptr to start of arrived data>,,<frag.first>
ADDI D,(H) ; Now have BLT pointer
ADDI E,(H) ; and now have terminating address
CAIN E,(D) ; But if only moving 1 word,
JRST [ HLRZ D,D ; Can't use BLT?
MOVE A,(D) ; So just move by hand
MOVEM A,(E)
JRST .+2] ; Skip over it.
BLT D,(E) ; Here we go!
; Now see if any holes left...
MOVEI W,(T) ; Save ptr to reassembly IP hdr (H already set)
MOVEI A,(R) ; No need for arrived dgm any more,
CALL PKTRTA ; so flush it now.
HRRE A,IP$CKS(W) ; See if any holes left
JUMPGE A,CPOPJ ; Jump if some left, nothing else to do.
HRRZ R,IPFDPE(I) ; Win!!! Get back packet-entry ptr
LDB A,[IP$IHL (W)] ; Must perform final TOL fixup. Get IHL
LSH A,2+4 ; in octets, shifted to TOL field
ADDM A,IP$TOL(W) ; Now have proper length!
SETZM IPFDPE(I)
SETOM IPFDID(I)
HRLOI A,377777
MOVEM A,IPFTTL(I)
AOS IPMFRD ; Bump cnt of # datagrams reassembled!
JRST IPRD20 ; Go dispatch the datagram!
; Create entry in table to store 1st fragment in.
IPRD70: MOVEI I,NIPF-1
SKIPE IPFDPE(I)
SOJGE I,.-1
JUMPL I,[METER("IP: Ifls Fragtab full") ; Barf, fragment table full.
JRST IPRD90]
LDB A,[IP$TTL (W)] ; Get time-to-live
JUMPE A,IPRD90 ; Might as well hack zero case
IMULI A,30. ; Turn into 30ths
ADD A,TIME
MOVEM A,IPFTTL(I) ; Store timeout value
MOVEM D,IPFDID(I) ; Store ptcl,,ID
HRRZM R,IPFDPE(I) ; Store PE ptr
; Messy stuff, must get data set up into right place in buffer.
; If this is the 1st fragment we are OK, and can use original
; datagram buffer, else we have to shuffle data. Simplest way
; to handle latter case is to just get a new buffer and copy
; it over.
LDB A,[IP$FRG (W)] ; Get fragment offset field
JUMPN A,IPRD75 ; If not zero, jump to do copy.
LDB A,[IP$TOL (W)] ; Hurray, 1st fragment! Get total length
LSH A,-2 ; Round down to # words
LDB B,[IP$IHL (W)]
SUBI A,(B) ; Find # words that fragment uses
TRZ A,1 ; Ensure # wds is rounded down to 8-octet chunk
JUMPLE A,[CALL IPFDFL ; Sigh, flush entry.
RET] ; Just return, only flushing one PE.
HRRM A,IP$CKS(W) ; Store first hole.next in header.
MOVEI B,(A)
ADDI B,(H) ; Get addr of start of hole
SETOM (B) ; Make it an infinite hole.
RET
; Fragment entry must be stored, but it isn't the 1st thing in
; the datagram. We must cons up a fake initial fragment and
; then copy normally into that fragment.
; Note that this fake fragment must be carefully initiallized
; since certain IP fields are referred to in the reassembly code
; (via pointer in T)
IPRD75: CALL PKTGFI ; Get a PE ptr at PI lvl
JRST IPFDFL ; Failed, must flush entry
TRCPKT A,"Reassembly alloc"
MOVEM A,IPFDPE(I) ; Store it
HRRZ T,PK.BUF(A)
HRLM T,PK.IP(A) ; Say IP header at start of buffer.
MOVEI H,15. ; Use maximum IHL for offset
HRRZM H,IP$CKS(T) ; Store this offset as ptr to 1st hole desc
ADDI H,(T) ; and make data start at end of max IP hdr.
HRLM H,PK.TCP(A)
SETOM (H) ; Make 1st hole descriptor be infinite
SETOM IP$FRG(T) ; Put crap in frag offset field
MOVE B,IP$SRC(W) ; and ensure source host copied too.
MOVEM B,IP$SRC(T)
JRST IPRD55 ; Now go do the copy...
IPRD80: METER("IP: Ifl bad len") ; Bad IP length field
JRST IPRD90 ; Go flush the dgm.
; IPFCLK - Called every few seconds at clock level to check
; reassembly tables and flush any partially filled datagrams
; which have timed out.
IPFCLK: MOVEI I,NIPF-1
MOVE B,TIME
CONO PI,NETOFF ; Hack with net ints deferred.
CAML B,IPFTTL(I)
CALL IPFDFL ; Flush the partial dgm
SOJGE I,.-2
CONO PI,NETON ; Done, re-enable net ints.
RET
; IPFDFL - Flush reassembly entry in I
; Clobbers A, Q, T
IPFDFL: SKIPE A,IPFDPE(I)
CALL PKTRTA ; Flush the packet buffer
SETZM IPFDPE(I)
SETOM IPFDID(I) ; Clear out other table stuffs.
HRLOI A,377777
MOVEM A,IPFTTL(I)
RET
; Datagram Fragment table.
; Free entries have IPFDPE 0, IPFDID -1, and IPFTTL SETZ-1 (max pos time)
EBLK
NIPF==:30 ; Max # of outstanding IP datagram reassembly buffers
IPFDPE: BLOCK NIPF ; <PE ptr>
IPFDID: REPEAT NIPF,-1 ; <protocol>,,<datagram ID from IP header>
IPFTTL: REPEAT NIPF,SETZ-1 ; Sys time after which entry flushed.
BBLK
SUBTTL IP Output Interrupt Level
IFE IPUNCP,[
EBLK
IPOUTQ: 0
IPOBLQ: 0
BBLK
; IPGIOQ - Get IP Output Queue entry for IMP
; Returns .+1 if nothing in queue
; Returns .+2
; A/ Pointer to datagram structure
; B/ Output BLKO pointer to buffer, -<# wds>,,<addr-1>
; C/ Arpanet host address
; H/ host-table index
; Clobbers Q,T,W,D,E
IPGOQ1: METER("IP: ODs flushed")
CALL PKTRT ; Internal looping point
IPGIOQ: MOVEI Q,IPOUTQ
CALL PKQGF(PK.IP) ; Get first thing off IP output list
JUMPE A,IPGOQ9 ; Jump and return if nothing there.
MOVE T,PK.FLG(A) ; Get packet flags
TLNE T,(%PKFLS) ; Should we flush this one?
JRST IPGOQ1 ; Yes, down the drain it goes.
TLO T,(%PKPIL)
IORM T,PK.FLG(A) ; Say packet locked at PI level.
SKIPLE C,PK.BUF(A)
CAMG C,[2,,0]
BUG HALT,[IP: Null dgm on queue]
IFE KS10P,[
;KS doesn't care, save 2 usec..
MOVN B,C ; Straightforward way to put together AOBJN ptr.
HRRI B,-1(C) ; Now have BLKO
]
MOVE C,PK.DST(A) ; Get destination address
; IMP-specific!!!
; Ask interface if it wants this particular datagram right now.
;
CALL IMPCTS
JRST IPGOQ5 ; Can't send, requeue
; Got valid dgm, must ensure that block queue is merged back
; onto beginning of output queue.
IPGOQ6: METER("IP: ODs sent")
SKIPN D,IPOBLQ ; See if anything was blocked
JRST POPJ1 ; Nope, just take win return.
SETZM IPOBLQ ; Yes, block queue exists!
SKIPN T,IPOUTQ ; Get ptr to 1st node on output queue
JRST [ MOVEM D,IPOUTQ ; If nothing was left on output queue,
JRST POPJ1] ; can simply move the list.
HLRZ E,D ; Get ptr to last node on blocked queue
HRRM T,PK.IP(E) ; Point end of blocked Q to start of output Q
HRRM D,IPOUTQ ; and point start of output Q to start of block Q
JRST POPJ1 ; and return with nice winning dgm.
; Come here to handle blockage of IP datagram.
IPGOQ5: MOVSI T,(%PKPIL)
ANDCAM T,PK.FLG(A) ; Say not locked at PI after all
MOVEI Q,IPOBLQ
CALL PKQPL(PK.IP) ; Put blocked dgm onto block queue
JRST IPGIOQ ; Now go try next dgm.
; Output queue empty, just shift block queue back.
IPGOQ9: SKIPN A,IPOBLQ ; See if anything was put on block queue
RET ; Nope, all's clear.
MOVEM A,IPOUTQ ; Aha, move it to standard output queue
SETZM IPOBLQ ; and clear the block-queue ptr.
RET ; Nothing to send from IP at moment.
] ;IFE IPUNCP
; IPIODN - Output of IP datagram complete, wrap up.
; Called by all device drivers.
; A/ pointer to datagram structure
; Clobbers T,Q
; Returns .+1 always
IPIODN: TRCPKT A,"IPIODN Packet output complete"
MOVE T,PK.FLG(A) ; Get flags for packet
TLO T,(%PKODN) ; Say output done,
TLZ T,(%PKPIL) ; and unlock PI level output flag.
MOVEM T,PK.FLG(A) ; Store flags back.
CALRET PKTRT ; Return to freelist if not otherwise queued
SUBTTL ICMP - Internet Control Message Protocol
; ICMP called at NET interrupt level to process just-received ICMP
; datagram.
ICMP:
; First compute and verify checksum for ICMP data.
; Then dispatch on type for processing.
LDB E,[IP$SRC (W)] ; Load up source addr (commonly needed)
LDB A,[IC$TYP (H)] ; Get ICMP type field
CAIL A,NICMPT
JRST ICMP19
AOS ICMPCT(A) ; Bump count of types
JRST @ICMPTB(A) ; Dispatch on type
; Bad type
ICMP19: BUG INFO,[ICMP: Bad type ],DEC,A,[from ],OCT,E
ICMP90: MOVEI A,(R)
CALL PKTRTA
RET
ICMPTB: ICMP90 ; 0 Echo Reply (ignored)
ICMP19 ; 1 -
ICMP19 ; 2 -
ICMP90 ; 3 Destination Unreachable (ignored)
ICMP90 ; 4 Source Quench (ignored)
ICMRD ; 5 Re-direct
ICMP19 ; 6 -
ICMP19 ; 7 -
ICMEK ; 8 Echo
ICMP19 ; 9 -
ICMP19 ; 10 -
ICMP90 ; 11 Time Exceeded (ignored)
ICMPP ; 12 Parameter Problem
ICMP90 ; 13 TimeStamp (ignored)
ICMP90 ; 14 TimeStamp Reply (ignored)
ICMP90 ; 15 Information Request (ignored)
ICMP90 ; 16 Information Reply (ignored)
NICMPT==.-ICMPTB
EBLK
IPMICM: 0 ; # of ICMP datagrams
ICMPCT: BLOCK NICMPT ; # of ICMP datagrams, by type
BBLK
; Type 8 - Echo
ICMEK: MOVEI A,0 ; Set type to Echo Reply
DPB A,[IC$TYP (H)]
LDB A,[IC$CKS (H)] ; Fix checksum for change of 8 to 0
ADDI A,8_8
TRNE A,1_16.
ADDI A,1
DPB A,[IC$CKS (H)]
MOVE A,IP$SRC(W) ; Exchange source and destination
EXCH A,IP$DST(W)
MOVEM A,IP$SRC(W)
MOVEI B,60. ; Reset time to live
JRST ICMEK1 ; Go send packet
; Type 12 - Parameter Problem.
ICMPP: LDB B,[IC$COD (H)] ; Get code field
JUMPE B,ICMPP2
BUG INFO,[ICMP: Param err, code ],OCT,B,[from ],OCT,E
JRST ICMP90
ICMPP2: LDB A,[341000,,1(H)] ; Get pointer into bad IP header
MOVEI B,(A)
LSH B,-2 ; Find word # error is in
ADDI B,IC$IPH(H) ; Make addr to word
BUG INFO,[ICMP: Param err, ptr ],OCT,A,[wd ],OCT,(B),[from ],OCT,E
JRST ICMP90
; ICMP type 5 - Redirect
ICMRD: MOVEI D,IC$IPH(H)
LDB A,[IP$SRC(D)] ; Get source addr of alleged IP header
REPEAT NNIFS,[
CAMN A,NIFIPA+.RPCNT ; Is it us?
JRST ICMRD1 ; Yep!
]
JRST ICMP90 ; Bah, flush. Probably should log it.
ICMRD1: LDB A,[IP$DST (D)] ; Get dest addr we used
GETNET A ; Derive net number
LDB B,[IC$GWA (H)] ; Get gateway addr recommended for this net
MOVEI C,NIPGW-1 ; Scan backwards thru gateway table
SETOB T,TT ; Index of free slot, index of oldest slot
ICMRD2: CAMN A,IPGWTN(C)
JRST [ SKIPN IPGWTG(C) ; Don't change a direct-route entry!
JRST ICMP90
JRST ICMRD3 ]
CAIL C,NIPPGW ; Skip if permanent gateway, not replaceable
JRST [ SKIPN IPGWTN(C)
MOVEI T,(C) ; Save index of last free slot found
SKIPL TT
CAML D,IPGWTM(C)
MOVEI TT,(C) ; Save index of least recently used slot
MOVE D,IPGWTM(TT)
SOJA C,ICMRD2 ]
SOJGE C,ICMRD2
; Network not found in gateway table, must make new entry.
SKIPL C,T ; If there was one free,
JRST ICMRD3 ; go use that one.
MOVE C,TT ; Otherwise use least recently used entry
MOVE T,TIME
SUB T,IPGWTM(C)
CAIGE T,60.*60.*30. ; Flushing entry less than 1 hour old?
BUG INFO,[ICMP: GW table full, net/gw ],OCT,IPGWTN(C),OCT,IPGWTG(C),[=>],OCT,A,OCT,B
; Figure out which interface this gateway is on
ICMRD3:
REPEAT NNIFS,[ ; Check local interfaces
MOVE T,B ; Set up GW address
AND T,NIFIPM+.RPCNT ; Use interface netmask (may be subnetting)
CAMN T,NIFIPN+.RPCNT ; Does it match interface net?
JRST [ MOVE T,NIFIPO+.RPCNT ; Yes! Go use it!
JRST ICMRD4]
]
JRST ICMP90 ; Failed, can't figure out how to get there.
ICMRD4: MOVEM A,IPGWTN(C) ; Set network number
MOVEM B,IPGWTG(C) ; and its corresponding gateway addr
MOVEM T,IPGWTI(C) ; and its interface routine
MOVE T,TIME ; Pretend it was used so it
MOVEM T,IPGWTM(C) ; stays around for a while
JRST ICMP90 ; Done!
SUBTTL IPQ Device - Internet Protocol Queues
; Internet Protocol User Datagram Queue stuff, manipulated with
; IPKIOT system call.
; Queue 0 is special:
; Must be asked for explicitly
; All Input datagrams are vectored through it.
; No limit on input queue length
; Can put datagrams back into system for further processing
; Can send datagrams (like ordinary queue actually in this respect)
; Queue 1 is also special:
; Must be asked for explicitly
; All output datagrams are vectored through it.
; No limit on queue length
; Can put datagrams back onto device output queue.
IFNDEF NIPUQ,NIPUQ==10 ; # User queues allowed
EBLK
IPUQUS: BLOCK NIPUQ ; <flags><channel>,,<user index>
IQ%CH==<77,,> ; Field for channel #
IQ$CH==<.BP IQ%CH,IPUQUS> ; BP to channel #
IPUQHD: BLOCK NIPUQ ; Input queue header
IPUQCT: BLOCK NIPUQ ; # datagrams on input queue,,vector args
IPQOSW: -1 ? 0 ; IP Queue assignment lock
BBLK
; IPQO - IPQ OPEN routine
; Control bits currently defined are
%IQSYS==100 ; Set up System Queue (0 or 1)
%IQSOU==200 ; System Queue 1 if set, otherwise 0
%IQUDP==400 ; Set up random queue for UDP (port # in FN1)
IPQO: CALL SWTL ; Only one job at a time hacking IQ allocation.
IPQOSW
SETZB E,I ; Set up convenient zeros
TLNE C,%IQSYS ; Asking for system queue?
JRST [ TLNE C,%IQSOU ; Yes, want input or output?
MOVEI I,1 ; Output, use queue 1
SKIPE IPUQUS(I) ; Skip if it's free
JRST OPNL23 ; Nope, say "file locked".
JRST IPQO2] ; Can grab it, do so!
MOVE I,[-<NIPUQ-2>,,2] ; Scan tables, skipping 0'th entry
SKIPE IPUQUS(I) ; Look for free slot
AOBJN I,.-1
JUMPGE I,OPNL6 ; If none available, claim "device full"
TLNN C,%IQUDP ; Got it. If will use UDP vectoring,
JRST OPNL33 ; No, complain "meaningless args"
; since nothing else understood yet.
TLO E,%IQUDP ; then set flag for IPUQUS.
HRRZM A,IPUQCT(I) ; Store FN1 as UDP port number
CAIA
IPQO2: SETZM IPUQCT(I)
SETZM IPUQHD(I) ; Clear input queue
MOVEI A,IPQDN ; IOCHNM device index to use
HRLI A,(I) ; Save IQ index in LH
MOVEM A,(R)
MOVEI A,-IOCHNM(R) ; Start putting together the IPUQUS entry.
SUBI A,(U) ; Get channel #
DPB A,[.BP IQ%CH,E] ; Remember it in IPUQUS word
HRRI E,(U) ; Put user index in RH
MOVEM E,IPUQUS(I) ; Store, queue is now activated!
; Note this must be last thing, to avoid
; timing errors.
CALRET LSWPJ1 ; Unlock switch and return!
; IPQCLS - IPQ CLOSE routine
IPQCLS: HLRZ I,(R) ; Get IQ idx
CAILE I,1 ; Is it the Sys In or Out queue?
JRST IPQCL5 ; Nope, can handle normal case.
CONO PI,NETOFF ; Keep anything from being added meanwhile
SETZM IPUQUS(I) ; Mark queue not active, to avoid revector loops.
SETZM IPUQCT(I) ; Be tidy and clear other stuff too.
JUMPE I,IPQCL3
; Close down System Output queue. This means all output
; on this queue gets moved directly onto the real output
; queue.
IPQCL1: MOVEI Q,IPUQHD(I)
CALL PKQGF(PK.IP) ; Get first thing queued up
JUMPE A,[CONO PI,NETON ; Exit if no more.
CALRET IPOGO] ; Ensure output fired up.
MOVEI Q,IPOUTQ
CALL PKQPL(PK.IP) ; Put at end of real output queue
JRST IPQCL1
; Close down System Input queue. This means all currently
; queued input gets processed immediately. Note I gets
; clobbered, but isn't necessary since we know this is queue 0.
IPQCL3: MOVEI Q,IPUQHD ; Get header for queue 0
CALL PKQGF(PK.IP) ; Get A/ packet ptr
JUMPE A,NETONJ
HLRZ B,PK.BUF(A) ; Get B/ # words in packet
SETZ C, ; Get C/ # wds offset to IP header
CALL IPRDGM ; Process and vector it.
JRST IPQCL3 ; Get next
; Normal datagram input queue. Doesn't need NETOFF since
; PI level ignores the queue entry if it's inactive. Just
; need to keep another job from assigning it...
IPQCL5: CONO PI,CLKOFF
SETZM IPUQUS(I) ; Clear its "active" entry word to stop queueing
CALL IPQRS2 ; Flush its input queue (clears IPUQHD)
SETZM IPUQCT(I)
CONO PI,CLKON
RET
; IPQRST - IPQ RESET routine. Clears queue for channel.
; This is pretty drastic for the System I/O queues.
IPQRST: HLRZ I,(R) ; Get IQ idx
CONO PI,NETOFF ; Prevent new dgms from arriving meanwhile.
CALL IPQRS2 ; Flush the queue
JRST NETONJ
IPQRS2: MOVEI Q,IPUQHD(I)
CALL PKQGF(PK.IP) ; Pull off 1st thing
JUMPE A,CPOPJ ; Return when no more
MOVE T,PK.FLG(A)
CAIN I,1 ; If queue is the Sys Output queue
JRST [ TLNE T,(%PKFLS) ; Then do special stuff.
JRST IPQRS3 ; Flush only if explicitly requested
TLZ T,(%PKPIL) ; Otherwise clear PI-Locked bit
TLO T,(%PKODN) ; and claim "output done" (ha ha)
MOVEM T,PK.FLG(A)
JRST IPQRS2]
IPQRS3: CALL PKTRT ; Put all stuff on freelist.
JRST IPQRS2
; IPQIO - IPQ I/O routine (if anything actually tries using this)
IPQIO: JRST OPNL34 ; Say "Wrong Type Device"
POPJ P,
; IPQSTA - IPQ STATUS routine
IPQSTA:
POPJ P,
; IPQWHY - IPQ WHYINT routine
IPQWHY:
JRST POPJ1
; IPQRCH - IPQ RFNAME/RCHST routine
IPQRCH:
POPJ P,
; IPQRFP - IPQ RFPNTR routine
IPQRFP: JRST OPNL34
; IPQIOP - IPQ IOPUSH/IOPOP routine
IPQIOP: MOVEI T,(R)
SUBI T,IOCHNM(U)
CAIN I,
MOVEI T,77 ; IOPUSH, use 77
HLRZ I,(R) ; Get IPQ index
DPB T,[IQ$CH (I)] ; Deposit channel #
POPJ P,
; IPQFRC - IPQ FORCE routine
IPQFRC:
JRST POPJ1
; IPQFIN - IPQ FINISH routine
IPQFIN:
JRST POPJ1
; IPQUSI - Give User Interrupt on I/O channel. Not a system call,
; but called by PI level routines when input arrives for
; a previously empty queue.
; Clobbers T,Q
; I/ index to IP Queue
IPQUSI: LDB Q,[IQ$CH (I)] ; Get channel #
CAIN Q,77 ; If IOPUSHed, no interrupt.
RET
PUSH P,U
HRRZ U,IPUQUS(I) ; Get user index
CAIN U,
BUG
; MOVSI T,(SETZ) ; Needn't force PCLSR'ing.
; IORM T,PIRQC(U)
MOVE T,CHNBIT(Q)
AND T,MSKST2(U)
IORM T,IFPIR(U)
POP P,U
RET
SUBTTL .CALL IPKIOT - IPQ data transfer
; .CALL IPKIOT - Internet Protocol Packet Transfer.
; Arg 1 is channel (must be open on IPQ:, specifies queue #)
; Arg 2 is address of buffer
; Arg 3 is count of words
; Val 1 is count of words read into user space (if any)
; Control bits specify function. If none, "read" is assumed.
; Get datagram from:
%IPIUS==100 ; 1 = Get datagram from user space, not from a queue
%IPNOC==200 ; Global input no-check flag, suppresses normal check.
; For User Space, "check" means verify, set cksum.
; For Input Queue, "check" means verify IP header.
; For SysIn Queue, "check" means verify IP hdr.
; For SysOut Queue, means nothing.
%IPNOH==400 ; Don't Hang waiting for datagram (Queues only)
%IPIQK==1000 ; Keep on queue, don't remove (only for %IPOUS)
; Put datagram to:
%IPOUS==0 ; User space
%IPOUT==1 ; Output to network (bypasses SysOut queue)
%IPOFL==2 ; Flush it
%IPORV==3 ; Re-vector to input queues past this one
IPKIOT:
HRRZ A,(R)
CAIE A,IPQDN ; Must be right type device (IPQ)
JRST OPNL34 ; Wrong device
HLRZ I,(R) ; Get IP input queue index
CAIL I,NIPUQ ; Ensure it's valid.
BUG HALT,[Bad IPUQ idx in IOCHNM]
MOVE E,CTLBTS(U) ; Get control bits for this call
MOVEI J,(E)
ANDI J,3 ; Get output type in J
TRNN E,%IPIUS ; Getting datagram from user?
JRST [ CAIN J,%IPOUS ; Giving datagram to user?
CAIL W,3 ; Yes, ensure at least 3 args.
JRST IPKIO2 ; All's OK, go check input queue.
JRST OPNL30] ; Will write to user, but too few args!
CAIGE W,3 ; Must have at least 3 args for this one.
JRST OPNL30 ; Too few args.
; Get datagram from user.
; B/ user addr of buffer
; C/ # of 32-bit words in buffer
TRZ E,%IPIQK ; Flush "keep" bit since won't be on any list!
CAIL C,5 ; Must have at least 5 words for IP
CAIL C,%IMXLN ; Must be less or eq to maximum datagram size
JRST OPNL33 ; Too big, say meaningless args.
CAIN J,%IPOUS ; Outputting back to self?
JRST POPJ1 ; Yeah, just turn into a NOP.
CALL PKTGF ; Get a free packet buffer (hangs until got it)
PUSHJ P,LOSSET ; Must put back on freelist if we PCLSR on BLT fault
PKTPCL ; Standard routine expects ptr in A
TRCPKT A,"IPKIOT Alloc"
MOVSI B,(B)
HRR B,PK.BUF(A)
MOVEI D,(C)
ADDI D,-1(B) ; Find last address copying into
XCTR XBR,[BLT B,(D)] ; Gobble up user's buffer! May fault.
PUSHJ P,LSWDEL ; Made it through, can flush PCLSR protection
HRLM C,PK.BUF(A) ; Set # words used in buffer
MOVE B,PK.BUF(A) ; Find addr of start of buffer
HRLZM B,PK.IP(A) ; and set start of IP header.
LDB D,[IP$IHL (B)] ; Find claimed length of IP header
ADDI D,(B) ; Get addr of start of IP data
HRLZM D,PK.TCP(A) ; Set that too.
JRST IPKIO3 ; Now decide about checking datagram!
; Get datagram from input queue.
IPKIO2: CONO PI,NETOFF
SKIPN A,IPUQHD(I) ; Anything in the queue?
JRST [ CONO PI,NETON
TRNE E,%IPNOH ; No, see if ok to hang.
JRST POPJ1 ; Don't hang, win-return zero wds-read in A.
SKIPN IPUQHD(I) ; Hang, here we go.
CALL UFLS
JRST IPKIO2]
TRNN A,-1 ; Make sure something was there!
BUG
CAIN I,1 ; Is this SysOut queue?
JRST [ MOVE T,PK.FLG(A) ; Yes, get flags
TLNN T,(%PKFLS) ; Actually wants to flush now?
JRST .+1 ; No, let's go with it.
MOVEI Q,IPUQHD(I)
CALL PKQGF(PK.IP) ; Remove from queue
CAIN A,
BUG
CALL PKTRT ; Flush it.
JRST IPKIO2]
CONO PI,NETON
MOVE T,PK.BUF(A) ; Verify that something exists
TLNE T,-1 ; in both <# wds> field
TRNN T,-1 ; and <buff addr> field.
BUG HALT,[IPQ: Null dgm found on queue]
HLRZ T,PK.IP(A) ; Should also be an IP pointer
CAIN T,
BUG HALT,[IPQ: IP-less dgm on queue]
; Now have pointer in A to a datagram. It is still linked
; on the input queue, unless %IPIUS is set.
IPKIO3: TRNE E,%IPNOC ; Should we check the contents at all?
JRST IPKIO5 ; Nope, just go straight ahead.
JFCL ; Here we should verify/set checksum, but...
; Now figure out where datagram wants to go!
IPKIO5: JRST @.+1(J) ; Only have 4 possibilities so far.
IQIO70 ; %IPOUS Output to user
IQIO60 ; %IPOUT Output to network
IQIO55 ; %IPOFL Flush it
IQIO80 ; %IPORV Re-vector through input queues
; %IPOFL Flush datagram.
IQIO55: TRNN E,%IPIUS ; Is it from input queue list?
CALL IPIQGF ; Yes, take it off input queue list
CALL PKTRT ; Now can return to packet freelist!
JRST POPJ1 ; Win return.
; %IPOUT Output datagram to network.
IQIO60: TRNN E,%IPIUS ; Is it still on an input list?
CALL IPIQGF ; Yes, take it off input queue list
CAILE I,1 ; If not from Sys I/O queue,
JRST [ CALL IPKSNQ ; Possibly send onto SysOut queue.
JRST POPJ1]
CALL IPKSNI ; Dgm from Sys queue, never goes back to SysOut
JRST POPJ1
; %IPOUS Output datagram to user (a "read" from user viewpoint)
; This is the only place where we can PCLSR on "output". Note
; that we cannot get here if datagram came from user, so the
; datagram we point to is always still on input queue, and
; we can safely PCLSR without any special backup.
IQIO70: HLRZ D,PK.BUF(A) ; Find # words available
JUMPLE C,OPNL33 ; Neg or zero count -> meaningless arg error
CAILE C,(D) ; If asking for more wds than exist,
MOVEI C,(D) ; only furnish what we've got.
MOVEI D,(B)
ADDI D,-1(C) ; Find last user word to write
HRL B,PK.BUF(A)
XCTR XBW,[BLT B,(D)] ; Shove it at him; can PCLSR here.
TRNE E,%IPIQK ; Done! Should we keep datagram around?
JRST IQIO75 ; Yes, don't flush it.
CALL IPIQGF ; Take datagram off the input queue.
CALL PKTRT ; Return entry/buffer to freelist.
IQIO75: MOVEI A,(C) ; Return count as 1st val!
JRST POPJ1
; Must re-vector through stuff...
; Note that it is illegal to re-vector a datagram from the SysOut
; queue, because it still shares pointers and stuff with
; (for example) TCP retransmit queues. Later, could add code to
; get another packet buffer and copy it over, but this is better
; done at the device driver level probably.
IQIO80: TRNN E,%IPIUS ; Came from user?
JRST [ CAIN I,1 ; No, from a queue; is it the SysOut queue?
JRST OPNL2 ; Yes, illegal. Say "Wrong direction".
CALL IPIQGF ; No, is OK. Take it off input list.
JRST .+1]
MOVEI R,(A)
HLRZ W,PK.IP(R) ; Get pointer to IP header
HLRZ H,PK.TCP(R) ; and to IP data.
SETZ J,
CONO PI,NETOFF
CALL IPRDGV ; Go vector and process the datagram.
CONO PI,NETON
JRST POPJ1
; Auxiliary, clobbers D to do checking.
IPIQGF: MOVEI D,(A)
MOVEI Q,IPUQHD(I) ; Is from list, must take it off.
CALL PKQGF(PK.IP) ; Remove from IP queue list
CAME A,D
BUG ; Something added in meantime???
RET
SUBTTL IP TCP Interface Routines
; IPMTU - Size of largest datagram we want to send to a given destination
; A/ Destination address
; Returns T/ MTU
SUBN27==:<HOSTN 18,27,0,0> ; Damn macro generates an error inside literal
NW%CHW==:<HOSTN 128,31,0,0> ; Old CHAOS-wrapping scheme, probably unused
IPMTU:
IFE IPUNCP,[
PUSH P,A ; Save address for a bit
MOVEI T,576. ; Default value
GETNET A ; Network part only
CAMN A,[NW%ARP] ; Arpanet?
MOVEI T,%IMMTU ; MTU of IMP
CAMN A,[NW%AI]
MOVEI T,%IMMTU ; AI net. We know we have a good path
CAMN A,[NW%CHW] ; Wrapped chaos packets
MOVEI T,488. ; Smaller MTU
CAME A,[NW%LCS] ; Net 18 is ugly, must check subnets
JRST IPMTU1
MOVE A,(P) ; Get full address back
TRZ A,177777 ; Mask off all but 18.<subnet>
CAMN A,[SUBN27] ; Subnet 27 is fed by chaos-wrapping.
SKIPA T,[488.-40.] ; Giving it a very small MTU
MOVEI T,%IMMTU ; Good path to all others
IPMTU1: POP P,A
] ;IFE IPUNCP
IFN IPUNCP, MOVEI T,488.-40. ; This should be small enough...
RET
IF1,.ERR Amazing MIT-Specific crocks near IPMTU...
; IPBSLA - Best Local Address for a given destination
; Scans backward thru interface tables, so as to default to first entry.
; A/ Destination IP Address
; Return A/ Local Address to use
; Clobbers T.
IPBSLA:
REPEAT NNIFS-1,[
MOVE T,A
AND T,NIFIPM+<NNIFS-1-.RPCNT> ; Mask to get net #
CAMN T,NIFIPN+<NNIFS-1-.RPCNT> ; Is it the net # for this interface?
JRST [ MOVE A,NIFIPA+<NNIFS-1-.RPCNT> ; Yes, use our IP addr for it!
RET]
]
MOVE A,NIFIPA ; Default - use first interface
RET ; (IMP, if it exists, else CHAOS)
; IPLCLH - Skip return if address in A is one of us.
; Called with JSP T,IPLCLH
IPLCLH:
REPEAT NNIFS,[
CAMN A,NIFIPA+.RPCNT
JRST 1(T) ; Yup, won
]
JRST (T) ; Nope, no match
; IPKSND - Invoked by TCP to send off a segment.
; Fills in the IP header fields, checksums, and puts on output queue.
; R, W, H set up pointing to segment
; The out-of-TCP information is contained in the "IP header" that
; W points to:
; IP$SRC - Source addr
; IP$DST - Dest Addr
; IP$TOL - Length of segment in bytes (must add IP header length)
; Clobbers A,B,C,D,E,Q,T
EBLK
IPIDCT: 0 ; IP identification #, incremented for each datagram
BBLK
IPKHDR: MOVE A,IP$VER(W) ; Get first word
ADDI A,<5*4>_4 ; Add length of IP header (5 wds for now)
HRLI A,212000 ; Fill in Ver, IHL, TOS
MOVEM A,IP$VER(W) ; Set 1st wd
ADDI A,3_4 ; Now, to get # of words, round up
LSH A,-<4+2> ; (note flush 4 spare bits then divide by 4)
ANDI A,37777 ; 14 bit field now
HRLM A,PK.BUF(R) ; Store # of words, for device driver.
MOVSI A,170030 ; TTL and PTC (TCP)
MOVEM A,IP$TTL(W) ; Set 3rd wd
IPKHD2: AOS A,IPIDCT ; Get new ID number
LSH A,<16.+4> ; Left justify it
MOVEM A,IP$ID(W) ; Use to set up 2nd wd (no flags/frags)
CALL IPCKSM ; Get IP header checksum
DPB A,[IP$CKS (W)] ; In it goes!
RET
IPKSND: TRCPKT R,"IPKSND output call"
CALL IPKHDR
MOVEI A,(R) ; Set up PE ptr arg for following stuff.
; IPKSNQ - entry point from IPKIOT, to send a datagram.
; A/ PE ptr to datagram - PK.BUF must be set up.
; Clobbers A,B,T,Q
IPKSNQ: MOVSI T,(%PKODN) ; Clear the "output-done" flag.
ANDCAM T,PK.FLG(A)
TRCPKT A,"IPKSNQ output call"
SKIPE IPUQUS+1 ; Check - have System Output queue?
JRST IPKSN5 ; Yes, put on that queue.
; No, drop into IPKSNI
; IPKSNI - Route packet to appropriate gateway and interface
; A/ PE ptr to datagram - PK.BUF must be set up.
; Clobbers A,B,T,Q
IPKSNI: SKIPLE B,PK.BUF(A) ; Get the packet buffer from the PE
CAMG B,[2,,0]
BUG HALT,[IP: Null dgm being sent]
LDB B,[IP$DST(B)] ; Get destination address
;; This is where to apply final gateway routing code, based on Internet address in B.
REPEAT NNIFS,[ ; Check local interfaces first, quickly.
MOVE T,B
AND T,NIFIPM+.RPCNT ; Use interface netmask (may be subnetting)
CAMN T,NIFIPN+.RPCNT ; Does it match interface net?
JRST [ MOVEM B,PK.DST(A) ; Yes! Go use it now!
CALRET @NIFIPO+.RPCNT]
]
; Didn't match any interface's network, so look for a gateway.
GETNET T,B ; Get net # in T, using standard class A/B/C
MOVSI Q,-NIPGW ; Search table of gateways
CAME B,IPGWTN(Q) ; Skip if network # matches
AOBJN Q,.-1
JUMPL Q,IPSNI1 ; Jump if found entry in table
AOS Q,IPGWPG ; No gateway known for this network, so try a
CAIL Q,NIPMGW ; prime gateway and hope for an ICMP redirect!
SETZB Q,IPGWPG ; Try a different prime gateway each time
IPSNI1: MOVE T,TIME ; Remember that this gateway entry was used
MOVEM T,IPGWTM(Q)
MOVE B,IPGWTG(Q) ; Get gateway address
MOVEM B,PK.DST(A) ; Save gateway address for interface to use
CALRET @IPGWTI(Q) ; Dispatch to interface
EBLK
; Network Interface tables. Currently only knows about IMP and CHAOS.
NIFIPA: ; IP Address for network interface
IFN IMPP, IMPUS3 ; IP address on IMP interface
IFN CHAOSP, IMPUS4 ; IP address on CHAOS interface
IFN .-NIFIPA-NNIFS, .ERR Wrong size table - NIFIPA
NIFIPM: ; IP Network Mask for network interface
IFN IMPP, NM%IMP ; Netmask for IMP interface
IFN CHAOSP, NM%CHA ; Netmask for CHAOS interface
IFN .-NIFIPM-NNIFS, .ERR Wrong size table - NIFIPM
NIFIPN: ; IP Network # for network interface
IFN IMPP, <IMPUS3>&<NM%IMP>
IFN CHAOSP, <IMPUS4>&<NM%CHA>
IFN .-NIFIPN-NNIFS, .ERR Wrong size table - NIFIPN
NIFIPO: ; IP Output routine for network interface
IFN IMPP, IPKSNA ; Send IP dgm to IMP output
IFN CHAOSP, IPKSNC ; Send IP dgm to Chaos, may fragment
IFN .-NIFIPO-NNIFS, .ERR Wrong size table - NIFIPO
;NIFNAM: ; Interface name (in case of PEEKing someday?)
; IFN IMPP, SIXBIT /IMP/
; IFN CHAOSP, SIXBIT /CHA/
;IFN .-NIFNAM-NNIFS, .ERR Wrong size table - NIFNAM
; Gateway routing tables. Currently only routes by network, not by
; specific host. Note that the entries in IPGWTI (interface pointers) had
; better be able to directly send to the corresponding IP addresses in IPGWTG
; (IP addrs of GWs)! This saves another pass through the NIF tables.
IPFORW: NNIFS-1 ; > 0 to forward IP packets.
IPGWPG: 0 ; Index of current prime gateway
IFNDEF NIPGW, NIPGW==:64. ; Max # IP gateway entries
IPGWTN: BLOCK NIPGW ; Network # served by this gateway entry
IPGWTG: BLOCK NIPGW ; IP address of gateway
IPGWTI: BLOCK NIPGW ; Interface routine to use for this IP address
IPGWTM: BLOCK NIPGW ; TIME that entry was last used
NIPPGW==0 ; # of permanent GWs
DEFINE GWDEF (NET,GW,NIF) ; Store permanent GW defs
%%%GSV==.
LOC IPGWTN+NIPPGW ? NET
LOC IPGWTG+NIPPGW ? GW
LOC IPGWTI+NIPPGW ? NIF
LOC %%%GSV
EXPUNGE %%%GSV
NIPPGW==NIPPGW+1
TERMIN
; Now define the prime & permanent gateways.
; Someday this really ought to be configurable either in CONFIG
; at assembly time, or from a file at runtime.
IFE ITSMCH-SIXBIT/NX/,[
GWDEF 0,<HOSTN 139,185,5,1>,IPKSNA ; NPD cisco router, use for all.
NIPMGW==NIPPGW ; Number of prime gateways
].ELSE [
IFE IPUNCP,[
GWDEF NW%LCS,<HOSTN 10,0,0,77>,IPKSNA ; MIT-GW
GWDEF NW%AI, <HOSTN 10,3,0,6>, IPKSNA ; MIT-AI-GW
] ;IFE IPUNCP
IFN IPUNCP,[
GWDEF NW%LCS,<HOSTN 128,31,6,1>,IPKSNC ; ???
GWDEF NW%AI, <HOSTN 128,31,6,2>,IPKSNC ; ???
] ;IFN IPUNCP
NIPMGW==NIPPGW ; Number of prime gateways
] ; IF NOT NX
BBLK
IFN IMPP,[
; Queue packet for Arpanet interface
IPKSNA: MOVEI Q,IPOUTQ ; Otherwise use direct IP output queue.
MOVE B,(Q) ; Save previous contents of queue header
CALL PKQPL(PK.IP) ; Put on IP output queue
CAIE B,0 ; Kick off IP output if necessary.
RET ; Not necessary, queue was not empty
IPOGO: CALRET IMPIOS ; Just means kicking IMP for now.
] ;IFN IMPP
IFN CHAOSP,[
; Queue packet for Chaosnet interface
; A has the pe
; PK.DST(A) has the Internet address to send to, 128.31.subnet.host
; The low 16 bits are Chaosnet address to send an UNC to
IPKSNC: PUSH P,C
PUSH P,H
PUSH P,J
PUSH P,E
PUSH P,W
MOVE J,A ;J has address of PE
MOVE H,PK.BUF(A) ;H has address of IP header
MOVEI E,0 ;E has number of bytes sent so far
IPKSC1: CALL CHABGI ;Get a Chaosnet buffer in A
JRST IPKSC9 ;Give up if can't get one
MOVSI T,-%CPKDT ;Zero out the Chaosnet header
HRRI T,(A)
SETZM (T)
AOBJN T,.-1
MOVEI T,%COUNC
DPB T,[$CPKOP(A)]
MOVE C,PK.DST(J)
DPB C,[$CPKDA(A)]
MOVEI T,MYCHAD
DPB T,[$CPKSA(A)]
MOVEI T,8_8 ;DOD Internet #x0800
DPB T,[$CPKAN(A)] ;Protocol number
AOS CHNIPO ;Meter Internet packets out to Chaosnet
LDB Q,[IP$IHL(H)] ;Internet header length in words
MOVE T,Q ;Save header length for later
MOVSI B,(H) ;BLT IP header into Chaos packet
HRRI B,%CPKDT(A)
ADDI Q,(B)
BLT B,-1(Q) ;Q saves address of first data word
LDB B,[IP$TOL(H)] ;Total length in octets including header
SUB B,E ;Number of bytes remaining to be sent
MOVEI C,IPKSC9 ;Continuation if no more fragments needed
CAIG B,%CPMXC ;Skip if need to fragment
JRST IPKSC2
MOVEI B,%CPMXC/4 ;Compute number of 32-bit data words in fragment
SUB B,T
TRZ B,1 ;Round down to even multiple of 8 octets
ADD B,T
LSH B,2 ;Number of bytes in this fragment including header
MOVEI W,IP%FMF ;Set more-fragments flag
IORM W,IP$FLG+%CPKDT(A)
MOVEI C,IPKSC1 ;Continuation sends another fragment
IPKSC2: DPB B,[IP$TOL+%CPKDT(A)] ;Total length of this fragment
DPB B,[$CPKNB(A)]
PUSH P,C ;Save continuation address
MOVE W,E ;Get fragment offset
LSH W,-3 ;8-octet units
LSH T,2 ;Number of bytes in header
SUB B,T ;Number of data bytes
LDB C,[IP$FRG+%CPKDT(A)];Set fragment offset
ADD C,W
DPB C,[IP$FRG+%CPKDT(A)]
ADD T,E ;Byte offset of start of data to send
LSH T,-2 ;Word offset
ADD T,H ;Word address
HRL Q,T ;BLT pointer to copy data
MOVEI T,3(B)
LSH T,-2 ;Number of words to copy
ADDI T,-1(Q) ;Address of last word to store
BLT Q,(T) ;Copy the data
ADD E,B ;Offset for next fragment
MOVEI W,%CPKDT(A)
CALL IPCKSM ;Compute header checksum
DPB A,[IP$CKS (W)] ;Store header checksum
MOVEI A,-%CPKDT(W) ;Restore address of chaos packet
SETOM -2(A) ;Not on any packet lists
PUSH P,J ;Save registers clobbered by CHAXMT
PUSH P,D
PUSH P,E
PUSH P,TT
CALL CHAXMT ;Launch packet into Chaosnet
POP P,TT
POP P,E
POP P,D
POP P,J
POPJ P, ;Take continuation
IPKSC9: MOVE A,J ; The PE
CALL IPIODN ; Say we're done transmitting this packet,
POP P,W ; although it's still in Chaos NCP somewhere
POP P,E
POP P,J
POP P,H
POP P,C
POPJ P,
] ; IFN CHAOSP
IPKSN5: MOVEI Q,IPUQHD+1 ; Put on System Output queue
MOVE B,(Q) ; Save prev contents of header
CALL PKQPL(PK.IP)
CAIE B, ; If stuff already there,
RET ; Just return, else
PUSH P,I ; Nothing there before, give user interrupt.
MOVEI I,1 ; On IPQ SysOut queue.
CALL IPQUSI
POP P,I
RET
; IPCKSM - Computes checksum for IP header.
; W/ points to IP header.
; Clobbers B,C
; Returns A/ checksum
IFNDEF JCRY0,JCRY0==:<JFCL 4,> ; Jump on Carry from bit 0 (and clear flag)
IPCKSM: SETZ A,
LDB C,[IP$IHL (W)] ; Get IP header length
MOVE B,IP$CKS(W) ; Get 3rd word
ANDCM B,[IP%CKS] ; Mask out the checksum field
JFCL 17,.+1 ; Clear flags
ADD B,IP$VER(W) ; Add 1st wd
JCRY0 [AOJA A,.+1]
ADD B,IP$ID(W) ; Add 2nd
JCRY0 [AOJA A,.+1]
ADD B,IP$SRC(W) ; Add 4th
JCRY0 [AOJA A,.+1]
ADD B,IP$DST(W) ; Add 5th
JCRY0 [AOJA A,.+1]
CAILE C,5
JRST IPCKS4 ; Longer than 5 words, must hack options.
IPCKS2: LSHC A,16. ; Get high 2 bytes (plus carries) in A
LSH B,-<16.+4> ; Get low 2 bytes in B
IPCKS3: ADDI A,(B) ; Get total sum
CAILE A,177777 ; Fits?
JRST [ LDB B,[202400,,A] ; No, must get overflow bits
ANDI A,177777 ; then clear them
JRST IPCKS3] ; and add in at low end.
ANDCAI A,177777 ; Return ones complement
RET
IPCKS4: SUBI C,5 ; C has a 4 bit value.
MOVN C,C ; Get neg of # words left
LSH C,1 ; Double it
JUMPL C,IPCKS5(C)
RET ; Something is wrong, so just return bad val.
REPEAT 10.,[
ADD B,5+<10.-.RPCNT>(W)
JCRY0 [AOJA A,.+1]
]
IPCKS5: JRST IPCKS2 ; Options all added, now go fold sum.
IFN 0,[ ; Old version
IPCKSM: MOVEI C,(W)
HRLI C,442000 ; Gobble 16-bit bytes
ILDB A,C ; wd 0 byte 1
ILDB B,C
ADDI A,(B) ; Add 2nd byte of 1st wd
ILDB B,C ? ADDI A,(B) ? ILDB B,C ? ADDI A,(B) ; 1 ID,frag
ILDB B,C ? ADDI A,(B) ? IBP C ; 2 Skip chksum field
ILDB B,C ? ADDI A,(B) ? ILDB B,C ? ADDI A,(B) ; 3 source addr
ILDB B,C ? ADDI A,(B) ? ILDB B,C ? ADDI A,(B) ; 4 dest addr
IPCKS8: CAIG A,177777
JRST IPCKS9
LDB B,[202400,,A] ; Get any overflow
ANDI A,177777
ADDI A,(B)
JRST IPCKS8
IPCKS9: ANDCAI A,177777
RET
] ;IFN 0