* Copyright (c) 1982, 1986, 1988 Regents of the University of California.
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
* @(#)if_ether.c 7.8 (Berkeley) %G%
* 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
);
int useloopback
= 1; /* use loopback interface for local traffic */
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
;
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 */
if (destip
->s_addr
== ac
->ac_ipaddr
.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
);
* 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 */
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
));
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!! sent from ethernet address",
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
);
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
);
ea
= mtod(mcopy
, struct ether_arp
*);
ea
->arp_pro
= htons(ETHERTYPE_IPTRAILERS
);
(*ac
->ac_if
.if_output
)(&ac
->ac_if
, mcopy
, &sa
);
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
;
if (ar
->arp_pa
.sa_family
!= AF_INET
||
ar
->arp_ha
.sa_family
!= AF_UNSPEC
)
sin
= (struct sockaddr_in
*)&ar
->arp_pa
;
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
,
ar
->arp_flags
= at
->at_flags
;