/* if_ether.c 6.5 84/07/08 */
* Ethernet address resolution protocol.
#include "../netinet/in.h"
#include "../netinet/in_systm.h"
#include "../netinet/ip.h"
#include "../netinet/if_ether.h"
#define ARPTAB_BSIZ 5 /* 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 */
((short)((((a) >> 16) ^ (a)) & 0x7fff) % 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) \
int arpt_age
; /* aging timer */
#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 */
struct ether_addr etherbroadcastaddr
= {{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }};
extern struct ifnet loif
;
* Local addresses in the range oldmap to infinity are
* mapped according to the old mapping scheme. That is,
* mapping of Internet to Ethernet addresses is performed
* by taking the high three bytes of the network interface's
* address and the low three bytes of the local address part.
* This only allows boards from the same manufacturer to
* communicate unless the on-board address is overridden
* (not possible in many manufacture's hardware).
* NB: setting oldmap to zero completely disables ARP
* (i.e. identical to setting IFF_NOARP with an ioctl).
* Timeout routine. Age arp_tab entries once a minute.
register struct arptab
*at
;
timeout(arptimer
, (caddr_t
)0, hz
);
if (++arpt_age
> ARPT_AGE
) {
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_get(M_DONTWAIT
, MT_DATA
)) == NULL
)
m
->m_off
= MMAXOFF
- m
->m_len
;
ea
= mtod(m
, struct ether_arp
*);
eh
= (struct ether_header
*)sa
.sa_data
;
bzero((caddr_t
)ea
, sizeof (*ea
));
eh
->ether_dhost
= etherbroadcastaddr
;
eh
->ether_type
= ETHERPUP_ARPTYPE
; /* if_output will swap */
ea
->arp_hrd
= htons(ARPHRD_ETHER
);
ea
->arp_pro
= htons(ETHERPUP_IPTYPE
);
ea
->arp_hln
= sizeof arp_sha(ea
); /* hardware address length */
ea
->arp_pln
= sizeof arp_spa(ea
); /* protocol address length */
ea
->arp_op
= htons(ARPOP_REQUEST
);
arp_sha(ea
) = ac
->ac_enaddr
;
arp_spa(ea
) = ((struct sockaddr_in
*)&ac
->ac_if
.if_addr
)->sin_addr
;
sa
.sa_family
= AF_UNSPEC
;
return ((*ac
->ac_if
.if_output
)(&ac
->ac_if
, m
, &sa
));
* Resolve an IP address into an ethernet address. If success,
* desten is filled in and 1 is returned. If there is no entry
* in arptab, set one up and broadcast a request
* for the IP address; return 0. Hold onto this mbuf and
* resend it once the address is finally resolved.
* We do some (conservative) locking here at splimp, since
* arptab is also altered from input interrupt service (ecintr/ilintr
* calls arpinput when ETHERPUP_ARPTYPE packets come in).
arpresolve(ac
, m
, destip
, desten
)
register struct arpcom
*ac
;
register struct in_addr
*destip
;
register struct ether_addr
*desten
;
register struct arptab
*at
;
register struct ifnet
*ifp
;
if (lna
== INADDR_ANY
) { /* broadcast address */
*desten
= etherbroadcastaddr
;
/* if for us, then use software loopback driver */
((struct sockaddr_in
*)&ifp
->if_addr
)-> sin_addr
.s_addr
&&
(loif
.if_flags
& IFF_UP
)) {
sin
.sin_family
= AF_INET
;
(void) looutput(&loif
, m
, (struct sockaddr
*)&sin
);
* We really don't want to indicate failure,
* but the packet has already been sent and freed.
ARPTAB_LOOK(at
, destip
->s_addr
);
if (at
== 0) { /* not found */
if ((ifp
->if_flags
& IFF_NOARP
) || lna
>= oldmap
) {
desten
->ether_addr_octet
[3] = (lna
>> 16) & 0x7f;
desten
->ether_addr_octet
[4] = (lna
>> 8) & 0xff;
desten
->ether_addr_octet
[5] = lna
& 0xff;
at
->at_timer
= 0; /* restart the timer */
if (at
->at_flags
& ATF_COM
) { /* entry IS complete */
* There is an arptab entry, but no ethernet address
* response yet. Replace the held mbuf with this
arpwhohas(ac
, destip
); /* ask again */
* Called from ecintr/ilintr when ether packet type ETHERPUP_ARP
* is received. Algorithm is that given in RFC 826.
* In addition, a sanity check is performed on the sender
* protocol address, to catch impersonators.
register struct arpcom
*ac
;
register struct ether_arp
*ea
;
register struct arptab
*at
= 0; /* same as "merge" flag */
struct in_addr isaddr
,itaddr
,myaddr
;
if (m
->m_len
< sizeof *ea
)
if (ac
->ac_if
.if_flags
& IFF_NOARP
)
myaddr
= ((struct sockaddr_in
*)&ac
->ac_if
.if_addr
)->sin_addr
;
ea
= mtod(m
, struct ether_arp
*);
if (ntohs(ea
->arp_pro
) != ETHERPUP_IPTYPE
)
if (!bcmp((caddr_t
)&arp_sha(ea
), (caddr_t
)&ac
->ac_enaddr
,
goto out
; /* it's from me, ignore it. */
if (isaddr
.s_addr
== myaddr
.s_addr
) {
printf("duplicate IP address!! sent from ethernet address: ");
printf("%x %x %x %x %x %x\n", ea
->arp_xsha
[0], ea
->arp_xsha
[1],
ea
->arp_xsha
[2], ea
->arp_xsha
[3],
ea
->arp_xsha
[4], ea
->arp_xsha
[5]);
if (ntohs(ea
->arp_op
) == ARPOP_REQUEST
)
ARPTAB_LOOK(at
, isaddr
.s_addr
);
if (at
) { /* XXX ? - can overwrite ATF_PERM */
at
->at_enaddr
= arp_sha(ea
);
sin
.sin_family
= AF_INET
;
(*ac
->ac_if
.if_output
)(&ac
->ac_if
,
mhold
, (struct sockaddr
*)&sin
);
} else if (itaddr
.s_addr
== myaddr
.s_addr
) {
/* ensure we have a table entry */
at
->at_enaddr
= arp_sha(ea
);
if (ntohs(ea
->arp_op
) != ARPOP_REQUEST
)
ARPTAB_LOOK(at
, itaddr
.s_addr
);
if (itaddr
.s_addr
!= myaddr
.s_addr
)
goto out
; /* if I am not the target */
at
->at_enaddr
= ac
->ac_enaddr
;
if (itaddr
.s_addr
!= myaddr
.s_addr
&& (at
->at_flags
& ATF_PUBL
) == 0)
arp_tha(ea
) = arp_sha(ea
);
arp_tpa(ea
) = arp_spa(ea
);
arp_sha(ea
) = at
->at_enaddr
;
ea
->arp_op
= htons(ARPOP_REPLY
);
eh
= (struct ether_header
*)sa
.sa_data
;
eh
->ether_dhost
= arp_tha(ea
);
eh
->ether_type
= ETHERPUP_ARPTYPE
;
sa
.sa_family
= AF_UNSPEC
;
(*ac
->ac_if
.if_output
)(&ac
->ac_if
, m
, &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).
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 (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 (if_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
);
at
->at_enaddr
= *(struct ether_addr
*)ar
->arp_ha
.sa_data
;
at
->at_flags
= ATF_COM
| ATF_INUSE
|
(ar
->arp_flags
& (ATF_PERM
|ATF_PUBL
));
case SIOCDARP
: /* delete entry */
case SIOCGARP
: /* get entry */
*(struct ether_addr
*)ar
->arp_ha
.sa_data
= at
->at_enaddr
;
ar
->arp_flags
= at
->at_flags
;