* Copyright (c) 1982, 1986, 1988 Regents of the University of California.
* %sccs.include.redist.c%
* @(#)if_ether.c 7.27 (Berkeley) %G%
* Ethernet address resolution protocol.
* add "inuse/lock" bit (or ref. count) along with valid bit
#include <netinet/in_systm.h>
#include <netinet/in_var.h>
#include <netinet/if_ether.h>
#define SIN(s) ((struct sockaddr_in *)s)
#define SDL(s) ((struct sockaddr_dl *)s)
#define SRP(s) ((struct sockaddr_inarp *)s)
* ARP trailer negotiation. Trailer protocol is not IP specific,
* but ARP request/response use IP addresses.
#define ETHERTYPE_IPTRAILERS ETHERTYPE_TRAIL
int arpt_prune
= (5*60*1); /* walk list every 5 minutes */
int arpt_keep
= (20*60); /* once resolved, good for 20 more minutes */
int arpt_down
= 20; /* once declared down, don't send for 20 secs */
#define rt_expire rt_rmx.rmx_expire
static void arprequest
__P((struct arpcom
*, u_long
*, u_long
*, u_char
*));
static void arptfree
__P((struct llinfo_arp
*));
static struct llinfo_arp
*arplookup
__P((u_long
, int, int));
static void arpcatchme
__P(());
extern struct ifnet loif
;
extern struct timeval time
;
struct llinfo_arp llinfo_arp
= {&llinfo_arp
, &llinfo_arp
};
struct ifqueue arpintrq
= {0, 0, 0, 50};
int arp_inuse
, arp_allocated
, arp_intimer
;
int useloopback
= 1; /* use loopback interface for local traffic */
* Timeout routine. Age arp_tab entries periodically.
register struct llinfo_arp
*la
= llinfo_arp
.la_next
;
timeout(arptimer
, (caddr_t
)0, arpt_prune
* hz
);
while (la
!= &llinfo_arp
) {
register struct rtentry
*rt
= la
->la_rt
;
if (rt
->rt_expire
&& rt
->rt_expire
<= time
.tv_sec
)
arptfree(la
->la_prev
); /* timer has expired, clear */
* Parallel to llc_rtrequest.
arp_rtrequest(req
, rt
, sa
)
register struct rtentry
*rt
;
register struct sockaddr
*gate
= rt
->rt_gateway
;
register struct llinfo_arp
*la
= (struct llinfo_arp
*)rt
->rt_llinfo
;
static struct sockaddr_dl null_sdl
= {sizeof(null_sdl
), AF_LINK
};
timeout(arptimer
, (caddr_t
)0, hz
);
if (rt
->rt_flags
& RTF_GATEWAY
)
* XXX: If this is a manually added route to interface
* such as older version of routed or gated might provide,
if ((rt
->rt_flags
& RTF_HOST
) == 0 &&
SIN(rt_mask(rt
))->sin_addr
.s_addr
!= 0xffffffff)
rt
->rt_flags
|= RTF_CLONING
;
if (rt
->rt_flags
& RTF_CLONING
) {
* Case 1: This route should come from a route to iface.
rt_setgate(rt
, rt_key(rt
), &null_sdl
);
SDL(gate
)->sdl_type
= rt
->rt_ifp
->if_type
;
SDL(gate
)->sdl_index
= rt
->rt_ifp
->if_index
;
rt
->rt_expire
= time
.tv_sec
;
/* Announce a new entry if requested. */
if (rt
->rt_flags
& RTF_ANNOUNCE
)
arprequest((struct arpcom
*)rt
->rt_ifp
,
&SIN(rt_key(rt
))->sin_addr
.s_addr
,
&SIN(rt_key(rt
))->sin_addr
.s_addr
,
(u_char
*)LLADDR(SDL(gate
)));
if (gate
->sa_family
!= AF_LINK
||
gate
->sa_len
< sizeof(null_sdl
)) {
log(LOG_DEBUG
, "arp_rtrequest: bad gateway value");
SDL(gate
)->sdl_type
= rt
->rt_ifp
->if_type
;
SDL(gate
)->sdl_index
= rt
->rt_ifp
->if_index
;
break; /* This happens on a route change */
* Case 2: This route may come from cloning, or a manual route
R_Malloc(la
, struct llinfo_arp
*, sizeof(*la
));
rt
->rt_llinfo
= (caddr_t
)la
;
log(LOG_DEBUG
, "arp_rtrequest: malloc failed\n");
arp_inuse
++, arp_allocated
++;
rt
->rt_flags
|= RTF_LLINFO
;
if (SIN(rt_key(rt
))->sin_addr
.s_addr
==
(IA_SIN(rt
->rt_ifa
))->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" and remove
* the route to force traffic out to the hardware.
Bcopy(((struct arpcom
*)rt
->rt_ifp
)->ac_enaddr
,
LLADDR(SDL(gate
)), SDL(gate
)->sdl_alen
= 6);
rt
->rt_flags
&= ~RTF_LLINFO
;
* Broadcast an ARP packet, asking who has addr on interface ac.
register struct arpcom
*ac
;
register struct in_addr
*addr
;
arprequest(ac
, &ac
->ac_ipaddr
.s_addr
, &addr
->s_addr
, ac
->ac_enaddr
);
* Broadcast an ARP request. Caller specifies:
* - arp header source ip address
* - arp header target ip address
* - arp header source ethernet address
arprequest(ac
, sip
, tip
, enaddr
)
register struct arpcom
*ac
;
register u_long
*sip
, *tip
;
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
)enaddr
, (caddr_t
)ea
->arp_sha
, sizeof(ea
->arp_sha
));
bcopy((caddr_t
)sip
, (caddr_t
)ea
->arp_spa
, sizeof(ea
->arp_spa
));
bcopy((caddr_t
)tip
, (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.
arpresolve(ac
, rt
, m
, dst
, desten
)
register struct arpcom
*ac
;
register struct rtentry
*rt
;
register struct sockaddr
*dst
;
register struct llinfo_arp
*la
;
if (m
->m_flags
& M_BCAST
) { /* broadcast */
bcopy((caddr_t
)etherbroadcastaddr
, (caddr_t
)desten
,
sizeof(etherbroadcastaddr
));
if (m
->m_flags
& M_MCAST
) { /* multicast */
ETHER_MAP_IP_MULTICAST(&SIN(dst
)->sin_addr
, desten
);
la
= (struct llinfo_arp
*)rt
->rt_llinfo
;
if (la
= arplookup(SIN(dst
)->sin_addr
.s_addr
, 1, 0))
if (la
== 0 || rt
== 0) {
log(LOG_DEBUG
, "arpresolve: can't allocate llinfo");
sdl
= SDL(rt
->rt_gateway
);
* Check the address family and length is valid, the address
* is resolved; otherwise, try to resolve.
if ((rt
->rt_expire
== 0 || rt
->rt_expire
> time
.tv_sec
) &&
sdl
->sdl_family
== AF_LINK
&& sdl
->sdl_alen
!= 0) {
bcopy(LLADDR(sdl
), desten
, sdl
->sdl_alen
);
* There is an arptab entry, but no ethernet address
* response yet. Replace the held mbuf with this
rt
->rt_flags
&= ~RTF_REJECT
;
if (la
->la_asked
== 0 || rt
->rt_expire
!= time
.tv_sec
) {
rt
->rt_expire
= time
.tv_sec
;
if (la
->la_asked
++ < arp_maxtries
)
arpwhohas(ac
, &(SIN(dst
)->sin_addr
));
rt
->rt_flags
|= RTF_REJECT
;
rt
->rt_expire
+= arpt_down
;
* Common length and type checks are done here,
* then the protocol-specific routine is called.
register struct arphdr
*ar
;
while (arpintrq
.ifq_head
) {
IF_DEQUEUE(&arpintrq
, m
);
if (m
== 0 || (m
->m_flags
& M_PKTHDR
) == 0)
if (m
->m_len
>= sizeof(struct arphdr
) &&
(ar
= mtod(m
, struct arphdr
*)) &&
ntohs(ar
->ar_hrd
) == ARPHRD_ETHER
&&
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 no longer handle negotiations for use of trailer protocol:
* Formerly, ARP replied for protocol type ETHERTYPE_TRAIL sent
* along with IP replies if we wanted trailers sent to us,
* and also sent them in response to IP replies.
* This allowed either end to announce the desire to receive
* We no longer reply to requests for ETHERTYPE_TRAIL protocol either,
* but formerly didn't normally send requests.
register struct ether_arp
*ea
;
register struct arpcom
*ac
= (struct arpcom
*)m
->m_pkthdr
.rcvif
;
register struct llinfo_arp
*la
= 0;
register struct rtentry
*rt
;
struct in_ifaddr
*ia
, *maybe_ia
= 0;
struct in_addr isaddr
, itaddr
, myaddr
;
ea
= mtod(m
, struct ether_arp
*);
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
));
la
= arplookup(isaddr
.s_addr
, itaddr
.s_addr
== myaddr
.s_addr
, 0);
if (la
&& (rt
= la
->la_rt
) && (sdl
= SDL(rt
->rt_gateway
))) {
bcmp((caddr_t
)ea
->arp_sha
, LLADDR(sdl
), sdl
->sdl_alen
))
log(LOG_INFO
, "arp info overwritten for %x by %s\n",
isaddr
.s_addr
, ether_sprintf(ea
->arp_sha
));
bcopy((caddr_t
)ea
->arp_sha
, LLADDR(sdl
),
sdl
->sdl_alen
= sizeof(ea
->arp_sha
));
rt
->rt_expire
= time
.tv_sec
+ arpt_keep
;
rt
->rt_flags
&= ~RTF_REJECT
;
(*ac
->ac_if
.if_output
)(&ac
->ac_if
, la
->la_hold
,
if (op
!= ARPOP_REQUEST
) {
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
,
la
= arplookup(itaddr
.s_addr
, 0, SIN_PROXY
);
bcopy((caddr_t
)ea
->arp_sha
, (caddr_t
)ea
->arp_tha
,
sdl
= SDL(rt
->rt_gateway
);
bcopy(LLADDR(sdl
), (caddr_t
)ea
->arp_sha
, sizeof(ea
->arp_sha
));
bcopy((caddr_t
)ea
->arp_spa
, (caddr_t
)ea
->arp_tpa
, sizeof(ea
->arp_spa
));
bcopy((caddr_t
)&itaddr
, (caddr_t
)ea
->arp_spa
, sizeof(ea
->arp_spa
));
ea
->arp_op
= htons(ARPOP_REPLY
);
ea
->arp_pro
= htons(ETHERTYPE_IP
); /* let's be sure! */
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);
register struct llinfo_arp
*la
;
register struct rtentry
*rt
= la
->la_rt
;
register struct sockaddr_dl
*sdl
;
if (rt
->rt_refcnt
> 0 && (sdl
= SDL(rt
->rt_gateway
)) &&
sdl
->sdl_family
== AF_LINK
) {
rt
->rt_flags
&= ~RTF_REJECT
;
rtrequest(RTM_DELETE
, rt_key(rt
), (struct sockaddr
*)0, rt_mask(rt
),
0, (struct rtentry
**)0);
* Lookup or enter a new address in arptab.
static struct llinfo_arp
*
arplookup(addr
, create
, proxy
)
register struct rtentry
*rt
;
static struct sockaddr_inarp sin
= {sizeof(sin
), AF_INET
};
sin
.sin_addr
.s_addr
= addr
;
sin
.sin_other
= proxy
? SIN_PROXY
: 0;
rt
= rtalloc1((struct sockaddr
*)&sin
, create
);
if ((rt
->rt_flags
& RTF_GATEWAY
) || (rt
->rt_flags
& RTF_LLINFO
) == 0 ||
rt
->rt_gateway
->sa_family
!= AF_LINK
) {
log(LOG_DEBUG
, "arptnew failed on %x\n", ntohl(addr
));
return ((struct llinfo_arp
*)rt
->rt_llinfo
);