* Split allocation and table linking for lle's.
Before that, the logic besides lle_create() was the following: return existing if found, create if not. This behaviour was error-prone since we had to deal with 'sudden' static<>dynamic lle changes. This commit fixes bunch of different issues like: - refcount leak when lle is converted to static. Simple check case: console 1: while true; do for i in `arp -an|awk '$4~/incomp/{print$2}'|tr -d '()'`; do arp -s $i 00:22:44:66:88:00 ; arp -d $i; done; done console 2: ping -f any-dead-host-in-L2 console 3: # watch for memory consumption: vmstat -m | awk '$1~/lltable/{print$2}' - possible problems in arptimer() / nd6_timer() when dropping/reacquiring lock. New logic explicitly handles use-or-create cases in every lla_create user. Basically, most of the changes are purely mechanical. However, we explicitly avoid using existing lle's for interface/static LLE records. * While here, call lle_event handlers on all real table lle change. * Create lltable_free_entry() calling existing per-lltable lle_free_t callback for entry deletion
This commit is contained in:
parent
5c714b29ff
commit
5a2555160f
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=286955
@ -228,7 +228,7 @@ htable_prefix_free(struct lltable *llt, const struct sockaddr *prefix,
|
||||
IF_AFDATA_WUNLOCK(llt->llt_ifp);
|
||||
|
||||
LIST_FOREACH_SAFE(lle, &pmd.dchain, lle_chain, next)
|
||||
llt->llt_free_entry(llt, lle);
|
||||
lltable_free_entry(llt, lle);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -278,10 +278,13 @@ lltable_drop_entry_queue(struct llentry *lle)
|
||||
}
|
||||
|
||||
/*
|
||||
* Deletes an address from the address table.
|
||||
* This function is called by the timer functions
|
||||
* such as arptimer() and nd6_llinfo_timer(), and
|
||||
* the caller does the locking.
|
||||
*
|
||||
* Performes generic cleanup routines and frees lle.
|
||||
*
|
||||
* Called for non-linked entries, with callouts and
|
||||
* other AF-specific cleanups performed.
|
||||
*
|
||||
* @lle must be passed WLOCK'ed
|
||||
*
|
||||
* Returns the number of held packets, if any, that were dropped.
|
||||
*/
|
||||
@ -316,21 +319,35 @@ struct llentry *
|
||||
llentry_alloc(struct ifnet *ifp, struct lltable *lt,
|
||||
struct sockaddr_storage *dst)
|
||||
{
|
||||
struct llentry *la;
|
||||
struct llentry *la, *la_tmp;
|
||||
|
||||
IF_AFDATA_RLOCK(ifp);
|
||||
la = lla_lookup(lt, LLE_EXCLUSIVE, (struct sockaddr *)dst);
|
||||
IF_AFDATA_RUNLOCK(ifp);
|
||||
if ((la == NULL) &&
|
||||
(ifp->if_flags & (IFF_NOARP | IFF_STATICARP)) == 0) {
|
||||
IF_AFDATA_WLOCK(ifp);
|
||||
la = lla_create(lt, 0, (struct sockaddr *)dst);
|
||||
IF_AFDATA_WUNLOCK(ifp);
|
||||
}
|
||||
|
||||
if (la != NULL) {
|
||||
LLE_ADDREF(la);
|
||||
LLE_WUNLOCK(la);
|
||||
return (la);
|
||||
}
|
||||
|
||||
if ((ifp->if_flags & (IFF_NOARP | IFF_STATICARP)) == 0) {
|
||||
la = lltable_alloc_entry(lt, 0, (struct sockaddr *)dst);
|
||||
if (la == NULL)
|
||||
return (NULL);
|
||||
IF_AFDATA_WLOCK(ifp);
|
||||
LLE_WLOCK(la);
|
||||
/* Prefer any existing LLE over newly-created one */
|
||||
la_tmp = lla_lookup(lt, LLE_EXCLUSIVE, (struct sockaddr *)dst);
|
||||
if (la_tmp == NULL)
|
||||
lltable_link_entry(lt, la);
|
||||
IF_AFDATA_WUNLOCK(ifp);
|
||||
if (la_tmp != NULL) {
|
||||
lltable_free_entry(lt, la);
|
||||
la = la_tmp;
|
||||
}
|
||||
LLE_ADDREF(la);
|
||||
LLE_WUNLOCK(la);
|
||||
}
|
||||
|
||||
return (la);
|
||||
@ -483,6 +500,21 @@ lltable_foreach_lle(struct lltable *llt, llt_foreach_cb_t *f, void *farg)
|
||||
return (llt->llt_foreach_entry(llt, f, farg));
|
||||
}
|
||||
|
||||
struct llentry *
|
||||
lltable_alloc_entry(struct lltable *llt, u_int flags,
|
||||
const struct sockaddr *l3addr)
|
||||
{
|
||||
|
||||
return (llt->llt_alloc_entry(llt, flags, l3addr));
|
||||
}
|
||||
|
||||
void
|
||||
lltable_free_entry(struct lltable *llt, struct llentry *lle)
|
||||
{
|
||||
|
||||
llt->llt_free_entry(llt, lle);
|
||||
}
|
||||
|
||||
void
|
||||
lltable_link_entry(struct lltable *llt, struct llentry *lle)
|
||||
{
|
||||
@ -531,7 +563,7 @@ lla_rt_output(struct rt_msghdr *rtm, struct rt_addrinfo *info)
|
||||
struct sockaddr *dst = (struct sockaddr *)info->rti_info[RTAX_DST];
|
||||
struct ifnet *ifp;
|
||||
struct lltable *llt;
|
||||
struct llentry *lle;
|
||||
struct llentry *lle, *lle_tmp;
|
||||
u_int laflags = 0;
|
||||
int error;
|
||||
|
||||
@ -560,13 +592,9 @@ lla_rt_output(struct rt_msghdr *rtm, struct rt_addrinfo *info)
|
||||
switch (rtm->rtm_type) {
|
||||
case RTM_ADD:
|
||||
/* Add static LLE */
|
||||
IF_AFDATA_WLOCK(ifp);
|
||||
lle = lla_create(llt, 0, dst);
|
||||
if (lle == NULL) {
|
||||
IF_AFDATA_WUNLOCK(ifp);
|
||||
lle = lltable_alloc_entry(llt, 0, dst);
|
||||
if (lle == NULL)
|
||||
return (ENOMEM);
|
||||
}
|
||||
|
||||
|
||||
bcopy(LLADDR(dl), &lle->ll_addr, ifp->if_addrlen);
|
||||
if ((rtm->rtm_flags & RTF_ANNOUNCE))
|
||||
@ -589,8 +617,39 @@ lla_rt_output(struct rt_msghdr *rtm, struct rt_addrinfo *info)
|
||||
} else
|
||||
lle->la_expire = rtm->rtm_rmx.rmx_expire;
|
||||
laflags = lle->la_flags;
|
||||
LLE_WUNLOCK(lle);
|
||||
|
||||
/* Try to link new entry */
|
||||
lle_tmp = NULL;
|
||||
IF_AFDATA_WLOCK(ifp);
|
||||
LLE_WLOCK(lle);
|
||||
lle_tmp = lla_lookup(llt, LLE_EXCLUSIVE, dst);
|
||||
if (lle_tmp != NULL) {
|
||||
/* Check if we are trying to replace immutable entry */
|
||||
if ((lle_tmp->la_flags & LLE_IFADDR) != 0) {
|
||||
IF_AFDATA_WUNLOCK(ifp);
|
||||
LLE_WUNLOCK(lle_tmp);
|
||||
lltable_free_entry(llt, lle);
|
||||
return (EPERM);
|
||||
}
|
||||
/* Unlink existing entry from table */
|
||||
lltable_unlink_entry(llt, lle_tmp);
|
||||
}
|
||||
lltable_link_entry(llt, lle);
|
||||
IF_AFDATA_WUNLOCK(ifp);
|
||||
|
||||
if (lle_tmp != NULL) {
|
||||
EVENTHANDLER_INVOKE(lle_event, lle_tmp,LLENTRY_EXPIRED);
|
||||
lltable_free_entry(llt, lle_tmp);
|
||||
}
|
||||
|
||||
/*
|
||||
* By invoking LLE handler here we might get
|
||||
* two events on static LLE entry insertion
|
||||
* in routing socket. However, since we might have
|
||||
* other subscribers we need to generate this event.
|
||||
*/
|
||||
EVENTHANDLER_INVOKE(lle_event, lle, LLENTRY_RESOLVED);
|
||||
LLE_WUNLOCK(lle);
|
||||
#ifdef INET
|
||||
/* gratuitous ARP */
|
||||
if ((laflags & LLE_PUB) && dst->sa_family == AF_INET)
|
||||
|
@ -133,7 +133,7 @@ struct llentry {
|
||||
|
||||
typedef struct llentry *(llt_lookup_t)(struct lltable *, u_int flags,
|
||||
const struct sockaddr *l3addr);
|
||||
typedef struct llentry *(llt_create_t)(struct lltable *, u_int flags,
|
||||
typedef struct llentry *(llt_alloc_t)(struct lltable *, u_int flags,
|
||||
const struct sockaddr *l3addr);
|
||||
typedef int (llt_delete_t)(struct lltable *, u_int flags,
|
||||
const struct sockaddr *l3addr);
|
||||
@ -161,7 +161,7 @@ struct lltable {
|
||||
struct ifnet *llt_ifp;
|
||||
|
||||
llt_lookup_t *llt_lookup;
|
||||
llt_create_t *llt_create;
|
||||
llt_alloc_t *llt_alloc_entry;
|
||||
llt_delete_t *llt_delete;
|
||||
llt_prefix_free_t *llt_prefix_free;
|
||||
llt_dump_entry_t *llt_dump_entry;
|
||||
@ -209,8 +209,9 @@ struct llentry *llentry_alloc(struct ifnet *, struct lltable *,
|
||||
/* helper functions */
|
||||
size_t lltable_drop_entry_queue(struct llentry *);
|
||||
|
||||
struct llentry *lltable_create_lle(struct lltable *llt, u_int flags,
|
||||
const void *paddr);
|
||||
struct llentry *lltable_alloc_entry(struct lltable *llt, u_int flags,
|
||||
const struct sockaddr *l4addr);
|
||||
void lltable_free_entry(struct lltable *llt, struct llentry *lle);
|
||||
void lltable_link_entry(struct lltable *llt, struct llentry *lle);
|
||||
void lltable_unlink_entry(struct lltable *llt, struct llentry *lle);
|
||||
void lltable_fill_sa_entry(const struct llentry *lle, struct sockaddr *sa);
|
||||
@ -229,13 +230,6 @@ lla_lookup(struct lltable *llt, u_int flags, const struct sockaddr *l3addr)
|
||||
return (llt->llt_lookup(llt, flags, l3addr));
|
||||
}
|
||||
|
||||
static __inline struct llentry *
|
||||
lla_create(struct lltable *llt, u_int flags, const struct sockaddr *l3addr)
|
||||
{
|
||||
|
||||
return (llt->llt_create(llt, flags, l3addr));
|
||||
}
|
||||
|
||||
static __inline int
|
||||
lla_delete(struct lltable *llt, u_int flags, const struct sockaddr *l3addr)
|
||||
{
|
||||
|
@ -218,8 +218,8 @@ arptimer(void *arg)
|
||||
|
||||
/* Guard against race with other llentry_free(). */
|
||||
if (lle->la_flags & LLE_LINKED) {
|
||||
size_t pkts_dropped;
|
||||
|
||||
size_t pkts_dropped;
|
||||
LLE_REMREF(lle);
|
||||
pkts_dropped = llentry_free(lle);
|
||||
ARPSTAT_ADD(dropped, pkts_dropped);
|
||||
@ -323,7 +323,7 @@ static int
|
||||
arpresolve_full(struct ifnet *ifp, int is_gw, int create, struct mbuf *m,
|
||||
const struct sockaddr *dst, u_char *desten, uint32_t *pflags)
|
||||
{
|
||||
struct llentry *la = NULL;
|
||||
struct llentry *la = NULL, *la_tmp;
|
||||
struct mbuf *curr = NULL;
|
||||
struct mbuf *next = NULL;
|
||||
int error, renew;
|
||||
@ -337,16 +337,28 @@ arpresolve_full(struct ifnet *ifp, int is_gw, int create, struct mbuf *m,
|
||||
IF_AFDATA_RUNLOCK(ifp);
|
||||
}
|
||||
if (la == NULL && (ifp->if_flags & (IFF_NOARP | IFF_STATICARP)) == 0) {
|
||||
create = 1;
|
||||
IF_AFDATA_WLOCK(ifp);
|
||||
la = lla_create(LLTABLE(ifp), 0, dst);
|
||||
IF_AFDATA_WUNLOCK(ifp);
|
||||
}
|
||||
if (la == NULL) {
|
||||
if (create != 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), ifp->if_xname);
|
||||
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);
|
||||
}
|
||||
@ -608,7 +620,7 @@ in_arpinput(struct mbuf *m)
|
||||
struct rm_priotracker in_ifa_tracker;
|
||||
struct arphdr *ah;
|
||||
struct ifnet *ifp = m->m_pkthdr.rcvif;
|
||||
struct llentry *la = NULL;
|
||||
struct llentry *la = NULL, *la_tmp;
|
||||
struct rtentry *rt;
|
||||
struct ifaddr *ifa;
|
||||
struct in_ifaddr *ia;
|
||||
@ -620,6 +632,7 @@ in_arpinput(struct mbuf *m)
|
||||
int bridged = 0, is_bridge = 0;
|
||||
int carped;
|
||||
struct sockaddr_in sin;
|
||||
struct sockaddr *dst;
|
||||
sin.sin_len = sizeof(struct sockaddr_in);
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_addr.s_addr = 0;
|
||||
@ -778,8 +791,9 @@ in_arpinput(struct mbuf *m)
|
||||
sin.sin_len = sizeof(struct sockaddr_in);
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_addr = isaddr;
|
||||
dst = (struct sockaddr *)&sin;
|
||||
IF_AFDATA_RLOCK(ifp);
|
||||
la = lla_lookup(LLTABLE(ifp), LLE_EXCLUSIVE, (struct sockaddr *)&sin);
|
||||
la = lla_lookup(LLTABLE(ifp), LLE_EXCLUSIVE, dst);
|
||||
IF_AFDATA_RUNLOCK(ifp);
|
||||
if (la != NULL)
|
||||
arp_check_update_lle(ah, isaddr, ifp, bridged, la);
|
||||
@ -788,15 +802,47 @@ in_arpinput(struct mbuf *m)
|
||||
* Reply to our address, but no lle exists yet.
|
||||
* do we really have to create an entry?
|
||||
*/
|
||||
la = lltable_alloc_entry(LLTABLE(ifp), 0, dst);
|
||||
if (la == NULL)
|
||||
goto drop;
|
||||
arp_update_lle(ah, ifp, la);
|
||||
|
||||
IF_AFDATA_WLOCK(ifp);
|
||||
la = lla_create(LLTABLE(ifp), 0, (struct sockaddr *)&sin);
|
||||
if (la != NULL)
|
||||
arp_update_lle(ah, ifp, la);
|
||||
LLE_WLOCK(la);
|
||||
la_tmp = lla_lookup(LLTABLE(ifp), LLE_EXCLUSIVE, dst);
|
||||
|
||||
/*
|
||||
* Check if lle still does not exists.
|
||||
* If it does, that means that we either
|
||||
* 1) have configured it explicitly, via
|
||||
* 1a) 'arp -s' static entry or
|
||||
* 1b) interface address static record
|
||||
* or
|
||||
* 2) it was the result of sending first packet to-host
|
||||
* or
|
||||
* 3) it was another arp reply packet we handled in
|
||||
* different thread.
|
||||
*
|
||||
* In all cases except 3) we definitely need to prefer
|
||||
* existing lle. For the sake of simplicity, prefer any
|
||||
* existing lle over newly-create one.
|
||||
*/
|
||||
if (la_tmp == NULL)
|
||||
lltable_link_entry(LLTABLE(ifp), la);
|
||||
IF_AFDATA_WUNLOCK(ifp);
|
||||
if (la != NULL) {
|
||||
|
||||
if (la_tmp == NULL) {
|
||||
arp_mark_lle_reachable(la);
|
||||
LLE_WUNLOCK(la);
|
||||
} else {
|
||||
/* Free newly-create entry and handle packet */
|
||||
lltable_free_entry(LLTABLE(ifp), la);
|
||||
la = la_tmp;
|
||||
la_tmp = NULL;
|
||||
arp_check_update_lle(ah, isaddr, ifp, bridged, la);
|
||||
/* arp_check_update_lle() returns @la unlocked */
|
||||
}
|
||||
la = NULL;
|
||||
}
|
||||
reply:
|
||||
if (op != ARPOP_REQUEST)
|
||||
@ -1044,30 +1090,53 @@ arp_mark_lle_reachable(struct llentry *la)
|
||||
void
|
||||
arp_ifinit(struct ifnet *ifp, struct ifaddr *ifa)
|
||||
{
|
||||
struct llentry *lle;
|
||||
struct llentry *lle, *lle_tmp;
|
||||
struct sockaddr_in *dst_in;
|
||||
struct sockaddr *dst;
|
||||
|
||||
if (ifa->ifa_carp != NULL)
|
||||
return;
|
||||
|
||||
if (ntohl(IA_SIN(ifa)->sin_addr.s_addr) != INADDR_ANY) {
|
||||
arprequest(ifp, &IA_SIN(ifa)->sin_addr,
|
||||
&IA_SIN(ifa)->sin_addr, IF_LLADDR(ifp));
|
||||
/*
|
||||
* interface address is considered static entry
|
||||
* because the output of the arp utility shows
|
||||
* that L2 entry as permanent
|
||||
*/
|
||||
IF_AFDATA_LOCK(ifp);
|
||||
lle = lla_create(LLTABLE(ifp), LLE_IFADDR | LLE_STATIC,
|
||||
(struct sockaddr *)IA_SIN(ifa));
|
||||
IF_AFDATA_UNLOCK(ifp);
|
||||
if (lle == NULL)
|
||||
log(LOG_INFO, "arp_ifinit: cannot create arp "
|
||||
"entry for interface address\n");
|
||||
else
|
||||
LLE_WUNLOCK(lle);
|
||||
}
|
||||
ifa->ifa_rtrequest = NULL;
|
||||
|
||||
dst_in = IA_SIN(ifa);
|
||||
dst = (struct sockaddr *)dst_in;
|
||||
|
||||
if (ntohl(IA_SIN(ifa)->sin_addr.s_addr) == INADDR_ANY)
|
||||
return;
|
||||
|
||||
arprequest(ifp, &IA_SIN(ifa)->sin_addr,
|
||||
&IA_SIN(ifa)->sin_addr, IF_LLADDR(ifp));
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
@ -1202,25 +1202,15 @@ in_lltable_delete(struct lltable *llt, u_int flags,
|
||||
}
|
||||
|
||||
static struct llentry *
|
||||
in_lltable_create(struct lltable *llt, u_int flags, const struct sockaddr *l3addr)
|
||||
in_lltable_alloc(struct lltable *llt, u_int flags, const struct sockaddr *l3addr)
|
||||
{
|
||||
const struct sockaddr_in *sin = (const struct sockaddr_in *)l3addr;
|
||||
struct ifnet *ifp = llt->llt_ifp;
|
||||
struct llentry *lle;
|
||||
|
||||
IF_AFDATA_WLOCK_ASSERT(ifp);
|
||||
KASSERT(l3addr->sa_family == AF_INET,
|
||||
("sin_family %d", l3addr->sa_family));
|
||||
|
||||
lle = in_lltable_find_dst(llt, sin->sin_addr);
|
||||
|
||||
if (lle != NULL) {
|
||||
LLE_WLOCK(lle);
|
||||
return (lle);
|
||||
}
|
||||
|
||||
/* no existing record, we need to create new one */
|
||||
|
||||
/*
|
||||
* A route that covers the given address must have
|
||||
* been installed 1st because we are doing a resolution,
|
||||
@ -1241,9 +1231,6 @@ in_lltable_create(struct lltable *llt, u_int flags, const struct sockaddr *l3add
|
||||
lle->la_flags |= (LLE_VALID | LLE_STATIC);
|
||||
}
|
||||
|
||||
lltable_link_entry(llt, lle);
|
||||
LLE_WLOCK(lle);
|
||||
|
||||
return (lle);
|
||||
}
|
||||
|
||||
@ -1346,7 +1333,7 @@ in_lltattach(struct ifnet *ifp)
|
||||
llt->llt_ifp = ifp;
|
||||
|
||||
llt->llt_lookup = in_lltable_lookup;
|
||||
llt->llt_create = in_lltable_create;
|
||||
llt->llt_alloc_entry = in_lltable_alloc;
|
||||
llt->llt_delete = in_lltable_delete;
|
||||
llt->llt_dump_entry = in_lltable_dump_entry;
|
||||
llt->llt_hash = in_lltable_hash;
|
||||
|
@ -456,7 +456,7 @@ toe_route_redirect_event(void *arg __unused, struct rtentry *rt0,
|
||||
static int
|
||||
toe_nd6_resolve(struct ifnet *ifp, struct sockaddr *sa, uint8_t *lladdr)
|
||||
{
|
||||
struct llentry *lle;
|
||||
struct llentry *lle, *lle_tmp;
|
||||
struct sockaddr_in6 *sin6 = (void *)sa;
|
||||
int rc, flags = 0;
|
||||
|
||||
@ -465,19 +465,32 @@ toe_nd6_resolve(struct ifnet *ifp, struct sockaddr *sa, uint8_t *lladdr)
|
||||
lle = lla_lookup(LLTABLE6(ifp), flags, sa);
|
||||
IF_AFDATA_RUNLOCK(ifp);
|
||||
if (lle == NULL) {
|
||||
IF_AFDATA_LOCK(ifp);
|
||||
lle = nd6_create(&sin6->sin6_addr, 0, ifp);
|
||||
IF_AFDATA_UNLOCK(ifp);
|
||||
lle = nd6_alloc(&sin6->sin6_addr, 0, ifp);
|
||||
if (lle == NULL)
|
||||
return (ENOMEM); /* Couldn't create entry in cache. */
|
||||
lle->ln_state = ND6_LLINFO_INCOMPLETE;
|
||||
nd6_llinfo_settimer_locked(lle,
|
||||
(long)ND_IFINFO(ifp)->retrans * hz / 1000);
|
||||
LLE_WUNLOCK(lle);
|
||||
IF_AFDATA_WLOCK(ifp);
|
||||
LLE_WLOCK(lle);
|
||||
lle_tmp = nd6_lookup(&sin6->sin6_addr, ND6_EXCLUSIVE, ifp);
|
||||
/* Prefer any existing lle over newly-created one */
|
||||
if (lle_tmp == NULL)
|
||||
lltable_link_entry(LLTABLE6(ifp), lle);
|
||||
IF_AFDATA_WUNLOCK(ifp);
|
||||
if (lle_tmp == NULL) {
|
||||
/* Arm timer for newly-created entry and send NS */
|
||||
nd6_llinfo_settimer_locked(lle,
|
||||
(long)ND_IFINFO(ifp)->retrans * hz / 1000);
|
||||
LLE_WUNLOCK(lle);
|
||||
|
||||
nd6_ns_output(ifp, NULL, &sin6->sin6_addr, NULL, 0);
|
||||
nd6_ns_output(ifp, NULL, &sin6->sin6_addr, NULL, 0);
|
||||
|
||||
return (EWOULDBLOCK);
|
||||
return (EWOULDBLOCK);
|
||||
} else {
|
||||
/* Drop newly-created lle and switch to existing one */
|
||||
lltable_free_entry(LLTABLE6(ifp), lle);
|
||||
lle = lle_tmp;
|
||||
lle_tmp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (lle->ln_state == ND6_LLINFO_STALE) {
|
||||
|
@ -2239,24 +2239,16 @@ in6_lltable_delete(struct lltable *llt, u_int flags,
|
||||
}
|
||||
|
||||
static struct llentry *
|
||||
in6_lltable_create(struct lltable *llt, u_int flags,
|
||||
in6_lltable_alloc(struct lltable *llt, u_int flags,
|
||||
const struct sockaddr *l3addr)
|
||||
{
|
||||
const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)l3addr;
|
||||
struct ifnet *ifp = llt->llt_ifp;
|
||||
struct llentry *lle;
|
||||
|
||||
IF_AFDATA_WLOCK_ASSERT(ifp);
|
||||
KASSERT(l3addr->sa_family == AF_INET6,
|
||||
("sin_family %d", l3addr->sa_family));
|
||||
|
||||
lle = in6_lltable_find_dst(llt, &sin6->sin6_addr);
|
||||
|
||||
if (lle != NULL) {
|
||||
LLE_WLOCK(lle);
|
||||
return (lle);
|
||||
}
|
||||
|
||||
/*
|
||||
* A route that covers the given address must have
|
||||
* been installed 1st because we are doing a resolution,
|
||||
@ -2277,9 +2269,6 @@ in6_lltable_create(struct lltable *llt, u_int flags,
|
||||
lle->la_flags |= (LLE_VALID | LLE_STATIC);
|
||||
}
|
||||
|
||||
lltable_link_entry(llt, lle);
|
||||
LLE_WLOCK(lle);
|
||||
|
||||
return (lle);
|
||||
}
|
||||
|
||||
@ -2382,7 +2371,7 @@ in6_lltattach(struct ifnet *ifp)
|
||||
llt->llt_ifp = ifp;
|
||||
|
||||
llt->llt_lookup = in6_lltable_lookup;
|
||||
llt->llt_create = in6_lltable_create;
|
||||
llt->llt_alloc_entry = in6_lltable_alloc;
|
||||
llt->llt_delete = in6_lltable_delete;
|
||||
llt->llt_dump_entry = in6_lltable_dump_entry;
|
||||
llt->llt_hash = in6_lltable_hash;
|
||||
|
@ -939,12 +939,8 @@ nd6_lookup(struct in6_addr *addr6, int flags, struct ifnet *ifp)
|
||||
return (ln);
|
||||
}
|
||||
|
||||
/*
|
||||
* the caller acquires and releases the lock on the lltbls
|
||||
* Returns the llentry wlocked
|
||||
*/
|
||||
struct llentry *
|
||||
nd6_create(struct in6_addr *addr6, int flags, struct ifnet *ifp)
|
||||
nd6_alloc(struct in6_addr *addr6, int flags, struct ifnet *ifp)
|
||||
{
|
||||
struct sockaddr_in6 sin6;
|
||||
struct llentry *ln;
|
||||
@ -954,9 +950,7 @@ nd6_create(struct in6_addr *addr6, int flags, struct ifnet *ifp)
|
||||
sin6.sin6_family = AF_INET6;
|
||||
sin6.sin6_addr = *addr6;
|
||||
|
||||
IF_AFDATA_WLOCK_ASSERT(ifp);
|
||||
|
||||
ln = lla_create(LLTABLE6(ifp), 0, (struct sockaddr *)&sin6);
|
||||
ln = lltable_alloc_entry(LLTABLE6(ifp), 0, (struct sockaddr *)&sin6);
|
||||
if (ln != NULL)
|
||||
ln->ln_state = ND6_LLINFO_NOSTATE;
|
||||
|
||||
@ -1640,7 +1634,7 @@ struct llentry *
|
||||
nd6_cache_lladdr(struct ifnet *ifp, struct in6_addr *from, char *lladdr,
|
||||
int lladdrlen, int type, int code)
|
||||
{
|
||||
struct llentry *ln = NULL;
|
||||
struct llentry *ln = NULL, *ln_tmp;
|
||||
int is_newentry;
|
||||
int do_update;
|
||||
int olladdr;
|
||||
@ -1674,22 +1668,33 @@ nd6_cache_lladdr(struct ifnet *ifp, struct in6_addr *from, char *lladdr,
|
||||
IF_AFDATA_RLOCK(ifp);
|
||||
ln = nd6_lookup(from, flags, ifp);
|
||||
IF_AFDATA_RUNLOCK(ifp);
|
||||
is_newentry = 0;
|
||||
if (ln == NULL) {
|
||||
flags |= ND6_EXCLUSIVE;
|
||||
IF_AFDATA_LOCK(ifp);
|
||||
ln = nd6_create(from, 0, ifp);
|
||||
IF_AFDATA_UNLOCK(ifp);
|
||||
is_newentry = 1;
|
||||
} else {
|
||||
/* do nothing if static ndp is set */
|
||||
if (ln->la_flags & LLE_STATIC) {
|
||||
ln = nd6_alloc(from, 0, ifp);
|
||||
if (ln == NULL)
|
||||
return (NULL);
|
||||
IF_AFDATA_WLOCK(ifp);
|
||||
LLE_WLOCK(ln);
|
||||
/* Prefer any existing lle over newly-created one */
|
||||
ln_tmp = nd6_lookup(from, ND6_EXCLUSIVE, ifp);
|
||||
if (ln_tmp == NULL)
|
||||
lltable_link_entry(LLTABLE6(ifp), ln);
|
||||
IF_AFDATA_WUNLOCK(ifp);
|
||||
if (ln_tmp == NULL)
|
||||
/* No existing lle, mark as new entry */
|
||||
is_newentry = 1;
|
||||
else {
|
||||
lltable_free_entry(LLTABLE6(ifp), ln);
|
||||
ln = ln_tmp;
|
||||
ln_tmp = NULL;
|
||||
}
|
||||
}
|
||||
/* do nothing if static ndp is set */
|
||||
if ((ln->la_flags & LLE_STATIC)) {
|
||||
static_route = 1;
|
||||
goto done;
|
||||
}
|
||||
is_newentry = 0;
|
||||
}
|
||||
if (ln == NULL)
|
||||
return (NULL);
|
||||
|
||||
olladdr = (ln->la_flags & LLE_VALID) ? 1 : 0;
|
||||
if (olladdr && lladdr) {
|
||||
@ -2032,7 +2037,7 @@ static int
|
||||
nd6_output_lle(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *m,
|
||||
struct sockaddr_in6 *dst)
|
||||
{
|
||||
struct llentry *lle = NULL;
|
||||
struct llentry *lle = NULL, *lle_tmp;
|
||||
|
||||
KASSERT(m != NULL, ("NULL mbuf, nothing to send"));
|
||||
/* discard the packet if IPv6 operation is disabled on the interface */
|
||||
@ -2063,19 +2068,35 @@ nd6_output_lle(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *m,
|
||||
* the condition below is not very efficient. But we believe
|
||||
* it is tolerable, because this should be a rare case.
|
||||
*/
|
||||
IF_AFDATA_LOCK(ifp);
|
||||
lle = nd6_create(&dst->sin6_addr, 0, ifp);
|
||||
IF_AFDATA_UNLOCK(ifp);
|
||||
lle = nd6_alloc(&dst->sin6_addr, 0, ifp);
|
||||
if (lle == NULL) {
|
||||
char ip6buf[INET6_ADDRSTRLEN];
|
||||
log(LOG_DEBUG,
|
||||
"nd6_output: can't allocate llinfo for %s "
|
||||
"(ln=%p)\n",
|
||||
ip6_sprintf(ip6buf, &dst->sin6_addr), lle);
|
||||
m_freem(m);
|
||||
return (ENOBUFS);
|
||||
}
|
||||
lle->ln_state = ND6_LLINFO_NOSTATE;
|
||||
|
||||
IF_AFDATA_WLOCK(ifp);
|
||||
LLE_WLOCK(lle);
|
||||
/* Prefer any existing entry over newly-created one */
|
||||
lle_tmp = nd6_lookup(&dst->sin6_addr, ND6_EXCLUSIVE, ifp);
|
||||
if (lle_tmp == NULL)
|
||||
lltable_link_entry(LLTABLE6(ifp), lle);
|
||||
IF_AFDATA_WUNLOCK(ifp);
|
||||
if (lle_tmp != NULL) {
|
||||
lltable_free_entry(LLTABLE6(ifp), lle);
|
||||
lle = lle_tmp;
|
||||
lle_tmp = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lle == NULL) {
|
||||
if ((ifp->if_flags & IFF_POINTOPOINT) == 0 &&
|
||||
!(ND_IFINFO(ifp)->flags & ND6_IFF_PERFORMNUD)) {
|
||||
char ip6buf[INET6_ADDRSTRLEN];
|
||||
log(LOG_DEBUG,
|
||||
"nd6_output: can't allocate llinfo for %s "
|
||||
"(ln=%p)\n",
|
||||
ip6_sprintf(ip6buf, &dst->sin6_addr), lle);
|
||||
m_freem(m);
|
||||
return (ENOBUFS);
|
||||
}
|
||||
@ -2241,24 +2262,40 @@ int
|
||||
nd6_add_ifa_lle(struct in6_ifaddr *ia)
|
||||
{
|
||||
struct ifnet *ifp;
|
||||
struct llentry *ln;
|
||||
struct llentry *ln, *ln_tmp;
|
||||
struct sockaddr *dst;
|
||||
|
||||
ifp = ia->ia_ifa.ifa_ifp;
|
||||
if (nd6_need_cache(ifp) == 0)
|
||||
return (0);
|
||||
IF_AFDATA_LOCK(ifp);
|
||||
ia->ia_ifa.ifa_rtrequest = nd6_rtrequest;
|
||||
ln = lla_create(LLTABLE6(ifp), LLE_IFADDR,
|
||||
(struct sockaddr *)&ia->ia_addr);
|
||||
IF_AFDATA_UNLOCK(ifp);
|
||||
if (ln != NULL) {
|
||||
ln->la_expire = 0; /* for IPv6 this means permanent */
|
||||
ln->ln_state = ND6_LLINFO_REACHABLE;
|
||||
LLE_WUNLOCK(ln);
|
||||
return (0);
|
||||
}
|
||||
|
||||
return (ENOBUFS);
|
||||
ia->ia_ifa.ifa_rtrequest = nd6_rtrequest;
|
||||
dst = (struct sockaddr *)&ia->ia_addr;
|
||||
ln = lltable_alloc_entry(LLTABLE6(ifp), LLE_IFADDR, dst);
|
||||
if (ln == NULL)
|
||||
return (ENOBUFS);
|
||||
|
||||
ln->la_expire = 0; /* for IPv6 this means permanent */
|
||||
ln->ln_state = ND6_LLINFO_REACHABLE;
|
||||
|
||||
IF_AFDATA_WLOCK(ifp);
|
||||
LLE_WLOCK(ln);
|
||||
/* Unlink any entry if exists */
|
||||
ln_tmp = lla_lookup(LLTABLE6(ifp), LLE_EXCLUSIVE, dst);
|
||||
if (ln_tmp != NULL)
|
||||
lltable_unlink_entry(LLTABLE6(ifp), ln_tmp);
|
||||
lltable_link_entry(LLTABLE6(ifp), ln);
|
||||
IF_AFDATA_WUNLOCK(ifp);
|
||||
|
||||
if (ln_tmp != NULL)
|
||||
EVENTHANDLER_INVOKE(lle_event, ln_tmp, LLENTRY_EXPIRED);
|
||||
EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_RESOLVED);
|
||||
|
||||
LLE_WUNLOCK(ln);
|
||||
if (ln_tmp != NULL)
|
||||
llentry_free(ln_tmp);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -407,7 +407,7 @@ void nd6_option_init(void *, int, union nd_opts *);
|
||||
struct nd_opt_hdr *nd6_option(union nd_opts *);
|
||||
int nd6_options(union nd_opts *);
|
||||
struct llentry *nd6_lookup(struct in6_addr *, int, struct ifnet *);
|
||||
struct llentry *nd6_create(struct in6_addr *, int, struct ifnet *);
|
||||
struct llentry *nd6_alloc(struct in6_addr *, int, struct ifnet *);
|
||||
void nd6_setmtu(struct ifnet *);
|
||||
void nd6_llinfo_settimer(struct llentry *, long);
|
||||
void nd6_llinfo_settimer_locked(struct llentry *, long);
|
||||
|
Loading…
Reference in New Issue
Block a user