\ ========== Copyright Header Begin ==========================================
\ Hypervisor Software File: arp.fth
\ Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
\ - Do no alter or remove copyright notices
\ - Redistribution and use of this software in source and binary forms, with
\ or without modification, are permitted provided that the following
\ - Redistribution of source code must retain the above copyright notice,
\ this list of conditions and the following disclaimer.
\ - Redistribution in binary form must reproduce the above copyright notice,
\ this list of conditions and the following disclaimer in the
\ documentation and/or other materials provided with the distribution.
\ Neither the name of Sun Microsystems, Inc. or the names of contributors
\ may be used to endorse or promote products derived from this software
\ without specific prior written permission.
\ This software is provided "AS IS," without a warranty of any kind.
\ ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
\ INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
\ PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
\ MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
\ ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
\ DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN
\ OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR
\ FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
\ DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
\ ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
\ SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
\ You acknowledge that this software is not designed, licensed or
\ intended for use in the design, construction, operation or maintenance of
\ ========== Copyright Header End ============================================
id: @(#)arp.fth 1.1 04/09/07
copyright: Copyright 2004 Sun Microsystems, Inc. All Rights Reserved
copyright: Use is subject to license terms.
\ RFC 826: Ethernet Address Resolution Protocol
fload ${BP}/pkg/netinet/arp-h.fth
\ The ARP cache maintains recent mappings of IP to hardware addresses.
\ Unresolved entries maintain a queue of datagrams to be sent when the
\ address is resolved. We only queue the most recent datagram to be
\ sent to a specified destination while that address is being resolved.
\ Enqueue datagram waiting for ARP resolution.
: arpq-enqueue ( arpentry pkt -- )
swap >ae-pktq dup queue-empty? 0= if ( pkt queue )
dup pkt-dequeue pkt-free ( pkt queue )
\ Send datagrams waiting for ARP resolution.
: arpq-send ( arpentry -- )
dup >ae-pktq swap >ae-hwaddr ( queue hwaddr )
begin over pkt-dequeue ?dup while ( queue hwaddr pkt )
2dup ip-len@ swap IP_TYPE if-output drop ( queue hwaddr )
\ Destroy queued datagrams
>ae-pktq begin dup pkt-dequeue ?dup while pkt-free repeat drop
\ Allocate an entry in the ARP table. Choose an unused entry if one
\ exists. Otherwise, entries are replaced in a round-robin fashion.
0 instance value ae-next \ Next entry to replace/write
: arp-alloc-entry ( -- entry )
ae-next index>arp-entry ae-state@ AE_FREE = if leave then
ae-next 1+ ARP_TABLE_SIZE mod to ae-next
ae-next index>arp-entry ( entry )
dup ae-state@ AE_PENDING = if ( entry )
dup >ae-pktq queue-init ( entry )
AE_PENDING over ae-state! ( entry )
0 over >ae-attempts l! ( entry )
ae-next 1+ ARP_TABLE_SIZE mod to ae-next ( entry )
\ Add a RESOLVED entry to the ARP cache.
: arp-add-entry ( ipaddr hwaddr -- )
arp-alloc-entry ( ipaddr hwaddr entry )
tuck >ae-hwaddr copy-hw-addr ( ipaddr entry )
tuck >ae-ipaddr copy-ip-addr ( entry )
ARP_ENTRY_TTL over >ae-timer swap set-timer ( entry )
AE_RESOLVED swap ae-state! ( )
\ Mark an entry as FREE. Free queued datagram, if any.
: arp-free-entry ( entry -- )
dup ae-state@ AE_FREE <> if ( entry )
AE_FREE swap ae-state! ( )
\ Find ARP entry for specified protocol address.
: arp-find-entry ( ipaddr -- entry )
ARP_TABLE_SIZE 0 do ( ipaddr )
i index>arp-entry dup ae-state@ AE_FREE <> if ( ipaddr adr )
2dup >ae-ipaddr ip= if ( ipaddr adr )
nip unloop exit ( entry )
\ Handle ARP table updates. If a translation for the sender's IP address
\ already exists, update the sender's hardware address and "refresh"
\ the cache entry; if this address was pending resolution, mark the
\ cache entry as RESOLVED and send any queued packet. Else, create
\ an entry for the sender if we are the target of the request.
: arptable-update ( pkt -- )
dup >arp-spa arp-find-entry ?dup if ( pkt entry )
swap >arp-sha over >ae-hwaddr copy-hw-addr ( entry )
dup >ae-timer ARP_ENTRY_TTL set-timer ( entry )
dup ae-state@ AE_PENDING = if ( entry )
AE_RESOLVED over ae-state! ( entry )
dup >arp-tpa my-ip-addr ip= if ( pkt )
dup >arp-spa over >arp-sha arp-add-entry ( pkt )
: arp-packet-ok? ( pkt len -- ok? )
over >arp-hwtype ntohw@ if-htype@ = if ( pkt len )
over >arp-prtype ntohw@ IP_TYPE = if ( pkt len )
nip /arp-packet >= exit ( ok? )
\ ARP input packet processing. Respond to incoming ARP requests, and
\ handle ARP cache updates.
: arp-input ( pkt len -- )
over swap arp-packet-ok? 0= if pkt-free exit then ( pkt )
\ Process each packet for ARP cache updates. If address spoofing
\ is detected, an error is logged and a reply is sent even if
\ this host was not the target.
dup >arp-spa my-ip-addr ip= if ( pkt )
my-ip-addr inaddr-any? 0= if
dup >arp-sha if-showaddr ." is also using " my-ip-addr .ipaddr cr
my-ip-addr over >arp-tpa copy-ip-addr
\ If this is an ARP request and this host is the target, send a
\ response. Dont respond to ARP requests until our IP address
my-ip-addr inaddr-any? if pkt-free exit then ( pkt )
dup >arp-tpa my-ip-addr ip<> if pkt-free exit then
dup >arp-op ntohw@ ARP_REQ <> if pkt-free exit then
ARP_REPLY over >arp-op htonw! ( pkt )
dup >arp-sha over >arp-tha copy-hw-addr ( pkt )
if-hwaddr over >arp-sha copy-hw-addr ( pkt )
dup >arp-spa over >arp-tpa copy-ip-addr ( pkt )
my-ip-addr over >arp-spa copy-ip-addr ( pkt )
/arp-packet over >arp-tha ARP_TYPE if-output drop ( )
['] arp-input to (arp-input)
\ Common code to construct and transmit ARP and RARP packets.
: send-arp/rarp-packet ( target.ip target.ha arp.op type -- )
pkt-alloc ?dup 0= if 2drop 2drop exit then ( tpa tha op type pkt )
swap >r ( tpa tha op pkt )
if-htype@ over >arp-hwtype htonw! ( tpa tha op pkt )
IP_TYPE over >arp-prtype htonw! ( tpa tha op pkt )
if-addrlen@ over >arp-hwlen c! ( tpa tha op pkt )
/ip-addr over >arp-prlen c! ( tpa tha op pkt )
if-hwaddr over >arp-sha copy-hw-addr ( tpa tha op pkt )
my-ip-addr over >arp-spa copy-ip-addr ( tpa tha op pkt )
tuck >arp-op htonw! ( tpa tha pkt )
tuck >arp-tha copy-hw-addr ( tpa pkt )
tuck >arp-tpa copy-ip-addr ( pkt )
/arp-packet if-broadcast r> if-output drop ( ) ( r: )
\ Transmit an ARP request for the sought IP address.
: arprequest ( ipaddr -- )
if-broadcast ARP_REQ ARP_TYPE send-arp/rarp-packet
\ ARP address resolution. If a translation exists, the interface layer
\ can send the packet. If not, initiate an ARP query and queue this
\ packet for transmission once the address is resolved. If the destination
\ is pending address resolution, only the most recent datagram is held
: arp-resolve ( pkt ipaddr -- hwaddr true | false )
dup ip=broadcast? if ( pkt ipaddr )
2drop if-broadcast true exit ( hwaddr true )
dup arp-find-entry ?dup if ( pkt ipaddr entry )
nip dup ae-state@ AE_RESOLVED = if ( pkt entry )
nip >ae-hwaddr true ( hwaddr true )
swap arpq-enqueue false ( false )
then ( hwaddr true | false )
arp-alloc-entry ( pkt ipaddr entry )
2dup >ae-ipaddr copy-ip-addr ( pkt ipaddr entry )
rot over swap arpq-enqueue ( ipaddr entry )
swap arprequest ( entry )
>ae-timer ARP_RETRY_INTERVAL set-timer ( )
then ( hwaddr true | false )
\ Handling timer expiration events. If this entry is pending address
\ resolution, retransmit the ARP request; if the retransmission limit
\ has been reached, deallocate the queued packet and free the entry.
\ Else, if this is a resolved entry, its maximum time-to-live has
\ expired, and the entry is freed.
: ae-do-timer-events ( arpentry -- )
dup >ae-timer timer-expired? 0= if drop exit then ( entry )
dup ae-state@ AE_RESOLVED = if ( entry )
1 over >ae-attempts +! ( entry )
dup >ae-attempts l@ ARP_MAX_RETRIES > if ( entry )
dup >ae-ipaddr arprequest ( entry )
>ae-timer ARP_RETRY_INTERVAL set-timer ( )
\ Periodic ARP cache maintenance. Runs once every second; iterate through
\ ARP cache entries checking for and handling timer expiration events.
/timer instance buffer: arp-event-timer
: arp-do-timer-events ( -- )
arp-event-timer timer-expired? if ( )
arp-event-timer d# 1000 set-timer ( )
i index>arp-entry dup ae-state@ AE_FREE <> if ( arpentry )
dup ae-do-timer-events ( arpentry )
\ Check to see if a given IP address is already in use.
: arp-check ( sought-ip ntries -- in-use? )
get-msecs ARP_RETRY_INTERVAL + ( ip timeout )
over arprequest ( ip timeout )
over arp-find-entry ?dup if ( ip timeout entry )
ae-state@ AE_RESOLVED = if ( ip timeout )
2drop true unloop exit ( true )
dup timed-out? ( ip timeout flag )
loop drop false ( false )
\ Initialize ARP layer state.
ARP_TABLE_SIZE /arp-entry * alloc-mem to arp-table ( )
AE_FREE i index>arp-entry ae-state! ( )
arp-event-timer d# 1000 set-timer ( )
\ Free all ARP resources.
ARP_TABLE_SIZE 0 do i index>arp-entry arp-free-entry loop
arp-table ARP_TABLE_SIZE /arp-entry * free-mem
i index>arp-entry dup ae-state@ case
AE_FREE of ." Free" endof
." Pending " dup >ae-ipaddr .ipaddr
dup >ae-ipaddr .ipaddr 2 spaces
dup >ae-hwaddr if-showaddr