226 lines
5.5 KiB
Forth

( ARP: Address Resolution Protocol JCB 13:12 08/24/10)
module[ arp"
\ ARP uses a small cache of entries. Each entry has an age counter; new
\ entries have an age of 0, any entry with an age >N is old.
\
d# 12 constant arp-cache-entry-size
d# 5 constant arp-cache-entries
TARGET? [IF]
meta
arp-cache-entry-size arp-cache-entries * d# 64 max
target
constant arp-size
create arp-cache arp-size allot
meta
arp-cache-entries 1- arp-cache-entry-size * arp-cache +
target
constant arp-cache-last
[ELSE]
arp-cache-entry-size arp-cache-entries * d# 64 max constant arp-size
create arp-cache arp-size allot
arp-cache-entries 1- arp-cache-entry-size * arp-cache + constant arp-cache-last
[THEN]
: arp-foreach \ (func -- )
arp-cache-last 2>r
begin
2r@ swap \ ptr func
execute
r> dup arp-cache-entry-size - >r
arp-cache =
until
2r> 2drop
;
build-debug? [IF]
: arp-.
dup @ hex4 space \ age
dup 2+ dup @ swap d# 2 + dup @ swap d# 2 + @ ethaddr-pretty space
d# 8 + 2@ ip-pretty
cr
;
: arp-dump
['] arp-. arp-foreach
;
[THEN]
: arp-del h# ff swap ! ;
: arp-reset ['] arp-del arp-foreach ;
: used? @ h# ff <> ;
: arp-age-1 dup used? d# 1 and swap +! ;
: arp-age ['] arp-age-1 arp-foreach ;
: arp-cmp ( ptr0 ptr1 -- ptr) over @ over @ > ?: ;
: arp-oldest \ return the address of the oldest ARP entry
arp-cache ['] arp-cmp arp-foreach ;
\ ARP offsets
\ d# 28 sender ethaddr
\ d# 34 sender ip
\ d# 38 target ethaddr
\ d# 44 target ip
d# 20 constant OFFSET_ARP_OPCODE
d# 22 constant OFFSET_ARP_SRC_ETH
d# 28 constant OFFSET_ARP_SRC_IP
d# 32 constant OFFSET_ARP_DST_ETH
d# 38 constant OFFSET_ARP_DST_IP
: arp-is-response
OFFSET_ETH_TYPE packet@ h# 806 =
OFFSET_ARP_OPCODE packet@ d# 2 =
and
;
\ write the current arp response into the cache, replacing the oldest entry
: !-- \ ( val ptr -- ptr-2 )
tuck \ ptr val ptr
!
2-
;
\ Current packet is an ARP response; write it to the given slot in the ARP cache, ageing all others
: arp-cache-write \ ( ptr -- )
arp-age \ because this new entry will have age d# 0
d# 0 over ! \ age d# 0
>r
d# 3 OFFSET_ARP_SRC_ETH mac-inoffset mac@n
r@ d# 6 + !-- !-- !-- drop
d# 2 OFFSET_ARP_SRC_IP mac-inoffset mac@n
r> d# 8 + 2!
;
\ Comparison of IP
: arp-cmpip \ (ip01 ip23 ptr/0 ptr -- ip01 ip23 ptr)
dup used? if
dup d# 8 + 2@ d# 2 2pick d<> ?:
else
drop
then
;
: arp-cache-find ( ip01 ip23 -- ip01 ip23 ptr )
\ Find an IP. Zero if the IP was not found in the cache, ptr to entry otherwise
d# 0 ['] arp-cmpip arp-foreach ;
: arp-issue-whohas \ (ip01 ip23 -- ptr)
mac-pkt-begin
ethaddr-broadcast mac-pkt-3,
net-my-mac mac-pkt-3,
h# 806 \ frame type
d# 1 \ hard type
h# 800 \ prot type
mac-pkt-3,
h# 0604 \ hard size, prot size
d# 1 \ op (1=request)
mac-pkt-2,
net-my-mac mac-pkt-3,
net-my-ip mac-pkt-2,
ethaddr-broadcast mac-pkt-3,
mac-pkt-2,
mac-pkt-complete drop
mac-send
;
\ Look up ethaddr for given IP.
\ If found, return pointer to the 6-byte ethaddr
\ If not found, issue an ARP request and return d# 0.
: arp-lookup \ ( ip01 ip23 -- ptr)
2dup
ip-router 2@ dxor ip-subnetmask 2@ dand
d0<>
if
2drop
ip-router 2@
then
arp-cache-find \ ip01 ip23 ptr
dup 0= if
-rot \ d# 0 ip01 ip23
arp-issue-whohas \ d# 0
else
nip nip 2+ \ ptr
then
;
\ If the current packet is an ARP request for our IP, answer it
: arp-responder
\ is destination ff:ff:ff:ff:ff:ff or my mac
d# 3 OFFSET_ETH_DST mac-inoffset mac@n
and and invert 0=
net-my-mac \ a b c
d# 2 OFFSET_ETH_DST 2+ mac-inoffset mac@n
d= swap \ F a
OFFSET_ETH_DST packet@ = and
or
OFFSET_ETH_TYPE packet@ h# 806 = and
\ is target IP mine?
d# 2 OFFSET_ARP_DST_IP mac-inoffset mac@n net-my-ip d= and
if
mac-pkt-begin
d# 3 OFFSET_ARP_SRC_ETH mac-pkt-src
net-my-mac mac-pkt-3,
h# 806 \ frame type
d# 1 \ hard type
h# 800 \ prot type
mac-pkt-3,
h# 0604 \ hard size, prot size
d# 2 \ op (2=reply)
mac-pkt-2,
net-my-mac mac-pkt-3,
net-my-ip mac-pkt-2,
d# 3 OFFSET_ARP_SRC_ETH mac-pkt-src
d# 2 OFFSET_ARP_SRC_IP mac-pkt-src
mac-pkt-complete drop
mac-send
then
;
: arp-announce
mac-pkt-begin
ethaddr-broadcast mac-pkt-3,
net-my-mac mac-pkt-3,
h# 806 \ frame type
d# 1 \ hard type
h# 800 \ prot type
mac-pkt-3,
h# 0604 \ hard size, prot size
d# 2 \ op (2=reply)
mac-pkt-2,
net-my-mac mac-pkt-3,
net-my-ip mac-pkt-2,
ethaddr-broadcast mac-pkt-3,
net-my-ip mac-pkt-2,
mac-pkt-complete drop
mac-send
;
: arp-handler
arp-responder
arp-is-response
if
d# 2 OFFSET_ARP_SRC_IP mac-inoffset mac@n
arp-cache-find nip nip
dup 0= if
drop arp-oldest
then
arp-cache-write
then
;
]module