Changeset View
Changeset View
Standalone View
Standalone View
sys/netinet/if_ether.c
Show First 20 Lines • Show All 60 Lines • ▼ Show 20 Lines | |||||
#include <net/ethernet.h> | #include <net/ethernet.h> | ||||
#include <net/route.h> | #include <net/route.h> | ||||
#include <net/vnet.h> | #include <net/vnet.h> | ||||
#include <netinet/in.h> | #include <netinet/in.h> | ||||
#include <netinet/in_fib.h> | #include <netinet/in_fib.h> | ||||
#include <netinet/in_var.h> | #include <netinet/in_var.h> | ||||
#include <net/if_llatbl.h> | #include <net/if_llatbl.h> | ||||
#include <net/if_llatbl_var.h> | |||||
#include <netinet/if_ether.h> | #include <netinet/if_ether.h> | ||||
#ifdef INET | #ifdef INET | ||||
#include <netinet/ip_carp.h> | #include <netinet/ip_carp.h> | ||||
#endif | #endif | ||||
#include <security/mac/mac_framework.h> | #include <security/mac/mac_framework.h> | ||||
#define SIN(s) ((const struct sockaddr_in *)(s)) | #define SIN(s) ((const struct sockaddr_in *)(s)) | ||||
static struct timeval arp_lastlog; | static struct timeval arp_lastlog; | ||||
static int arp_curpps; | static int arp_curpps; | ||||
static int arp_maxpps = 1; | static int arp_maxpps = 1; | ||||
/* Simple ARP state machine */ | |||||
enum arp_llinfo_state { | |||||
ARP_LLINFO_INCOMPLETE = 0, /* No LLE data */ | |||||
ARP_LLINFO_REACHABLE, /* LLE is valid */ | |||||
ARP_LLINFO_VERIFY, /* LLE is valid, need refresh */ | |||||
ARP_LLINFO_DELETED, /* LLE is deleted */ | |||||
}; | |||||
SYSCTL_DECL(_net_link_ether); | SYSCTL_DECL(_net_link_ether); | ||||
static SYSCTL_NODE(_net_link_ether, PF_INET, inet, CTLFLAG_RW, 0, ""); | static SYSCTL_NODE(_net_link_ether, PF_INET, inet, CTLFLAG_RW, 0, ""); | ||||
static SYSCTL_NODE(_net_link_ether, PF_ARP, arp, CTLFLAG_RW, 0, ""); | static SYSCTL_NODE(_net_link_ether, PF_ARP, arp, CTLFLAG_RW, 0, ""); | ||||
/* timer values */ | /* timer values */ | ||||
static VNET_DEFINE(int, arpt_keep) = (20*60); /* once resolved, good for 20 | VNET_DEFINE(int, arpt_keep) = (20*60); /* once resolved, good for 20 minutes */ | ||||
* minutes */ | VNET_DEFINE(int, arp_maxtries) = 5; | ||||
static VNET_DEFINE(int, arp_maxtries) = 5; | VNET_DEFINE(int, arp_proxyall) = 0; | ||||
static VNET_DEFINE(int, arp_proxyall) = 0; | VNET_DEFINE(int, arpt_down) = 20; /* keep incomplete entries for 20 sec */ | ||||
static VNET_DEFINE(int, arpt_down) = 20; /* keep incomplete entries for | VNET_DEFINE(int, arpt_rexmit) = 1; /* retransmit arp entries, sec*/ | ||||
* 20 seconds */ | |||||
static VNET_DEFINE(int, arpt_rexmit) = 1; /* retransmit arp entries, sec*/ | |||||
VNET_PCPUSTAT_DEFINE(struct arpstat, arpstat); /* ARP statistics, see if_arp.h */ | VNET_PCPUSTAT_DEFINE(struct arpstat, arpstat); /* ARP statistics, see if_arp.h */ | ||||
VNET_PCPUSTAT_SYSINIT(arpstat); | VNET_PCPUSTAT_SYSINIT(arpstat); | ||||
#ifdef VIMAGE | #ifdef VIMAGE | ||||
VNET_PCPUSTAT_SYSUNINIT(arpstat); | VNET_PCPUSTAT_SYSUNINIT(arpstat); | ||||
#endif /* VIMAGE */ | #endif /* VIMAGE */ | ||||
static VNET_DEFINE(int, arp_maxhold) = 1; | VNET_DEFINE(int, arp_maxhold) = 1; | ||||
#define V_arpt_keep VNET(arpt_keep) | |||||
#define V_arpt_down VNET(arpt_down) | |||||
#define V_arpt_rexmit VNET(arpt_rexmit) | |||||
#define V_arp_maxtries VNET(arp_maxtries) | |||||
#define V_arp_proxyall VNET(arp_proxyall) | |||||
#define V_arp_maxhold VNET(arp_maxhold) | |||||
SYSCTL_INT(_net_link_ether_inet, OID_AUTO, max_age, CTLFLAG_VNET | CTLFLAG_RW, | SYSCTL_INT(_net_link_ether_inet, OID_AUTO, max_age, CTLFLAG_VNET | CTLFLAG_RW, | ||||
&VNET_NAME(arpt_keep), 0, | &VNET_NAME(arpt_keep), 0, | ||||
"ARP entry lifetime in seconds"); | "ARP entry lifetime in seconds"); | ||||
SYSCTL_INT(_net_link_ether_inet, OID_AUTO, maxtries, CTLFLAG_VNET | CTLFLAG_RW, | SYSCTL_INT(_net_link_ether_inet, OID_AUTO, maxtries, CTLFLAG_VNET | CTLFLAG_RW, | ||||
&VNET_NAME(arp_maxtries), 0, | &VNET_NAME(arp_maxtries), 0, | ||||
"ARP resolution attempts before returning error"); | "ARP resolution attempts before returning error"); | ||||
SYSCTL_INT(_net_link_ether_inet, OID_AUTO, proxyall, CTLFLAG_VNET | CTLFLAG_RW, | SYSCTL_INT(_net_link_ether_inet, OID_AUTO, proxyall, CTLFLAG_VNET | CTLFLAG_RW, | ||||
&VNET_NAME(arp_proxyall), 0, | &VNET_NAME(arp_proxyall), 0, | ||||
Show All 14 Lines | |||||
#define ARP_LOG(pri, ...) do { \ | #define ARP_LOG(pri, ...) do { \ | ||||
if (ppsratecheck(&arp_lastlog, &arp_curpps, arp_maxpps)) \ | if (ppsratecheck(&arp_lastlog, &arp_curpps, arp_maxpps)) \ | ||||
log((pri), "arp: " __VA_ARGS__); \ | log((pri), "arp: " __VA_ARGS__); \ | ||||
} while (0) | } while (0) | ||||
static void arp_init(void); | static void arp_init(void); | ||||
static void arpintr(struct mbuf *); | static void arpintr(struct mbuf *); | ||||
static void arptimer(void *); | |||||
#ifdef INET | #ifdef INET | ||||
static void in_arpinput(struct mbuf *); | static void in_arpinput(struct mbuf *); | ||||
#endif | #endif | ||||
static void arp_check_update_lle(struct arphdr *ah, struct in_addr isaddr, | static void arp_check_update_lle(struct arphdr *ah, struct in_addr isaddr, | ||||
struct ifnet *ifp, int bridged, struct llentry *la); | struct ifnet *ifp, int bridged, struct llentry *la); | ||||
static void arp_mark_lle_reachable(struct llentry *la); | |||||
static void arp_iflladdr(void *arg __unused, struct ifnet *ifp); | |||||
static eventhandler_tag iflladdr_tag; | |||||
static const struct netisr_handler arp_nh = { | static const struct netisr_handler arp_nh = { | ||||
.nh_name = "arp", | .nh_name = "arp", | ||||
.nh_handler = arpintr, | .nh_handler = arpintr, | ||||
.nh_proto = NETISR_ARP, | .nh_proto = NETISR_ARP, | ||||
.nh_policy = NETISR_POLICY_SOURCE, | .nh_policy = NETISR_POLICY_SOURCE, | ||||
}; | }; | ||||
/* | /* | ||||
* Timeout routine. Age arp_tab entries periodically. | |||||
*/ | |||||
static void | |||||
arptimer(void *arg) | |||||
{ | |||||
struct llentry *lle = (struct llentry *)arg; | |||||
struct ifnet *ifp; | |||||
int r_skip_req; | |||||
if (lle->la_flags & LLE_STATIC) { | |||||
return; | |||||
} | |||||
LLE_WLOCK(lle); | |||||
if (callout_pending(&lle->lle_timer)) { | |||||
/* | |||||
* Here we are a bit odd here in the treatment of | |||||
* active/pending. If the pending bit is set, it got | |||||
* rescheduled before I ran. The active | |||||
* bit we ignore, since if it was stopped | |||||
* in ll_tablefree() and was currently running | |||||
* it would have return 0 so the code would | |||||
* not have deleted it since the callout could | |||||
* not be stopped so we want to go through | |||||
* with the delete here now. If the callout | |||||
* was restarted, the pending bit will be back on and | |||||
* we just want to bail since the callout_reset would | |||||
* return 1 and our reference would have been removed | |||||
* by arpresolve() below. | |||||
*/ | |||||
LLE_WUNLOCK(lle); | |||||
return; | |||||
} | |||||
ifp = lle->lle_tbl->llt_ifp; | |||||
CURVNET_SET(ifp->if_vnet); | |||||
switch (lle->ln_state) { | |||||
case ARP_LLINFO_REACHABLE: | |||||
/* | |||||
* Expiration time is approaching. | |||||
* Let's try to refresh entry if it is still | |||||
* in use. | |||||
* | |||||
* Set r_skip_req to get feedback from | |||||
* fast path. Change state and re-schedule | |||||
* ourselves. | |||||
*/ | |||||
LLE_REQ_LOCK(lle); | |||||
lle->r_skip_req = 1; | |||||
LLE_REQ_UNLOCK(lle); | |||||
lle->ln_state = ARP_LLINFO_VERIFY; | |||||
callout_schedule(&lle->lle_timer, hz * V_arpt_rexmit); | |||||
LLE_WUNLOCK(lle); | |||||
CURVNET_RESTORE(); | |||||
return; | |||||
case ARP_LLINFO_VERIFY: | |||||
LLE_REQ_LOCK(lle); | |||||
r_skip_req = lle->r_skip_req; | |||||
LLE_REQ_UNLOCK(lle); | |||||
if (r_skip_req == 0 && lle->la_preempt > 0) { | |||||
/* Entry was used, issue refresh request */ | |||||
struct in_addr dst; | |||||
dst = lle->r_l3addr.addr4; | |||||
lle->la_preempt--; | |||||
callout_schedule(&lle->lle_timer, hz * V_arpt_rexmit); | |||||
LLE_WUNLOCK(lle); | |||||
arprequest(ifp, NULL, &dst, NULL); | |||||
CURVNET_RESTORE(); | |||||
return; | |||||
} | |||||
/* Nothing happened. Reschedule if not too late */ | |||||
if (lle->la_expire > time_uptime) { | |||||
callout_schedule(&lle->lle_timer, hz * V_arpt_rexmit); | |||||
LLE_WUNLOCK(lle); | |||||
CURVNET_RESTORE(); | |||||
return; | |||||
} | |||||
break; | |||||
case ARP_LLINFO_INCOMPLETE: | |||||
case ARP_LLINFO_DELETED: | |||||
break; | |||||
} | |||||
if ((lle->la_flags & LLE_DELETED) == 0) { | |||||
int evt; | |||||
if (lle->la_flags & LLE_VALID) | |||||
evt = LLENTRY_EXPIRED; | |||||
else | |||||
evt = LLENTRY_TIMEDOUT; | |||||
EVENTHANDLER_INVOKE(lle_event, lle, evt); | |||||
} | |||||
callout_stop(&lle->lle_timer); | |||||
/* XXX: LOR avoidance. We still have ref on lle. */ | |||||
LLE_WUNLOCK(lle); | |||||
IF_AFDATA_LOCK(ifp); | |||||
LLE_WLOCK(lle); | |||||
/* Guard against race with other llentry_free(). */ | |||||
if (lle->la_flags & LLE_LINKED) { | |||||
LLE_REMREF(lle); | |||||
lltable_unlink_entry(lle->lle_tbl, lle); | |||||
} | |||||
IF_AFDATA_UNLOCK(ifp); | |||||
size_t pkts_dropped = llentry_free(lle); | |||||
ARPSTAT_ADD(dropped, pkts_dropped); | |||||
ARPSTAT_INC(timeouts); | |||||
CURVNET_RESTORE(); | |||||
} | |||||
/* | |||||
* Stores link-layer header for @ifp in format suitable for if_output() | * Stores link-layer header for @ifp in format suitable for if_output() | ||||
* into buffer @buf. Resulting header length is stored in @bufsize. | * into buffer @buf. Resulting header length is stored in @bufsize. | ||||
* | * | ||||
* Returns 0 on success. | * Returns 0 on success. | ||||
*/ | */ | ||||
static int | static int | ||||
arp_fillheader(struct ifnet *ifp, struct arphdr *ah, int bcast, u_char *buf, | arp_fillheader(struct ifnet *ifp, struct arphdr *ah, int bcast, u_char *buf, | ||||
size_t *bufsize) | size_t *bufsize) | ||||
▲ Show 20 Lines • Show All 108 Lines • ▼ Show 20 Lines | #endif | ||||
ro.ro_flags = 0; | ro.ro_flags = 0; | ||||
m->m_flags |= M_BCAST; | m->m_flags |= M_BCAST; | ||||
m_clrprotoflags(m); /* Avoid confusing lower layers. */ | m_clrprotoflags(m); /* Avoid confusing lower layers. */ | ||||
(*ifp->if_output)(ifp, m, &sa, &ro); | (*ifp->if_output)(ifp, m, &sa, &ro); | ||||
ARPSTAT_INC(txrequests); | ARPSTAT_INC(txrequests); | ||||
} | } | ||||
/* | /* | ||||
* Resolve an IP address into an ethernet address - heavy version. | |||||
* Used internally by arpresolve(). | |||||
* We have already checked than we can't use existing lle without | |||||
* modification so we have to acquire LLE_EXCLUSIVE lle lock. | |||||
* | |||||
* On success, desten and flags are filled in and the function returns 0; | |||||
* If the packet must be held pending resolution, we return EWOULDBLOCK | |||||
* On other errors, we return the corresponding error code. | |||||
* Note that m_freem() handles NULL. | |||||
*/ | |||||
static int | |||||
arpresolve_full(struct ifnet *ifp, int is_gw, int flags, struct mbuf *m, | |||||
const struct sockaddr *dst, u_char *desten, uint32_t *pflags) | |||||
{ | |||||
struct llentry *la = NULL, *la_tmp; | |||||
struct mbuf *curr = NULL; | |||||
struct mbuf *next = NULL; | |||||
int error, renew; | |||||
char *lladdr; | |||||
int ll_len; | |||||
if (pflags != NULL) | |||||
*pflags = 0; | |||||
if ((flags & LLE_CREATE) == 0) { | |||||
IF_AFDATA_RLOCK(ifp); | |||||
la = lla_lookup(LLTABLE(ifp), LLE_EXCLUSIVE, dst); | |||||
IF_AFDATA_RUNLOCK(ifp); | |||||
} | |||||
if (la == NULL && (ifp->if_flags & (IFF_NOARP | IFF_STATICARP)) == 0) { | |||||
la = lltable_alloc_entry(LLTABLE(ifp), 0, dst); | |||||
if (la == NULL) { | |||||
log(LOG_DEBUG, | |||||
"arpresolve: can't allocate llinfo for %s on %s\n", | |||||
inet_ntoa(SIN(dst)->sin_addr), if_name(ifp)); | |||||
m_freem(m); | |||||
return (EINVAL); | |||||
} | |||||
IF_AFDATA_WLOCK(ifp); | |||||
LLE_WLOCK(la); | |||||
la_tmp = lla_lookup(LLTABLE(ifp), LLE_EXCLUSIVE, dst); | |||||
/* Prefer ANY existing lle over newly-created one */ | |||||
if (la_tmp == NULL) | |||||
lltable_link_entry(LLTABLE(ifp), la); | |||||
IF_AFDATA_WUNLOCK(ifp); | |||||
if (la_tmp != NULL) { | |||||
lltable_free_entry(LLTABLE(ifp), la); | |||||
la = la_tmp; | |||||
} | |||||
} | |||||
if (la == NULL) { | |||||
m_freem(m); | |||||
return (EINVAL); | |||||
} | |||||
if ((la->la_flags & LLE_VALID) && | |||||
((la->la_flags & LLE_STATIC) || la->la_expire > time_uptime)) { | |||||
if (flags & LLE_ADDRONLY) { | |||||
lladdr = la->ll_addr; | |||||
ll_len = ifp->if_addrlen; | |||||
} else { | |||||
lladdr = la->r_linkdata; | |||||
ll_len = la->r_hdrlen; | |||||
} | |||||
bcopy(lladdr, desten, ll_len); | |||||
/* Check if we have feedback request from arptimer() */ | |||||
if (la->r_skip_req != 0) { | |||||
LLE_REQ_LOCK(la); | |||||
la->r_skip_req = 0; /* Notify that entry was used */ | |||||
LLE_REQ_UNLOCK(la); | |||||
} | |||||
if (pflags != NULL) | |||||
*pflags = la->la_flags & (LLE_VALID|LLE_IFADDR); | |||||
LLE_WUNLOCK(la); | |||||
return (0); | |||||
} | |||||
renew = (la->la_asked == 0 || la->la_expire != time_uptime); | |||||
/* | |||||
* There is an arptab entry, but no ethernet address | |||||
* response yet. Add the mbuf to the list, dropping | |||||
* the oldest packet if we have exceeded the system | |||||
* setting. | |||||
*/ | |||||
if (m != NULL) { | |||||
if (la->la_numheld >= V_arp_maxhold) { | |||||
if (la->la_hold != NULL) { | |||||
next = la->la_hold->m_nextpkt; | |||||
m_freem(la->la_hold); | |||||
la->la_hold = next; | |||||
la->la_numheld--; | |||||
ARPSTAT_INC(dropped); | |||||
} | |||||
} | |||||
if (la->la_hold != NULL) { | |||||
curr = la->la_hold; | |||||
while (curr->m_nextpkt != NULL) | |||||
curr = curr->m_nextpkt; | |||||
curr->m_nextpkt = m; | |||||
} else | |||||
la->la_hold = m; | |||||
la->la_numheld++; | |||||
} | |||||
/* | |||||
* Return EWOULDBLOCK if we have tried less than arp_maxtries. It | |||||
* will be masked by ether_output(). Return EHOSTDOWN/EHOSTUNREACH | |||||
* if we have already sent arp_maxtries ARP requests. Retransmit the | |||||
* ARP request, but not faster than one request per second. | |||||
*/ | |||||
if (la->la_asked < V_arp_maxtries) | |||||
error = EWOULDBLOCK; /* First request. */ | |||||
else | |||||
error = is_gw != 0 ? EHOSTUNREACH : EHOSTDOWN; | |||||
if (renew) { | |||||
int canceled; | |||||
LLE_ADDREF(la); | |||||
la->la_expire = time_uptime; | |||||
canceled = callout_reset(&la->lle_timer, hz * V_arpt_down, | |||||
arptimer, la); | |||||
if (canceled) | |||||
LLE_REMREF(la); | |||||
la->la_asked++; | |||||
LLE_WUNLOCK(la); | |||||
arprequest(ifp, NULL, &SIN(dst)->sin_addr, NULL); | |||||
return (error); | |||||
} | |||||
LLE_WUNLOCK(la); | |||||
return (error); | |||||
} | |||||
/* | |||||
* Resolve an IP address into an ethernet address. | |||||
*/ | |||||
int | |||||
arpresolve_addr(struct ifnet *ifp, int flags, const struct sockaddr *dst, | |||||
char *desten, uint32_t *pflags) | |||||
{ | |||||
int error; | |||||
flags |= LLE_ADDRONLY; | |||||
error = arpresolve_full(ifp, 0, flags, NULL, dst, desten, pflags); | |||||
return (error); | |||||
} | |||||
/* | |||||
* Lookups link header based on an IP address. | |||||
* On input: | |||||
* ifp is the interface we use | |||||
* is_gw != 0 if @dst represents gateway to some destination | |||||
* m is the mbuf. May be NULL if we don't have a packet. | |||||
* dst is the next hop, | |||||
* desten is the storage to put LL header. | |||||
* flags returns subset of lle flags: LLE_VALID | LLE_IFADDR | |||||
* | |||||
* On success, full/partial link header and flags are filled in and | |||||
* the function returns 0. | |||||
* If the packet must be held pending resolution, we return EWOULDBLOCK | |||||
* On other errors, we return the corresponding error code. | |||||
* Note that m_freem() handles NULL. | |||||
*/ | |||||
int | |||||
arpresolve(struct ifnet *ifp, int is_gw, struct mbuf *m, | |||||
const struct sockaddr *dst, u_char *desten, uint32_t *pflags) | |||||
{ | |||||
struct llentry *la = 0; | |||||
if (pflags != NULL) | |||||
*pflags = 0; | |||||
if (m != NULL) { | |||||
if (m->m_flags & M_BCAST) { | |||||
/* broadcast */ | |||||
(void)memcpy(desten, | |||||
ifp->if_broadcastaddr, ifp->if_addrlen); | |||||
return (0); | |||||
} | |||||
if (m->m_flags & M_MCAST) { | |||||
/* multicast */ | |||||
ETHER_MAP_IP_MULTICAST(&SIN(dst)->sin_addr, desten); | |||||
return (0); | |||||
} | |||||
} | |||||
IF_AFDATA_RLOCK(ifp); | |||||
la = lla_lookup(LLTABLE(ifp), LLE_UNLOCKED, dst); | |||||
if (la != NULL && (la->r_flags & RLLE_VALID) != 0) { | |||||
/* Entry found, let's copy lle info */ | |||||
bcopy(la->r_linkdata, desten, la->r_hdrlen); | |||||
if (pflags != NULL) | |||||
*pflags = LLE_VALID | (la->r_flags & RLLE_IFADDR); | |||||
/* Check if we have feedback request from arptimer() */ | |||||
if (la->r_skip_req != 0) { | |||||
LLE_REQ_LOCK(la); | |||||
la->r_skip_req = 0; /* Notify that entry was used */ | |||||
LLE_REQ_UNLOCK(la); | |||||
} | |||||
IF_AFDATA_RUNLOCK(ifp); | |||||
return (0); | |||||
} | |||||
IF_AFDATA_RUNLOCK(ifp); | |||||
return (arpresolve_full(ifp, is_gw, la == NULL ? LLE_CREATE : 0, m, dst, | |||||
desten, pflags)); | |||||
} | |||||
/* | |||||
* Common length and type checks are done here, | * Common length and type checks are done here, | ||||
* then the protocol-specific routine is called. | * then the protocol-specific routine is called. | ||||
*/ | */ | ||||
static void | static void | ||||
arpintr(struct mbuf *m) | arpintr(struct mbuf *m) | ||||
{ | { | ||||
struct arphdr *ar; | struct arphdr *ar; | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
▲ Show 20 Lines • Show All 571 Lines • ▼ Show 20 Lines | for (; m_hold != NULL; m_hold = m_hold_next) { | ||||
/* Avoid confusing lower layers. */ | /* Avoid confusing lower layers. */ | ||||
m_clrprotoflags(m_hold); | m_clrprotoflags(m_hold); | ||||
(*ifp->if_output)(ifp, m_hold, &sa, NULL); | (*ifp->if_output)(ifp, m_hold, &sa, NULL); | ||||
} | } | ||||
} else | } else | ||||
LLE_WUNLOCK(la); | LLE_WUNLOCK(la); | ||||
} | } | ||||
static void | |||||
arp_mark_lle_reachable(struct llentry *la) | |||||
{ | |||||
int canceled, wtime; | |||||
LLE_WLOCK_ASSERT(la); | |||||
la->ln_state = ARP_LLINFO_REACHABLE; | |||||
EVENTHANDLER_INVOKE(lle_event, la, LLENTRY_RESOLVED); | |||||
if (!(la->la_flags & LLE_STATIC)) { | |||||
LLE_ADDREF(la); | |||||
la->la_expire = time_uptime + V_arpt_keep; | |||||
wtime = V_arpt_keep - V_arp_maxtries * V_arpt_rexmit; | |||||
if (wtime < 0) | |||||
wtime = V_arpt_keep; | |||||
canceled = callout_reset(&la->lle_timer, | |||||
hz * wtime, arptimer, la); | |||||
if (canceled) | |||||
LLE_REMREF(la); | |||||
} | |||||
la->la_asked = 0; | |||||
la->la_preempt = V_arp_maxtries; | |||||
} | |||||
/* | |||||
* Add pernament link-layer record for given interface address. | |||||
*/ | |||||
static __noinline void | |||||
arp_add_ifa_lle(struct ifnet *ifp, const struct sockaddr *dst) | |||||
{ | |||||
struct llentry *lle, *lle_tmp; | |||||
/* | |||||
* Interface address LLE record is considered static | |||||
* because kernel code relies on LLE_STATIC flag to check | |||||
* if these entries can be rewriten by arp updates. | |||||
*/ | |||||
lle = lltable_alloc_entry(LLTABLE(ifp), LLE_IFADDR | LLE_STATIC, dst); | |||||
if (lle == NULL) { | |||||
log(LOG_INFO, "arp_ifinit: cannot create arp " | |||||
"entry for interface address\n"); | |||||
return; | |||||
} | |||||
IF_AFDATA_WLOCK(ifp); | |||||
LLE_WLOCK(lle); | |||||
/* Unlink any entry if exists */ | |||||
lle_tmp = lla_lookup(LLTABLE(ifp), LLE_EXCLUSIVE, dst); | |||||
if (lle_tmp != NULL) | |||||
lltable_unlink_entry(LLTABLE(ifp), lle_tmp); | |||||
lltable_link_entry(LLTABLE(ifp), lle); | |||||
IF_AFDATA_WUNLOCK(ifp); | |||||
if (lle_tmp != NULL) | |||||
EVENTHANDLER_INVOKE(lle_event, lle_tmp, LLENTRY_EXPIRED); | |||||
EVENTHANDLER_INVOKE(lle_event, lle, LLENTRY_RESOLVED); | |||||
LLE_WUNLOCK(lle); | |||||
if (lle_tmp != NULL) | |||||
lltable_free_entry(LLTABLE(ifp), lle_tmp); | |||||
} | |||||
void | void | ||||
arp_ifinit(struct ifnet *ifp, struct ifaddr *ifa) | |||||
{ | |||||
const struct sockaddr_in *dst_in; | |||||
const struct sockaddr *dst; | |||||
if (ifa->ifa_carp != NULL) | |||||
return; | |||||
dst = ifa->ifa_addr; | |||||
dst_in = (const struct sockaddr_in *)dst; | |||||
if (ntohl(dst_in->sin_addr.s_addr) == INADDR_ANY) | |||||
return; | |||||
arp_announce_ifaddr(ifp, dst_in->sin_addr, IF_LLADDR(ifp)); | |||||
arp_add_ifa_lle(ifp, dst); | |||||
} | |||||
void | |||||
arp_announce_ifaddr(struct ifnet *ifp, struct in_addr addr, u_char *enaddr) | arp_announce_ifaddr(struct ifnet *ifp, struct in_addr addr, u_char *enaddr) | ||||
{ | { | ||||
if (ntohl(addr.s_addr) != INADDR_ANY) | if (ntohl(addr.s_addr) != INADDR_ANY) | ||||
arprequest(ifp, &addr, &addr, enaddr); | arprequest(ifp, &addr, &addr, enaddr); | ||||
} | } | ||||
/* | |||||
* Sends gratuitous ARPs for each ifaddr to notify other | |||||
* nodes about the address change. | |||||
*/ | |||||
static __noinline void | |||||
arp_handle_ifllchange(struct ifnet *ifp) | |||||
{ | |||||
struct ifaddr *ifa; | |||||
TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { | |||||
if (ifa->ifa_addr->sa_family == AF_INET) | |||||
arp_ifinit(ifp, ifa); | |||||
} | |||||
} | |||||
/* | |||||
* A handler for interface link layer address change event. | |||||
*/ | |||||
static void | static void | ||||
arp_iflladdr(void *arg __unused, struct ifnet *ifp) | |||||
{ | |||||
lltable_update_ifaddr(LLTABLE(ifp)); | |||||
if ((ifp->if_flags & IFF_UP) != 0) | |||||
arp_handle_ifllchange(ifp); | |||||
} | |||||
static void | |||||
arp_init(void) | arp_init(void) | ||||
{ | { | ||||
netisr_register(&arp_nh); | netisr_register(&arp_nh); | ||||
if (IS_DEFAULT_VNET(curvnet)) | lle4_init(); | ||||
iflladdr_tag = EVENTHANDLER_REGISTER(iflladdr_event, | |||||
arp_iflladdr, NULL, EVENTHANDLER_PRI_ANY); | |||||
} | } | ||||
SYSINIT(arp, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY, arp_init, 0); | SYSINIT(arp, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY, arp_init, 0); |