;PUPGAT.MAC;15    14-DEC-81 19:00:08    EDIT BY TAFT
; Keep routing table in external file PUPSRV.RT for benefit of
; other programs that need routing information.
;PUPGAT.MAC;14     2-SEP-79 16:00:23    EDIT BY TAFT
;PUPGAT.MAC;13    27-APR-77 12:34:35    EDIT BY TAFT
;PUPGAT.MAC;12    18-MAR-77 17:08:43    EDIT BY TAFT
; SEARCH PSVDEF
;PUPGAT.MAC;11    30-JAN-77 16:24:34    EDIT BY TAFT
; Do not advertise as being a gateway if ENTFLG is off
;PUPGAT.MAC;10    29-OCT-76 14:36:16    EDIT BY TAFT
; Unconditionally update Tenex's RT upon first call to GATCHK
;PUPGAT.MAC;9    25-OCT-76 21:30:46    EDIT BY TAFT
; Randomize broadcast interval to avoid getting in sync with
; other gateways.
;PUPGAT.MAC;8    20-OCT-76 13:27:31    EDIT BY TAFT
; Remove directly connected net restriction for gateway info

; Copyright 1979 by Xerox Corporation

	TITLE PUPGAT -- GATEWAY INFORMATION SERVER FOR PUPSRV
	SUBTTL E. A. Taft / October, 1976

	SEARCH PUPDEF,PSVDEF,STENEX
	USEVAR TOPVAR,TOPPVR	; This is part of the top fork




; Gateway Information server (socket 2)

GATSRV::CAIN A,200		; Type = "Are you a gateway"?
	 JRST SNDINF		; Yes, send info response
	CAIN A,201		; Type = "I am a gateway"?
	 JRST RECINF		; Yes, absorb info into our RT
	TLNE F,(DEBUGF)
	 ELOG 
	POPJ P,

; Send response to "Are you a gateway" packet
SNDINF:	TLNE F,(GATEWF)		; Is this host a gateway?
	TLNN F,(GATINF)		; Has initialization finished?
	 POPJ P,		; No, just ignore request
	PUSHJ P,GATENT		; Yes, is ENTFLG set?
	 POPJ P,		; No, ignore
	PUSHJ P,SWPPRT##	; Yes, swap source and destination
	PUSHJ P,BLDGIP		; Build gateway info packet
	MOVEI A,201		; Reply Pup Type
	PUSHJ P,SNDPUP##	; Send it off
	 POPJ P,		; Failed
	TLNE F,(DEBUGF)		; Log reply only if debugging
	 LOG 
	POPJ P,

; Process "I am a gateway" packet and update local routing table
RECINF:	LDB A,PPUPSS		; Get source socket
	LDB B,PPUPSN		; Ignore info from non-directly-
	CAMLE B,MAXNET		;  connected nets
	 TDZA B,B
	 HRRZ B,RTADR-1(B)
	SKIPE B
	CAIE A,2		; Make sure from right guy
	 JRST [	ELOG 
		POPJ P,]
	LDB A,PPUPSN		; Ignore packets from myself
	HRRZ A,RTADR-1(A)
	LDB B,PPUPSH
	CAIN B,(A)
	 POPJ P,
	PUSHJ P,SAVE2##		; Get some more ac's
	MOVEI D,PBCONT(PB)	; Init byte ptr into packet
	HRLI D,(POINT 8)
	LDB C,PUPLEN		; Get pup length
	SUBI C,MNPLEN		; Compute number of content bytes
	ASH C,-2		; Compute number of info blocks
	JUMPLE C,CPOPJ##	; Forget it if none
	HRRZS 0(P)		; Note no RT changes made yet

