Consolidate license copies
[its.git] / system / inet.137
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 IFE IPUNCP, 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         CALRET PKTRT            ; Return to freelist if not otherwise queued
592
593 \f
594 SUBTTL ICMP - Internet Control Message Protocol
595
596 ; ICMP called at NET interrupt level to process just-received ICMP
597 ;       datagram.
598
599 ICMP:   
600
601         ; First compute and verify checksum for ICMP data.
602
603         ; Then dispatch on type for processing.
604         LDB E,[IP$SRC (W)]      ; Load up source addr (commonly needed)
605         LDB A,[IC$TYP (H)]      ; Get ICMP type field
606         CAIL A,NICMPT
607          JRST ICMP19
608         AOS ICMPCT(A)           ; Bump count of types
609         JRST @ICMPTB(A)         ; Dispatch on type
610
611         ; Bad type
612 ICMP19: BUG INFO,[ICMP: Bad type ],DEC,A,[from ],OCT,E
613 ICMP90: MOVEI A,(R)
614         CALL PKTRTA
615         RET
616
617 ICMPTB: ICMP90          ;  0 Echo Reply (ignored)
618         ICMP19          ;  1 -
619         ICMP19          ;  2 -
620         ICMP90          ;  3 Destination Unreachable (ignored)
621         ICMP90          ;  4 Source Quench (ignored)
622         ICMRD           ;  5 Re-direct
623         ICMP19          ;  6 -
624         ICMP19          ;  7 -
625         ICMEK           ;  8 Echo
626         ICMP19          ;  9 -
627         ICMP19          ; 10 -
628         ICMP90          ; 11 Time Exceeded (ignored)
629         ICMPP           ; 12 Parameter Problem
630         ICMP90          ; 13 TimeStamp (ignored)
631         ICMP90          ; 14 TimeStamp Reply (ignored)
632         ICMP90          ; 15 Information Request (ignored)
633         ICMP90          ; 16 Information Reply (ignored)
634 NICMPT==.-ICMPTB
635 EBLK
636 IPMICM: 0               ; # of ICMP datagrams
637 ICMPCT: BLOCK NICMPT    ; # of ICMP datagrams, by type
638 BBLK
639
640 ; Type 8 - Echo
641
642 ICMEK:  MOVEI A,0               ; Set type to Echo Reply
643         DPB A,[IC$TYP (H)]
644         LDB A,[IC$CKS (H)]      ; Fix checksum for change of 8 to 0
645         ADDI A,8_8
646         TRNE A,1_16.
647          ADDI A,1
648         DPB A,[IC$CKS (H)]
649         MOVE A,IP$SRC(W)        ; Exchange source and destination
650         EXCH A,IP$DST(W)
651         MOVEM A,IP$SRC(W)
652         MOVEI B,60.             ; Reset time to live
653         JRST ICMEK1             ; Go send packet
654
655 ; Type 12 - Parameter Problem.
656
657 ICMPP:  LDB B,[IC$COD (H)]      ; Get code field
658         JUMPE B,ICMPP2
659         BUG INFO,[ICMP: Param err, code ],OCT,B,[from ],OCT,E
660         JRST ICMP90
661 ICMPP2: LDB A,[341000,,1(H)]    ; Get pointer into bad IP header
662         MOVEI B,(A)
663         LSH B,-2                ; Find word # error is in
664         ADDI B,IC$IPH(H)        ; Make addr to word
665         BUG INFO,[ICMP: Param err, ptr ],OCT,A,[wd ],OCT,(B),[from ],OCT,E
666         JRST ICMP90
667
668 ; ICMP type 5 - Redirect
669
670 ICMRD:  MOVEI D,IC$IPH(H)
671         MOVE A,IP$SRC(D)        ; Get source addr of alleged IP header
672         CAME A,[IMPUS4_4]       ; Must be a datagram WE sent.
673 IFE IPUNCP,[
674          CAMN A,[IMPUS3_4]
675           CAIA
676         ]
677            JRST ICMP90          ; Bah, flush.  Probably should log it.
678         LDB A,[IP$DST (D)]      ; Get dest addr we used
679         GETNET A                ; Derive net number
680         LDB B,[IC$GWA (H)]      ; Get gateway addr recommended for this net
681         MOVEI C,NIPGW-1         ; Scan backwards thru gateway table
682         SETOB T,TT              ; Index of free slot, index of oldest slot
683 ICMRD2: CAMN A,IPGWTN(C)
684          JRST [ SKIPN IPGWTG(C) ; Don't change a direct-route entry!
685                  JRST ICMP90
686                 JRST ICMRD3 ]
687         CAIL C,NIPPGW           ; Skip if prime gateway, not replaceable
688          JRST [ SKIPN IPGWTN(C)
689                  MOVEI T,(C)    ; Save index of last free slot found
690                 SKIPL TT
691                  CAML D,IPGWTM(C)
692                   MOVEI TT,(C)  ; Save index of least recently used slot
693                 MOVE D,IPGWTM(TT)
694                 SOJA C,ICMRD2 ]
695         SOJGE C,ICMRD2
696
697         ; Network not found in gateway table, must make new entry.
698         SKIPL C,T               ; If there was one free,
699          JRST ICMRD3            ; go use that one.
700         MOVE C,TT               ; Otherwise use least recently used entry
701         MOVE T,TIME
702         SUB T,IPGWTM(C)
703         CAIGE T,60.*60.*30.     ; Flushing entry less than 1 hour old?
704          BUG INFO,[ICMP: GW table full, net/gw ],OCT,IPGWTN(C),OCT,IPGWTG(C),[=>],OCT,A,OCT,B
705 ICMRD3: GETNET D,B              ; Figure out which interface this gateway is on
706         MOVEI T,NIPPGW-1
707         SKIPN IPGWTG(T)
708          CAME D,IPGWTN(T)
709           SOJGE T,.-2
710         JUMPL T,ICMP90          ; I can't figure out how to get to this gateway anyway
711         MOVEM A,IPGWTN(C)       ; Set network number
712         MOVEM B,IPGWTG(C)       ; and its corresponding gateway addr
713         MOVE T,IPGWTI(T)        ; and its interface
714         MOVEM T,IPGWTI(C)
715         MOVE T,TIME             ; Pretend it was used so it
716         MOVEM T,IPGWTM(C)       ; stays around for a while
717         JRST ICMP90             ; Done!
718 \f
719 SUBTTL IPQ Device - Internet Protocol Queues
720
721 ; Internet Protocol User Datagram Queue stuff, manipulated with
722 ; IPKIOT system call.
723 ; Queue 0 is special:
724 ;       Must be asked for explicitly
725 ;       All Input datagrams are vectored through it.
726 ;       No limit on input queue length
727 ;       Can put datagrams back into system for further processing
728 ;       Can send datagrams (like ordinary queue actually in this respect)
729 ; Queue 1 is also special:
730 ;       Must be asked for explicitly
731 ;       All output datagrams are vectored through it.
732 ;       No limit on queue length
733 ;       Can put datagrams back onto device output queue.
734
735 IFNDEF NIPUQ,NIPUQ==10          ; # User queues allowed
736 EBLK
737 IPUQUS: BLOCK NIPUQ             ; <flags><channel>,,<user index>
738         IQ%CH==<77,,>           ; Field for channel #
739         IQ$CH==<.BP IQ%CH,IPUQUS> ; BP to channel #
740 IPUQHD: BLOCK NIPUQ             ; Input queue header
741 IPUQCT: BLOCK NIPUQ             ; # datagrams on input queue,,vector args
742
743 IPQOSW: -1 ? 0          ; IP Queue assignment lock
744  BBLK
745
746 ; IPQO - IPQ OPEN routine
747 ;       Control bits currently defined are
748         %IQSYS==100     ; Set up System Queue (0 or 1)
749         %IQSOU==200     ; System Queue 1 if set, otherwise 0
750         %IQUDP==400     ; Set up random queue for UDP (port # in FN1)
751
752 IPQO:   CALL SWTL       ; Only one job at a time hacking IQ allocation.
753             IPQOSW
754         SETZB E,I               ; Set up convenient zeros
755         TLNE C,%IQSYS           ; Asking for system queue?
756          JRST [ TLNE C,%IQSOU   ; Yes, want input or output?
757                  MOVEI I,1      ; Output, use queue 1
758                 SKIPE IPUQUS(I) ; Skip if it's free
759                  JRST OPNL23    ; Nope, say "file locked".
760                 JRST IPQO2]     ; Can grab it, do so!
761         MOVE I,[-<NIPUQ-2>,,2]  ; Scan tables, skipping 0'th entry
762         SKIPE IPUQUS(I)         ; Look for free slot
763          AOBJN I,.-1
764         JUMPGE I,OPNL6          ; If none available, claim "device full"
765         TLNN C,%IQUDP           ; Got it.  If will use UDP vectoring,
766          JRST OPNL33            ; No, complain "meaningless args"
767                                 ; since nothing else understood yet.
768         TLO E,%IQUDP            ; then set flag for IPUQUS.
769         HRRZM A,IPUQCT(I)       ; Store FN1 as UDP port number
770         CAIA
771 IPQO2:   SETZM IPUQCT(I)
772         SETZM IPUQHD(I)         ; Clear input queue
773         MOVEI A,IPQDN           ; IOCHNM device index to use
774         HRLI A,(I)              ; Save IQ index in LH
775         MOVEM A,(R)
776         MOVEI A,-IOCHNM(R)      ; Start putting together the IPUQUS entry.
777         SUBI A,(U)              ; Get channel #
778         DPB A,[.BP IQ%CH,E]     ; Remember it in IPUQUS word
779         HRRI E,(U)              ; Put user index in RH
780         MOVEM E,IPUQUS(I)       ; Store, queue is now activated!
781                                 ; Note this must be last thing, to avoid
782                                 ; timing errors.
783         CALRET LSWPJ1           ; Unlock switch and return!
784
785 ; IPQCLS - IPQ CLOSE routine
786
787 IPQCLS: HLRZ I,(R)      ; Get IQ idx
788         CAILE I,1       ; Is it the Sys In or Out queue?
789          JRST IPQCL5    ; Nope, can handle normal case.
790         CONO PI,NETOFF  ; Keep anything from being added meanwhile
791         SETZM IPUQUS(I) ; Mark queue not active, to avoid revector loops.
792         SETZM IPUQCT(I) ; Be tidy and clear other stuff too.
793         JUMPE I,IPQCL3
794
795         ; Close down System Output queue.  This means all output
796         ; on this queue gets moved directly onto the real output
797         ; queue.
798 IPQCL1: MOVEI Q,IPUQHD(I)
799         CALL PKQGF(PK.IP)       ; Get first thing queued up
800         JUMPE A,[CONO PI,NETON  ; Exit if no more.
801                 CALRET IPOGO]   ; Ensure output fired up.
802         MOVEI Q,IPOUTQ
803         CALL PKQPL(PK.IP)       ; Put at end of real output queue
804         JRST IPQCL1
805
806         ; Close down System Input queue.  This means all currently
807         ; queued input gets processed immediately.  Note I gets
808         ; clobbered, but isn't necessary since we know this is queue 0.
809 IPQCL3: MOVEI Q,IPUQHD          ; Get header for queue 0
810         CALL PKQGF(PK.IP)       ; Get A/ packet ptr
811         JUMPE A,NETONJ
812         HLRZ B,PK.BUF(A)        ; Get B/ # words in packet
813         SETZ C,                 ; Get C/ # wds offset to IP header
814         CALL IPRDGM             ; Process and vector it.
815         JRST IPQCL3             ; Get next
816         
817
818         ; Normal datagram input queue.  Doesn't need NETOFF since
819         ; PI level ignores the queue entry if it's inactive.  Just
820         ; need to keep another job from assigning it...
821 IPQCL5: CONO PI,CLKOFF
822         SETZM IPUQUS(I) ; Clear its "active" entry word to stop queueing
823         CALL IPQRS2     ; Flush its input queue (clears IPUQHD)
824         SETZM IPUQCT(I)
825         CONO PI,CLKON
826         RET
827
828 ; IPQRST - IPQ RESET routine.  Clears queue for channel.
829 ;       This is pretty drastic for the System I/O queues.
830
831 IPQRST: HLRZ I,(R)      ; Get IQ idx
832         CONO PI,NETOFF  ; Prevent new dgms from arriving meanwhile.
833         CALL IPQRS2     ; Flush the queue
834         JRST NETONJ
835
836 IPQRS2: MOVEI Q,IPUQHD(I)
837         CALL PKQGF(PK.IP)       ; Pull off 1st thing
838         JUMPE A,CPOPJ           ; Return when no more
839         MOVE T,PK.FLG(A)
840         CAIN I,1                ; If queue is the Sys Output queue
841          JRST [ TLNE T,(%PKFLS) ; Then do special stuff.
842                  JRST IPQRS3    ; Flush only if explicitly requested
843                 TLZ T,(%PKPIL)  ; Otherwise clear PI-Locked bit
844                 TLO T,(%PKODN)  ; and claim "output done" (ha ha)
845                 MOVEM T,PK.FLG(A)
846                 JRST IPQRS2]
847 IPQRS3:  CALL PKTRT     ; Put all stuff on freelist.
848         JRST IPQRS2
849
850 ; IPQIO - IPQ I/O routine (if anything actually tries using this)
851
852 IPQIO:  JRST OPNL34     ; Say "Wrong Type Device"
853         POPJ P,
854
855 ; IPQSTA - IPQ STATUS routine
856
857 IPQSTA:
858         POPJ P,
859
860 ; IPQWHY - IPQ WHYINT routine
861
862 IPQWHY:
863         JRST POPJ1
864
865 ; IPQRCH - IPQ RFNAME/RCHST routine
866
867 IPQRCH:
868         POPJ P,
869
870 ; IPQRFP - IPQ RFPNTR routine
871
872 IPQRFP: JRST OPNL34
873
874 ; IPQIOP - IPQ IOPUSH/IOPOP routine
875
876 IPQIOP: MOVEI T,(R)
877         SUBI T,IOCHNM(U)
878         CAIN I,
879          MOVEI T,77     ; IOPUSH, use 77
880         HLRZ I,(R)      ; Get IPQ index
881         DPB T,[IQ$CH (I)]       ; Deposit channel #
882         POPJ P,
883
884 ; IPQFRC - IPQ FORCE routine
885
886 IPQFRC:
887         JRST POPJ1
888
889 ; IPQFIN - IPQ FINISH routine
890
891 IPQFIN:
892         JRST POPJ1
893
894 ; IPQUSI - Give User Interrupt on I/O channel.  Not a system call,
895 ;       but called by PI level routines when input arrives for
896 ;       a previously empty queue.
897 ;       Clobbers T,Q
898 ;       I/ index to IP Queue
899
900 IPQUSI: LDB Q,[IQ$CH (I)]       ; Get channel #
901         CAIN Q,77               ; If IOPUSHed, no interrupt.
902          RET
903         PUSH P,U
904         HRRZ U,IPUQUS(I)        ; Get user index
905         CAIN U,
906          BUG
907 ;       MOVSI T,(SETZ)          ; Needn't force PCLSR'ing.
908 ;       IORM T,PIRQC(U)
909         MOVE T,CHNBIT(Q)
910         AND T,MSKST2(U)
911         IORM T,IFPIR(U)
912         POP P,U
913         RET
914
915
916 \f
917 SUBTTL .CALL IPKIOT - IPQ data transfer
918
919 ; .CALL IPKIOT - Internet Protocol Packet Transfer.
920 ;       Arg 1 is channel (must be open on IPQ:, specifies queue #)
921 ;       Arg 2 is address of buffer
922 ;       Arg 3 is count of words
923 ;       Val 1 is count of words read into user space (if any)
924 ;       Control bits specify function.  If none, "read" is assumed.
925 ; Get datagram from:
926         %IPIUS==100     ; 1 = Get datagram from user space, not from a queue
927         %IPNOC==200     ; Global input no-check flag, suppresses normal check.
928                         ;   For User Space, "check" means verify, set cksum.
929                         ;   For Input Queue, "check" means verify IP header.
930                         ;   For SysIn Queue, "check" means verify IP hdr.
931                         ;   For SysOut Queue, means nothing.
932         %IPNOH==400     ; Don't Hang waiting for datagram (Queues only)
933         %IPIQK==1000    ; Keep on queue, don't remove (only for %IPOUS)
934 ; Put datagram to:
935         %IPOUS==0       ; User space
936         %IPOUT==1       ; Output to network (bypasses SysOut queue)
937         %IPOFL==2       ; Flush it
938         %IPORV==3       ; Re-vector to input queues past this one
939
940 IPKIOT:
941         HRRZ A,(R)
942         CAIE A,IPQDN    ; Must be right type device (IPQ)
943          JRST OPNL34    ; Wrong device
944         HLRZ I,(R)      ; Get IP input queue index
945         CAIL I,NIPUQ    ; Ensure it's valid.
946          BUG HALT,[Bad IPUQ idx in IOCHNM]
947         MOVE E,CTLBTS(U)        ; Get control bits for this call
948         MOVEI J,(E)
949         ANDI J,3                ; Get output type in J
950
951         TRNN E,%IPIUS   ; Getting datagram from user?
952          JRST [ CAIN J,%IPOUS   ; Giving datagram to user?
953                  CAIL W,3       ; Yes, ensure at least 3 args.
954                   JRST IPKIO2   ; All's OK, go check input queue.
955                 JRST OPNL30]    ; Will write to user, but too few args!
956         CAIGE W,3       ; Must have at least 3 args for this one.
957          JRST OPNL30    ;  Too few args.
958         
959         ; Get datagram from user.
960         ; B/ user addr of buffer
961         ; C/ # of 32-bit words in buffer
962         TRZ E,%IPIQK    ; Flush "keep" bit since won't be on any list!
963         CAIL C,5        ; Must have at least 5 words for IP
964          CAIL C,%IMXLN  ; Must be less or eq to maximum datagram size
965           JRST OPNL33   ; Too big, say meaningless args.
966         CAIN J,%IPOUS   ; Outputting back to self?
967          JRST POPJ1     ;  Yeah, just turn into a NOP.
968         CALL PKTGF      ; Get a free packet buffer (hangs until got it)
969         PUSHJ P,LOSSET  ; Must put back on freelist if we PCLSR on BLT fault
970             PKTPCL      ; Standard routine expects ptr in A
971         TRCPKT A,"IPKIOT Alloc"
972         MOVSI B,(B)
973         HRR B,PK.BUF(A)
974         MOVEI D,(C)
975         ADDI D,-1(B)            ; Find last address copying into
976         XCTR XBR,[BLT B,(D)]    ; Gobble up user's buffer!  May fault.
977         PUSHJ P,LSWDEL          ; Made it through, can flush PCLSR protection
978         HRLM C,PK.BUF(A)        ; Set # words used in buffer
979         MOVE B,PK.BUF(A)        ; Find addr of start of buffer
980         HRLZM B,PK.IP(A)        ; and set start of IP header.
981         LDB D,[IP$IHL (B)]      ; Find claimed length of IP header
982         ADDI D,(B)              ; Get addr of start of IP data
983         HRLZM D,PK.TCP(A)       ; Set that too.
984         JRST IPKIO3             ; Now decide about checking datagram!
985
986         ; Get datagram from input queue.
987 IPKIO2: CONO PI,NETOFF
988         SKIPN A,IPUQHD(I)       ; Anything in the queue?
989          JRST [ CONO PI,NETON
990                 TRNE E,%IPNOH   ; No, see if ok to hang.
991                  JRST POPJ1     ; Don't hang, win-return zero wds-read in A.
992                 SKIPN IPUQHD(I) ; Hang, here we go.
993                  CALL UFLS
994                 JRST IPKIO2]
995         TRNN A,-1               ; Make sure something was there!
996          BUG
997         CAIN I,1                ; Is this SysOut queue?
998          JRST [ MOVE T,PK.FLG(A) ; Yes, get flags
999                 TLNN T,(%PKFLS) ; Actually wants to flush now?
1000                  JRST .+1       ; No, let's go with it.
1001                 MOVEI Q,IPUQHD(I)
1002                 CALL PKQGF(PK.IP)       ; Remove from queue
1003                 CAIN A,
1004                  BUG
1005                 CALL PKTRT              ; Flush it.
1006                 JRST IPKIO2]
1007         CONO PI,NETON
1008         MOVE T,PK.BUF(A)        ; Verify that something exists
1009         TLNE T,-1               ; in both <# wds> field
1010          TRNN T,-1              ; and <buff addr> field.
1011           BUG HALT,[IPQ: Null dgm found on queue]
1012         HLRZ T,PK.IP(A)         ; Should also be an IP pointer
1013         CAIN T,
1014          BUG HALT,[IPQ: IP-less dgm on queue]
1015
1016         ; Now have pointer in A to a datagram.  It is still linked
1017         ; on the input queue, unless %IPIUS is set.
1018 IPKIO3: TRNE E,%IPNOC           ; Should we check the contents at all?
1019          JRST IPKIO5            ; Nope, just go straight ahead.
1020         JFCL            ; Here we should verify/set checksum, but...
1021
1022         ; Now figure out where datagram wants to go!
1023 IPKIO5: JRST @.+1(J)            ; Only have 4 possibilities so far.
1024           IQIO70                ; %IPOUS Output to user
1025           IQIO60                ; %IPOUT Output to network
1026           IQIO55                ; %IPOFL Flush it
1027           IQIO80                ; %IPORV Re-vector through input queues
1028
1029         ; %IPOFL Flush datagram.
1030 IQIO55: TRNN E,%IPIUS           ; Is it from input queue list?
1031          CALL IPIQGF            ;  Yes, take it off input queue list
1032         CALL PKTRT              ; Now can return to packet freelist!
1033         JRST POPJ1              ; Win return.
1034
1035         ; %IPOUT Output datagram to network.
1036 IQIO60: TRNN E,%IPIUS           ; Is it still on an input list?
1037          CALL IPIQGF            ;  Yes, take it off input queue list
1038         CAILE I,1               ; If not from Sys I/O queue,
1039          JRST [ CALL IPKSNQ     ; Possibly send onto SysOut queue.
1040                 JRST POPJ1]
1041         CALL IPKSNI             ; Dgm from Sys queue, never goes back to SysOut
1042         JRST POPJ1
1043
1044
1045         ; %IPOUS Output datagram to user (a "read" from user viewpoint)
1046         ; This is the only place where we can PCLSR on "output".  Note
1047         ; that we cannot get here if datagram came from user, so the
1048         ; datagram we point to is always still on input queue, and
1049         ; we can safely PCLSR without any special backup.
1050 IQIO70: HLRZ D,PK.BUF(A)        ; Find # words available
1051         JUMPLE C,OPNL33         ; Neg or zero count -> meaningless arg error
1052         CAILE C,(D)             ; If asking for more wds than exist,
1053          MOVEI C,(D)            ; only furnish what we've got.
1054         MOVEI D,(B)
1055         ADDI D,-1(C)            ; Find last user word to write
1056         HRL B,PK.BUF(A)
1057         XCTR XBW,[BLT B,(D)]    ; Shove it at him; can PCLSR here.
1058         TRNE E,%IPIQK           ; Done!  Should we keep datagram around?
1059          JRST IQIO75            ; Yes, don't flush it.
1060         CALL IPIQGF             ; Take datagram off the input queue.
1061         CALL PKTRT              ; Return entry/buffer to freelist.
1062 IQIO75: MOVEI A,(C)             ; Return count as 1st val!
1063         JRST POPJ1
1064
1065         ; Must re-vector through stuff...
1066         ; Note that it is illegal to re-vector a datagram from the SysOut
1067         ; queue, because it still shares pointers and stuff with
1068         ; (for example) TCP retransmit queues.  Later, could add code to
1069         ; get another packet buffer and copy it over, but this is better
1070         ; done at the device driver level probably.
1071 IQIO80: TRNN E,%IPIUS           ; Came from user?
1072          JRST [ CAIN I,1        ; No, from a queue; is it the SysOut queue?
1073                  JRST OPNL2     ; Yes, illegal.  Say "Wrong direction".
1074                 CALL IPIQGF     ; No, is OK.  Take it off input list.
1075                 JRST .+1]
1076         MOVEI R,(A)
1077         HLRZ W,PK.IP(R)         ; Get pointer to IP header
1078         HLRZ H,PK.TCP(R)        ; and to IP data.
1079         SETZ J,
1080         CONO PI,NETOFF
1081         CALL IPRDGV             ; Go vector and process the datagram.
1082         CONO PI,NETON
1083         JRST POPJ1
1084
1085         ; Auxiliary, clobbers D to do checking.
1086 IPIQGF: MOVEI D,(A)
1087         MOVEI Q,IPUQHD(I)       ; Is from list, must take it off.
1088         CALL PKQGF(PK.IP)       ; Remove from IP queue list
1089         CAME A,D
1090          BUG                    ; Something added in meantime???
1091         RET
1092 \f
1093 SUBTTL IP TCP Interface Routines
1094
1095 ; IPMTU - Size of largest datagram we want to send to a given destination
1096 ;       A/ Destination address
1097 ;       Returns T/ MTU
1098
1099 SUBN27==:<HOSTN 18,27,0,0>      ; Damn macro generates an error inside literal
1100 NW%CHW==:<HOSTN 128,31,0,0>     ; Old CHAOS-wrapping scheme, probably unused
1101
1102 IPMTU:  PUSH P,A                ; Save address for a bit
1103         MOVEI T,576.            ; Default value
1104         GETNET A                ; Network part only
1105         CAMN A,[NW%ARP]         ; Arpanet?
1106          MOVEI T,%IMMTU         ; MTU of IMP
1107         CAMN A,[NW%AI]
1108          MOVEI T,%IMMTU         ; AI net. We know we have a good path
1109         CAMN A,[NW%CHW]         ; Wrapped chaos packets
1110          MOVEI T,488.           ; Smaller MTU
1111         CAME A,[NW%LCS]         ; Net 18 is ugly, must check subnets
1112          JRST IPMTU1
1113         MOVE A,(P)              ; Get full address back
1114         TRZ A,177777            ; Mask off all but 18.<subnet>
1115         CAMN A,[SUBN27]         ; Subnet 27 is fed by chaos-wrapping.
1116          SKIPA T,[488.-40.]     ; Giving it a very small MTU
1117           MOVEI T,%IMMTU        ; Good path to all others
1118 IPMTU1: POP P,A
1119         RET
1120
1121 IF1,.ERR Amazing MIT-Specific crocks near IPMTU...
1122
1123 ; IPBSLA - Best Local Address for a given destination
1124 ;       A/ Destination IP Address
1125 ;       Return A/ Local Address to use
1126
1127 IPBSLA:
1128 IFE IPUNCP,[
1129         GETNET A
1130         CAMN A,NW%CHW
1131          SKIPA A,[IMPUS4]       ; Local Address on wrapped-chaos net
1132         MOVE A,[IMPUS3]         ; Default local host address to IMP
1133 ] ;IFE IPUNCP
1134 IFN IPUNCP, MOVE A,[IMPUS4]
1135         RET
1136
1137 ; IPLCLH - Skip return if address in A is one of us.
1138 ;       Called with JSP T,IPLCLH
1139
1140 IPLCLH:
1141 IFE IPUNCP, CAME A,[IMPUS3]
1142          CAMN A,[IMPUS4]
1143           JRST 1(T)
1144         JRST (T)
1145
1146 ; IPKSND - Invoked by TCP to send off a segment.
1147 ;       Fills in the IP header fields, checksums, and puts on output queue.
1148 ;       R, W, H set up pointing to segment
1149 ; The out-of-TCP information is contained in the "IP header" that
1150 ;       W points to:
1151 ;               IP$SRC - Source addr
1152 ;               IP$DST - Dest Addr
1153 ;               IP$TOL - Length of segment in bytes (must add IP header length)
1154 ; Clobbers A,B,C,D,E,Q,T
1155 EBLK
1156 IPIDCT: 0       ; IP identification #, incremented for each datagram
1157 BBLK
1158
1159 IPKHDR: MOVE A,IP$VER(W)        ; Get first word
1160         ADDI A,<5*4>_4          ; Add length of IP header (5 wds for now)
1161         HRLI A,212000           ; Fill in Ver, IHL, TOS
1162         MOVEM A,IP$VER(W)       ; Set 1st wd
1163         ADDI A,3_4              ; Now, to get # of words, round up
1164         LSH A,-<4+2>            ; (note flush 4 spare bits then divide by 4)
1165         ANDI A,37777            ; 14 bit field now
1166         HRLM A,PK.BUF(R)        ; Store # of words, for device driver.
1167         MOVSI A,170030          ; TTL and PTC (TCP)
1168         MOVEM A,IP$TTL(W)       ; Set 3rd wd
1169
1170 IPKHD2: AOS A,IPIDCT            ; Get new ID number
1171         LSH A,<16.+4>           ; Left justify it
1172         MOVEM A,IP$ID(W)        ; Use to set up 2nd wd (no flags/frags)
1173         CALL IPCKSM             ; Get IP header checksum
1174         DPB A,[IP$CKS (W)]      ; In it goes!
1175         RET
1176
1177 IPKSND: TRCPKT R,"IPKSND output call"
1178         CALL IPKHDR
1179         MOVEI A,(R)             ; Set up PE ptr arg for following stuff.
1180
1181 ; IPKSNQ - entry point from IPKIOT, to send a datagram.
1182 ;       A/ PE ptr to datagram - PK.BUF must be set up.
1183 ;       Clobbers A,B,T,Q
1184
1185 IPKSNQ: MOVSI T,(%PKODN)        ; Clear the "output-done" flag.
1186         ANDCAM T,PK.FLG(A)
1187         TRCPKT A,"IPKSNQ output call"
1188         SKIPE IPUQUS+1          ; Check - have System Output queue?
1189          JRST IPKSN5            ; Yes, put on that queue.
1190                                 ; No, drop into IPKSNI
1191
1192 ; IPKSNI - Route packet to appropriate gateway and interface
1193 ;       A/ PE ptr to datagram - PK.BUF must be set up.
1194 ;       Clobbers A,B,T,Q
1195 IPKSNI: PUSH P,C
1196         SKIPLE C,PK.BUF(A)      ; Get the packet buffer from the PE
1197          CAMG C,[2,,0]
1198           BUG HALT,[IP: Null dgm being sent]
1199         LDB C,[IP$DST(C)]       ; Get destination address
1200
1201         ;; This is where to apply final gateway routing code, based on Internet address in C.
1202         GETNET T,C              ; Get network # into T
1203         MOVSI Q,-NIPGW          ; Search table of gateways and direct routes
1204         CAME T,IPGWTN(Q)        ; Skip if network # matches
1205          AOBJN Q,.-1
1206         JUMPL Q,IPSNI1          ; Jump if found entry in table
1207         AOS Q,IPGWPG            ; No gateway known for this network, so try a
1208         CAIL Q,NIPMGW           ; prime gateway and hope for an ICMP redirect!
1209          SETZB Q,IPGWPG         ; Try a different prime gateway each time
1210 IPSNI1: MOVE T,TIME             ; Remember that this gateway entry was used
1211         MOVEM T,IPGWTM(Q)
1212         SKIPE IPGWTG(Q)         ; Skip if this is a direct route
1213          MOVE C,IPGWTG(Q)       ; Get gateway address
1214         MOVEM C,PK.DST(A)       ; Save gateway address for interface to use
1215         CALL @IPGWTI(Q)         ; Dispatch to interface
1216         POP P,C
1217         RET
1218
1219 EBLK
1220 IPGWPG: 0               ; Index of current prime gateway
1221
1222                                 ; Network number
1223 IPGWTN:
1224         NW%LCS                  ; LCS net
1225         NW%AI                   ; MIT-AI-NET
1226 NIPMGW==<.-IPGWTN>              ; Number of prime gateways
1227 IFE IPUNCP, NW%ARP              ; ARPA Net
1228         HOSTN 128,31,0,0        ; MIT Chaosnet
1229 NIPPGW==<.-IPGWTN>              ; Number of permanent gateways
1230         BLOCK 64.               ; Extra stuff to patch in and for redirects
1231 NIPGW==<.-IPGWTN>
1232
1233 ; Internet address of gateway servicing given net number
1234 IPGWTG:
1235 IFE IPUNCP,[
1236         HOSTN 10,0,0,77         ; MIT-GW
1237         HOSTN 10,3,0,6          ; MIT-AI-GW
1238 ] ;IFE IPUNCP
1239 IFN IPUNCP,[
1240         HOSTN 128,31,6,1        ; ???
1241         HOSTN 128,31,6,2        ; ???
1242 ] ;IFN IPUNCP
1243 IFE IPUNCP, 0                   ; Send direct to Arpanet
1244         0                       ; Send direct to Chaosnet
1245 IFN .-IPGWTG-NIPPGW, .ERR Permanent gateway table at IPGWTG wrong size
1246 LOC IPGWTG+NIPGW
1247
1248 IPGWTI:
1249 IFE IPUNCP,[
1250         IPKSNA                  ; MIT-GW
1251         IPKSNA                  ; MIT-AI-GW
1252 ] ;IFE IPUNCP
1253 IFN IPUNCP,[
1254         IPKSNC                  ; ???
1255         IPKSNC                  ; ???
1256 ] ;IFN IPUNCP
1257 IFE IPUNCP, IPKSNA              ; direct to Arpanet
1258         IPKSNC                  ; direct to Chaosnet
1259 IFN .-IPGWTI-NIPPGW, .ERR Permanent gateway table at IPGWTI wrong size
1260 REPEAT NIPGW-NIPPGW,IPKSNA
1261
1262 IPGWTM: BLOCK NIPGW             ; TIME entry last used
1263
1264 BBLK
1265
1266 IFE IPUNCP, [
1267
1268 ; Queue packet for Arpanet interface
1269 IPKSNA: MOVEI Q,IPOUTQ          ; Otherwise use direct IP output queue.
1270         MOVE B,(Q)              ; Save previous contents of queue header
1271         CALL PKQPL(PK.IP)       ; Put on IP output queue
1272         CAIE B,0                ; Kick off IP output if necessary.
1273          RET                    ; Not necessary, queue was not empty
1274 IPOGO:  CALRET IMPIOS           ; Just means kicking IMP for now.
1275
1276 ] ;IFE IPUNCP
1277
1278 ; Queue packet for Chaosnet interface
1279 ; A has the pe
1280 ; PK.DST(A) has the Internet address to send to, 128.31.subnet.host
1281 ; The low 16 bits are Chaosnet address to send an UNC to
1282 IPKSNC: PUSH P,H
1283         PUSH P,J
1284         PUSH P,E
1285         PUSH P,W
1286         MOVE J,A                ;J has address of PE
1287         MOVE H,PK.BUF(A)        ;H has address of IP header     
1288         MOVEI E,0               ;E has number of bytes sent so far
1289 IPKSC1: CALL CHABGI             ;Get a Chaosnet buffer in A
1290          JRST IPKSC9            ;Give up if can't get one
1291         MOVSI T,-%CPKDT         ;Zero out the Chaosnet header
1292         HRRI T,(A)
1293         SETZM (T)
1294         AOBJN T,.-1
1295         MOVEI T,%COUNC
1296         DPB T,[$CPKOP(A)]
1297         MOVE C,PK.DST(J)
1298         DPB C,[$CPKDA(A)]
1299         MOVEI T,MYCHAD
1300         DPB T,[$CPKSA(A)]
1301         MOVEI T,8_8             ;DOD Internet #x0800    
1302         DPB T,[$CPKAN(A)]       ;Protocol number
1303         AOS CHNIPO              ;Meter Internet packets out to Chaosnet
1304         LDB Q,[IP$IHL(H)]       ;Internet header length in words
1305         MOVE T,Q                ;Save header length for later
1306         MOVSI B,(H)             ;BLT IP header into Chaos packet
1307         HRRI B,%CPKDT(A)
1308         ADDI Q,(B)
1309         BLT B,-1(Q)             ;Q saves address of first data word
1310         LDB B,[IP$TOL(H)]       ;Total length in octets including header
1311         SUB B,E                 ;Number of bytes remaining to be sent
1312         MOVEI C,IPKSC9          ;Continuation if no more fragments needed
1313         CAIG B,%CPMXC           ;Skip if need to fragment
1314          JRST IPKSC2
1315         MOVEI B,%CPMXC/4        ;Compute number of 32-bit data words in fragment
1316         SUB B,T
1317         TRZ B,1                 ;Round down to even multiple of 8 octets
1318         ADD B,T 
1319         LSH B,2                 ;Number of bytes in this fragment including header
1320         MOVEI W,IP%FMF          ;Set more-fragments flag
1321         IORM W,IP$FLG+%CPKDT(A)
1322         MOVEI C,IPKSC1          ;Continuation sends another fragment
1323 IPKSC2: DPB B,[IP$TOL+%CPKDT(A)]        ;Total length of this fragment
1324         DPB B,[$CPKNB(A)]
1325         PUSH P,C                ;Save continuation address
1326         MOVE W,E                ;Get fragment offset
1327         LSH W,-3                ;8-octet units
1328         LSH T,2                 ;Number of bytes in header
1329         SUB B,T                 ;Number of data bytes
1330         LDB C,[IP$FRG+%CPKDT(A)];Set fragment offset
1331         ADD C,W
1332         DPB C,[IP$FRG+%CPKDT(A)]
1333         ADD T,E                 ;Byte offset of start of data to send
1334         LSH T,-2                ;Word offset
1335         ADD T,H                 ;Word address
1336         HRL Q,T                 ;BLT pointer to copy data
1337         MOVEI T,3(B)
1338         LSH T,-2                ;Number of words to copy
1339         ADDI T,-1(Q)            ;Address of last word to store
1340         BLT Q,(T)               ;Copy the data
1341         ADD E,B                 ;Offset for next fragment
1342         MOVEI W,%CPKDT(A)
1343         CALL IPCKSM             ;Compute header checksum
1344         DPB A,[IP$CKS (W)]      ;Store header checksum
1345         MOVEI A,-%CPKDT(W)      ;Restore address of chaos packet
1346         SETOM -2(A)             ;Not on any packet lists
1347         PUSH P,J                ;Save registers clobbered by CHAXMT
1348         PUSH P,D
1349         PUSH P,E
1350         PUSH P,TT
1351         CALL CHAXMT             ;Launch packet into Chaosnet
1352         POP P,TT
1353         POP P,E
1354         POP P,D
1355         POP P,J
1356         POPJ P,                 ;Take continuation
1357
1358 IPKSC9: MOVE A,J                ; The PE
1359         CALL IPIODN             ; Say we're done transmitting this packet,
1360         POP P,W                 ; although it's still in Chaos NCP somewhere
1361         POP P,E
1362         POP P,J
1363         POP P,H
1364         POPJ P,
1365
1366 IPKSN5: MOVEI Q,IPUQHD+1        ; Put on System Output queue
1367         MOVE B,(Q)              ; Save prev contents of header
1368         CALL PKQPL(PK.IP)
1369         CAIE B,                 ; If stuff already there,
1370          RET                    ; Just return, else
1371         PUSH P,I                ; Nothing there before, give user interrupt.
1372         MOVEI I,1               ; On IPQ SysOut queue.
1373         CALL IPQUSI
1374         POP P,I
1375         RET
1376 \f
1377 ; IPCKSM - Computes checksum for IP header.
1378 ;       W/ points to IP header.
1379 ;       Clobbers B,C
1380 ; Returns A/ checksum
1381
1382 IFNDEF JCRY0,JCRY0==:<JFCL 4,>  ; Jump on Carry from bit 0 (and clear flag)
1383
1384 IPCKSM: SETZ A,
1385         LDB C,[IP$IHL (W)]      ; Get IP header length
1386         MOVE B,IP$CKS(W)        ; Get 3rd word
1387         ANDCM B,[IP%CKS]        ; Mask out the checksum field
1388         JFCL 17,.+1             ; Clear flags
1389         ADD B,IP$VER(W)         ; Add 1st wd
1390         JCRY0 [AOJA A,.+1]
1391         ADD B,IP$ID(W)          ; Add 2nd
1392         JCRY0 [AOJA A,.+1]
1393         ADD B,IP$SRC(W)         ; Add 4th
1394         JCRY0 [AOJA A,.+1]
1395         ADD B,IP$DST(W)         ; Add 5th
1396         JCRY0 [AOJA A,.+1]
1397         CAILE C,5
1398          JRST IPCKS4            ; Longer than 5 words, must hack options.
1399 IPCKS2: LSHC A,16.              ; Get high 2 bytes (plus carries) in A
1400         LSH B,-<16.+4>          ; Get low 2 bytes in B
1401 IPCKS3: ADDI A,(B)              ; Get total sum
1402         CAILE A,177777          ; Fits?
1403          JRST [ LDB B,[202400,,A]       ; No, must get overflow bits
1404                 ANDI A,177777           ; then clear them
1405                 JRST IPCKS3]            ; and add in at low end.
1406         ANDCAI A,177777         ; Return ones complement
1407         RET
1408
1409 IPCKS4: SUBI C,5                ; C has a 4 bit value.
1410         MOVN C,C                ; Get neg of # words left
1411         LSH C,1                 ; Double it
1412         JUMPL C,IPCKS5(C)       
1413         RET                     ; Something is wrong, so just return bad val.
1414
1415 REPEAT 10.,[
1416         ADD B,5+<10.-.RPCNT>(W)
1417         JCRY0 [AOJA A,.+1]
1418 ]
1419 IPCKS5: JRST IPCKS2             ; Options all added, now go fold sum.
1420
1421 IFN 0,[ ; Old version
1422 IPCKSM: MOVEI C,(W)
1423         HRLI C,442000           ; Gobble 16-bit bytes
1424         ILDB A,C                ; wd 0 byte 1
1425         ILDB B,C
1426         ADDI A,(B)              ; Add 2nd byte of 1st wd
1427         ILDB B,C ? ADDI A,(B) ? ILDB B,C ? ADDI A,(B)   ; 1 ID,frag
1428         ILDB B,C ? ADDI A,(B) ? IBP C                   ; 2 Skip chksum field
1429         ILDB B,C ? ADDI A,(B) ? ILDB B,C ? ADDI A,(B)   ; 3 source addr
1430         ILDB B,C ? ADDI A,(B) ? ILDB B,C ? ADDI A,(B)   ; 4 dest addr
1431 IPCKS8: CAIG A,177777
1432          JRST IPCKS9
1433         LDB B,[202400,,A]       ; Get any overflow
1434         ANDI A,177777
1435         ADDI A,(B)
1436         JRST IPCKS8
1437 IPCKS9: ANDCAI A,177777
1438         RET
1439
1440 ] ;IFN 0