* Copyright (c) 1982, 1986, 1988 Regents of the University of California.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* @(#)if_ether.c 7.13 (Berkeley) 10/31/90
* Ethernet address resolution protocol.
* run at splnet (add ARP protocol intr.)
* link entries onto hash chains, keep free list
* add "inuse/lock" bit (or ref. count) along with valid bit
#define ARPTAB_BSIZ 16 /* bucket size */
#define ARPTAB_NB 37 /* number of buckets */
#define ARPTAB_BSIZ 9 /* bucket size */
#define ARPTAB_NB 19 /* number of buckets */
#define ARPTAB_SIZE (ARPTAB_BSIZ * ARPTAB_NB)
struct arptab arptab
[ARPTAB_SIZE
];
int arptab_size
= ARPTAB_SIZE
; /* for arp command */
* ARP trailer negotiation. Trailer protocol is not IP specific,
* but ARP request/response use IP addresses.
#define ETHERTYPE_IPTRAILERS ETHERTYPE_TRAIL
((u_long)(a) % ARPTAB_NB)
#define ARPTAB_LOOK(at,addr) { \
at = &arptab[ARPTAB_HASH(addr) * ARPTAB_BSIZ]; \
for (n = 0 ; n < ARPTAB_BSIZ ; n++,at++) \
if (at->at_iaddr.s_addr == addr) \
#define ARPT_AGE (60*1) /* aging timer, 1 min. */
#define ARPT_KILLC 20 /* kill completed entry in 20 mins. */
#define ARPT_KILLI 3 /* kill incomplete entry in 3 minutes */
extern struct ifnet loif
;
* Timeout routine. Age arp_tab entries once a minute.
register struct arptab
*at
;
timeout(arptimer
, (caddr_t
)0, ARPT_AGE
* hz
);
for (i
= 0; i
< ARPTAB_SIZE
; i
++, at
++) {
if (at
->at_flags
== 0 || (at
->at_flags
& ATF_PERM
))
if (++at
->at_timer
< ((at
->at_flags
&ATF_COM
) ?
ARPT_KILLC
: ARPT_KILLI
))
/* timer has expired, clear entry */
* Broadcast an ARP packet, asking who has addr on interface ac.
register struct arpcom
*ac
;
register struct ether_header
*eh
;
register struct ether_arp
*ea
;
if ((m
= m_gethdr(M_DONTWAIT
, MT_DATA
)) == NULL
)
m
->m_pkthdr
.len
= sizeof(*ea
);
MH_ALIGN(m
, sizeof(*ea
));
ea
= mtod(m
, struct ether_arp
*);
eh
= (struct ether_header
*)sa
.sa_data
;
bzero((caddr_t
)ea
, sizeof (*ea
));
bcopy((caddr_t
)etherbroadcastaddr
, (caddr_t
)eh
->ether_dhost
,
sizeof(eh
->ether_dhost
));
eh
->ether_type
= ETHERTYPE_ARP
; /* if_output will swap */
ea
->arp_hrd
= htons(ARPHRD_ETHER
);
ea
->arp_pro
= htons(ETHERTYPE_IP
);
ea
->arp_hln
= sizeof(ea
->arp_sha
); /* hardware address length */
ea
->arp_pln
= sizeof(ea
->arp_spa
); /* protocol address length */
ea
->arp_op
= htons(ARPOP_REQUEST
);
bcopy((caddr_t
)ac
->ac_enaddr
, (caddr_t
)ea
->arp_sha
,
bcopy((caddr_t
)&ac
->ac_ipaddr
, (caddr_t
)ea
->arp_spa
,
bcopy((caddr_t
)addr
, (caddr_t
)ea
->arp_tpa
, sizeof(ea
->arp_tpa
));
sa
.sa_family
= AF_UNSPEC
;
(*ac
->ac_if
.if_output
)(&ac
->ac_if
, m
, &sa
, (struct rtentry
*)0);
int useloopback
= 1; /* use loopback interface for local traffic */
* Resolve an IP address into an ethernet address. If success,
* desten is filled in. If there is no entry in arptab,
* set one up and broadcast a request for the IP address.
* Hold onto this mbuf and resend it once the address
* is finally resolved. A return value of 1 indicates
* that desten has been filled in and the packet should be sent
* normally; a 0 return indicates that the packet has been
* taken over here, either now or for later transmission.
* We do some (conservative) locking here at splimp, since
* arptab is also altered from input interrupt service (ecintr/ilintr
* calls arpinput when ETHERTYPE_ARP packets come in).
arpresolve(ac
, m
, destip
, desten
, usetrailers
)
register struct arpcom
*ac
;
register struct in_addr
*destip
;
register struct arptab
*at
;
register struct in_ifaddr
*ia
;
if (m
->m_flags
& M_BCAST
) { /* broadcast */
bcopy((caddr_t
)etherbroadcastaddr
, (caddr_t
)desten
,
sizeof(etherbroadcastaddr
));
/* if for us, use software loopback driver if up */
for (ia
= in_ifaddr
; ia
; ia
= ia
->ia_next
)
if ((ia
->ia_ifp
== &ac
->ac_if
) &&
(destip
->s_addr
== ia
->ia_addr
.sin_addr
.s_addr
)) {
* if (loif.if_flags & IFF_UP)
* It allowed local traffic to be forced
* through the hardware by configuring the loopback down.
* However, it causes problems during network configuration
* for boards that can't receive packets they send.
* It is now necessary to clear "useloopback"
* to force traffic out to the hardware.
sin
.sin_family
= AF_INET
;
(void) looutput(&loif
, m
, (struct sockaddr
*)&sin
, 0);
* The packet has already been sent and freed.
bcopy((caddr_t
)ac
->ac_enaddr
, (caddr_t
)desten
,
ARPTAB_LOOK(at
, destip
->s_addr
);
if (at
== 0) { /* not found */
if (ac
->ac_if
.if_flags
& IFF_NOARP
) {
bcopy((caddr_t
)ac
->ac_enaddr
, (caddr_t
)desten
, 3);
desten
[3] = (lna
>> 16) & 0x7f;
desten
[4] = (lna
>> 8) & 0xff;
panic("arpresolve: no free entry");
at
->at_timer
= 0; /* restart the timer */
if (at
->at_flags
& ATF_COM
) { /* entry IS complete */
bcopy((caddr_t
)at
->at_enaddr
, (caddr_t
)desten
,
if (at
->at_flags
& ATF_USETRAILERS
)
* There is an arptab entry, but no ethernet address
* response yet. Replace the held mbuf with this
arpwhohas(ac
, destip
); /* ask again */
* Called from 10 Mb/s Ethernet interrupt handlers
* when ether packet type ETHERTYPE_ARP
* is received. Common length and type checks are done here,
* then the protocol-specific routine is called.
register struct arphdr
*ar
;
if (ac
->ac_if
.if_flags
& IFF_NOARP
)
if (m
->m_len
< sizeof(struct arphdr
))
ar
= mtod(m
, struct arphdr
*);
if (ntohs(ar
->ar_hrd
) != ARPHRD_ETHER
)
if (m
->m_len
< sizeof(struct arphdr
) + 2 * ar
->ar_hln
+ 2 * ar
->ar_pln
)
switch (ntohs(ar
->ar_pro
)) {
case ETHERTYPE_IPTRAILERS
:
* ARP for Internet protocols on 10 Mb/s Ethernet.
* Algorithm is that given in RFC 826.
* In addition, a sanity check is performed on the sender
* protocol address, to catch impersonators.
* We also handle negotiations for use of trailer protocol:
* ARP replies for protocol type ETHERTYPE_TRAIL are sent
* along with IP replies if we want trailers sent to us,
* and also send them in response to IP replies.
* This allows either end to announce the desire to receive
* We reply to requests for ETHERTYPE_TRAIL protocol as well,
* but don't normally send requests.
register struct arpcom
*ac
;
register struct ether_arp
*ea
;
register struct arptab
*at
; /* same as "merge" flag */
register struct in_ifaddr
*ia
;
struct in_ifaddr
*maybe_ia
= 0;
struct in_addr isaddr
, itaddr
, myaddr
;
int proto
, op
, s
, completed
= 0;
ea
= mtod(m
, struct ether_arp
*);
proto
= ntohs(ea
->arp_pro
);
bcopy((caddr_t
)ea
->arp_spa
, (caddr_t
)&isaddr
, sizeof (isaddr
));
bcopy((caddr_t
)ea
->arp_tpa
, (caddr_t
)&itaddr
, sizeof (itaddr
));
for (ia
= in_ifaddr
; ia
; ia
= ia
->ia_next
)
if (ia
->ia_ifp
== &ac
->ac_if
) {
if ((itaddr
.s_addr
== ia
->ia_addr
.sin_addr
.s_addr
) ||
(isaddr
.s_addr
== ia
->ia_addr
.sin_addr
.s_addr
))
myaddr
= ia
? ia
->ia_addr
.sin_addr
: maybe_ia
->ia_addr
.sin_addr
;
if (!bcmp((caddr_t
)ea
->arp_sha
, (caddr_t
)ac
->ac_enaddr
,
goto out
; /* it's from me, ignore it. */
if (!bcmp((caddr_t
)ea
->arp_sha
, (caddr_t
)etherbroadcastaddr
,
"arp: ether address is broadcast for IP address %x!\n",
if (isaddr
.s_addr
== myaddr
.s_addr
) {
"duplicate IP address %x!! sent from ethernet address: %s\n",
ntohl(isaddr
.s_addr
), ether_sprintf(ea
->arp_sha
));
ARPTAB_LOOK(at
, isaddr
.s_addr
);
bcopy((caddr_t
)ea
->arp_sha
, (caddr_t
)at
->at_enaddr
,
if ((at
->at_flags
& ATF_COM
) == 0)
sin
.sin_family
= AF_INET
;
(*ac
->ac_if
.if_output
)(&ac
->ac_if
, at
->at_hold
,
(struct sockaddr
*)&sin
, (struct rtentry
*)0);
if (at
== 0 && itaddr
.s_addr
== myaddr
.s_addr
) {
/* ensure we have a table entry */
if (at
= arptnew(&isaddr
)) {
bcopy((caddr_t
)ea
->arp_sha
, (caddr_t
)at
->at_enaddr
,
case ETHERTYPE_IPTRAILERS
:
/* partner says trailers are OK */
at
->at_flags
|= ATF_USETRAILERS
;
* Reply to request iff we want trailers.
if (op
!= ARPOP_REQUEST
|| ac
->ac_if
.if_flags
& IFF_NOTRAILERS
)
* Reply if this is an IP request,
* or if we want to send a trailer response.
* Send the latter only to the IP response
* that completes the current ARP entry.
if (op
!= ARPOP_REQUEST
&&
(completed
== 0 || ac
->ac_if
.if_flags
& IFF_NOTRAILERS
))
if (itaddr
.s_addr
== myaddr
.s_addr
) {
bcopy((caddr_t
)ea
->arp_sha
, (caddr_t
)ea
->arp_tha
,
bcopy((caddr_t
)ac
->ac_enaddr
, (caddr_t
)ea
->arp_sha
,
ARPTAB_LOOK(at
, itaddr
.s_addr
);
if (at
== NULL
|| (at
->at_flags
& ATF_PUBL
) == 0)
bcopy((caddr_t
)ea
->arp_sha
, (caddr_t
)ea
->arp_tha
,
bcopy((caddr_t
)at
->at_enaddr
, (caddr_t
)ea
->arp_sha
,
bcopy((caddr_t
)ea
->arp_spa
, (caddr_t
)ea
->arp_tpa
,
bcopy((caddr_t
)&itaddr
, (caddr_t
)ea
->arp_spa
,
ea
->arp_op
= htons(ARPOP_REPLY
);
* If incoming packet was an IP reply,
* we are sending a reply for type IPTRAILERS.
* If we are sending a reply for type IP
* and we want to receive trailers,
* send a trailer reply as well.
ea
->arp_pro
= htons(ETHERTYPE_IPTRAILERS
);
else if (proto
== ETHERTYPE_IP
&&
(ac
->ac_if
.if_flags
& IFF_NOTRAILERS
) == 0)
mcopy
= m_copy(m
, 0, (int)M_COPYALL
);
eh
= (struct ether_header
*)sa
.sa_data
;
bcopy((caddr_t
)ea
->arp_tha
, (caddr_t
)eh
->ether_dhost
,
sizeof(eh
->ether_dhost
));
eh
->ether_type
= ETHERTYPE_ARP
;
sa
.sa_family
= AF_UNSPEC
;
(*ac
->ac_if
.if_output
)(&ac
->ac_if
, m
, &sa
, (struct rtentry
*)0);
ea
= mtod(mcopy
, struct ether_arp
*);
ea
->arp_pro
= htons(ETHERTYPE_IPTRAILERS
);
(*ac
->ac_if
.if_output
)(&ac
->ac_if
,
mcopy
, &sa
, (struct rtentry
*)0);
register struct arptab
*at
;
at
->at_timer
= at
->at_flags
= 0;
* Enter a new address in arptab, pushing out the oldest entry
* from the bucket if there is no room.
* This always succeeds since no bucket can be completely filled
* with permanent entries (except from arpioctl when testing whether
* another permanent entry will fit).
* MUST BE CALLED AT SPLIMP.
register struct arptab
*at
, *ato
= NULL
;
timeout(arptimer
, (caddr_t
)0, hz
);
at
= &arptab
[ARPTAB_HASH(addr
->s_addr
) * ARPTAB_BSIZ
];
for (n
= 0; n
< ARPTAB_BSIZ
; n
++,at
++) {
goto out
; /* found an empty entry */
if (at
->at_flags
& ATF_PERM
)
if ((int) at
->at_timer
> oldest
) {
at
->at_flags
= ATF_INUSE
;
register struct arpreq
*ar
= (struct arpreq
*)data
;
register struct arptab
*at
;
register struct sockaddr_in
*sin
;
sin
= (struct sockaddr_in
*)&ar
->arp_ha
;
#if defined(COMPAT_43) && BYTE_ORDER != BIG_ENDIAN
if (sin
->sin_family
== 0 && sin
->sin_len
< 16)
sin
->sin_family
= sin
->sin_len
;
sin
->sin_len
= sizeof(ar
->arp_ha
);
sin
= (struct sockaddr_in
*)&ar
->arp_pa
;
#if defined(COMPAT_43) && BYTE_ORDER != BIG_ENDIAN
if (sin
->sin_family
== 0 && sin
->sin_len
< 16)
sin
->sin_family
= sin
->sin_len
;
sin
->sin_len
= sizeof(ar
->arp_pa
);
if (ar
->arp_pa
.sa_family
!= AF_INET
||
ar
->arp_ha
.sa_family
!= AF_UNSPEC
)
ARPTAB_LOOK(at
, sin
->sin_addr
.s_addr
);
if (at
== NULL
) { /* not found */
if (ifa_ifwithnet(&ar
->arp_pa
) == NULL
) {
case SIOCSARP
: /* set entry */
at
= arptnew(&sin
->sin_addr
);
if (ar
->arp_flags
& ATF_PERM
) {
/* never make all entries in a bucket permanent */
register struct arptab
*tat
;
tat
= arptnew(&sin
->sin_addr
);
bcopy((caddr_t
)ar
->arp_ha
.sa_data
, (caddr_t
)at
->at_enaddr
,
at
->at_flags
= ATF_COM
| ATF_INUSE
|
(ar
->arp_flags
& (ATF_PERM
|ATF_PUBL
|ATF_USETRAILERS
));
case SIOCDARP
: /* delete entry */
case SIOCGARP
: /* get entry */
bcopy((caddr_t
)at
->at_enaddr
, (caddr_t
)ar
->arp_ha
.sa_data
,
*(u_short
*)&ar
->arp_ha
= ar
->arp_ha
.sa_family
;
ar
->arp_flags
= at
->at_flags
;