; GATSRV (CONT'D)

; Loop here for each info block.
; Update my RT entry for the net given in the block if:
; (1) my RT entry says the net is inaccessible, or
; (2) the info block's hop count +1 is less than my hop count, or
; (3) the Pup source is the gateway thru which we already route, or
; (4) my entry has timed out (not updated in RTTINT seconds).
RECIN1:	ILDB P1,D		; Get net number
	IBP D			; Flush gateway net number
	IBP D			; Flush gateway host number
	ILDB P2,D		; Hop count
	ADDI P2,1		; My hops = his hops +1
	CAIL P1,1		; Make sure net number in range
	CAMLE P1,MAXNET
	 JRST RECIN5		; Not in range, ignore
	HRRZ A,RTADR-1(P1)	; Check local host adr on net
	JUMPN A,RECIN5		; Ignore if directly connected
	SKIPL RTADR-1(P1)	; Net previously inaccessible?
	CAMGE P2,RTHOPS-1(P1)	; Fewer hops on new route?
	 JRST RECIN3		; One of those, set new entry
	LDB A,[POINT 8,RTADR-1(P1),9]  ; Get gateway net in my RT
	LDB B,PPUPSN		; Source net of gateway info Pup
	CAIE A,(B)		; Same?
	 JRST RECIN2		; No, continue
	LDB A,[POINT 8,RTADR-1(P1),17]  ; Get gateway host in my RT
	LDB B,PPUPSH		; Source host of gateway info Pup
	CAIN A,(B)		; Same?
	 JRST [	CAME P2,RTHOPS-1(P1)  ; Yes, same hop count too?
		 JRST RECIN3	; No, do the update
		TIME		; Yes, just reset the timer and
		ADDI A,RTTINT*↑D1000  ; avoid the update expense
		MOVEM A,RTTIME-1(P1)
		JRST RECIN5]
RECIN2:	TIME			; Get now
	CAMGE A,RTTIME-1(P1)	; Has entry timed out?
	 JRST RECIN5		; No, ignore gateway info block

; Replace existing entry with new info block
RECIN3:	CAILE P2,MAXHOP		; Hop count too large?
	 JRST [	MOVSI A,(1B0)	; Yes, declare net inaccessible
		MOVEM A,RTADR-1(P1)
		HRLOI A,377777	; Set timer to infinity
		JRST RECIN4]
	LDB A,PPUPSN		; Gateway net ← Pup source net
	LDB B,PPUPSH		; Gateway host ← Pup source host
	LSH A,8
	IORI A,(B)
	HRLZM A,RTADR-1(P1)	; Zero out rest of entry
	TIME			; Reset timer
	ADDI A,RTTINT*↑D1000
RECIN4:	MOVEM A,RTTIME-1(P1)
	MOVEM P2,RTHOPS-1(P1)	; Set hop count
	TLNE F,(GATINF)		; Initialization complete?
	 HRROS 0(P)		; Yes, remember to update Tenex
RECIN5:	SOJG C,RECIN1		; Repeat for all info blocks
	SKIPGE 0(P)		; Did anything change?
	 PUSHJ P,STNXRT		; Yes, set Tenex RT
	POPJ P,			; Done

; Check routing table and broadcast gateway info packet
; Returns +1
; Clobbers A-D, PB, SV

GATCHK::PUSHJ P,RANDOM##	; Compute time of next call randomly in
	MULI B,/4  ;  range .75*GATINT to 1.25*GATINT
	ADDI B,GATINT*↑D1000
	MOVEM B,GATTIM
	TIME
	ADDM A,GATTIM
	MOVEI SV,SV.GAT		; Set service table index
	TLON F,(GATINF)		; Say initialization is complete
	 PUSHJ P,STNXRT		; First time, update Tenex's RT

; Flush routing table entries that have not been updated
; in the past (2*RTTINT) seconds
	HLLZ C,PUPROU##		; Init AOBJN pointer
	HRRZS 0(P)		; No RT changes made yet
	SUBI A,RTTINT*↑D1000	; RT timer cutoff = 2*RTTINT

GATCH1:	CAMGE A,RTTIME(C)	; Has entry timed out?
	 JRST GATCH2		; No
	MOVSI B,(1B0)		; Yes, declare net inaccessible
	MOVEM B,RTADR(C)
	HRLOI B,377777		; Reset timer to infinity
	MOVEM B,RTTIME(C)
	MOVEI B,MAXHOP+1	; Make route look very poor
	MOVEM B,RTHOPS(C)
	HRROS 0(P)		; Remember that RT has changed
GATCH2:	AOBJN C,GATCH1		; Repeat for all entries in RT

	SKIPGE 0(P)		; Did anything change?
	 PUSHJ P,STNXRT		; Yes, set Tenex RT

; If this host is a gateway, broadcast gateway info packets
; on all directly connected networks
	TLNE F,(GATEWF)		; Are we a gateway?
	TLNN F,(ENABLF)		; In control of system sockets?
	 JRST GATCH9		; No, done
	PUSHJ P,GATENT		; Is ENTFLG set?
	 JRST GATCH9		; No, don't send gateway info
	PUSHJ P,SAVE1##		; Yes, get another ac
	MOVEI PB,SRVPKT##	; Set pointer to PB
	PUSHJ P,BLDGIP		; Build gateway info packet
	HLLZ P1,PUPROU##	; Init AOBJN pointer

GATCH4:	MOVE B,RTADR(P1)	; Get RT entry for net
	TRNE B,-1		; Directly connected?
	TLNN B,(1B1)		; Able to be broadcast upon?
	 JRST GATCH6		; No, bypass
	MOVEI A,1(P1)		; Yes, get net number
	SETZB B,C		; Let Tenex default host and socket
	PUSHJ P,STSPRT##	; Set source port in Pup
	MOVEI C,2		; Dest socket = gateway info
	PUSHJ P,STDPRT##	; Set destination port in Pup
	SETZM PBHEAD+1(PB)	; Zero Pup ID just for kicks
	MOVEI A,201		; Pup type = "I'm a gateway"
	PUSHJ P,SNDPUP##	; Send the Pup
	 CAI			; Failed, forget it
GATCH6:	AOBJN P1,GATCH4		; Repeat for all nets
GATCH9:	SETO SV,		; No service now in progress
	POPJ P,

; Build gateway info packet
;	PB/ Pointer to packet buffer
; Returns +1:  Contents and length have been setup
; Clobbers A-C

BLDGIP:	HLLZ C,PUPROU##		; Init AOBJN ptr to routing table
	MOVEI A,PBCONT(PB)	; Init byte ptr into packet
	HRLI A,(POINT 8)
BLDGI1:	SKIPGE RTADR(C)		; Check for accessible network
	 JRST BLDGI2		; Not accessible, ignore
	MOVEI B,1(C)		; Ok, compute network number
	IDPB B,A		; Store in packet
	HLRZ B,RTADR(C)		; Get net/host for routing
	ROT B,-8		; Right-justify net
	IDPB B,A		; Append it
	ROT B,8			; Right-justify host
	IDPB B,A		; Append it
	MOVE B,RTHOPS(C)	; Get hop count
	IDPB B,A		; Append it
BLDGI2:	AOBJN C,BLDGI1		; Repeat for all networks
	PUSHJ P,ENDPUP##	; Finish up, set size
	POPJ P,


; Set Tenex's routing table to be equal to mine
; Returns +1
; Clobbers A-D

STNXRT:	TLNN F,(ENABLF)		; Am I enabled?
	 POPJ P,		; No, forget it
	PUSHJ P,SAVE1##		; Save another ac
	HLLZ P1,PUPROU##	; Init AOBJN ptr to routing table
STNXR1:	MOVE D,RTADR(P1)	; Get my RT entry for net
	TRNE D,-1		; Are we directly connected?
	 JRST STNXR2		; Yes, skip it
	MOVE A,[SIXBIT /PUPROU/]  ; Function name
	MOVEI B,1(P1)		; Net number
	SETO C,			; Mask of bits to change
	OPRFN			; Set the entry in Tenex
	 PUSHJ P,SCREWUP##
STNXR2:	AOBJN P1,STNXR1		; Repeat for all nets
	POPJ P,


; Check whether ENTFLG is on
; Returns +1:  Off
;	+2:  On
; Clobbers A

GATENT:	HRRZ A,ENTFLG##
	GETAB
	 PUSHJ P,SCREWUP##
	JUMPN A,SKPRET##
	POPJ P,

; Initialize gateway information server
; Returns +1
; Clobbers A-D, PB, SV

GATINI::MOVEI SV,SV.GAT		; Set service table index
	HLRE A,PUPROU##		; Get negative length of Tenex RT
	MOVNS A			; Make positive
	CAILE A,NNETS		; Make sure in range
	 JRST [	TYPE 
		HALTF]
	MOVEM A,MAXNET		; Store for my use
	HRRZ A,PUPPAR##		; Get word 2 of pup parameter table
	HRLI A,2
	GETAB
	 PUSHJ P,SCREWUP##
	TLNE A,(1B0)		; Is this host a gateway?
	 TLO F,(GATEWF)		; Yes, set flag
	TIME			; Get now
	ADDI A,↑D10000		; Call GATCHK 10 seconds from now
	MOVEM A,GATTIM

; Init routing table mapped onto PUPSRV.RT
	MOVSI A,(1B8+1B17)	; Ignore deleted, short form
	HRROI B,[ASCIZ /PUPSRV.RT;1/]
	TLNE F,(DEBUGF)		; Debugging?
	 HRROI B,[ASCIZ /PUPSRV.RT;1/]  ; Yes, make private file
	GTJFN
	 JRST GATINE
	MOVE C,A
	MOVE B,[↑D36B5+1B19+1B20+1B25]  ; R+W, thawed
	OPENF
	 JRST [	EXCH C,A
		RLJFN
		 CAI
		MOVE C,A
		JRST GATINE]
	HRLI A,12		; Make file appear 1 page long
	SETO B,
	MOVEI C,1000
	CHFDB
	MOVSI A,0(A)		; Source is file, page 0
	MOVEI B,RTPAGE		; Destination is this fork, RTPAGE
	LSH B,-9
	HRLI B,400000
	MOVSI C,(1B2+1B3)	; R+W
	PMAP

; Initialize local routing table
GATIN0:	MOVSI A,(1B0)		; Init unused entries to "inaccessible"
	MOVEM A,RTPAGE
	MOVE A,[RTPAGE,,RTPAGE+1]
	BLT A,RTPAGE+377
	MOVEI A,MAXHOP+1
	MOVEM A,RTPAGE+400
	MOVE A,[RTPAGE+400,,RTPAGE+401]
	BLT A,RTPAGE+777
	MOVE A,PUPROU##		; Get descriptor for Tenex RT
	MOVEI B,RTADR		; Where to put it
	PUSHJ P,REDGTB##	; Read into my space

; Initialization (cont'd)

	TIME			; Get now
	ADDI A,RTTINT*↑D1000	; When to time out RT entry
	HLLZ C,PUPROU##		; Init AOBJN pointer
GATIN1:	SETZM RTHOPS(C)		; Assume directly connected
	HRLOI B,377777		; Set timeout to infinity
	MOVEM B,RTTIME(C)
	SKIPGE B,RTADR(C)	; Network accessible?
	 JRST GATIN2		; No, go set hops to maximum
	TRNE B,-1		; Yes, directly connected?
	 JRST GATIN3		; Yes, done
	MOVEM A,RTTIME(C)	; Indirectly connected, set timer
GATIN2:	MOVEI B,MAXHOP+1	; Make routing look very poor
	MOVEM B,RTHOPS(C)
GATIN3:	AOBJN C,GATIN1		; Repeat for all nets

; Broadcast gateway info requests on all directly connected nets
	PUSHJ P,SAVE1##		; Get another ac
	MOVEI PB,SRVPKT##	; Set pointer to PB
	MOVEI A,MNPLEN		; Minimum-length Pup
	DPB A,PUPLEN
	HLLZ P1,PUPROU##	; Init AOBJN pointer

GATIN4:	MOVE B,RTADR(P1)	; Get RT entry for net
	TRNE B,-1		; Directly connected?
	TLNN B,(1B1)		; Able to be broadcast upon?
	 JRST GATIN6		; No, bypass
	MOVEI A,1(P1)		; Yes, get net number
	SETZB B,C		; Let Tenex default host and socket
	PUSHJ P,STSPRT##	; Set source port in Pup
	MOVEI C,2		; Dest socket = gateway info
	PUSHJ P,STDPRT##	; Set destination port in Pup
	SETZM PBHEAD+1(PB)	; Zero Pup ID just for kicks
	MOVEI A,200		; Pup type = "Are you a gateway"
	PUSHJ P,SNDPUP##	; Send the Pup
	 CAI			; Failed, forget it
GATIN6:	AOBJN P1,GATIN4		; Repeat for all nets
	SETO SV,		; No service now in progress
	POPJ P,

; Here if couldn't access PUPSRV.RT
GATINE:	ELOG 
	JRST GATIN0		; Press on


; Storage for this module

GS MAXNET		; Highest allowable net number
GS GATTIM		; Time for next call to GATCHK

; Routing table stuff, indexed by (net number -1)
GS RTTIME,NNETS		; When to time out routing table entry

GSP RTPAGE,1		; Routing information mapped to file PUPSRV.RT
  RTADR==:RTPAGE+1	; Routing table (same format as Tenex's)
  RTHOPS==:RTPAGE+401	; Hop count table


	END