Trailing-Edge
-
PDP-10 Archives
-
SRI_NIC_PERM_SRC_1_19910112
-
7/ft3/monitor/stanford/arp.mac
There are 3 other files named arp.mac in the archive. Click here to see a list.
;[MACBETH.STANFORD.EDU]SRC:<6.1.MONITOR.AP16.STANFORD>ARP.MAC.3, 17-Dec-87 12:47:03, Edit by A.ALDERSON
; Add MONSYM to search path so that SC30SW is picked up properly
;[MACBETH.STANFORD.EDU]SRC:<6.1.MONITOR.AP16.STANFORD>ARP.MAC.2, 15-Dec-87 15:10:06, Edit by A.ALDERSON
; Install NI-related code for SC30-M
;<6-1-MONITOR>ARP.MAC.72, 20-Jul-85 21:13:06, Edit by LOUGHEED
; Fix XRXREP to set destination hardware address of reply to be
; the original requestor instead of broadcast.
;<6-1-MONITOR>ARP.MAC.71, 7-Jun-85 15:01:09, Edit by LOUGHEED
; ARPFIL discards Xerox ARP requests if we don't have subnets
; HASH guards against returning negative offsets
;<6-1-MONITOR>ARP.MAC.70, 16-Mar-85 22:51:57, Edit by LOUGHEED
; Don't use SE1ENT under Release 6.1
;<6-1-MONITOR>ARP.MAC.69, 16-Mar-85 20:50:31, Edit by LOUGHEED
; Change MSEC1 reference to XCDSEC
;<6-1-MONITOR>ARP.MAC.68, 15-Mar-85 15:06:17, Edit by LOUGHEED
;<6-1-MONITOR>ARP.MAC.67, 15-Mar-85 12:08:08, Edit by LOUGHEED
; Move into extended code section
;<6-MONITOR>ARP.MAC.66, 30-Nov-84 15:07:20, Edit by LOUGHEED
; Correctly set up Target Protocol field in ARP reply
;<6-MONITOR>ARP.MAC.65, 27-Nov-84 02:08:17, Edit by LOUGHEED
; Similar precautions for RESOLV routine (check for NOSKED)
;<6-MONITOR>ARP.MAC.64, 26-Nov-84 19:45:25, Edit by LOUGHEED
; LCKARP is careful not to block if caller is NOSKED
;<6-MONITOR>ARP.MAC.63, 23-Nov-84 18:35:05, Edit by LOUGHEED
;<6-MONITOR>ARP.MAC.62, 23-Nov-84 18:13:52, Edit by LOUGHEED
; RESOLV returns failure if ARP process is not yet initialized
; Remove previous edit history
SEARCH PROLOG,ANAUNV,MONSYM
IFN STANSW&SC30SW,<SEARCH NIPAR>
TTITLE (ARP,ARP,< -- RFC826 and Xerox Address Resolution Protocols>)
SUBTTL Kirk Lougheed / Stanford University / 4-November-1983
; Copyright (c) 1983, 1984, 1985
; The Board of Trustees, Leland Stanford Junior University, Stanford, CA 94305
; All Rights Reserved.
; The information in this software is subject to change without notice
; and should not be construed as a commitment by Stanford University.
; Stanford assumes no responsibility for the use or reliability of
; this software.
IFNDEF REL6,<REL6==1> ;Default to assembly for Release 6
IFE REL6,<DEFINE XSWAPCD <SWAPCD>>
IFE REL6,<DEFINE XRESCD <RESCD>>
EXTN <RETBUF,MEIBUG> ;Symbols defined in ENET.MAC
EXTN <PKOWAF,WAIFLG,WA%ARP> ;Symbols defined in PKOPR.MAC
DEFAC (ARP,P4) ;Pointer to current ARP packet
SUBTTL Definitions and Storage
;Ethernet encapsulation fields. Note that offset zero is a free storage header.
OFFETH==<PKTELI-4-LCLPKT> ;Offset of start of 10MB encapsulation
;Sanity check in case someone changes MAXLDR again
IFL MAXLDR-5 <PRINTX % MAXLDR too small for 10MB Ethernet operation>
DEFSTR ETDH0,OFFETH+0,31,16 ;10MB First 16 bits of Destination host
DEFSTR ETDH1,OFFETH+1,31,32 ;10MB Last 32 bits of Destination host
DEFSTR ETSH0,OFFETH+2,31,32 ;10MB First 32 bits of Source host
DEFSTR ETSH1,OFFETH+3,15,16 ;10MB Last 16 bits of Source host
DEFSTR ETTYP,OFFETH+3,31,16 ;10MB Type code
ET%ARP==4006 ;Ethernet type code for RFC826 style ARP
ET%XRX==1001 ;Ethernet type code for Xerox PUP ARP
ET%PUP==1000 ;Ethernet type code for PUP datagrams
;Definitions for ARP header
DEFSTR ARHRD,OFFETH+4,15,16 ;Hardware address space
HW$ETH==1 ;10MB Ethernet
DEFSTR ARPRO,OFFETH+4,31,16 ;Protocol address space
PR$PUP==1000 ;Xerox PUP
PR$IP==4000 ;DOD IP
DEFSTR ARHLN,OFFETH+5,7,8 ;Byte length of each hardware address
HL$ETH==6 ;10MB length
DEFSTR ARPLN,OFFETH+5,15,8 ;Byte length of each protocol address
PL$PUP==1 ;PUP address length
PL$IP==4 ;IP address length
DEFSTR AROPC,OFFETH+5,31,16 ;Opcode field
OP$REQ==1 ;Request for information
OP$REP==2 ;Reply
;Definitions for IP ARP packet (10MB Ethernet hardware)
DEFSTR IPSH0,OFFETH+6,31,32 ;First 32 bits of source hardware address
DEFSTR IPSH1,OFFETH+7,15,16 ;Last 16 bits of source hardware address
DEFSTR IPSP0,OFFETH+7,31,16 ;First 16 bits of source IP protocol address
DEFSTR IPSP1,OFFETH+10,15,16 ;Last 16 bits of source IP protocol address
DEFSTR IPTH0,OFFETH+10,31,16 ;First 16 bits of target hardware address
DEFSTR IPTH1,OFFETH+11,31,32 ;Last 32 bits of target hardware address
DEFSTR IPTP,OFFETH+12,31,32 ;32 bits of target IP protocol address
IPLEN==^D28 ;Length of an IP ARP packet
;Definitions for Xerox style ARP (10MB hardware, PUP protocols)
XR$REQ==10101 ;Requesting information
XR$REP==7070 ;Replying with information
DEFSTR XROPC,OFFETH+4,15,16 ;Opcode (one of XR$REQ or XR$REP)
DEFSTR XRTH0,OFFETH+4,31,16 ;First 16 bits of target hardware address
DEFSTR XRTH1,OFFETH+5,31,32 ;Last 32 bits of target hardware address
DEFSTR XRTP,OFFETH+6,7,8 ;8 bits of target protocol address
;DEFSTR XRB1,OFFETH+6,15,8 ;Blank space
DEFSTR XRSH0,OFFETH+6,31,16 ;First 16 bits of source hardware address
DEFSTR XRSH1,OFFETH+7,31,32 ;Last 32 bits of source hardware address
DEFSTR XRSP,OFFETH+10,7,8 ;8 bits of target protocol address
;DEFSTR XRB2,OFFETH+10,15,8 ;Blank space
XRXLEN==^D18
;Definitions for a hash table entry.
ENTSIZ==5 ;Number of words in an entry
DEFSTR HSNXT,0,35,36 ;Full word for pointer to next record
DEFSTR HSPROT,1,35,36 ;Allow a full word for the protocol address
DEFSTR HSPTYP,2,17,18 ;Half word for protocol type
DEFSTR HSHW0,2,35,18 ;Other half word for first 16-bits of hdw addr
DEFSTR HSHW1,3,35,36 ;Full word for last 32-bits of hdw address
DEFSTR HSLTAD,4,35,36 ;TODCLK of last update
;Offsets into Internet buffers used by ARP
;N.B. there are only 6 available offsets (we are using 1 of them)
..PKT==PKTSII-LCLPKT ;We are using datagram relative pointers
INPNCT==..PKT+0 ;NCT that datagram was received on
NXTTIM==^D60000 ;Wakeup at least once a minute
LCKTIM==^D500 ;No one should ever hold ARPLOK this long
HSHLEN==^D19 ;Length of hash table (must be prime)
TRYCNT==4 ;RESOLV tries four times before giving up
NR ARPFRK,1 ;FORKX of ARP Fork
NR HSBASE,1 ;Pointer to hash table
NR HSHTMR,1 ;Timer for hash table GC routine
RS ARPFLG,1 ;Wakeup flag for ARP Fork
RS ARPTMR,1 ;Timer for ARP fork
RS ARPHIQ,1 ;Head of ARP input queue
RS ARPTIQ,1 ;Tail of ARP input queue
RS ARPBUG,1 ;-1 if ARP is being debugged
IFN STANSW&SC30SW,<
RS ARPPID ;ARP's NISRV portal ID
RS ARPRBC ;Total number of receive buffers ARP has
;posted to NISRV.
RSI ARPDRB,2 ;Number of buffers we want to keep posted
>;IFN STANSW&SC30SW
RS ARPLOK,1 ;Lock on hash table
NR ARPLKT,1 ;TODCLK when lock was claimed
NR ARPLPC,1 ;PC of code that claimed the lock
NR ARPLKF,1 ;FORKX of last locker
;ARP Canonicalization Storage
NR ARPTYP,1 ;Type of ARP we are doing (ET%ARP or ET%XRX)
NR REPLYF,1 ;-1 if we should reply to this packet
NR CPROTO,1 ;Current protocol we are interested in
NR NPROTO,1 ;Protocol address of interface
NR SPADDR,1 ;Source protocol address, right justified
NR TPADDR,1 ;Target protocol address, right justified
NR SHRDW0,1 ;First 16 bits of source hardware address
NR SHRDW1,1 ;Last 32 bits of source hardware address
;Statistics Storage
NR STAACC,1 ;Packets accepted by ARP
NR STAFLX,1 ;Packets rejected by ARP filter
NR STAIP,1 ;Of valid packets, no. of RFC826 IP requests
NR STAXRX,1 ;Of valid packets, no. of Xerox PUP requests
NR STARPR,1 ;No. of RFC826 replies
NR STARPX,1 ;No. of Xerox replies
NR STARQR,1 ;No. of RFC826 requests
NR STARQX,1 ;No. of Xerox requests
NR STAHCT,1 ;Count of hash table entries
NR STALCK,1 ;Count of misses trying to lock ARP table
SUBTTL Startup and Main Loop
XSWAPCD
;Start up the ARP fork
;Returns +1 always
IFE REL6,<ARPBEG::>
IFN REL6,<XNENT ARPBEG,G>
MOVX T1,CR%CAP ;Transmit capabilities
CFORK% ;Create fork
BUG.(CHK,ARPBEA,SOFT,ENET,<ARP - CFORK% failed on background fork>)
XMOVEI T2,ARPBAK ;Start address of background fork
MSFRK% ;Start in monitor mode
RET ;Return to caller
;ARPBAK - ARP process startup code
ARPBAK:
IFE REL6,<MOVX T1,UMODF ;Need to set up fork's context>
IFN REL6,<MOVX T1,USRCTX ;Init context>
MOVEM T1,FFL ;Set up flags
SETZM FPC ;Set up PC word (location 0)
MCENTR ;Establish JSYS context
IFE REL6,<SE1ENT ;Run in section one>
IFE REL6,<MOVE T1,[XWD ITFPC,ARPUXI] ;Trap fatal interrupts>
IFN REL6,<MOVE T1,[XWD XCDSEC,ARPUXI] ;Trap fatal interrupts>
MOVEM T1,MONBK ; ...
MOVE T1,CHNSON ; ...
MOVEM T1,MONCHN ; ...
MOVEI T1,INTON ;Address of IP layer on flag
CALL DISL ;Wait for IP layer to be initialized
CALL ARPINI ;Initialize data structures
ARPBA0: SETZM ARPFLG ;Clear request flag
CALL DOARPQ ;Process ARP queue
MOVE T1,TODCLK ;Get now
CAML T1,ARPTMR ;Has timer expired?
CALL DOARPT ;Do ARP time driven things
JSP T4,ARPBKT ;Check flags again, avoid scheduler overhead
SKIPA T1,[ARPBKT] ;Nothing to do, block for a while
JRST ARPBA0 ;More work to do, go to top of loop
MDISMS ;Sleep for a while
JRST ARPBA0 ;Loop forever
;Here on fatal interrupt
ARPUXI: BUG.(CHK,ARPPSI,SOFT,ARP,<ARP - Background fork crashed, restarting>)
IFE REL6,<SE1ENT ;Make sure we're in section one>
MCENTR ;Re-establish monitor context
JRST ARPBA0 ;Restart at top of loop
;ARPBKT - scheduler test for ARP process
;Takes no data.
RESCD
ARPBKT: MOVE T1,TODCLK ;Get now
CAML T1,ARPTMR ;Has timer expired?
JRST 1(T4) ;Yes, wakeup
SKIPN ARPFLG ;Something to do?
JRST 0(T4) ;No, keep on sleeping
JRST 1(T4) ;Yes, unblock
XSWAPCD
;ARPINI - initialize Address Resolution Protocol (ARP) data structures
;Note that setting HSBASE non-zero is a flag that the ARP process is
; fully initialized.
;Returns +1 always
ARPINI: MOVE T1,FORKX ;Record our fork number
MOVEM T1,ARPFRK ; ...
SETZM ARPTMR ;Make timer go off right away first time
SETOM ARPLOK ;Make sure hash table lock is free
MOVEI T1,HSHLEN ;Size of hash table
CALL GETBLK ;Get a piece of Internet storage
SKIPN T1 ;Skip if we got something
BUG.(HLT,ARPSTG,ARP,SOFT,<ARP - No storage for ARP hash table>)
PUSH P,T1 ;Remember address of block
MOVEI T2,HSHLEN ;Size of block
CALL CLRBLK ;Zero the block
MOVE T1,DBUGSW ;Get value of master debug switch
CAILE T1,1 ;Is system standalone?
SETOM ARPBUG ;Yes, enable ARP bug reporting/tracing
POP P,HSBASE ;Set base address of table, ARP is now up.
IFN STANSW&SC30SW,<
XMOVEI T1,NCTVT ;Set up pointer to NCT vector table
ARPIN0: LOAD T1,NTLNK,(T1) ;Get NCT pointer
JUMPE T1,RTN ;If nil, then done
LOAD T2,NTDEV,(T1) ;Get the device type
CAIN T2,NT.NIP ;An NI?
SKIPA ;Yes, init ARP for the NI
JRST ARPIN0 ;Loop over all NCT's
;Initialize ARP for the NI
;Called from ARPINI with T1 pointing at the NCT address
TRVAR (<<UNBLOK,UN.LEN>>) ;Arg block for NISRV
MOVE T2,T1 ;Get the NCT address
SETZM ARPRBC ;Reset the number of buffers posted
STOR T2,UNUID,+UNBLOK ;Save the NCT address as the user ID
SETZRO <UNCHN,UNPAD>,+UNBLOK ;Use channel number 0, no padding
MOVX T1,ET%XRX ;Get the ARP protocol type
STOR T1,UNPRO,+UNBLOK ;Put it in the arg block
XMOVEI T1,ARPCBK ;Get the callback address
STOR T1,UNCBA,+UNBLOK ;Put callback address in the arg block
MOVX T1,NU.OPN ;Get the NISRV function code
XMOVEI T2,UNBLOK ;Get the address of the arg block
CALL DLLUNI ;Open the portal
RET ;Sigh
LOAD T1,UNPID,+UNBLOK ;Get the portal ID
MOVEM T1,ARPPID ;Put it in a safe place
SETONE UNDAD,+UNBLOK ;Setup high order multicast address
SETONE UNDAD,+1+UNBLOK ;Setup low order multicast address
MOVX T1,NU.EMA ;Get NISRV function code
XMOVEI T2,UNBLOK ;Get NISRV arg block address
CALL DLLUNI ;Enable the broadcast address
RET ; Sigh...
CALL ARPPST ;Post some buffers
RET
;ARPPST - Post NI buffers for the ARP protocol
;Has no args, smashes all Ts.
XRESCD
ARPPST: MOVE T1,ARPDRB ;Get the number of receive buffers desired
CAMG T1,ARPRBC ;Are enough posted?
RET ; Yes, don't add any more
TRVAR (<<UNBLOK,UN.LEN>>) ;NISRV arg block
MOVE T1,ARPPID ;Get our portal ID
STOR T1,UNPID,+UNBLOK ;Put it in the arg block
PSTLOP: CALL GETBFF ;Get a buffer
RET ; Just punt if no buffers available
STOR T1,UNRID,+UNBLOK ; Setup request ID to be buffer address
XMOVEI T1,$ARHRD(T1) ; Get start address of user data portion
; of buffer
TXO T1,OWGP.(8) ; Make it a byte pointer
STOR T1,UNBFA,+UNBLOK ; Put it in the NISRV arg block
MOVX T1,^D300 ; Get the length of the buffer
STOR T1,UNBSZ,+UNBLOK ; Put it in the arg block
MOVX T1,UNA.EV ; Get address space indicator
STOR T1,UNADS,+UNBLOK ; Buffer is in Exec virtual address space
MOVX T1,NU.RCV ; Get function code for NISRV
XMOVEI T2,UNBLOK ; Setup arg block address
CALL DLLUNI ; Post a receive buffer
JRST ARPPS1 ; Failed, clean up nicely
AOS T1,ARPRBC ; Account for this buffer
CAMGE T1,ARPDRB ; Do we have enough buffers?
JRST PSTLOP ; No, do some more
RET ; Yes, all done
ARPPS1: BUG.(CHK,ARPNPF,ARP,SOFT,<ARP - Post receive buffer failed>)
LOAD T1,UNRID,+UNBLOK ;Get the buffer back
CALLRET RETBUF ;Dump the buffer and get out of here
>;IFN STANSW&SC30SW
Subttl ARP Background Processing
;DOARPQ - process ARP requests, update data structures
;Returns +1 always
DOARPQ: CALL ARPDEQ ;Dequeue an ARP packet
RET ;No pending request, so all done
SKIPE ARPBUG ;Debugging ARP activity?
CALL ARPDEB ;Yes, see if someone wants to look at this
CALL ARPFIL ;Do some validity checking
JRST DOARPX ;Bad packet, flush it
CALL ARPCAN ;"Canonicalize" the packet
CALL MERGE ;Update tables
JRST DOARPX ;All done, flush packet
CALL SNDREP ;Send ARP reply using current buffer
JRST DOARPQ ;Loop over input queue
DOARPX: MOVE T1,ARP ;Get buffer pointer into place
CALL RETBUF ;Return buffer to free list
JRST DOARPQ ;Loop over input queue
;ARPDEB - here if debugging ARP activity
;Returns +1 always
;Clobbers T1-T4
ARPDEB: MOVE T1,WAIFLG ;Get flag word for waif monitoring process
TXNN T1,WA%ARP ;Accept ARP packets?
RET ;No, return now
MOVE T1,ARP ;T1/ buffer pointer
LOAD T2,ETTYP,(ARP) ;T2/ protocol type
MOVEI T3,^D100 ;T3/ approximate packet length
CALL PKOWAF ;Let waif listener look at this packet
RET ;Return to caller
;ARPFIL - filter out bad ARP requests
;Takes ARP/ buffer pointer
; P1/ NCT pointer
;Returns +1 flush the packet
; +2 packet appears to be good
ARPFIL: AOS STAACC ;Count packets received
LOAD T1,ETTYP,(ARP) ;Get datagram type
CAIE T1,ET%XRX ;Xerox style ARP?
JRST ARPFL0 ;No, must be RFC826 ARP
SKIPG NTSUBN(P1) ;Do we have subnets on this interface?
JRST ARPFLX ;No, flush the datagram
RETSKP ;Accept the Xerox AR request
ARPFL0: LOAD T1,ARHRD,(ARP) ;Get hardware type
CAIE T1,HW$ETH ;10MB Ethernet?
JRST ARPFLX ;No, flush it
LOAD T1,ARHLN,(ARP) ;Get length of hardware addresses
CAIE T1,HL$ETH ;Must be six octets
JRST ARPFLX ;No, flush it
LOAD T1,ARPRO,(ARP) ;Get protocol type
LOAD T2,ARPLN,(ARP) ;Get protocol length
CAIN T1,PR$IP ;IP protocol?
CAIE T2,PL$IP ;Yes, is address 4 octets long?
JRST ARPFLX ;No to either, flush it
RETSKP ;Packet is valid AR request for an IP address
ARPFLX: SKIPE ARPBUG ;Skip if not debugging
BUG.(INF,BADARP,ARP,SOFT,<ARP - bad datagram>,<<T1,PRO>,<T2,LEN>>)
AOS STAFLX ;Count filter rejections
RET ;Return to caller
;ARPCAN - Canonicalize the RFC826 and Xerox ARP requests
;Takes ARP/ buffer pointer
; P1/ NCT pointer
;Returns +1 always, following (global) locations updated:
; ARPTYP - type of ARP packet we are processing
; REPLYF - set to -1 if we should generate a reply
; CPROTO - current protocol we are interested in
; NPROTO - protocol address of interface
; SPADDR - source protocol address, right justified
; TPADDR - target protocol address, right justified
; SHRDW0 - first 16 bits of source hardware address
; SHRDW1 - last 32 bits of source hardware address
ARPCAN: SETZM REPLYF ;Assume no reply needed
LOAD T1,ETTYP,(ARP) ;Get Ethernet type
MOVEM T1,ARPTYP ;Remember it
CAIE T1,ET%ARP ;Is is RFC826 ARP?
JRST ARPCNX ;No, has to be Xerox ARP
;Canonicalize for IP
AOS STAIP ;Count one for the statisticians
LOAD T1,AROPC,(ARP) ;Get our opcode
CAIN T1,OP$REQ ;A request?
SETOM REPLYF ;Yes, set flag to generate a reply
LOAD T1,ARPRO,(ARP) ;Get target protocol
MOVEM T1,CPROTO ;Remember it
MOVE T1,NTLADR(P1) ;Get IP address of NCT we came in on
MOVEM T1,NPROTO ;Save it
LOAD T1,IPTP,(ARP) ;Get target IP protocol address
MOVEM T1,TPADDR ;Save target protocol address
LOAD T1,IPSP0,(ARP) ;Get first 16 bits of source protocol addr
LSH T1,^D16 ;Make some room
LOAD T2,IPSP1,(ARP) ;Get last 16 bits of source protocol addr
IORI T1,(T2) ;Canonicalize
MOVEM T1,SPADDR ;Save source protocol address
LOAD T1,IPSH0,(ARP) ;Get 32 bits of source hdw addr
SETZ T2, ;Clear T2 for shifting into
LSHC T1,-^D16 ;Shovel middle 16 into T2
MOVEM T1,SHRDW0 ;Save those first 16
LOAD T1,IPSH1,(ARP) ;Get last 16 bits
LSH T2,-4 ;Push the middle 16 over a bit
IORI T2,(T1) ;OR together last 32 bits
MOVEM T2,SHRDW1 ;Save canonicalized form
RET ;Return to caller
;Canonicalize for Xerox style PUP ARP
ARPCNX: AOS STAXRX ;Count one for the statisticians
MOVEI T1,ET%PUP ;Protocol type is PUP
MOVEM T1,CPROTO ;Set current protocol type
MOVE T2,NTSUBN(P1) ;Get our PUP subnet number
MOVE T1,NTHSTN(P1) ;Get our PUP host number from NCT
HRLI T1,(T2) ;Form subnet,,host
MOVEM T1,NPROTO ;Save as NCT protocol
LOAD T3,XRTP,(ARP) ;Get "target" PUP protocol address
HRLI T3,(T2) ;Form subnet,,host
MOVEM T1,TPADDR ;Save it
LOAD T4,XRSP,(ARP) ;Get "source" PUP protocol address
HRLI T4,(T2) ;Form subnet,,host
LOAD T1,XROPC,(ARP) ;Get opcode
CAIE T1,XR$REQ ;Is it a request?
EXCH T3,T4 ;No, a reply. Swap the fields
MOVEM T3,TPADDR ;Set target protocol address
MOVEM T4,SPADDR ;Set source protocol address
CAIE T1,XR$REQ ;Check again for a request
IFSKP.
SETOM REPLYF ;Yes, must send a reply
LOAD T1,XRSH0,(ARP) ;Get first 16 bits
LOAD T2,XRSH1,(ARP) ;Get last 32 bits of hardware address
ELSE.
LOAD T1,XRTH0,(ARP) ;Get first 16 bits
LOAD T2,XRTH1,(ARP) ;Get last 32 bits of hardware address
ENDIF.
MOVEM T1,SHRDW0 ;Save first part of hardware address
MOVEM T2,SHRDW1 ;Save last part of hardware address
RET ;Return to caller
;MERGE - merge information from ARP packet
;Takes ARP/ buffer pointer
; P1/ NCT pointer
;Returns +1 no further processing required, flush buffer
; +2 we are target protocol address, may need to send reply
MERGE: STKVAR <MERGEF,BUCKET> ;Declare local storage
SETZM MERGEF ;Set merge flag to false
MOVE T1,SPADDR ;Get sender's protocol address
MOVE T2,CPROTO ;Get protocol type
CALL HASH ;Compute hash code
MOVE T2,HSBASE ;Get base adddress of hash table
ADDI T2,(T1) ;Compute bucket address
MOVEM T2,BUCKET ;Save bucket address
CALL LCKARP ;Lock hash table to ensure consistency
RET ;We (ARP process) should never get this return
SKIPN T1,(T2) ;Load first pointer
JRST MERGE2 ;Nothing in table, go check target protocol
MERGE0: LOAD T3,HSPROT,(T1) ;Get protocol address of hash entry
CAME T3,SPADDR ;Match the packet?
JRST MERGE1 ;No, keep on looking
LOAD T4,HSPTYP,(T1) ;Get protocol type of hash entry?
CAME T4,CPROTO ;Match the packet?
JRST MERGE1 ;No, keep on looking
MOVE T3,SHRDW0 ;A hit! Get first part of hardware address.
STOR T3,HSHW0,(T1) ;Update hash entry
MOVE T3,SHRDW1 ;Get second part of hardware address
STOR T3,HSHW1,(T1) ;Update hash entry
MOVE T3,TODCLK ;Get now
STOR T3,HSLTAD,(T1) ;Store value of last TODCLK for aging
SETOM MERGEF ;Set merge flag to true
JRST MERGE2 ;Go check target protocol
MERGE1: LOAD T1,HSNXT,(T1) ;Get pointer to next hash entry in this bucket
JUMPN T1,MERGE0 ;If non-nil, go look at it
MERGE2: MOVE T4,NPROTO ;Get protocol address of NCT packet came in on
CAME T4,TPADDR ;Are we the target?
JRST MERGE4 ;No, no more to do
SKIPE MERGEF ;Is the merge flag false?
JRST MERGE3 ;No, return now. May need to send reply.
CALL GETHSH ;Get some free storage
JRST MERGE3 ;No storage! Continue along.
MOVE T2,SPADDR
STOR T2,HSPROT,(T1) ;Store protocol address
MOVE T2,CPROTO
STOR T2,HSPTYP,(T1) ;Store protocol type
MOVE T2,SHRDW0
STOR T2,HSHW0,(T1) ;Store first part of hardware address
MOVE T2,SHRDW1
STOR T2,HSHW1,(T1) ;Store last part of hardware address
MOVE T2,TODCLK
STOR T2,HSLTAD,(T1) ;Store time and date of last reference
MOVE T2,@BUCKET ;Get pointer in bucket header
STOR T2,HSNXT,(T1) ;Hang list off of new entry
MOVEM T1,@BUCKET ;Hang new entry off of bucket
AOS STAHCT ;Count another entry
MERGE3: SKIPN REPLYF ;Requestor wants reply from us?
JRST MERGE4 ;No, just flush packet
CALL ULKARP ;Unlock hash table
RETSKP ;Skip return, need to send reply
MERGE4: CALL ULKARP ;Unlock hash table
RET ;Take single return
;SNDREP - send an ARP Reply for the current packet
;Takes ARP/ buffer pointer
; P1/ NCT pointer
;Returns +1 always
SNDREP: MOVE T1,ARPTYP ;Get style of ARP we are using
CAIE T1,ET%ARP ;RFC826 style ARP?
IFSKP.
AOS STARPR ;Count number of RFC826 replies generated
XMOVEI T2,RFCREP ;Generate an RFC826 style reply
ELSE.
AOS STARPX ;Count number of Xerox replies generated
XMOVEI T2,XRXREP ;Generate a Xerox style ARP reply
ENDIF.
CALL 0(T2) ;Copy source to destination, set new source
IFN STANSW&SC30SW,<
LOAD T2,NTDEV,(P1) ;Get the device type
CAIN T2,NT.NIP ;Is it an NI?
JRST NIARPX ; Yes, send it over the NI
>;IFN STANSW&SC30SW
MOVE T2,ARP ;T2/ buffer pointer
MOVEI T3,.PM32 ;T3/ 32-bit MEIS data mode
MOVEI T4,RETBUF ;T4/ buffer disposal routine
CALL BLDIOW ;Construct and queue up an Ethernet write
RET ;Return to caller
;RFCREP - copy IP source fields to destination, set new source fields
;Also performs 10MB Ethernet encapsulation
;Takes ARP/ buffer pointer
; P1/ NCT pointer
;Returns +1 always, T1/ byte count of datagram body
RFCREP: MOVEI T1,OP$REP ;Set opcode to reply
STOR T1,AROPC,(ARP) ;...
SETZ T2, ;Clear this AC for shifting
LOAD T1,IPSH0,(ARP) ;Get 32 bits of hardware source
LSHC T1,-^D16
STOR T1,IPTH0,(ARP) ;Set first 16-bits of hardware destination
STOR T1,ETDH0,(ARP) ;Do same for encapsulation
LOAD T1,IPSH1,(ARP) ;Get last 16 bits of hardware source
LSH T2,-4 ;Nudge over bits from first shift
IOR T1,T2 ;Create last 32 bits of new hardware dest
STOR T1,IPTH1,(ARP) ;Set it
STOR T1,ETDH1,(ARP) ;Set last 32 bits of encapsulation dest.
LOAD T1,IPSP0,(ARP) ;Get first 16 bits of source protocol
LOAD T2,IPSP1,(ARP) ;Get last 16 bits of source protocol
LSH T1,^D16 ;Slide first bits left
IORI T1,(T2) ;OR in last few bits
STOR T1,IPTP,(ARP) ;Set 32 bits of target protocol
CALL IPSRC ;Set us up as an IP source
RET ;Return to caller
;IPSRC - Set source fields appropriate to selected interface
;Takes P1/ NCT pointer
; ARP/ buffer pointer
;Returns +1 always, T1/ total length of datagram
IPSRC: LOAD T1,HRDW0,(P1) ;Get first 32 bits of our hdw address
STOR T1,IPSH0,(ARP) ;Set it in ARP datagram
STOR T1,ETSH0,(ARP) ;Set in encapsulation field as well
LOAD T1,HRDW1,(P1) ;Get last 16 bits of our hdw address
STOR T1,IPSH1,(ARP) ;Set it in ARP datagram
STOR T1,ETSH1,(ARP) ;Set in encapsulation field as well
MOVE T1,NTLADR(P1) ;Get our IP protocol address
LSHC T1,-^D16 ;Split it into two
STOR T1,IPSP0,(ARP) ;Set first 16 bits in ARP datagram
LSH T2,-^D20 ;Shift the remaining bits over
STOR T2,IPSP1,(ARP) ;Set last 16 bits
MOVEI T1,IPLEN ;T1/ return length of IP ARP
RET ;All done, return to caller
;XRXREP - generate a reply datagram for Xerox style ARP
;Takes ARP/ buffer pointer
; P1/ NCT pointer
;Returns +1 always
XRXREP: MOVEI T1,XR$REP ;Opcode value is "reply"
STOR T1,XROPC,(ARP) ;Set it
LOAD T1,HRDW0,(P1) ;Get first 32 bits of our hardware address
STOR T1,ETSH0,(ARP) ;Set as source in encapsulation
SETZ T2, ;Clear this AC for shifting
LSHC T1,-^D16 ;Isolate the first 16 bits
STOR T1,XRTH0,(ARP) ;Store first part of target field
LOAD T1,HRDW1,(P1) ;Get last 16 bits of our hardware address
STOR T1,ETSH1,(ARP) ;Set in encapsulation
LSH T2,-4 ;Position the middle 16 bits
IORI T2,(T1) ;Create final 32 bits
STOR T2,XRTH1,(ARP) ;Set last 32 bits of target field
LOAD T1,XRSH0,(ARP) ;Get first 16 bits of source
STOR T1,ETDH0,(ARP) ;Set in encapsulation
LOAD T1,XRSH1,(ARP) ;Get last 32 bits of source
STOR T1,ETDH1,(ARP) ;Set in encapsulation
MOVEI T1,XRXLEN ;T1/ length of datagram
RET ;Return to caller
;ARPENQ - append buffer to ARP input queue
;Called from interrupt level
;Takes T1/ buffer pointer
; T2/ NCT address
;Returns +1 always
XRESCD
ARPENQ::SETZRO NBQUE,(T1) ;Clear pointer to next packet
MOVEM T2,INPNCT(T1) ;Remember NCT address in buffer
PIOFF ;Turn off interrupts just in case
MOVE T3,ARPTIQ ;Get tail of input queue
JUMPN T3,ARPRC0 ;Queue not empty, go append buffer
MOVEM T1,ARPHIQ ;Was empty, this is now only item
SKIPA ;Make head and tail be same packet
ARPRC0: STOR T1,NBQUE,(T3) ;Make old packet point to newest packet
MOVEM T1,ARPTIQ ;Set new tail pointer
PION ;Turn interrupts back on
AOS ARPFLG ;Wakeup ARP process
RET ;Return to caller
IFN STANSW&SC30SW,<
; Here at NISRV interrupt level
ARPCBK: JRST @1+.-NU.OPN(T1) ; Do the dispatch
TABBEG NU.OPN,NU.MAX,<IFIW RTN>
TABENT NU.XMT,<IFIW ARPXMD> ;Transmit done
TABENT NU.RCV,<IFIW ARPRCD> ;Receive done
TABEND
; Here on completion of NISRV xtransmit
ARPXMD: LOAD T1,UNRID,(T2) ; Get the buffer address
CALLRET RETBUF ; Return the buffer
; Here on input available
ARPRCD: LOAD T1,UNRID,(T2) ; Get the buffer address
LOAD T2,UNUID,(T2) ; Get the NCT address
MOVX T3,ET%XRX ; Get the desired protocol type
STOR T3,ETTYP,(T1) ; Make ARPFIL happy
CALL ARPENQ ; Queue the packet up for later
SOS ARPRBC ; One less buffer posted
CALLRET ARPPST ; Go post more
>;IFN STANSW&SC30SW
XSWAPCD
;ARPDEQ - remove packet from input queue
;Call from process level
;Returns +1 queue empty
; +2 success, ARP/ buffer pointer
; P1/ NCT pointer
;Clobbers T1-T4, ARP, P1
XRESCD
ARPDEQ: PIOFF ;Protect queue integrity
MOVE ARP,ARPHIQ ;Get head of queue
JUMPE ARP,ARPDE1 ;Queue is empty
LOAD T1,NBQUE,(ARP) ;Get successor
JUMPN T1,ARPDE0 ;Queue not about to run dry
SETZM ARPTIQ ;Clear tail of queue
SKIPA ;Don't set section bits on a nil pointer
ARPDE0: SETSEC T1,INTSEC ;Set extended address
MOVEM T1,ARPHIQ ;Update queue head
MOVE P1,INPNCT(ARP) ;Get NCT this packet came in on
AOS (P) ;Prepare skip return
ARPDE1: PION ;Restore PI system
RET ;Return to caller
XSWAPCD
;DOARPT - do time dependent things
;Returns +1 always
DOARPT: SKIPE ARPLKT ;Is the hash table lock being held?
CALL CHKLOK ;Check if hash table lock is okay.
MOVE T1,TODCLK ;Get now
CAMLE T1,HSHTMR ;Time to GC the hash table?
CALL HSHTMO ;Yes, flush unused entries
MOVE T1,TODCLK ;Get now
ADDI T1,NXTTIM ;Compute next forced wakeup
MOVEM T1,ARPTMR ;Reset timer
RET ;Return to caller
Subttl Hash Table Management
;HASH - Compute the hash table index
;Takes T1/ protocol address
; T2/ protocol type
;Returns +1 always T1/ hash table index
HASH: HLRZ T2,T1 ;Copy left half of address
XORI T1,(T2) ;Mash the bits up a bit
MOVMS T1 ;Ensure non-negative offset
IDIVI T1,HSHLEN ;Compute hash code
MOVE T1,T2 ;Copy hash bucket index into expected place
RET ;Return to caller
;GETHSH - get a block of storage to use as a hash table entry
;Returns +1 no free storage
; +2 success, T1/ pointer
GETHSH: MOVEI T1,ENTSIZ ;Size of a block
CALL GETBLK ;Get some storage
JUMPE T1,R ;Failed
PUSH P,T1 ;Save pointer on stack
MOVEI T2,ENTSIZ ;Get size of block again
CALL CLRBLK ;Zero the storage
POP P,T1 ;Restore block pointer
RETSKP ;Return to caller
;RETHSH - return a hash block to free storage
;Takes T1/ buffer pointer
;Returns +1 always
RETHSH: JRST RETBLK ;Return block to Internet free storage
;LCKARP - lock the hash table
;We remember lots of information for debugging and recovering from any
; possible lock problems.
;Returns +1 failure
; +2 success, ARPLOK claimed
LCKARP: SKIPE NSKED ;Are we NOSKED?
JRST LCKAR1 ;Yes, be careful not to block
LOCK ARPLOK ;No, use standard AOSE lock
LCKAR0: PUSH P,T1 ;Save this register
MOVE T1,-1(P) ;Get our return PC
MOVEM T1,ARPLPC ;Save PC of locker for debugging
MOVE T1,TODCLK ;Get current time and date
MOVEM T1,ARPLKT ;Remember that for timing out a lock
MOVE T1,FORKX ;Get fork number of current process
MOVEM T1,ARPLKF ;Remember it
POP P,T1 ;Restore T1
RETSKP ;Return with the lock
;Here to try locking when we're NOSKED and can't block.
LCKAR1: AOSE ARPLOK ;Try for the lock
AOSA STALCK ;Failed, and NOSKED. Count failure and skip.
JRST LCKAR0 ;Got it. Go set up rest of data structure.
RET ;Take failure return
;ULKARP - unlock the hash table
;Returns +1 always
ULKARP: SETZM ARPLKT ;Forget last time lock was held
UNLOCK ARPLOK ;Standard unlocking routine
RET ;Return having freed the lock
;CHKLOK - see if hash table lock is hung and time it out if so.
;A bit of paranoia to prevent locking bugs from hanging Ethernet service.
;Returns +1 always
CHKLOK: SKIPN T1,ARPLKT ;Skip if lock is being held
RET ;Lock is free, return now
ADDI T1,LCKTIM ;Compute lock timeout
CAMLE T1,TODCLK ;Has lock been locked a long time?
RET ;No, return now
MOVE T1,ARPLKF ;Get FORKX of locker
MOVE T2,ARPLPC ;Get PC trace of locker
BUG.(CHK,ARPTMO,ARP,SOFT,<ARP - hash lock timed out>,<<T1,F>,<T2,PC>>)
CALLRET ULKARP ;Break the lock violently
;HSHTMO - garbage collect old hash table entries.
;Called by ARP background process
;Enter at HSHCLR to flush the entire ARP table
;Returns +1 always
HSTTIM==^D60*^D60*^D1000 ;Once an hour
LNGTIM==6*HSTTIM ;If no reference in six hours, flush the entry
HSHCLR::TDZA T1,T1 ;Enter here to flush entire ARP table
HSHTMO: MOVE T1,[LNGTIM] ;Standard entry point for timeouts
SAVEQ ;Preserve some registers
MOVE Q1,TODCLK ;Get now
SUB Q1,T1 ;Compute reference time cutoff
MOVSI Q2,-HSHLEN ;Set up aobjn pointer
HSHTM1: MOVEI T1,(Q2) ;Get offset into hash table
ADD T1,HSBASE ;Compute absolute address of bucket
SKIPE (T1) ;Skip if bucket is empty
CALL WALKIT ;Else walk the hash chain, flushing old entries
AOBJN Q2,HSHTM1 ;Loop over all entries
MOVE T1,TODCLK ;Get now
ADD T1,[HSTTIM] ;GC the hash table at some future time
MOVEM T1,HSHTMR ;Store next wakeup time
RET ;Return to caller
;WALKIT - helper routine for HSTTMO
;Walks down a hash chain flushing old entries. We lock the hash table at this
; level instead of in HSHTMO to minimize any waiting on the part of user
; processes. Note also that this code assumes HSNXT is a zero offset.
;Takes T1/ bucket address
; Q1/ reference time cutoff
;Returns +1 always.
WALKIT: CALL LCKARP ;Lock the entire hash table
RET ;Shouldn't happen for ARP process
MOVE T2,T1 ;Bucket address becomes the previous "record"
SKIPN T1,(T1) ;Get pointer to first record
JRST WALKI3 ;Quit if now if list is nil
WALKI1: LOAD T3,HSLTAD,(T1) ;Get TODCLK of last reference
CAMLE T3,Q1 ;Recently referenced?
JRST WALKI2 ;Record okay, go look at next one
SOS STAHCT ;Decrement count of hash entries
LOAD T3,HSNXT,(T1) ;Get pointer to next record
STOR T3,HSNXT,(T2) ;Make previous record point to next record
PUSH P,T2 ;Save pointer to previous record
CALL RETHSH ;Flush current record (pointed to by T1)
POP P,T1 ;New current pointer is old previous pointer
WALKI2: MOVE T2,T1 ;Current pointer becomes previous pointer
LOAD T1,HSNXT,(T1) ;Get pointer to next entry
JUMPN T1,WALKI1 ;If not nil, go look at it
WALKI3: CALL ULKARP ;Release the hash table lock
RET ;End of list, return
Subttl Address Resolution
;RESOLV - lookup hardware address
;On a successful lookup we move the target record to the head of the hash
; bucket list to speed up future lookups. On failure we send off an ARP
; request before returning to our caller.
;Takes T1/ protocol address
; T2/ protocol type
; P1/ NCT address
;Returns +1 Unable to resolve address
; +2 address resolved, T1/ first 16 bits of destination host
; T2/ last 32 bits of destination
;T4 - Retry count
;Q1 - protocol address
;Q2 - protocol type
;Q3 - address of hash bucket
RESOLV::SAVEQ ;We clobber these AC's
MOVEI T4,TRYCNT ;Set up retry count in case we need to request
DMOVE Q1,T1 ;Save arguments
CALL HASH ;Compute hash code
SKIPN Q3,HSBASE ;Get base adddress of hash table
RET ;Return now if ARP is not yet initialized
ADDI Q3,(T1) ;Compute bucket address
RESOL3: CALL LCKARP ;Lock hash table to ensure consistency
RET ;NOSKED and can't get lock on first try
SKIPN T1,(Q3) ;Load first pointer
JRST RESOL2 ;Nothing in table, go enqueue an ARP request
SETZ T2, ;No previous record at this point
RESOL0: LOAD T3,HSPROT,(T1) ;Get protocol address of hash entry
CAME Q1,T3 ;Match the packet?
JRST RESOL1 ;No, keep on looking
LOAD T3,HSPTYP,(T1) ;Get protocol type of hash entry
CAME Q2,T3 ;Match the packet?
JRST RESOL1 ;No, keep on looking
IFN. T2 ;A hit! If not the first record, move to head.
LOAD T3,HSNXT,(T1) ;Get pointer to successor of target
STOR T3,HSNXT,(T2) ;Make our predecessor point to that record
MOVE T3,(Q3) ;Get pointer to top of hash list
STOR T3,HSNXT,(T1) ;Hang list off of target (move it to head)
MOVEM T1,(Q3) ;Rehang list off of bucket
ENDIF.
MOVE T2,TODCLK ;Get current time and date
STOR T2,HSLTAD,(T1) ;Update time of last reference
LOAD T2,HSHW1,(T1) ;Get second part of hardware address
LOAD T1,HSHW0,(T1) ;Get first part of hardware address
CALL ULKARP ;Release lock on hash table
RETSKP ;Success return to caller
;Here to look at next entry in the hash list
RESOL1: MOVE T2,T1 ;Remember previous pointer
LOAD T1,HSNXT,(T1) ;Get pointer to next link in hash chain
JUMPN T1,RESOL0 ;If non-nil, go look at next entry
;Fall through if at end of list and no match
;Here to send an ARP request. If we haven't exhausted our retry count and
; we aren't a job zero process, we block for a while, waiting for a reply.
; This reduces the number of packets we drop because of ARP failures.
RESOL2: CALL ULKARP ;Release lock on hash table
PUSH P,T4 ;Save our retry count
CALL SNDREQ ;Send an ARP request
POP P,T4 ;Restore retry count
SOJL T4,R ;Return failure if retry count exhausted
SKIPE JOBNO ;If we are job zero
SKIPE NSKED ;Or if we are NOSKED
RET ;Then don't risk blocking
MOVEI T1,^D1000 ;Do a timed block of one second
CALL SETBKT ;Set up scheduler test argument
HRRI T1,BLOCKT ;Test is timed block
MDISMS ;Sleep for a while
JRST RESOL3 ;Try resolving the address again
;SNDREQ - send an ARP request for the RESOLV routine
;Takes Q1/ protocol address
; Q2/ protocol type
; P1/ NCT pointer
;Returns +1 always
SNDREQ: SAVEAC<ARP> ;We use this AC
CALL GETBFF ;Get a free buffer
RET ;No storage, quit now
MOVE ARP,T1 ;Set up standard buffer pointer
SETONE ETDH0,(ARP) ;Encapsulation. Set destination host...
SETONE ETDH1,(ARP) ; ...to -1, the broadcast address
XMOVEI T1,RFCREQ ;Assume RFC826 style ARP
CAIN Q2,ET%PUP ;Are we doing PUP?
XMOVEI T1,XRXREQ ;Yes, must use Xerox style ARP
CALL (T1) ;Stuff the datagram appropriately
IFN STANSW&SC30SW,<
LOAD T2,NTDEV,(P1) ;Get the device type
CAIN T2,NT.NIP ;Is it an NI?
IFSKP. ;No, do MEIS stuff
>;IFN STANSW&SC30SW
MOVE T2,ARP ;T2/ buffer pointer
MOVEI T3,.PM32 ;T3/ 32-bit MEIS data mode
MOVEI T4,RETBUF ;T4/ buffer disposal routine
CALL BLDIOW ;Construct and queue up an Ethernet write
IFN STANSW&SC30SW,<
ENDIF.
; Here to transmit an ARP over the NI
NIARPX: TRVAR(<<UNBLOK,UN.LEN>>) ;Get room for NISRV arg block
STOR T1,UNBSZ,+UNBLOK ;Save the size of the datagram
MOVE T1,ARPPID ;Get our NISRV portal ID
STOR T1,UNPID,+UNBLOK ;Put it in the NISRV arg block
STOR ARP,UNRID,+UNBLOK ;Save this buffer's ID
DMOVE T1,$ETDH0(ARP) ;Get the destination Ethernet address
LSH T1,-4 ;Close the gap
LSHC T1,^D16 ;Get two more bytes
LSH T1,4 ;Open the gap again
OPSTR <DMOVEM T1,>,UNDAD,+UNBLOK ;Put it in the arg block
SETZRO UNPTR,+UNBLOK ;Indicate that we supplied an immediate address
XMOVEI T2,$ETDH0+4(ARP) ;Get start address of data
TXO T2,OWGP.(8) ;Turn it into a byte pointer
STOR T2,UNBFA,+UNBLOK ;Setup pointer to the real data
MOVX T1,UNA.EV ;Get address space indicator
STOR T1,UNADS,+UNBLOK ;Buffer is in Exec virtual memory
LOAD T1,UNBSZ,+UNBLOK ;Get the length of the datagram
CAIGE T1,^D46 ;Is it long enough?
MOVX T1,^D46 ; Nope. Now it is
STOR T1,UNBSZ,+UNBLOK ;Setup the datagram size
MOVX T1,NU.XMT ;Get NISRV function code
XMOVEI T2,UNBLOK ;Get arg block address
CALL DLLUNI ;Transmit the datagram
BUG. (CHK,ARPNXF,ARP,SOFT,<ARP - NI transmit failed>)
>;IFN STANSW&SC30SW
RET ;Return to caller
;RFCREQ - set up an RFC826 ARP request
;Takes Q2/ protocol type
; ARP/ buffer pointer
; P1/ NCT pointer
;Returns +1 always, T1/ datagram length
RFCREQ: AOS STARQR ;Count a request send out
MOVEI T1,ET%ARP
STOR T1,ETTYP,(ARP) ;Datagram type is ARP
MOVEI T1,HW$ETH
STOR T1,ARHRD,(ARP) ;Set hardware type to 10MB Ethernet
MOVEI T1,HL$ETH
STOR T1,ARHLN,(ARP) ;Set hardware address length
STOR Q2,ARPRO,(ARP) ;Set user specified protocol type
MOVEI T1,PL$IP ;Get IP protocol address length
STOR T1,ARPLN,(ARP) ;Set protocol length
MOVEI T1,OP$REQ
STOR T1,AROPC,(ARP) ;Set opcode to request
SETZRO IPTH0,(ARP) ;Don't know target hardware address...
SETZRO IPTH1,(ARP) ;...so set those fields to zero
STOR Q1,IPTP,(ARP) ;Set target protocol address
CALL IPSRC ;Set us up as the IP source
RET ;Return to caller, T1/ datagram length
;XRXREQ - send a Xerox style ARP request for PUP
;Takes Q1/ target PUP address
; ARP/ buffer pointer
; P1/ NCT pointer
;Returns +1 always, T1/ datagram length
XRXREQ: AOS STARQX ;Count a request send out
MOVEI T1,ET%XRX ;Get type code
STOR T1,ETTYP,(ARP) ;Set it in encapsulation
MOVEI T1,XR$REQ ;Get opcode (request)
STOR T1,XROPC,(ARP) ;Set it up
SETZRO XRTH0,(ARP) ;Clear target hardware address
SETZRO XRTH1,(ARP) ;Clear target hardware address
STOR Q1,XRTP,(ARP) ;Set target protocol address
LOAD T1,HRDW0,(P1) ;Get first 32 bits of our hardware address
STOR T1,ETSH0,(ARP) ;Set up encapsulation
SETZ T2, ;Clear this for shifting
LSHC T1,-^D16 ;Isolate first 16 bits
STOR T1,XRSH0,(ARP) ;Set first part of source hardware address
LSH T2,-4 ;Nudge the middle 16 bits over
LOAD T1,HRDW1,(P1) ;Get last 16 bits of our hardware address
STOR T1,ETSH1,(ARP) ;Set up encapsulation
IORI T2,(T1) ;Build last 32 bits of our address
STOR T2,XRSH1,(ARP) ;Set last part of source hardware address
MOVE T1,NTHSTN(P1) ;Get our PUP host number
STOR T1,XRSP,(ARP) ;Set source protocol address
MOVEI T1,XRXLEN ;T1/ length of Xerox ARP datagram
RET ;Return to caller
TNXEND
END