Consolidate license copies
[its.git] / system / inet.130
1 ;:;; -*- Mode:MIDAS -*- 
2 ;;; Copyright (c) 1999 Massachusetts Institute of Technology
3 ;;;
4 ;;; This program is free software; you can redistribute it and/or
5 ;;; modify it under the terms of the GNU General Public License as
6 ;;; published by the Free Software Foundation; either version 3 of the
7 ;;; License, or (at your option) any later version.
8 ;;;
9 ;;; This program is distributed in the hope that it will be useful,
10 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 ;;; General Public License for more details.
13 ;;;
14 ;;; You should have received a copy of the GNU General Public License
15 ;;; along with this program; if not, write to the Free Software
16 ;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 ; Insert new buffer stuff...
19 $INSRT TCPBUF
20
21 IP%VER==740000,,        ; 0 IP Version # (= 4)
22 IP%IHL==036000,,        ; 0 IP Header Length in 32-bit wds - at least 5
23 IP%TOS==001774,,        ; 0 Type Of Service
24 IP%TOL==000003,,777760  ; 0 Total Length in octets (including header)
25 IP%ID== 777774,,        ; 1 Identification
26 IP%FLG==     3,,400000  ; 1 Flags
27   IP%FDF==   1,,0       ;       Don't-Fragment flag
28   IP%FMF==      400000  ;       More-Fragments flag
29 IP%FRG==     0,,377760  ; 1 Fragment Offset
30 IP%TTL==776000,,        ; 2 Time To Live
31 IP%PTC==  1774,,        ; 2 Protocol
32 IP%CKS==     3,,777760  ; 2 Header Checksum
33 IP%SRC==777777,,777760  ; 3 Source Address
34 IP%DST==777777,,777760  ; 4 Destination Address
35                         ; 5 Start of options
36 IP$VER==<.BP IP%VER,0>
37 IP$IHL==<.BP IP%IHL,0>
38 IP$TOS==<.BP IP%TOS,0>
39 IP$TOL==<.BP IP%TOL,0>
40 IP$ID== <.BP IP%ID, 1>
41 IP$FLG==<.BP IP%FLG,1>
42 IP$FRG==<.BP IP%FRG,1>
43 IP$TTL==<.BP IP%TTL,2>
44 IP$PTC==<.BP IP%PTC,2>
45         %PTCIC==:1              ; Protocol ICMP
46         %PTCTC==:6.             ; Protocol TCP
47         %PTCUD==:17.            ; Protocol UDP
48 IP$CKS==<.BP IP%CKS,2>
49 IP$SRC==<.BP IP%SRC,3>
50 IP$DST==<.BP IP%DST,4>
51
52         ; UDP fields
53 UD$SRC==<242000,,0>             ; 0 wd 1 Source port
54 UD$DST==<042000,,0>             ; 0 wd 2 Dest port
55 UD$LEN==<242000,,1>             ; 1 wd 1 # octets in data
56 UD$CKS==<042000,,1>             ; 1 wd 2 UDP checksum
57 UD$DAT==<441000,,2>             ; 2 Data - actually an ILDB pointer!
58
59         ; ICMP fields
60 IC$TYP==<341000,,0>             ; 0 Type of message
61 IC$COD==<241000,,0>             ; 0 Code (subtype)
62 IC$CKS==<042000,,0>             ; 0 ICMP Checksum
63 IC$GWA==<044000,,1>             ; 1 Random arg, usually Gateway Addr
64 IC$IPH==2                       ; 2 Random data, usually an IP header
65 \f
66 ; Hack metering macro, since we'll want lots of 'em to start with.
67 ; You know, MR% used to be MTR%, except that when %NMTRS is over 100
68 ; you get duplicated symbols, and since they used to be defined with
69 ; == instead of ==: you didn't hear about it, either.
70
71 %NMTRS==:140    ; Allow this many meters
72 DEFINE MTRCOD   ; Put this macro someplace after last meter.
73 EBLK
74 MTRCNT: BLOCK %NMTRS            ; Holds actual meter AOS'd
75 BBLK
76 MTRNAM: BLOCK %NMTRS    ; Holds <instr loc>,,<addr of ASCIZ meter name>
77         LOC MTRNAM
78         REPEAT %%%MTR,CONC MR%,\.RPCNT
79 IF2,    REPEAT %%%MTR,CONC EXPUNGE MR%,\.RPCNT
80         LOC MTRNAM+%NMTRS
81
82 TERMIN
83
84 ; METER - Must be used as in following example:
85 ;       METER("IP: # of bad cksums")
86 %%%MTR==0
87 DEFINE METER &(NAME)
88 IFGE %%%MTR-%NMTRS,.ERR Too many meters!
89 AOS MTRCNT+%%%MTR
90 CONC MR%,\%%%MTR,==:<.,,[ASCIZ NAME]>
91 ;CONC MR%,\%%%MTR,==:<.,,>
92 ;IF1 SHOMTR %%%MTR,NAME
93 %%%MTR==%%%MTR+1
94 TERMIN
95
96 DEFINE SHOMTR #OFF#,&STR&
97 PRINTX /;;;;;;;; METER :::: MTRCNT+!OFF! => /
98 PRINTX STR
99 PRINTX /
100 /
101 TERMIN
102
103 EBLK
104 IPMDFL: 0       ; # of flushed input datagrams
105 IPMCKF: 0       ; # of input datagrams with bad checksum
106 IPMFRG: 0       ; # of fragments received
107 IPMFRD: 0       ; # of sucessfully reassembled datagrams
108 BBLK
109 \f
110 SUBTTL IP Input Interrupt Level
111
112 ; IPGIPT - Get datagram input buffer
113 ;       Clobbers Q,T
114 ;       A/ Max size of buffer in words
115 ; Returns .+1 if failure (error message already printed)
116 ; Returns .+2
117 ;       A/ Pointer to datagram structure associated with buffer
118 ;       B/ Input BLKI pointer to buffer, -<# wds>,,<addr-1>
119
120 IPGIPT: CAILE A,PKBSIZ  ; Make sure size needed will fit in a packet buffer
121          JRST IPGIP9
122         CALL PKTGFI     ; Get a packet at int level
123          RET            ; Failed, none available.
124         TRCPKT A,"IPGIPT Net input alloc"
125         MOVE T,PK.BUF(A)        ; Get addr of buffer
126         HRLOI B,-PKBSIZ ;  -<#wds>,,-1
127         ADDI B,(T)      ; Now get BLKI pointer into buffer
128         JRST POPJ1      ; Win!
129 IPGIP9: BUG CHECK,[IP: Too-big buff reqd =],OCT,A
130         RET             ; Fail.
131
132
133 ; IPRDGM - Process a received datagram at PI level
134 ;       Must put datagram into one of the following lists:
135 ;               User IP input queue (IPQ)
136 ;               IP reassembly table
137 ;               ICMP processing
138 ;               TCP connection queue
139 ;       A/ Pointer to datagram structure
140 ;       B/ # words read in datagram
141 ;       C/ # wds offset to start of IP header
142 ;;; J is not used, and not supplied by all callers
143 ;;;     J/ host-table index of address datagram came from
144 ; Returns .+1 always
145 ; Can clobber all ACs except P
146 ; Sets up
147 ;       R/ addr of packet entry
148 ;       W/ addr of IP header
149 ;       H/ addr of IP data
150
151 IPRDGM: METER("IP: IDs rcvd")
152         MOVEI R,(A)             ; Set up packet entry ptr in canonical place
153         TRCPKT R,"IPRDGM Input from net"
154         CAIGE B,5(C)            ; Make sure it's big enough
155          JRST IPRDG9    
156         HRLM B,PK.BUF(R)        ; Store # words read
157         ADD C,PK.BUF(R)         ; Find addr of start of IP header
158         HRLZM C,PK.IP(R)        ; and set it.
159         MOVEI W,(C)
160         LDB H,[IP$IHL (W)]      ; Find claimed length of IP header
161         ADDI H,(W)              ; Get addr of start of IP data
162         HRLZM H,PK.TCP(R)       ; Set that too.
163
164         ; Do initial vectoring test.
165         SKIPE IPUQUS            ; Check Queue 0 (SysIn)
166          JRST IPRDG2            ; It exists!!  Always vector for it.
167
168         ; Perform initial checking for address, checksum, and so forth
169         ; to verify datagram is good; also dispatch to handle fragments.
170         ; This is entry point for re-vectors from SysIn IP queue.
171 IPRDGV: CALL IPCKSM             ; Compute checksum for IP header
172         LDB B,[IP$CKS (W)]      ; and get what the datagram had,
173         CAIE A,(B)              ; in order to compare them...
174          JRST [ METER("IP: Ifl bad cksm")
175                 AOS IPMCKF      ; Bump two meters
176                 JRST IPRD90]    ; Go flush it forthwith.
177         MOVE B,IP$DST(W)        ; Get destination host, should be us
178         CAME B,[IMPUS3_4]
179          CAMN B,[IMPUS4_4]
180           JRST IPRD10
181         ; Packet is to be forwarded
182         METER("IP: Packets forwarded")
183         LDB B,[IP$TTL(W)]       ; Decrement time to live
184         SOJLE B,[ METER("IP: Packets expired")
185                   JRST IPRD90 ]
186 ICMEK1: DPB B,[IP$TTL(W)]
187         CALL IPCKSM             ; Update the checksum
188         DPB A,[IP$CKS(W)]
189         MOVEI A,(R)             ; Transmit it
190         CALRET IPKSNQ
191
192 IPRD10: HRRE B,IP$FRG(W)        ; HACK!  Get both IP%FMF and IP%FRG!
193         JUMPN B,IPRD50          ; Jump if this is a fragment.
194
195         ; Do datagram vectoring.  This code is temporarily (?) crude,
196         ; it just scans the whole Internet Queue table.
197         ; This is entry point for re-vectoring.  W must point to IP
198         ; header, and H to IP data.  I should point at 1st queue entry
199         ; to start checking at.
200 IPRD20: MOVEI I,2               ; If drop in, start at 2 (leave 0+1 alone)
201         LDB B,[IP$PTC (W)]      ; Get protocol number
202         CAIN B,%PTCTC           ; Is it TCP?
203          JRST TCPIS             ; Yes, go process TCP input segment.
204         CAIN B,%PTCUD           ; Well, is it UDP?
205          JRST IPRD30            ; Yeah, can handle that one.
206         CAIN B,%PTCIC           ; Maybe ICMP?
207          JRST ICMP              ; Yup, hack it.
208 IPRD90: MOVEI A,(R)
209         CALL PKTRT              ; Bah, nothing we handle, flush it.
210         AOS IPMDFL              ; Bump count of flushed dgms.
211         RET
212
213         ; Here to dispatch a UDP datagram
214 IPRD30: LDB A,[IP$TOL (W)]      ; Make sure it's long enough!  Find dgm length
215         LDB B,[IP$IHL (W)]      ; and get IP header length
216         IMULI B,4               ; in octets
217         SUBI A,(B)              ; to subtract from dgm length.
218         CAIGE A,2*4             ; Must have enough data for UDP header!
219          JRST [ METER("IP: Ifl bad UDP len")
220                 JRST IPRD90]    ; Flush this dgm.
221 IPRD31: CAIL I,NIPUQ
222          JRST [ METER("IP: Ifl no UDP port")    ; Didn't find any queues,
223                 JRST IPRD90]    ; so flush it.
224         SKIPN IPUQUS(I)         ; Check each active UDP queue
225          AOJA I,IPRD31
226         LDB B,[UD$DST (H)]      ; Get UDP dest port number
227         HRRZ T,IPUQCT(I)        ; and port # we're watching for
228         CAIE B,(T)
229          AOJA I,IPRD31          ; No match, try another.
230         METER("IP: # UDP dgms queued")
231         CAIA
232 IPRDG2:  SETZ I,                ; Entry point for SysIn queueing
233         MOVEI Q,IPUQHD(I)       ; Hurray, got it!  Add to queue
234         MOVE B,(Q)              ; Save prev contents of header
235         MOVEI A,(R)
236         CALL PKQPL(PK.IP)       ; Put at end of input IP queue
237         JUMPE B,IPQUSI          ; If nothing previously there, give user int.
238         RET
239
240 IPRDG9: BUG INFO,[IP: Netin dgm too small, size ],OCT,B,[ offset ],OCT,C
241         JRST IPRD90             ; Try flushing the packet buffer.
242 \f
243 ; IP Datagram Reassembly - Handle received fragment.
244
245 IPRD50: AOS IPMFRG              ; Bump count of fragments received
246
247         LDB D,[IP$ID (W)]       ; Get datagram ID field
248         LDB C,[IP$PTC (W)]      ; Then protocol field
249         HRLI D,(C)              ; Make <ptcl>,,<ID>
250         MOVE E,IP$SRC(W)        ; Then source address
251         MOVEI I,NIPF-1
252 IPRD51: CAME D,IPFDID(I)
253 IPRD52:  SOJGE I,.-1
254         JUMPL I,IPRD70          ; If no more, must add to table.
255         MOVE B,IPFDPE(I)        ; Matching ID!  Get buffer ptr
256         HLRZ T,PK.IP(B)         ; Get IP header ptr for existing fragment
257         CAME E,IP$SRC(T)        ; Ensure same source host
258          JRST IPRD52            ; Nope, go check next entry.
259         HLRZ H,PK.TCP(B)        ; Get ptr to start of data in reassembly buff
260
261         ; OK, we matched up a fragment!  Now start reassembly procedure.
262         ; If fragment is first one (offset 0) then must copy IP header,
263         ;       unless already done.  Safe to BLT since we always reserve
264         ;       enough room for a full 15-word IP header.
265         ; If fragment is last one (IP%FMF 0) then must set IP$TOL to
266         ;       the total # octets in full datagram.  This gets fixed
267         ;       to include the IP header length when datagram is complete.
268         ; I/ idx of reassembly entry
269         ; T/ ptr to IP header in reassembly buff
270         ; H/ ptr to data in reassembly buff
271         ; R, W as for entry to IPRD50
272 IPRD55: LDB A,[IP$IHL (W)]      ; Get IP header length in 4-octet wds
273         LDB E,[IP$TOL (W)]      ; Get total length of this dgm in octets
274         HRRE D,IP$FRG(W)        ; Hack - get frag offset and more-frag flag
275         TRNN D,IP%FRG           ; Is frag offset 0 - 1st part of dgm?
276          JRST [ LDB C,[IP$FRG (T)]      ; Yeah.  Already copied header?
277                 JUMPE C,.+1             ; Jump if so, don't do again.
278                 MOVEI B,(T)
279                 HRLI B,(W)              ; Set up BLT from,,to
280                 MOVEI C,(T)
281                 ADDI C,(A)              ; Get to+IHL
282                 MOVE Q,IP$CKS(T)        ; Save ptr to hole list
283                 LDB TT,[IP$TOL (T)]     ; Save TOL, might already be set.
284                 BLT B,-1(C)             ; Copy the IP header
285                 HRRM Q,IP$CKS(T)        ; Restore hole list head
286                 DPB TT,[IP$TOL (T)]
287                 JRST .+1]
288         ASH D,-3                ; Get frag.first in terms of 4-octet words
289         JUMPGE D,[              ; Jump for special processing if last frag
290                 MOVNI B,(A)
291                 ASH B,2         ; Get -<# octets in header>
292                 ADDI B,(E)      ; Find # octets of data in this fragment
293                 MOVEI C,(D)
294                 LSH C,2         ; Get # octets data is offset
295                 ADDI B,(C)      ; Finally get total # data octets of full dgm
296                 DPB B,[IP$TOL (T)]
297                 ADDI E,3        ; Okay, round UP to full word
298                 LSH E,-2        ; Get rounded-up length in terms of 4-octet wds
299                 SUBI E,1(A)     ; Get # whole wds of data (minus 1)
300                 JRST IPRD56]    ; Go rejoin normal processing
301
302         ; Not last frag.  Only special check is to ensure length of data
303         ; is rounded down to a fragment boundary (frags are 8-octet chunks).
304         TRZ D,-1#<IP%FRG_-3>    ; Not last frag, clean up RH of frag.first
305         LSH E,-2                ; Get rounded length in terms of 4-octet words
306         SUBI E,1(A)             ; Get # whole words of data, minus 1
307         TRNN E,1                ; Paranoia: ensure # wds of data was EVEN
308          SUBI E,1               ; If not, round DOWN to ensure 8-octet boundary
309
310 IPRD56: JUMPL E,IPRD80          ; Flush if bad length
311         ADDI E,(D)              ; Get frag.last
312         CAIL E,<PKBSIZ-16.>     ; Make sure datagram won't be too big.
313          JRST [ METER("IP: Ifl huge dgm")
314                 CALL IPFDFL     ; Ugh, must flush whole datagram entry! 
315                 JRST IPRD90]    ; Would it be better instead to just
316                                 ; truncate it, and accept anyway since TCP
317                                 ; can ACK up to that much?  Probably not.
318 \f
319         ; Each hole descriptor is 1 word of format
320         ;   hole.first: <hole.last>,,<hole.next (hole.first of next hole)>
321         ;
322         ; During re-configuration of the hole descriptor list, following
323         ; ACs are used
324         ; A/ scratch
325         ; B/ hole.first (wd offset)
326         ; C/ hole.last
327         ; D/ <lastflg>,,frag.first      ; lastflg is 0 if last fragment.
328         ; E/ frag.last
329         ; Q/ ptr to current hole descriptor
330         ; TT/ ptr to previous hole descriptor
331         ; H/ ptr to start of data in reassembly buffer (base for offsets)
332         ; W/ ptr to IP header of just-arrived fragment
333         ; T/ ptr to IP header of reassembly buffer
334         ; R/ ptr to packet entry of just-arrived fragment
335         MOVEI Q,IP$CKS(T)       ; Get ptr to 1st hole descriptor
336 IPRD61: MOVEI TT,(Q)            ; Save old ptr
337         HRRE Q,(Q)              ; Get next descriptor
338         JUMPL Q,IPRD68          ; Jump if end of list
339         MOVEI B,(Q)             ; Set hole.first
340         ADDI Q,(H)              ; Make ptr to hole descriptor
341         HLRZ C,(Q)              ; Get hole.last
342         CAIGE C,(D)             ; If hole.last < frag.first,
343          JRST IPRD61            ;   back to try next hole farther on.
344         CAIGE E,(B)             ; If frag.last < hole.first,
345          JRST IPRD68            ;   passed affected area, so can stop now.
346
347         ; New fragment interacts with current hole in some way!
348         ; Remove current hole from the list, but keep Q pointing to
349         ; start of hole.  TT points to the last valid hole descriptor.
350         MOVE A,(Q)              ; Get hole.first of next hole
351         HRRM A,(TT)             ; Store in prev hole, so current is skipped.
352         CAIL B,(D)              ; If hole.first < frag.first, skip.
353          JRST IPRD66
354
355         ; Create new hole descriptor at start of old hole
356         ;       with new.first = hole.first and new.last = frag.first-1
357         ;  i.e. hole.first: <frag.first-1>,,<hole.next>
358         ; First get ptr to new hole and put it on list.
359         HRRM B,(TT)             ; Point prev hole to new hole.
360         HRLI A,-1(D)            ; Make <frag.first-1>,,<hole.first of next>
361         MOVEM A,(Q)             ; Store new hole descriptor.
362         MOVEI TT,(Q)            ; Make prev be current, in case test below wins
363                         ; Drop thru to check high bound of old hole
364
365 IPRD66: CAIL E,(C)      ; If frag.last < hole.last then hole not all filled
366          JRST IPRD61    ;  (hole all filled, so go check further holes)
367         CAIL D,         ; Some hole left; is this the last fragment?
368          JRST [ HLLOS (TT)      ; Yes!  Zap prev hole to ensure list ends.
369                 JRST IPRD68]    ; and get out of loop now.
370
371         ; Fragment didn't fill last part of hole, so need to create
372         ; new hole descriptor for it,
373         ;       with new.first = frag.last+1 and new.last = hole.last
374         ;  i.e. frag.last+1: <hole.last>,,<hole.next>
375         MOVEI Q,1(E)    ; Get frag.last+1
376         HRRM Q,(TT)     ; Point previous to new hole
377         ADDI Q,(H)      ; Make abs ptr to new hole
378         HRLI A,(C)      ; Make <hole.last>,,<hole.next>
379         MOVEM A,(Q)     ; Store new hole descriptor.
380                         ; Can drop through to end loop, since no further holes
381                         ; are affected.
382
383         ; No more holes on list, we can copy the data now!
384 IPRD68: HLL D,PK.TCP(R) ; Get <ptr to start of arrived data>,,<frag.first>
385         ADDI D,(H)      ; Now have BLT pointer
386         ADDI E,(H)      ; and now have terminating address
387         CAIN E,(D)      ; But if only moving 1 word,
388          JRST [ HLRZ D,D        ; Can't use BLT?
389                 MOVE A,(D)      ; So just move by hand
390                 MOVEM A,(E)
391                 JRST .+2]       ; Skip over it.
392         BLT D,(E)       ; Here we go!
393
394         ; Now see if any holes left...
395         MOVEI W,(T)             ; Save ptr to reassembly IP hdr (H already set)
396         MOVEI A,(R)             ; No need for arrived dgm any more,
397         CALL PKTRTA             ; so flush it now.
398         HRRE A,IP$CKS(W)        ; See if any holes left
399         JUMPGE A,CPOPJ          ; Jump if some left, nothing else to do.
400
401         HRRZ R,IPFDPE(I)        ; Win!!! Get back packet-entry ptr
402         LDB A,[IP$IHL (W)]      ; Must perform final TOL fixup.  Get IHL
403         LSH A,2+4               ; in octets, shifted to TOL field
404         ADDM A,IP$TOL(W)        ; Now have proper length!
405         SETZM IPFDPE(I)
406         SETOM IPFDID(I)
407         HRLOI A,377777
408         MOVEM A,IPFTTL(I)
409         AOS IPMFRD              ; Bump cnt of # datagrams reassembled!
410         JRST IPRD20             ; Go dispatch the datagram!
411
412
413         ; Create entry in table to store 1st fragment in.
414 IPRD70: MOVEI I,NIPF-1
415         SKIPE IPFDPE(I)
416          SOJGE I,.-1
417         JUMPL I,[METER("IP: Ifls Fragtab full") ; Barf, fragment table full.
418                 JRST IPRD90]
419         LDB A,[IP$TTL (W)]      ; Get time-to-live
420         JUMPE A,IPRD90          ; Might as well hack zero case
421         IMULI A,30.             ; Turn into 30ths
422         ADD A,TIME
423         MOVEM A,IPFTTL(I)       ; Store timeout value
424         MOVEM D,IPFDID(I)       ; Store ptcl,,ID
425         HRRZM R,IPFDPE(I)       ; Store PE ptr
426
427         ; Messy stuff, must get data set up into right place in buffer.
428         ; If this is the 1st fragment we are OK, and can use original
429         ; datagram buffer, else we have to shuffle data.  Simplest way
430         ; to handle latter case is to just get a new buffer and copy
431         ; it over.
432         LDB A,[IP$FRG (W)]      ; Get fragment offset field
433         JUMPN A,IPRD75          ; If not zero, jump to do copy.
434         LDB A,[IP$TOL (W)]      ; Hurray, 1st fragment!  Get total length
435         LSH A,-2                ; Round down to # words
436         LDB B,[IP$IHL (W)]
437         SUBI A,(B)              ; Find # words that fragment uses
438         TRZ A,1                 ; Ensure # wds is rounded down to 8-octet chunk
439         JUMPLE A,[CALL IPFDFL   ; Sigh, flush entry.
440                 RET]            ; Just return, only flushing one PE.
441         HRRM A,IP$CKS(W)        ; Store first hole.next in header.
442         MOVEI B,(A)
443         ADDI B,(H)              ; Get addr of start of hole
444         SETOM (B)               ; Make it an infinite hole.
445         RET
446
447         ; Fragment entry must be stored, but it isn't the 1st thing in
448         ; the datagram.  We must cons up a fake initial fragment and
449         ; then copy normally into that fragment.
450         ; Note that this fake fragment must be carefully initiallized
451         ; since certain IP fields are referred to in the reassembly code
452         ; (via pointer in T)
453 IPRD75: CALL PKTGFI             ; Get a PE ptr at PI lvl
454          JRST IPFDFL            ; Failed, must flush entry
455         TRCPKT A,"Reassembly alloc"
456         MOVEM A,IPFDPE(I)       ; Store it
457         HRRZ T,PK.BUF(A)
458         HRLM T,PK.IP(A)         ; Say IP header at start of buffer.
459         MOVEI H,15.             ; Use maximum IHL for offset
460         HRRZM H,IP$CKS(T)       ; Store this offset as ptr to 1st hole desc
461         ADDI H,(T)              ; and make data start at end of max IP hdr.
462         HRLM H,PK.TCP(A)
463         SETOM (H)               ; Make 1st hole descriptor be infinite
464         SETOM IP$FRG(T)         ; Put crap in frag offset field
465         MOVE B,IP$SRC(W)        ; and ensure source host copied too.
466         MOVEM B,IP$SRC(T)
467         JRST IPRD55             ; Now go do the copy...
468
469 IPRD80: METER("IP: Ifl bad len")        ; Bad IP length field
470         JRST IPRD90                     ; Go flush the dgm.
471
472 ; IPFCLK - Called every few seconds at clock level to check
473 ;       reassembly tables and flush any partially filled datagrams
474 ;       which have timed out.
475
476 IPFCLK: MOVEI I,NIPF-1
477         MOVE B,TIME
478         CONO PI,NETOFF          ; Hack with net ints deferred.
479         CAML B,IPFTTL(I)
480          CALL IPFDFL            ; Flush the partial dgm
481         SOJGE I,.-2
482         CONO PI,NETON           ; Done, re-enable net ints.
483         RET
484         
485 ; IPFDFL - Flush reassembly entry in I
486 ;       Clobbers A, Q, T
487
488 IPFDFL: SKIPE A,IPFDPE(I)
489          CALL PKTRTA            ; Flush the packet buffer
490         SETZM IPFDPE(I)
491         SETOM IPFDID(I)         ; Clear out other table stuffs.
492         HRLOI A,377777
493         MOVEM A,IPFTTL(I)
494         RET
495
496
497 ; Datagram Fragment table.
498 ;       Free entries have IPFDPE 0, IPFDID -1, and IPFTTL SETZ-1 (max pos time)
499 EBLK
500 NIPF==:30               ; Max # of outstanding IP datagram reassembly buffers
501 IPFDPE: BLOCK NIPF      ; <PE ptr>
502 IPFDID: REPEAT NIPF,-1  ; <protocol>,,<datagram ID from IP header>
503 IPFTTL: REPEAT NIPF,SETZ-1      ; Sys time after which entry flushed.
504 BBLK
505 \f
506 SUBTTL IP Output Interrupt Level
507
508 EBLK
509 IPOUTQ: 0
510 IPOBLQ: 0
511 BBLK
512
513 ; IPGIOQ - Get IP Output Queue entry for IMP
514 ; Returns .+1 if nothing in queue
515 ; Returns .+2
516 ;       A/ Pointer to datagram structure
517 ;       B/ Output BLKO pointer to buffer, -<# wds>,,<addr-1>
518 ;       C/ Arpanet host address
519 ;       H/ host-table index
520 ; Clobbers Q,T,W,D,E
521
522 IPGOQ1: METER("IP: ODs flushed")
523         CALL PKTRT              ; Internal looping point
524
525 IPGIOQ: MOVEI Q,IPOUTQ
526         CALL PKQGF(PK.IP)       ; Get first thing off IP output list
527         JUMPE A,IPGOQ9          ; Jump and return if nothing there.
528         MOVE T,PK.FLG(A)        ; Get packet flags
529         TLNE T,(%PKFLS)         ; Should we flush this one?
530          JRST IPGOQ1            ; Yes, down the drain it goes.
531         TLO T,(%PKPIL)
532         IORM T,PK.FLG(A)        ; Say packet locked at PI level.
533
534         SKIPLE C,PK.BUF(A)
535          CAMG C,[2,,0]
536           BUG HALT,[IP: Null dgm on queue]
537 IFE KS10P,[
538         ;KS doesn't care, save 2 usec..
539         MOVN B,C                ; Straightforward way to put together AOBJN ptr.
540         HRRI B,-1(C)            ; Now have BLKO
541 ]
542         MOVE C,PK.DST(A)        ; Get destination address
543
544         ; IMP-specific!!!
545         ; Ask interface if it wants this particular datagram right now.
546         ;
547         CALL IMPCTS
548          JRST IPGOQ5            ; Can't send, requeue
549
550         ; Got valid dgm, must ensure that block queue is merged back
551         ; onto beginning of output queue.
552 IPGOQ6: METER("IP: ODs sent")
553         SKIPN D,IPOBLQ          ; See if anything was blocked
554          JRST POPJ1             ; Nope, just take win return.
555         SETZM IPOBLQ    ; Yes, block queue exists!  
556         SKIPN T,IPOUTQ  ; Get ptr to 1st node on output queue
557          JRST [ MOVEM D,IPOUTQ  ; If nothing was left on output queue,
558                 JRST POPJ1]     ; can simply move the list.
559         HLRZ E,D        ; Get ptr to last node on blocked queue
560         HRRM T,PK.IP(E) ; Point end of blocked Q to start of output Q
561         HRRM D,IPOUTQ   ; and point start of output Q to start of block Q
562         JRST POPJ1      ; and return with nice winning dgm.
563         
564         ; Come here to handle blockage of IP datagram.
565 IPGOQ5: MOVSI T,(%PKPIL)
566         ANDCAM T,PK.FLG(A)      ; Say not locked at PI after all
567         MOVEI Q,IPOBLQ
568         CALL PKQPL(PK.IP)       ; Put blocked dgm onto block queue
569         JRST IPGIOQ             ; Now go try next dgm.
570
571         ; Output queue empty, just shift block queue back.
572 IPGOQ9: SKIPN A,IPOBLQ  ; See if anything was put on block queue
573          RET            ; Nope, all's clear.
574         MOVEM A,IPOUTQ  ; Aha, move it to standard output queue
575         SETZM IPOBLQ    ; and clear the block-queue ptr.
576         RET             ; Nothing to send from IP at moment.
577
578
579
580 ; IPIODN - Output of IP datagram complete, wrap up.
581 ;       Called by all device drivers.
582 ;       A/ pointer to datagram structure
583 ;       Clobbers T,Q
584 ; Returns .+1 always
585
586 IPIODN: TRCPKT A,"IPIODN Packet output complete"
587         MOVE T,PK.FLG(A)        ; Get flags for packet
588         TLO T,(%PKODN)          ; Say output done,
589         TLZ T,(%PKPIL)          ; and unlock PI level output flag.
590         MOVEM T,PK.FLG(A)       ; Store flags back.
591         TLNN T,(%PKFLS)         ; Asked to flush? (shudda caught at IPGIOQ)
592          TLNN T,(%PKNOF)        ; Unless requested not to free it,
593           CALRET PKTRT          ; Return it to freelist.
594
595         ; Keep around (assume its on some other list)
596         POPJ P,
597 \f
598 SUBTTL ICMP - Internet Control Message Protocol
599
600 ; ICMP called at NET interrupt level to process just-received ICMP
601 ;       datagram.
602
603 ICMP:   
604
605         ; First compute and verify checksum for ICMP data.
606
607         ; Then dispatch on type for processing.
608         LDB E,[IP$SRC (W)]      ; Load up source addr (commonly needed)
609         LDB A,[IC$TYP (H)]      ; Get ICMP type field
610         CAIL A,NICMPT
611          JRST ICMP19
612         AOS ICMPCT(A)           ; Bump count of types
613         JRST @ICMPTB(A)         ; Dispatch on type
614
615         ; Bad type
616 ICMP19: BUG INFO,[ICMP: Bad type ],DEC,A,[from ],OCT,E
617 ICMP90: MOVEI A,(R)
618         CALL PKTRTA
619         RET
620
621 ICMPTB: ICMP90          ;  0 Echo Reply (ignored)
622         ICMP19          ;  1 -
623         ICMP19          ;  2 -
624         ICMP90          ;  3 Destination Unreachable (ignored)
625         ICMP90          ;  4 Source Quench (ignored)
626         ICMRD           ;  5 Re-direct
627         ICMP19          ;  6 -
628         ICMP19          ;  7 -
629         ICMEK           ;  8 Echo
630         ICMP19          ;  9 -
631         ICMP19          ; 10 -
632         ICMP90          ; 11 Time Exceeded (ignored)
633         ICMPP           ; 12 Parameter Problem
634         ICMP90          ; 13 TimeStamp (ignored)
635         ICMP90          ; 14 TimeStamp Reply (ignored)
636         ICMP90          ; 15 Information Request (ignored)
637         ICMP90          ; 16 Information Reply (ignored)
638 NICMPT==.-ICMPTB
639 EBLK
640 IPMICM: 0               ; # of ICMP datagrams
641 ICMPCT: BLOCK NICMPT    ; # of ICMP datagrams, by type
642 BBLK
643
644 ; Type 8 - Echo
645
646 ICMEK:  MOVEI A,0               ; Set type to Echo Reply
647         DPB A,[IC$TYP (H)]
648         LDB A,[IC$CKS (H)]      ; Fix checksum for change of 8 to 0
649         ADDI A,8_8
650         TRNE A,1_16.
651          ADDI A,1
652         DPB A,[IC$CKS (H)]
653         MOVE A,IP$SRC(W)        ; Exchange source and destination
654         EXCH A,IP$DST(W)
655         MOVEM A,IP$SRC(W)
656         MOVEI B,60.             ; Reset time to live
657         JRST ICMEK1             ; Go send packet
658
659 ; Type 12 - Parameter Problem.
660
661 ICMPP:  LDB B,[IC$COD (H)]      ; Get code field
662         JUMPE B,ICMPP2
663         BUG INFO,[ICMP: Param err, code ],OCT,B,[from ],OCT,E
664         JRST ICMP90
665 ICMPP2: LDB A,[341000,,1(H)]    ; Get pointer into bad IP header
666         MOVEI B,(A)
667         LSH B,-2                ; Find word # error is in
668         ADDI B,IC$IPH(H)        ; Make addr to word
669         BUG INFO,[ICMP: Param err, ptr ],OCT,A,[wd ],OCT,(B),[from ],OCT,E
670         JRST ICMP90
671
672 ; ICMP type 5 - Redirect
673
674 ICMRD:  MOVEI D,IC$IPH(H)
675         MOVE A,IP$SRC(D)        ; Get source addr of alleged IP header
676         CAME A,[IMPUS3_4]       ; Must be a datagram WE sent.
677          CAMN A,[IMPUS4_4]
678           CAIA
679            JRST ICMP90          ; Bah, flush.  Probably should log it.
680         LDB A,[IP$DST (D)]      ; Get dest addr we used
681         GETNET A                ; Derive net number
682         LDB B,[IC$GWA (H)]      ; Get gateway addr recommended for this net
683         MOVEI C,NIPGW-1         ; Scan backwards thru gateway table
684         SETOB T,TT              ; Index of free slot, index of oldest slot
685 ICMRD2: CAMN A,IPGWTN(C)
686          JRST [ SKIPN IPGWTG(C) ; Don't change a direct-route entry!
687                  JRST ICMP90
688                 JRST ICMRD3 ]
689         CAIL C,NIPPGW           ; Skip if prime gateway, not replaceable
690          JRST [ SKIPN IPGWTN(C)
691                  MOVEI T,(C)    ; Save index of last free slot found
692                 SKIPL TT
693                  CAML D,IPGWTM(C)
694                   MOVEI TT,(C)  ; Save index of least recently used slot
695                 MOVE D,IPGWTM(TT)
696                 SOJA C,ICMRD2 ]
697         SOJGE C,ICMRD2
698
699         ; Network not found in gateway table, must make new entry.
700         SKIPL C,T               ; If there was one free,
701          JRST ICMRD3            ; go use that one.
702         MOVE C,TT               ; Otherwise use least recently used entry
703         MOVE T,TIME
704         SUB T,IPGWTM(C)
705         CAIGE T,60.*60.*30.     ; Flushing entry less than 1 hour old?
706          BUG INFO,[ICMP: GW table full, net/gw ],OCT,IPGWTN(C),OCT,IPGWTG(C),[=>],OCT,A,OCT,B
707 ICMRD3: GETNET D,B              ; Figure out which interface this gateway is on
708         MOVEI T,NIPPGW-1
709         SKIPN IPGWTG(T)
710          CAME D,IPGWTN(T)
711           SOJGE T,.-2
712         JUMPL T,ICMP90          ; I can't figure out how to get to this gateway anyway
713         MOVEM A,IPGWTN(C)       ; Set network number
714         MOVEM B,IPGWTG(C)       ; and its corresponding gateway addr
715         MOVE T,IPGWTI(T)        ; and its interface
716         MOVEM T,IPGWTI(C)
717         MOVE T,TIME             ; Pretend it was used so it
718         MOVEM T,IPGWTM(C)       ; stays around for a while
719         JRST ICMP90             ; Done!
720 \f
721 SUBTTL IPQ Device - Internet Protocol Queues
722
723 ; Internet Protocol User Datagram Queue stuff, manipulated with
724 ; IPKIOT system call.
725 ; Queue 0 is special:
726 ;       Must be asked for explicitly
727 ;       All Input datagrams are vectored through it.
728 ;       No limit on input queue length
729 ;       Can put datagrams back into system for further processing
730 ;       Can send datagrams (like ordinary queue actually in this respect)
731 ; Queue 1 is also special:
732 ;       Must be asked for explicitly
733 ;       All output datagrams are vectored through it.
734 ;       No limit on queue length
735 ;       Can put datagrams back onto device output queue.
736
737 IFNDEF NIPUQ,NIPUQ==10          ; # User queues allowed
738 EBLK
739 IPUQUS: BLOCK NIPUQ             ; <flags><channel>,,<user index>
740         IQ%CH==<77,,>           ; Field for channel #
741         IQ$CH==<.BP IQ%CH,IPUQUS> ; BP to channel #
742 IPUQHD: BLOCK NIPUQ             ; Input queue header
743 IPUQCT: BLOCK NIPUQ             ; # datagrams on input queue,,vector args
744
745 IPQOSW: -1 ? 0          ; IP Queue assignment lock
746  BBLK
747
748 ; IPQO - IPQ OPEN routine
749 ;       Control bits currently defined are
750         %IQSYS==100     ; Set up System Queue (0 or 1)
751         %IQSOU==200     ; System Queue 1 if set, otherwise 0
752         %IQUDP==400     ; Set up random queue for UDP (port # in FN1)
753
754 IPQO:   CALL SWTL       ; Only one job at a time hacking IQ allocation.
755             IPQOSW
756         SETZB E,I               ; Set up convenient zeros
757         TLNE C,%IQSYS           ; Asking for system queue?
758          JRST [ TLNE C,%IQSOU   ; Yes, want input or output?
759                  MOVEI I,1      ; Output, use queue 1
760                 SKIPE IPUQUS(I) ; Skip if it's free
761                  JRST OPNL23    ; Nope, say "file locked".
762                 JRST IPQO2]     ; Can grab it, do so!
763         MOVE I,[-<NIPUQ-2>,,2]  ; Scan tables, skipping 0'th entry
764         SKIPE IPUQUS(I)         ; Look for free slot
765          AOBJN I,.-1
766         JUMPGE I,OPNL6          ; If none available, claim "device full"
767         TLNN C,%IQUDP           ; Got it.  If will use UDP vectoring,
768          JRST OPNL33            ; No, complain "meaningless args"
769                                 ; since nothing else understood yet.
770         TLO E,%IQUDP            ; then set flag for IPUQUS.
771         HRRZM A,IPUQCT(I)       ; Store FN1 as UDP port number
772         CAIA
773 IPQO2:   SETZM IPUQCT(I)
774         SETZM IPUQHD(I)         ; Clear input queue
775         MOVEI A,IPQDN           ; IOCHNM device index to use
776         HRLI A,(I)              ; Save IQ index in LH
777         MOVEM A,(R)
778         MOVEI A,-IOCHNM(R)      ; Start putting together the IPUQUS entry.
779         SUBI A,(U)              ; Get channel #
780         DPB A,[.BP IQ%CH,E]     ; Remember it in IPUQUS word
781         HRRI E,(U)              ; Put user index in RH
782         MOVEM E,IPUQUS(I)       ; Store, queue is now activated!
783                                 ; Note this must be last thing, to avoid
784                                 ; timing errors.
785         CALRET LSWPJ1           ; Unlock switch and return!
786
787 ; IPQCLS - IPQ CLOSE routine
788
789 IPQCLS: HLRZ I,(R)      ; Get IQ idx
790         CAILE I,1       ; Is it the Sys In or Out queue?
791          JRST IPQCL5    ; Nope, can handle normal case.
792         CONO PI,NETOFF  ; Keep anything from being added meanwhile
793         SETZM IPUQUS(I) ; Mark queue not active, to avoid revector loops.
794         SETZM IPUQCT(I) ; Be tidy and clear other stuff too.
795         JUMPE I,IPQCL3
796
797         ; Close down System Output queue.  This means all output
798         ; on this queue gets moved directly onto the real output
799         ; queue.
800 IPQCL1: MOVEI Q,IPUQHD(I)
801         CALL PKQGF(PK.IP)       ; Get first thing queued up
802         JUMPE A,[CONO PI,NETON  ; Exit if no more.
803                 CALRET IPOGO]   ; Ensure output fired up.
804         MOVEI Q,IPOUTQ
805         CALL PKQPL(PK.IP)       ; Put at end of real output queue
806         JRST IPQCL1
807
808         ; Close down System Input queue.  This means all currently
809         ; queued input gets processed immediately.  Note I gets
810         ; clobbered, but isn't necessary since we know this is queue 0.
811 IPQCL3: MOVEI Q,IPUQHD          ; Get header for queue 0
812         CALL PKQGF(PK.IP)       ; Get A/ packet ptr
813         JUMPE A,NETONJ
814         HLRZ B,PK.BUF(A)        ; Get B/ # words in packet
815         SETZ C,                 ; Get C/ # wds offset to IP header
816         CALL IPRDGM             ; Process and vector it.
817         JRST IPQCL3             ; Get next
818         
819
820         ; Normal datagram input queue.  Doesn't need NETOFF since
821         ; PI level ignores the queue entry if it's inactive.  Just
822         ; need to keep another job from assigning it...
823 IPQCL5: CONO PI,CLKOFF
824         SETZM IPUQUS(I) ; Clear its "active" entry word to stop queueing
825         CALL IPQRS2     ; Flush its input queue (clears IPUQHD)
826         SETZM IPUQCT(I)
827         CONO PI,CLKON
828         RET
829
830 ; IPQRST - IPQ RESET routine.  Clears queue for channel.
831 ;       This is pretty drastic for the System I/O queues.
832
833 IPQRST: HLRZ I,(R)      ; Get IQ idx
834         CONO PI,NETOFF  ; Prevent new dgms from arriving meanwhile.
835         CALL IPQRS2     ; Flush the queue
836         JRST NETONJ
837
838 IPQRS2: MOVEI Q,IPUQHD(I)
839         CALL PKQGF(PK.IP)       ; Pull off 1st thing
840         JUMPE A,CPOPJ           ; Return when no more
841         MOVE T,PK.FLG(A)
842         CAIN I,1                ; If queue is the Sys Output queue
843          JRST [ TLNE T,(%PKFLS) ; Then do special stuff.
844                  JRST IPQRS3    ; Flush only if explicitly requested
845                 TLZ T,(%PKPIL)  ; Otherwise clear PI-Locked bit
846                 TLO T,(%PKODN)  ; and claim "output done" (ha ha)
847                 MOVEM T,PK.FLG(A)
848                 JRST IPQRS2]
849         TLNN T,%PKNOF   ; Ordinary queue.  Unless specifically protected,
850 IPQRS3:  CALL PKTRT     ; Put all stuff on freelist.
851         JRST IPQRS2
852
853 ; IPQIO - IPQ I/O routine (if anything actually tries using this)
854
855 IPQIO:  JRST OPNL34     ; Say "Wrong Type Device"
856         POPJ P,
857
858 ; IPQSTA - IPQ STATUS routine
859
860 IPQSTA:
861         POPJ P,
862
863 ; IPQWHY - IPQ WHYINT routine
864
865 IPQWHY:
866         JRST POPJ1
867
868 ; IPQRCH - IPQ RFNAME/RCHST routine
869
870 IPQRCH:
871         POPJ P,
872
873 ; IPQRFP - IPQ RFPNTR routine
874
875 IPQRFP: JRST OPNL34
876
877 ; IPQIOP - IPQ IOPUSH/IOPOP routine
878
879 IPQIOP: MOVEI T,(R)
880         SUBI T,IOCHNM(U)
881         CAIN I,
882          MOVEI T,77     ; IOPUSH, use 77
883         HLRZ I,(R)      ; Get IPQ index
884         DPB T,[IQ$CH (I)]       ; Deposit channel #
885         POPJ P,
886
887 ; IPQFRC - IPQ FORCE routine
888
889 IPQFRC:
890         JRST POPJ1
891
892 ; IPQFIN - IPQ FINISH routine
893
894 IPQFIN:
895         JRST POPJ1
896
897 ; IPQUSI - Give User Interrupt on I/O channel.  Not a system call,
898 ;       but called by PI level routines when input arrives for
899 ;       a previously empty queue.
900 ;       Clobbers T,Q
901 ;       I/ index to IP Queue
902
903 IPQUSI: LDB Q,[IQ$CH (I)]       ; Get channel #
904         CAIN Q,77               ; If IOPUSHed, no interrupt.
905          RET
906         PUSH P,U
907         HRRZ U,IPUQUS(I)        ; Get user index
908         CAIN U,
909          BUG
910 ;       MOVSI T,(SETZ)          ; Needn't force PCLSR'ing.
911 ;       IORM T,PIRQC(U)
912         MOVE T,CHNBIT(Q)
913         AND T,MSKST2(U)
914         IORM T,IFPIR(U)
915         POP P,U
916         RET
917
918
919 \f
920 SUBTTL .CALL IPKIOT - IPQ data transfer
921
922 ; .CALL IPKIOT - Internet Protocol Packet Transfer.
923 ;       Arg 1 is channel (must be open on IPQ:, specifies queue #)
924 ;       Arg 2 is address of buffer
925 ;       Arg 3 is count of words
926 ;       Val 1 is count of words read into user space (if any)
927 ;       Control bits specify function.  If none, "read" is assumed.
928 ; Get datagram from:
929         %IPIUS==100     ; 1 = Get datagram from user space, not from a queue
930         %IPNOC==200     ; Global input no-check flag, suppresses normal check.
931                         ;   For User Space, "check" means verify, set cksum.
932                         ;   For Input Queue, "check" means verify IP header.
933                         ;   For SysIn Queue, "check" means verify IP hdr.
934                         ;   For SysOut Queue, means nothing.
935         %IPNOH==400     ; Don't Hang waiting for datagram (Queues only)
936         %IPIQK==1000    ; Keep on queue, don't remove (only for %IPOUS)
937 ; Put datagram to:
938         %IPOUS==0       ; User space
939         %IPOUT==1       ; Output to network (bypasses SysOut queue)
940         %IPOFL==2       ; Flush it
941         %IPORV==3       ; Re-vector to input queues past this one
942
943 IPKIOT:
944         HRRZ A,(R)
945         CAIE A,IPQDN    ; Must be right type device (IPQ)
946          JRST OPNL34    ; Wrong device
947         HLRZ I,(R)      ; Get IP input queue index
948         CAIL I,NIPUQ    ; Ensure it's valid.
949          BUG HALT,[Bad IPUQ idx in IOCHNM]
950         MOVE E,CTLBTS(U)        ; Get control bits for this call
951         MOVEI J,(E)
952         ANDI J,3                ; Get output type in J
953
954         TRNN E,%IPIUS   ; Getting datagram from user?
955          JRST [ CAIN J,%IPOUS   ; Giving datagram to user?
956                  CAIL W,3       ; Yes, ensure at least 3 args.
957                   JRST IPKIO2   ; All's OK, go check input queue.
958                 JRST OPNL30]    ; Will write to user, but too few args!
959         CAIGE W,3       ; Must have at least 3 args for this one.
960          JRST OPNL30    ;  Too few args.
961         
962         ; Get datagram from user.
963         ; B/ user addr of buffer
964         ; C/ # of 32-bit words in buffer
965         TRZ E,%IPIQK    ; Flush "keep" bit since won't be on any list!
966         CAIL C,5        ; Must have at least 5 words for IP
967          CAIL C,%IMXLN  ; Must be less or eq to maximum datagram size
968           JRST OPNL33   ; Too big, say meaningless args.
969         CAIN J,%IPOUS   ; Outputting back to self?
970          JRST POPJ1     ;  Yeah, just turn into a NOP.
971         CALL PKTGF      ; Get a free packet buffer (hangs until got it)
972         PUSHJ P,LOSSET  ; Must put back on freelist if we PCLSR on BLT fault
973             PKTPCL      ; Standard routine expects ptr in A
974         TRCPKT A,"IPKIOT Alloc"
975         MOVSI B,(B)
976         HRR B,PK.BUF(A)
977         MOVEI D,(C)
978         ADDI D,-1(B)            ; Find last address copying into
979         XCTR XBR,[BLT B,(D)]    ; Gobble up user's buffer!  May fault.
980         PUSHJ P,LSWDEL          ; Made it through, can flush PCLSR protection
981         HRLM C,PK.BUF(A)        ; Set # words used in buffer
982         MOVE B,PK.BUF(A)        ; Find addr of start of buffer
983         HRLZM B,PK.IP(A)        ; and set start of IP header.
984         LDB D,[IP$IHL (B)]      ; Find claimed length of IP header
985         ADDI D,(B)              ; Get addr of start of IP data
986         HRLZM D,PK.TCP(A)       ; Set that too.
987         JRST IPKIO3             ; Now decide about checking datagram!
988
989         ; Get datagram from input queue.
990 IPKIO2: CONO PI,NETOFF
991         SKIPN A,IPUQHD(I)       ; Anything in the queue?
992          JRST [ CONO PI,NETON
993                 TRNE E,%IPNOH   ; No, see if ok to hang.
994                  JRST POPJ1     ; Don't hang, win-return zero wds-read in A.
995                 SKIPN IPUQHD(I) ; Hang, here we go.
996                  CALL UFLS
997                 JRST IPKIO2]
998         TRNN A,-1               ; Make sure something was there!
999          BUG
1000         CAIN I,1                ; Is this SysOut queue?
1001          JRST [ MOVE T,PK.FLG(A) ; Yes, get flags
1002                 TLNN T,(%PKFLS) ; Actually wants to flush now?
1003                  JRST .+1       ; No, let's go with it.
1004                 MOVEI Q,IPUQHD(I)
1005                 CALL PKQGF(PK.IP)       ; Remove from queue
1006                 CAIN A,
1007                  BUG
1008                 CALL PKTRT              ; Flush it.
1009                 JRST IPKIO2]
1010         CONO PI,NETON
1011         MOVE T,PK.BUF(A)        ; Verify that something exists
1012         TLNE T,-1               ; in both <# wds> field
1013          TRNN T,-1              ; and <buff addr> field.
1014           BUG HALT,[IPQ: Null dgm found on queue]
1015         HLRZ T,PK.IP(A)         ; Should also be an IP pointer
1016         CAIN T,
1017          BUG HALT,[IPQ: IP-less dgm on queue]
1018
1019         ; Now have pointer in A to a datagram.  It is still linked
1020         ; on the input queue, unless %IPIUS is set.
1021 IPKIO3: TRNE E,%IPNOC           ; Should we check the contents at all?
1022          JRST IPKIO5            ; Nope, just go straight ahead.
1023         JFCL            ; Here we should verify/set checksum, but...
1024
1025         ; Now figure out where datagram wants to go!
1026 IPKIO5: JRST @.+1(J)            ; Only have 4 possibilities so far.
1027           IQIO70                ; %IPOUS Output to user
1028           IQIO60                ; %IPOUT Output to network
1029           IQIO55                ; %IPOFL Flush it
1030           IQIO80                ; %IPORV Re-vector through input queues
1031
1032         ; %IPOFL Flush datagram.
1033 IQIO55: TRNN E,%IPIUS           ; Is it from input queue list?
1034          CALL IPIQGF            ;  Yes, take it off input queue list
1035         CALL PKTRT              ; Now can return to packet freelist!
1036         JRST POPJ1              ; Win return.
1037
1038         ; %IPOUT Output datagram to network.
1039 IQIO60: TRNN E,%IPIUS           ; Is it still on an input list?
1040          CALL IPIQGF            ;  Yes, take it off input queue list
1041         CAILE I,1               ; If not from Sys I/O queue,
1042          JRST [ CALL IPKSNQ     ; Possibly send onto SysOut queue.
1043                 JRST POPJ1]
1044         CALL IPKSNI             ; Dgm from Sys queue, never goes back to SysOut
1045         JRST POPJ1
1046
1047
1048         ; %IPOUS Output datagram to user (a "read" from user viewpoint)
1049         ; This is the only place where we can PCLSR on "output".  Note
1050         ; that we cannot get here if datagram came from user, so the
1051         ; datagram we point to is always still on input queue, and
1052         ; we can safely PCLSR without any special backup.
1053 IQIO70: HLRZ D,PK.BUF(A)        ; Find # words available
1054         JUMPLE C,OPNL33         ; Neg or zero count -> meaningless arg error
1055         CAILE C,(D)             ; If asking for more wds than exist,
1056          MOVEI C,(D)            ; only furnish what we've got.
1057         MOVEI D,(B)
1058         ADDI D,-1(C)            ; Find last user word to write
1059         HRL B,PK.BUF(A)
1060         XCTR XBW,[BLT B,(D)]    ; Shove it at him; can PCLSR here.
1061         TRNE E,%IPIQK           ; Done!  Should we keep datagram around?
1062          JRST IQIO75            ; Yes, don't flush it.
1063         CALL IPIQGF             ; Take datagram off the input queue.
1064         CALL PKTRT              ; Return entry/buffer to freelist.
1065 IQIO75: MOVEI A,(C)             ; Return count as 1st val!
1066         JRST POPJ1
1067
1068         ; Must re-vector through stuff...
1069         ; Note that it is illegal to re-vector a datagram from the SysOut
1070         ; queue, because it still shares pointers and stuff with
1071         ; (for example) TCP retransmit queues.  Later, could add code to
1072         ; get another packet buffer and copy it over, but this is better
1073         ; done at the device driver level probably.
1074 IQIO80: TRNN E,%IPIUS           ; Came from user?
1075          JRST [ CAIN I,1        ; No, from a queue; is it the SysOut queue?
1076                  JRST OPNL2     ; Yes, illegal.  Say "Wrong direction".
1077                 CALL IPIQGF     ; No, is OK.  Take it off input list.
1078                 JRST .+1]
1079         MOVEI R,(A)
1080         HLRZ W,PK.IP(R)         ; Get pointer to IP header
1081         HLRZ H,PK.TCP(R)        ; and to IP data.
1082         SETZ J,
1083         CONO PI,NETOFF
1084         CALL IPRDGV             ; Go vector and process the datagram.
1085         CONO PI,NETON
1086         JRST POPJ1
1087
1088         ; Auxiliary, clobbers D to do checking.
1089 IPIQGF: MOVEI D,(A)
1090         MOVEI Q,IPUQHD(I)       ; Is from list, must take it off.
1091         CALL PKQGF(PK.IP)       ; Remove from IP queue list
1092         CAME A,D
1093          BUG                    ; Something added in meantime???
1094         RET
1095 \f
1096 SUBTTL IP TCP Interface Routines
1097
1098 ; IPMTU - Size of largest datagram we want to send to a given destination
1099 ;       A/ Destination address
1100 ;       Returns T/ MTU
1101
1102 SUBN27==:<HOSTN 18,27,0,0>      ; Damn macro generates an error inside literal
1103 NW%CHW==:<HOSTN 128,31,0,0>     ; Old CHAOS-wrapping scheme, probably unused
1104
1105 IPMTU:  PUSH P,A                ; Save address for a bit
1106         MOVEI T,576.            ; Default value
1107         GETNET A                ; Network part only
1108         CAMN A,[NW%ARP]         ; Arpanet?
1109          MOVEI T,%IMMTU         ; MTU of IMP
1110         CAMN A,[NW%AI]
1111          MOVEI T,%IMMTU         ; AI net. We know we have a good path
1112         CAMN A,[NW%CHW]         ; Wrapped chaos packets
1113          MOVEI T,488.           ; Smaller MTU
1114         CAME A,[NW%LCS]         ; Net 18 is ugly, must check subnets
1115          JRST IPMTU1
1116         MOVE A,(P)              ; Get full address back
1117         TRZ A,177777            ; Mask off all but 18.<subnet>
1118         CAMN A,[SUBN27]         ; Subnet 27 is fed by chaos-wrapping.
1119          SKIPA T,[488.-40.]     ; Giving it a very small MTU
1120           MOVEI T,%IMMTU        ; Good path to all others
1121 IPMTU1: POP P,A
1122         RET
1123
1124 IF1,.ERR Amazing MIT-Specific crocks near IPMTU...
1125
1126 ; IPBSLA - Best Local Address for a given destination
1127 ;       A/ Destination IP Address
1128 ;       Return A/ Local Address to use
1129
1130 IPBSLA: GETNET A
1131         CAMN A,NW%CHW
1132          SKIPA A,[IMPUS4]       ; Local Address on wrapped-chaos net
1133         MOVE A,[IMPUS3]         ; Default local host address to IMP
1134         RET
1135
1136 ; IPLCLH - Skip return if address in A is one of us.
1137 ;       Called with JSP T,IPLCLH
1138
1139 IPLCLH: CAME A,[IMPUS3]
1140          CAMN A,[IMPUS4]
1141           JRST 1(T)
1142         JRST (T)
1143
1144 ; IPKSND - Invoked by TCP to send off a segment.
1145 ;       Fills in the IP header fields, checksums, and puts on output queue.
1146 ;       R, W, H set up pointing to segment
1147 ; The out-of-TCP information is contained in the "IP header" that
1148 ;       W points to:
1149 ;               IP$SRC - Source addr
1150 ;               IP$DST - Dest Addr
1151 ;               IP$TOL - Length of segment in bytes (must add IP header length)
1152 ; Clobbers A,B,C,D,E,Q,T
1153 EBLK
1154 IPIDCT: 0       ; IP identification #, incremented for each datagram
1155 BBLK
1156
1157 IPKHDR: MOVE A,IP$VER(W)        ; Get first word
1158         ADDI A,<5*4>_4          ; Add length of IP header (5 wds for now)
1159         HRLI A,212000           ; Fill in Ver, IHL, TOS
1160         MOVEM A,IP$VER(W)       ; Set 1st wd
1161         ADDI A,3_4              ; Now, to get # of words, round up
1162         LSH A,-<4+2>            ; (note flush 4 spare bits then divide by 4)
1163         ANDI A,37777            ; 14 bit field now
1164         HRLM A,PK.BUF(R)        ; Store # of words, for device driver.
1165         MOVSI A,170030          ; TTL and PTC (TCP)
1166         MOVEM A,IP$TTL(W)       ; Set 3rd wd
1167
1168 IPKHD2: AOS A,IPIDCT            ; Get new ID number
1169         LSH A,<16.+4>           ; Left justify it
1170         MOVEM A,IP$ID(W)        ; Use to set up 2nd wd (no flags/frags)
1171         CALL IPCKSM             ; Get IP header checksum
1172         DPB A,[IP$CKS (W)]      ; In it goes!
1173         RET
1174
1175 IPKSND: TRCPKT R,"IPKSND output call"
1176         CALL IPKHDR
1177         MOVEI A,(R)             ; Set up PE ptr arg for following stuff.
1178
1179 ; IPKSNQ - entry point from IPKIOT, to send a datagram.
1180 ;       A/ PE ptr to datagram - PK.BUF must be set up.
1181 ;       Clobbers A,B,T,Q
1182
1183 IPKSNQ: MOVSI T,(%PKODN)        ; Clear the "output-done" flag.
1184         ANDCAM T,PK.FLG(A)
1185         TRCPKT A,"IPKSNQ output call"
1186         SKIPE IPUQUS+1          ; Check - have System Output queue?
1187          JRST IPKSN5            ; Yes, put on that queue.
1188                                 ; No, drop into IPKSNI
1189
1190 ; IPKSNI - Route packet to appropriate gateway and interface
1191 ;       A/ PE ptr to datagram - PK.BUF must be set up.
1192 ;       Clobbers A,B,T,Q
1193 IPKSNI: MOVEI T,(%PKNOF)        ; Sanity check. Asking not to free?
1194         TLNN T,PK.FLG(A)
1195          JRST .+4
1196         MOVEI T,(%PQFLX)        ; But not on any queues?
1197         TLNN T,PK.FLG(A)
1198          BUG CHECK,[IP: Dgm about to be lost, caller ],OCT,(P)
1199
1200         PUSH P,C
1201         SKIPLE C,PK.BUF(A)      ; Get the packet buffer from the PE
1202          CAMG C,[2,,0]
1203           BUG HALT,[IP: Null dgm being sent]
1204         LDB C,[IP$DST(C)]       ; Get destination address
1205
1206         ;; This is where to apply final gateway routing code, based on Internet address in C.
1207         GETNET T,C              ; Get network # into T
1208         MOVSI Q,-NIPGW          ; Search table of gateways and direct routes
1209         CAME T,IPGWTN(Q)        ; Skip if network # matches
1210          AOBJN Q,.-1
1211         JUMPL Q,IPSNI1          ; Jump if found entry in table
1212         AOS Q,IPGWPG            ; No gateway known for this network, so try a
1213         CAIL Q,NIPMGW           ; prime gateway and hope for an ICMP redirect!
1214          SETZB Q,IPGWPG         ; Try a different prime gateway each time
1215 IPSNI1: MOVE T,TIME             ; Remember that this gateway entry was used
1216         MOVEM T,IPGWTM(Q)
1217         SKIPE IPGWTG(Q)         ; Skip if this is a direct route
1218          MOVE C,IPGWTG(Q)       ; Get gateway address
1219         MOVEM C,PK.DST(A)       ; Save gateway address for interface to use
1220         CALL @IPGWTI(Q)         ; Dispatch to interface
1221         POP P,C
1222         RET
1223
1224 EBLK
1225 IPGWPG: 0               ; Index of current prime gateway
1226
1227                                 ; Network number
1228 IPGWTN: HOSTN 26,0,0,0          ; MILNET (core gateway entry)
1229         HOSTN 128,9,0,0         ; ISINET (core gateway entry)
1230         NW%LCS                  ; LCS net
1231         NW%AI                   ; MIT-AI-NET
1232         HOSTN 36,0,0,0          ; Stanford
1233         HOSTN 128,2,0,0         ; CMU
1234         HOSTN 11,0,0,0          ; UCL
1235 NIPMGW==<.-IPGWTN>              ; Number of prime gateways
1236         NW%ARP                  ; ARPA Net
1237         HOSTN 128,31,0,0        ; MIT Chaosnet
1238 NIPPGW==<.-IPGWTN>              ; Number of permanent gateways
1239         BLOCK 64.               ; Extra stuff to patch in and for redirects
1240 NIPGW==<.-IPGWTN>
1241
1242 ; Internet address of gateway servicing given net number
1243 IPGWTG: HOSTN 10,5,0,5          ; BBN-MILNET-GATEWAY (core gateway entry)
1244         HOSTN 10,3,0,27         ; ISI-GATEWAY (core gateway entry)
1245         HOSTN 10,0,0,77         ; MIT-GW
1246         HOSTN 10,3,0,6          ; MIT-AI-GW
1247         HOSTN 10,1,0,11         ; STANFORD-GW
1248         HOSTN 10,2,0,14         ; CMU-GW
1249         HOSTN 10,1,0,20         ; DCEC-GATEWAY
1250         0                       ; Send direct to Arpanet
1251         0                       ; Send direct to Chaosnet
1252 IFN .-IPGWTG-NIPPGW, .ERR Permanent gateway table at IPGWTG wrong size
1253 LOC IPGWTG+NIPGW
1254
1255 IPGWTI: IPKSNA                  ; BBN-MILNET-GATEWAY (prime gateway entry)
1256         IPKSNA                  ; ISI-GATEWAY (prime gateway entry)
1257         IPKSNA                  ; MIT-GW
1258         IPKSNA                  ; MIT-AI-GW
1259         IPKSNA                  ; STANFORD-GW
1260         IPKSNA                  ; CMU-GW
1261         IPKSNA                  ; DCEC-GATEWAY
1262         IPKSNA                  ; direct to Arpanet
1263         IPKSNC                  ; direct to Chaosnet
1264 IFN .-IPGWTI-NIPPGW, .ERR Permanent gateway table at IPGWTI wrong size
1265 REPEAT NIPGW-NIPPGW,IPKSNA
1266
1267 IPGWTM: BLOCK NIPGW             ; TIME entry last used
1268
1269 BBLK
1270
1271 ; Queue packet for Arpanet interface
1272 IPKSNA: MOVEI Q,IPOUTQ          ; Otherwise use direct IP output queue.
1273         MOVE B,(Q)              ; Save previous contents of queue header
1274         CALL PKQPL(PK.IP)       ; Put on IP output queue
1275         CAIE B,0                ; Kick off IP output if necessary.
1276          RET                    ; Not necessary, queue was not empty
1277 IPOGO:  CALRET IMPIOS           ; Just means kicking IMP for now.
1278
1279 ; Queue packet for Chaosnet interface
1280 ; A has the pe
1281 ; PK.DST(A) has the Internet address to send to, 128.31.subnet.host
1282 ; The low 16 bits are Chaosnet address to send an UNC to
1283 IPKSNC: PUSH P,H
1284         PUSH P,J
1285         PUSH P,E
1286         PUSH P,W
1287         MOVE J,A                ;J has address of PE
1288         MOVE H,PK.BUF(A)        ;H has address of IP header     
1289         MOVEI E,0               ;E has number of bytes sent so far
1290 IPKSC1: CALL CHABGI             ;Get a Chaosnet buffer in A
1291          JRST IPKSC9            ;Give up if can't get one
1292         MOVSI T,-%CPKDT         ;Zero out the Chaosnet header
1293         HRRI T,(A)
1294         SETZM (T)
1295         AOBJN T,.-1
1296         MOVEI T,%COUNC
1297         DPB T,[$CPKOP(A)]
1298         MOVE C,PK.DST(J)
1299         DPB C,[$CPKDA(A)]
1300         MOVEI T,MYCHAD
1301         DPB T,[$CPKSA(A)]
1302         MOVEI T,8_8             ;DOD Internet #x0800    
1303         DPB T,[$CPKAN(A)]       ;Protocol number
1304         AOS CHNIPO              ;Meter Internet packets out to Chaosnet
1305         LDB Q,[IP$IHL(H)]       ;Internet header length in words
1306         MOVE T,Q                ;Save header length for later
1307         MOVSI B,(H)             ;BLT IP header into Chaos packet
1308         HRRI B,%CPKDT(A)
1309         ADDI Q,(B)
1310         BLT B,-1(Q)             ;Q saves address of first data word
1311         LDB B,[IP$TOL(H)]       ;Total length in octets including header
1312         SUB B,E                 ;Number of bytes remaining to be sent
1313         MOVEI C,IPKSC9          ;Continuation if no more fragments needed
1314         CAIG B,%CPMXC           ;Skip if need to fragment
1315          JRST IPKSC2
1316         MOVEI B,%CPMXC/4        ;Compute number of 32-bit data words in fragment
1317         SUB B,T
1318         TRZ B,1                 ;Round down to even multiple of 8 octets
1319         ADD B,T 
1320         LSH B,2                 ;Number of bytes in this fragment including header
1321         MOVEI W,IP%FMF          ;Set more-fragments flag
1322         IORM W,IP$FLG+%CPKDT(A)
1323         MOVEI C,IPKSC1          ;Continuation sends another fragment
1324 IPKSC2: DPB B,[IP$TOL+%CPKDT(A)]        ;Total length of this fragment
1325         DPB B,[$CPKNB(A)]
1326         PUSH P,C                ;Save continuation address
1327         MOVE W,E                ;Get fragment offset
1328         LSH W,-3                ;8-octet units
1329         LSH T,2                 ;Number of bytes in header
1330         SUB B,T                 ;Number of data bytes
1331         LDB C,[IP$FRG+%CPKDT(A)];Set fragment offset
1332         ADD C,W
1333         DPB C,[IP$FRG+%CPKDT(A)]
1334         ADD T,E                 ;Byte offset of start of data to send
1335         LSH T,-2                ;Word offset
1336         ADD T,H                 ;Word address
1337         HRL Q,T                 ;BLT pointer to copy data
1338         MOVEI T,3(B)
1339         LSH T,-2                ;Number of words to copy
1340         ADDI T,-1(Q)            ;Address of last word to store
1341         BLT Q,(T)               ;Copy the data
1342         ADD E,B                 ;Offset for next fragment
1343         MOVEI W,%CPKDT(A)
1344         CALL IPCKSM             ;Compute header checksum
1345         DPB A,[IP$CKS (W)]      ;Store header checksum
1346         MOVEI A,-%CPKDT(W)      ;Restore address of chaos packet
1347         SETOM -2(A)             ;Not on any packet lists
1348         PUSH P,J                ;Save registers clobbered by CHAXMT
1349         PUSH P,D
1350         PUSH P,E
1351         PUSH P,TT
1352         CALL CHAXMT             ;Launch packet into Chaosnet
1353         POP P,TT
1354         POP P,E
1355         POP P,D
1356         POP P,J
1357         POPJ P,                 ;Take continuation
1358
1359 IPKSC9: MOVE A,J                ; The PE
1360         CALL IPIODN             ; Say we're done transmitting this packet,
1361         POP P,W                 ; although it's still in Chaos NCP somewhere
1362         POP P,E
1363         POP P,J
1364         POP P,H
1365         POPJ P,
1366
1367 IPKSN5: MOVEI Q,IPUQHD+1        ; Put on System Output queue
1368         MOVE B,(Q)              ; Save prev contents of header
1369         CALL PKQPL(PK.IP)
1370         CAIE B,                 ; If stuff already there,
1371          RET                    ; Just return, else
1372         PUSH P,I                ; Nothing there before, give user interrupt.
1373         MOVEI I,1               ; On IPQ SysOut queue.
1374         CALL IPQUSI
1375         POP P,I
1376         RET
1377 \f
1378 ; IPCKSM - Computes checksum for IP header.
1379 ;       W/ points to IP header.
1380 ;       Clobbers B,C
1381 ; Returns A/ checksum
1382
1383 IFNDEF JCRY0,JCRY0==:<JFCL 4,>  ; Jump on Carry from bit 0 (and clear flag)
1384
1385 IPCKSM: SETZ A,
1386         LDB C,[IP$IHL (W)]      ; Get IP header length
1387         MOVE B,IP$CKS(W)        ; Get 3rd word
1388         ANDCM B,[IP%CKS]        ; Mask out the checksum field
1389         JFCL 17,.+1             ; Clear flags
1390         ADD B,IP$VER(W)         ; Add 1st wd
1391         JCRY0 [AOJA A,.+1]
1392         ADD B,IP$ID(W)          ; Add 2nd
1393         JCRY0 [AOJA A,.+1]
1394         ADD B,IP$SRC(W)         ; Add 4th
1395         JCRY0 [AOJA A,.+1]
1396         ADD B,IP$DST(W)         ; Add 5th
1397         JCRY0 [AOJA A,.+1]
1398         CAILE C,5
1399          JRST IPCKS4            ; Longer than 5 words, must hack options.
1400 IPCKS2: LSHC A,16.              ; Get high 2 bytes (plus carries) in A
1401         LSH B,-<16.+4>          ; Get low 2 bytes in B
1402 IPCKS3: ADDI A,(B)              ; Get total sum
1403         CAILE A,177777          ; Fits?
1404          JRST [ LDB B,[202400,,A]       ; No, must get overflow bits
1405                 ANDI A,177777           ; then clear them
1406                 JRST IPCKS3]            ; and add in at low end.
1407         ANDCAI A,177777         ; Return ones complement
1408         RET
1409
1410 IPCKS4: SUBI C,5                ; C has a 4 bit value.
1411         MOVN C,C                ; Get neg of # words left
1412         LSH C,1                 ; Double it
1413         JUMPL C,IPCKS5(C)       
1414         RET                     ; Something is wrong, so just return bad val.
1415
1416 REPEAT 10.,[
1417         ADD B,5+<10.-.RPCNT>(W)
1418         JCRY0 [AOJA A,.+1]
1419 ]
1420 IPCKS5: JRST IPCKS2             ; Options all added, now go fold sum.
1421
1422 IFN 0,[ ; Old version
1423 IPCKSM: MOVEI C,(W)
1424         HRLI C,442000           ; Gobble 16-bit bytes
1425         ILDB A,C                ; wd 0 byte 1
1426         ILDB B,C
1427         ADDI A,(B)              ; Add 2nd byte of 1st wd
1428         ILDB B,C ? ADDI A,(B) ? ILDB B,C ? ADDI A,(B)   ; 1 ID,frag
1429         ILDB B,C ? ADDI A,(B) ? IBP C                   ; 2 Skip chksum field
1430         ILDB B,C ? ADDI A,(B) ? ILDB B,C ? ADDI A,(B)   ; 3 source addr
1431         ILDB B,C ? ADDI A,(B) ? ILDB B,C ? ADDI A,(B)   ; 4 dest addr
1432 IPCKS8: CAIG A,177777
1433          JRST IPCKS9
1434         LDB B,[202400,,A]       ; Get any overflow
1435         ANDI A,177777
1436         ADDI A,(B)
1437         JRST IPCKS8
1438 IPCKS9: ANDCAI A,177777
1439         RET
1440
1441 ] ;IFN 0