Changeset View
Changeset View
Standalone View
Standalone View
sys/netinet6/nd6.c
Show First 20 Lines • Show All 133 Lines • ▼ Show 20 Lines | |||||
static void nd6_setmtu0(struct ifnet *, struct nd_ifinfo *); | static void nd6_setmtu0(struct ifnet *, struct nd_ifinfo *); | ||||
static void nd6_slowtimo(void *); | static void nd6_slowtimo(void *); | ||||
static int regen_tmpaddr(struct in6_ifaddr *); | static int regen_tmpaddr(struct in6_ifaddr *); | ||||
static void nd6_free(struct llentry **, int); | static void nd6_free(struct llentry **, int); | ||||
static void nd6_free_redirect(const struct llentry *); | static void nd6_free_redirect(const struct llentry *); | ||||
static void nd6_llinfo_timer(void *); | static void nd6_llinfo_timer(void *); | ||||
static void nd6_llinfo_settimer_locked(struct llentry *, long); | static void nd6_llinfo_settimer_locked(struct llentry *, long); | ||||
static void clear_llinfo_pqueue(struct llentry *); | static void clear_llinfo_pqueue(struct llentry *); | ||||
static int nd6_resolve_slow(struct ifnet *, int, struct mbuf *, | static int nd6_resolve_slow(struct ifnet *, int, int, struct mbuf *, | ||||
const struct sockaddr_in6 *, u_char *, uint32_t *, struct llentry **); | const struct sockaddr_in6 *, u_char *, uint32_t *, struct llentry **); | ||||
static int nd6_need_cache(struct ifnet *); | static int nd6_need_cache(struct ifnet *); | ||||
VNET_DEFINE_STATIC(struct callout, nd6_slowtimo_ch); | VNET_DEFINE_STATIC(struct callout, nd6_slowtimo_ch); | ||||
#define V_nd6_slowtimo_ch VNET(nd6_slowtimo_ch) | #define V_nd6_slowtimo_ch VNET(nd6_slowtimo_ch) | ||||
VNET_DEFINE_STATIC(struct callout, nd6_timer_ch); | VNET_DEFINE_STATIC(struct callout, nd6_timer_ch); | ||||
#define V_nd6_timer_ch VNET(nd6_timer_ch) | #define V_nd6_timer_ch VNET(nd6_timer_ch) | ||||
▲ Show 20 Lines • Show All 374 Lines • ▼ Show 20 Lines | |||||
*/ | */ | ||||
static void | static void | ||||
nd6_llinfo_settimer_locked(struct llentry *ln, long tick) | nd6_llinfo_settimer_locked(struct llentry *ln, long tick) | ||||
{ | { | ||||
int canceled; | int canceled; | ||||
LLE_WLOCK_ASSERT(ln); | LLE_WLOCK_ASSERT(ln); | ||||
/* Do not schedule timers for child LLEs. */ | |||||
if (ln->la_flags & LLE_CHILD) | |||||
return; | |||||
if (tick < 0) { | if (tick < 0) { | ||||
ln->la_expire = 0; | ln->la_expire = 0; | ||||
ln->ln_ntick = 0; | ln->ln_ntick = 0; | ||||
canceled = callout_stop(&ln->lle_timer); | canceled = callout_stop(&ln->lle_timer); | ||||
} else { | } else { | ||||
ln->la_expire = time_uptime + tick / hz; | ln->la_expire = time_uptime + tick / hz; | ||||
LLE_ADDREF(ln); | LLE_ADDREF(ln); | ||||
if (tick > INT_MAX) { | if (tick > INT_MAX) { | ||||
▲ Show 20 Lines • Show All 829 Lines • ▼ Show 20 Lines | nd6_is_addr_neighbor(const struct sockaddr_in6 *addr, struct ifnet *ifp) | ||||
IF_AFDATA_UNLOCK_ASSERT(ifp); | IF_AFDATA_UNLOCK_ASSERT(ifp); | ||||
if (nd6_is_new_addr_neighbor(addr, ifp)) | if (nd6_is_new_addr_neighbor(addr, ifp)) | ||||
return (1); | return (1); | ||||
/* | /* | ||||
* Even if the address matches none of our addresses, it might be | * Even if the address matches none of our addresses, it might be | ||||
* in the neighbor cache. | * in the neighbor cache. | ||||
*/ | */ | ||||
if ((lle = nd6_lookup(&addr->sin6_addr, 0, ifp)) != NULL) { | if ((lle = nd6_lookup(&addr->sin6_addr, LLE_SF(AF_INET6, 0), ifp)) != NULL) { | ||||
LLE_RUNLOCK(lle); | LLE_RUNLOCK(lle); | ||||
rc = 1; | rc = 1; | ||||
} | } | ||||
return (rc); | return (rc); | ||||
} | } | ||||
static __noinline void | |||||
nd6_free_children(struct llentry *lle) | |||||
{ | |||||
struct llentry *child_lle; | |||||
NET_EPOCH_ASSERT(); | |||||
LLE_WLOCK_ASSERT(lle); | |||||
while ((child_lle = CK_SLIST_FIRST(&lle->lle_children)) != NULL) { | |||||
LLE_WLOCK(child_lle); | |||||
lltable_unlink_child_entry(child_lle); | |||||
llentry_free(child_lle); | |||||
} | |||||
} | |||||
/* | /* | ||||
* Tries to update @lle address/prepend data with new @lladdr. | * Tries to update @lle address/prepend data with new @lladdr. | ||||
* | * | ||||
* Returns true on success. | * Returns true on success. | ||||
* In any case, @lle is returned wlocked. | * In any case, @lle is returned wlocked. | ||||
*/ | */ | ||||
bool | static __noinline bool | ||||
nd6_try_set_entry_addr(struct ifnet *ifp, struct llentry *lle, char *lladdr) | nd6_try_set_entry_addr_locked(struct ifnet *ifp, struct llentry *lle, char *lladdr) | ||||
{ | { | ||||
u_char linkhdr[LLE_MAX_LINKHDR]; | u_char buf[LLE_MAX_LINKHDR]; | ||||
size_t linkhdrsize; | int fam, off; | ||||
int lladdr_off; | size_t sz; | ||||
LLE_WLOCK_ASSERT(lle); | sz = sizeof(buf); | ||||
if (lltable_calc_llheader(ifp, AF_INET6, lladdr, buf, &sz, &off) != 0) | |||||
linkhdrsize = sizeof(linkhdr); | |||||
if (lltable_calc_llheader(ifp, AF_INET6, lladdr, | |||||
linkhdr, &linkhdrsize, &lladdr_off) != 0) { | |||||
return (false); | return (false); | ||||
/* Update data */ | |||||
lltable_set_entry_addr(ifp, lle, buf, sz, off); | |||||
struct llentry *child_lle; | |||||
CK_SLIST_FOREACH(child_lle, &lle->lle_children, lle_child_next) { | |||||
LLE_WLOCK(child_lle); | |||||
fam = child_lle->r_family; | |||||
sz = sizeof(buf); | |||||
if (lltable_calc_llheader(ifp, fam, lladdr, buf, &sz, &off) == 0) { | |||||
/* success */ | |||||
lltable_set_entry_addr(ifp, child_lle, buf, sz, off); | |||||
child_lle->ln_state = ND6_LLINFO_REACHABLE; | |||||
} | } | ||||
LLE_WUNLOCK(child_lle); | |||||
} | |||||
return (true); | |||||
} | |||||
bool | |||||
nd6_try_set_entry_addr(struct ifnet *ifp, struct llentry *lle, char *lladdr) | |||||
{ | |||||
NET_EPOCH_ASSERT(); | |||||
LLE_WLOCK_ASSERT(lle); | |||||
if (!lltable_acquire_wlock(ifp, lle)) | if (!lltable_acquire_wlock(ifp, lle)) | ||||
return (false); | return (false); | ||||
lltable_set_entry_addr(ifp, lle, linkhdr, linkhdrsize, lladdr_off); | bool ret = nd6_try_set_entry_addr_locked(ifp, lle, lladdr); | ||||
IF_AFDATA_WUNLOCK(ifp); | IF_AFDATA_WUNLOCK(ifp); | ||||
return (true); | return (ret); | ||||
} | } | ||||
/* | /* | ||||
* Free an nd6 llinfo entry. | * Free an nd6 llinfo entry. | ||||
* Since the function would cause significant changes in the kernel, DO NOT | * Since the function would cause significant changes in the kernel, DO NOT | ||||
* make it global, unless you have a strong reason for the change, and are sure | * make it global, unless you have a strong reason for the change, and are sure | ||||
* that the change is safe. | * that the change is safe. | ||||
* | * | ||||
* Set noinline to be dtrace-friendly | * Set noinline to be dtrace-friendly | ||||
*/ | */ | ||||
static __noinline void | static __noinline void | ||||
nd6_free(struct llentry **lnp, int gc) | nd6_free(struct llentry **lnp, int gc) | ||||
{ | { | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
struct llentry *ln; | struct llentry *ln; | ||||
struct nd_defrouter *dr; | struct nd_defrouter *dr; | ||||
ln = *lnp; | ln = *lnp; | ||||
*lnp = NULL; | *lnp = NULL; | ||||
LLE_WLOCK_ASSERT(ln); | LLE_WLOCK_ASSERT(ln); | ||||
ND6_RLOCK_ASSERT(); | ND6_RLOCK_ASSERT(); | ||||
KASSERT((ln->la_flags & LLE_CHILD) == 0, ("child lle")); | |||||
ifp = lltable_get_ifp(ln->lle_tbl); | ifp = lltable_get_ifp(ln->lle_tbl); | ||||
if ((ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV) != 0) | if ((ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV) != 0) | ||||
dr = defrouter_lookup_locked(&ln->r_l3addr.addr6, ifp); | dr = defrouter_lookup_locked(&ln->r_l3addr.addr6, ifp); | ||||
else | else | ||||
dr = NULL; | dr = NULL; | ||||
ND6_RUNLOCK(); | ND6_RUNLOCK(); | ||||
if ((ln->la_flags & LLE_DELETED) == 0) | if ((ln->la_flags & LLE_DELETED) == 0) | ||||
▲ Show 20 Lines • Show All 105 Lines • ▼ Show 20 Lines | nd6_free(struct llentry **lnp, int gc) | ||||
/* Guard against race with other llentry_free(). */ | /* Guard against race with other llentry_free(). */ | ||||
if (ln->la_flags & LLE_LINKED) { | if (ln->la_flags & LLE_LINKED) { | ||||
/* Remove callout reference */ | /* Remove callout reference */ | ||||
LLE_REMREF(ln); | LLE_REMREF(ln); | ||||
lltable_unlink_entry(ln->lle_tbl, ln); | lltable_unlink_entry(ln->lle_tbl, ln); | ||||
} | } | ||||
IF_AFDATA_UNLOCK(ifp); | IF_AFDATA_UNLOCK(ifp); | ||||
nd6_free_children(ln); | |||||
llentry_free(ln); | llentry_free(ln); | ||||
if (dr != NULL) | if (dr != NULL) | ||||
defrouter_rele(dr); | defrouter_rele(dr); | ||||
} | } | ||||
static int | static int | ||||
nd6_isdynrte(const struct rtentry *rt, const struct nhop_object *nh, void *xap) | nd6_isdynrte(const struct rtentry *rt, const struct nhop_object *nh, void *xap) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 258 Lines • ▼ Show 20 Lines | #undef ND | ||||
{ | { | ||||
struct llentry *ln; | struct llentry *ln; | ||||
struct in6_addr nb_addr = nbi->addr; /* make local for safety */ | struct in6_addr nb_addr = nbi->addr; /* make local for safety */ | ||||
if ((error = in6_setscope(&nb_addr, ifp, NULL)) != 0) | if ((error = in6_setscope(&nb_addr, ifp, NULL)) != 0) | ||||
return (error); | return (error); | ||||
NET_EPOCH_ENTER(et); | NET_EPOCH_ENTER(et); | ||||
ln = nd6_lookup(&nb_addr, 0, ifp); | ln = nd6_lookup(&nb_addr, LLE_SF(AF_INET6, 0), ifp); | ||||
NET_EPOCH_EXIT(et); | NET_EPOCH_EXIT(et); | ||||
if (ln == NULL) { | if (ln == NULL) { | ||||
error = EINVAL; | error = EINVAL; | ||||
break; | break; | ||||
} | } | ||||
nbi->state = ln->ln_state; | nbi->state = ln->ln_state; | ||||
nbi->asked = ln->la_asked; | nbi->asked = ln->la_asked; | ||||
▲ Show 20 Lines • Show All 133 Lines • ▼ Show 20 Lines | nd6_cache_lladdr(struct ifnet *ifp, struct in6_addr *from, char *lladdr, | ||||
* the caller. | * the caller. | ||||
* | * | ||||
* XXX If the link does not have link-layer adderss, what should | * XXX If the link does not have link-layer adderss, what should | ||||
* we do? (ifp->if_addrlen == 0) | * we do? (ifp->if_addrlen == 0) | ||||
* Spec says nothing in sections for RA, RS and NA. There's small | * Spec says nothing in sections for RA, RS and NA. There's small | ||||
* description on it in NS section (RFC 2461 7.2.3). | * description on it in NS section (RFC 2461 7.2.3). | ||||
*/ | */ | ||||
flags = lladdr ? LLE_EXCLUSIVE : 0; | flags = lladdr ? LLE_EXCLUSIVE : 0; | ||||
ln = nd6_lookup(from, flags, ifp); | ln = nd6_lookup(from, LLE_SF(AF_INET6, flags), ifp); | ||||
is_newentry = 0; | is_newentry = 0; | ||||
if (ln == NULL) { | if (ln == NULL) { | ||||
flags |= LLE_EXCLUSIVE; | flags |= LLE_EXCLUSIVE; | ||||
ln = nd6_alloc(from, 0, ifp); | ln = nd6_alloc(from, 0, ifp); | ||||
if (ln == NULL) | if (ln == NULL) | ||||
return; | return; | ||||
/* | /* | ||||
* Since we already know all the data for the new entry, | * Since we already know all the data for the new entry, | ||||
* fill it before insertion. | * fill it before insertion. | ||||
*/ | */ | ||||
if (lladdr != NULL) { | if (lladdr != NULL) { | ||||
linkhdrsize = sizeof(linkhdr); | linkhdrsize = sizeof(linkhdr); | ||||
if (lltable_calc_llheader(ifp, AF_INET6, lladdr, | if (lltable_calc_llheader(ifp, AF_INET6, lladdr, | ||||
linkhdr, &linkhdrsize, &lladdr_off) != 0) | linkhdr, &linkhdrsize, &lladdr_off) != 0) | ||||
return; | return; | ||||
lltable_set_entry_addr(ifp, ln, linkhdr, linkhdrsize, | lltable_set_entry_addr(ifp, ln, linkhdr, linkhdrsize, | ||||
lladdr_off); | lladdr_off); | ||||
} | } | ||||
IF_AFDATA_WLOCK(ifp); | IF_AFDATA_WLOCK(ifp); | ||||
LLE_WLOCK(ln); | LLE_WLOCK(ln); | ||||
/* Prefer any existing lle over newly-created one */ | /* Prefer any existing lle over newly-created one */ | ||||
ln_tmp = nd6_lookup(from, LLE_EXCLUSIVE, ifp); | ln_tmp = nd6_lookup(from, LLE_SF(AF_INET6, LLE_EXCLUSIVE), ifp); | ||||
if (ln_tmp == NULL) | if (ln_tmp == NULL) | ||||
lltable_link_entry(LLTABLE6(ifp), ln); | lltable_link_entry(LLTABLE6(ifp), ln); | ||||
IF_AFDATA_WUNLOCK(ifp); | IF_AFDATA_WUNLOCK(ifp); | ||||
if (ln_tmp == NULL) { | if (ln_tmp == NULL) { | ||||
/* No existing lle, mark as new entry (6,7) */ | /* No existing lle, mark as new entry (6,7) */ | ||||
is_newentry = 1; | is_newentry = 1; | ||||
if (lladdr != NULL) { /* (7) */ | if (lladdr != NULL) { /* (7) */ | ||||
nd6_llinfo_setstate(ln, ND6_LLINFO_STALE); | nd6_llinfo_setstate(ln, ND6_LLINFO_STALE); | ||||
▲ Show 20 Lines • Show All 68 Lines • ▼ Show 20 Lines | nd6_cache_lladdr(struct ifnet *ifp, struct in6_addr *from, char *lladdr, | ||||
if (flags & LLE_EXCLUSIVE) | if (flags & LLE_EXCLUSIVE) | ||||
LLE_WUNLOCK(ln); | LLE_WUNLOCK(ln); | ||||
else | else | ||||
LLE_RUNLOCK(ln); | LLE_RUNLOCK(ln); | ||||
if (chain != NULL) | if (chain != NULL) | ||||
nd6_flush_holdchain(ifp, ln, chain); | nd6_flush_holdchain(ifp, ln, chain); | ||||
if (do_update) | |||||
nd6_flush_children_holdchain(ifp, ln); | |||||
/* | /* | ||||
* When the link-layer address of a router changes, select the | * When the link-layer address of a router changes, select the | ||||
* best router again. In particular, when the neighbor entry is newly | * best router again. In particular, when the neighbor entry is newly | ||||
* created, it might affect the selection policy. | * created, it might affect the selection policy. | ||||
* Question: can we restrict the first condition to the "is_newentry" | * Question: can we restrict the first condition to the "is_newentry" | ||||
* case? | * case? | ||||
* XXX: when we hear an RA from a new router with the link-layer | * XXX: when we hear an RA from a new router with the link-layer | ||||
▲ Show 20 Lines • Show All 125 Lines • ▼ Show 20 Lines | |||||
* is required, call "slow" version. | * is required, call "slow" version. | ||||
* | * | ||||
* Return values: | * Return values: | ||||
* - 0 on success (address copied to buffer). | * - 0 on success (address copied to buffer). | ||||
* - EWOULDBLOCK (no local error, but address is still unresolved) | * - EWOULDBLOCK (no local error, but address is still unresolved) | ||||
* - other errors (alloc failure, etc) | * - other errors (alloc failure, etc) | ||||
*/ | */ | ||||
int | int | ||||
nd6_resolve(struct ifnet *ifp, int is_gw, struct mbuf *m, | nd6_resolve(struct ifnet *ifp, int gw_flags, struct mbuf *m, | ||||
const struct sockaddr *sa_dst, u_char *desten, uint32_t *pflags, | const struct sockaddr *sa_dst, u_char *desten, uint32_t *pflags, | ||||
struct llentry **plle) | struct llentry **plle) | ||||
{ | { | ||||
struct llentry *ln = NULL; | struct llentry *ln = NULL; | ||||
const struct sockaddr_in6 *dst6; | const struct sockaddr_in6 *dst6; | ||||
NET_EPOCH_ASSERT(); | NET_EPOCH_ASSERT(); | ||||
Show All 17 Lines | case IFT_BRIDGE: | ||||
desten); | desten); | ||||
return (0); | return (0); | ||||
default: | default: | ||||
m_freem(m); | m_freem(m); | ||||
return (EAFNOSUPPORT); | return (EAFNOSUPPORT); | ||||
} | } | ||||
} | } | ||||
ln = nd6_lookup(&dst6->sin6_addr, plle ? LLE_EXCLUSIVE : LLE_UNLOCKED, | int family = gw_flags >> 16; | ||||
ifp); | int lookup_flags = plle ? LLE_EXCLUSIVE : LLE_UNLOCKED; | ||||
ln = nd6_lookup(&dst6->sin6_addr, LLE_SF(family, lookup_flags), ifp); | |||||
if (ln != NULL && (ln->r_flags & RLLE_VALID) != 0) { | if (ln != NULL && (ln->r_flags & RLLE_VALID) != 0) { | ||||
/* Entry found, let's copy lle info */ | /* Entry found, let's copy lle info */ | ||||
bcopy(ln->r_linkdata, desten, ln->r_hdrlen); | bcopy(ln->r_linkdata, desten, ln->r_hdrlen); | ||||
if (pflags != NULL) | if (pflags != NULL) | ||||
*pflags = LLE_VALID | (ln->r_flags & RLLE_IFADDR); | *pflags = LLE_VALID | (ln->r_flags & RLLE_IFADDR); | ||||
llentry_provide_feedback(ln); | llentry_provide_feedback(ln); | ||||
if (plle) { | if (plle) { | ||||
LLE_ADDREF(ln); | LLE_ADDREF(ln); | ||||
*plle = ln; | *plle = ln; | ||||
LLE_WUNLOCK(ln); | LLE_WUNLOCK(ln); | ||||
} | } | ||||
return (0); | return (0); | ||||
} else if (plle && ln) | } else if (plle && ln) | ||||
LLE_WUNLOCK(ln); | LLE_WUNLOCK(ln); | ||||
return (nd6_resolve_slow(ifp, 0, m, dst6, desten, pflags, plle)); | return (nd6_resolve_slow(ifp, family, 0, m, dst6, desten, pflags, plle)); | ||||
} | } | ||||
/* | /* | ||||
* Finds or creates a new llentry for @addr. | * Finds or creates a new llentry for @addr and @family. | ||||
* Returns wlocked llentry or NULL. | * Returns wlocked llentry or NULL. | ||||
* | |||||
* | |||||
* Child LLEs. | |||||
* | |||||
* Do not have their own state machine (gets marked as static) | |||||
* settimer bails out for child LLEs just in case. | |||||
* | |||||
* Locking order: parent lle gets locked first, chen goes the child. | |||||
*/ | */ | ||||
static __noinline struct llentry * | static __noinline struct llentry * | ||||
nd6_get_llentry(struct ifnet *ifp, const struct in6_addr *addr) | nd6_get_llentry(struct ifnet *ifp, const struct in6_addr *addr, int family) | ||||
{ | { | ||||
zlei: Possible null pointer here. Missing `else` ? | |||||
Done Inline ActionsWhoops, good catch! melifaro: Whoops, good catch! | |||||
struct llentry *child_lle = NULL; | |||||
struct llentry *lle, *lle_tmp; | struct llentry *lle, *lle_tmp; | ||||
lle = nd6_alloc(addr, 0, ifp); | lle = nd6_alloc(addr, 0, ifp); | ||||
if (lle != NULL && family != AF_INET6) { | |||||
child_lle = nd6_alloc(addr, 0, ifp); | |||||
if (child_lle == NULL) { | |||||
lltable_free_entry(LLTABLE6(ifp), lle); | |||||
return (NULL); | |||||
} | |||||
child_lle->r_family = family; | |||||
child_lle->la_flags |= LLE_CHILD | LLE_STATIC; | |||||
child_lle->ln_state = ND6_LLINFO_INCOMPLETE; | |||||
} | |||||
if (lle == NULL) { | if (lle == NULL) { | ||||
char ip6buf[INET6_ADDRSTRLEN]; | char ip6buf[INET6_ADDRSTRLEN]; | ||||
log(LOG_DEBUG, | log(LOG_DEBUG, | ||||
"nd6_get_llentry: can't allocate llinfo for %s " | "nd6_get_llentry: can't allocate llinfo for %s " | ||||
"(ln=%p)\n", | "(ln=%p)\n", | ||||
ip6_sprintf(ip6buf, addr), lle); | ip6_sprintf(ip6buf, addr), lle); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
IF_AFDATA_WLOCK(ifp); | IF_AFDATA_WLOCK(ifp); | ||||
LLE_WLOCK(lle); | LLE_WLOCK(lle); | ||||
/* Prefer any existing entry over newly-created one */ | /* Prefer any existing entry over newly-created one */ | ||||
lle_tmp = nd6_lookup(addr, LLE_EXCLUSIVE, ifp); | lle_tmp = nd6_lookup(addr, LLE_SF(AF_INET6, LLE_EXCLUSIVE), ifp); | ||||
if (lle_tmp == NULL) | if (lle_tmp == NULL) | ||||
lltable_link_entry(LLTABLE6(ifp), lle); | lltable_link_entry(LLTABLE6(ifp), lle); | ||||
IF_AFDATA_WUNLOCK(ifp); | else { | ||||
if (lle_tmp != NULL) { | |||||
lltable_free_entry(LLTABLE6(ifp), lle); | lltable_free_entry(LLTABLE6(ifp), lle); | ||||
return (lle_tmp); | lle = lle_tmp; | ||||
} else | } | ||||
if (child_lle != NULL) { | |||||
/* Check if child lle for the same family exists */ | |||||
lle_tmp = llentry_lookup_family(lle, child_lle->r_family); | |||||
LLE_WLOCK(child_lle); | |||||
if (lle_tmp == NULL) { | |||||
/* Attach */ | |||||
lltable_link_child_entry(lle, child_lle); | |||||
} else { | |||||
/* child lle already exists, free newly-created one */ | |||||
lltable_free_entry(LLTABLE6(ifp), child_lle); | |||||
child_lle = lle_tmp; | |||||
} | |||||
LLE_WUNLOCK(lle); | |||||
lle = child_lle; | |||||
} | |||||
IF_AFDATA_WUNLOCK(ifp); | |||||
return (lle); | return (lle); | ||||
} | } | ||||
/* | /* | ||||
* Do L2 address resolution for @sa_dst address. Stores found | * Do L2 address resolution for @sa_dst address. Stores found | ||||
* address in @desten buffer. Copy of lle ln_flags can be also | * address in @desten buffer. Copy of lle ln_flags can be also | ||||
* saved in @pflags if @pflags is non-NULL. | * saved in @pflags if @pflags is non-NULL. | ||||
* | * | ||||
* Heavy version. | * Heavy version. | ||||
* Function assume that destination LLE does not exist, | * Function assume that destination LLE does not exist, | ||||
* is invalid or stale, so LLE_EXCLUSIVE lock needs to be acquired. | * is invalid or stale, so LLE_EXCLUSIVE lock needs to be acquired. | ||||
* | * | ||||
* Set noinline to be dtrace-friendly | * Set noinline to be dtrace-friendly | ||||
*/ | */ | ||||
static __noinline int | static __noinline int | ||||
nd6_resolve_slow(struct ifnet *ifp, int flags, struct mbuf *m, | nd6_resolve_slow(struct ifnet *ifp, int family, int flags, struct mbuf *m, | ||||
const struct sockaddr_in6 *dst, u_char *desten, uint32_t *pflags, | const struct sockaddr_in6 *dst, u_char *desten, uint32_t *pflags, | ||||
struct llentry **plle) | struct llentry **plle) | ||||
{ | { | ||||
struct llentry *lle = NULL; | struct llentry *lle = NULL; | ||||
struct in6_addr *psrc, src; | struct in6_addr *psrc, src; | ||||
int send_ns, ll_len; | int send_ns, ll_len; | ||||
char *lladdr; | char *lladdr; | ||||
NET_EPOCH_ASSERT(); | NET_EPOCH_ASSERT(); | ||||
/* | /* | ||||
* Address resolution or Neighbor Unreachability Detection | * Address resolution or Neighbor Unreachability Detection | ||||
* for the next hop. | * for the next hop. | ||||
* At this point, the destination of the packet must be a unicast | * At this point, the destination of the packet must be a unicast | ||||
* or an anycast address(i.e. not a multicast). | * or an anycast address(i.e. not a multicast). | ||||
*/ | */ | ||||
lle = nd6_lookup(&dst->sin6_addr, LLE_EXCLUSIVE, ifp); | lle = nd6_lookup(&dst->sin6_addr, LLE_SF(family, LLE_EXCLUSIVE), ifp); | ||||
if ((lle == NULL) && nd6_is_addr_neighbor(dst, ifp)) { | if ((lle == NULL) && nd6_is_addr_neighbor(dst, ifp)) { | ||||
/* | /* | ||||
* Since nd6_is_addr_neighbor() internally calls nd6_lookup(), | * Since nd6_is_addr_neighbor() internally calls nd6_lookup(), | ||||
* the condition below is not very efficient. But we believe | * the condition below is not very efficient. But we believe | ||||
* it is tolerable, because this should be a rare case. | * it is tolerable, because this should be a rare case. | ||||
*/ | */ | ||||
lle = nd6_get_llentry(ifp, &dst->sin6_addr); | lle = nd6_get_llentry(ifp, &dst->sin6_addr, family); | ||||
} | } | ||||
if (lle == NULL) { | if (lle == NULL) { | ||||
m_freem(m); | m_freem(m); | ||||
return (ENOBUFS); | return (ENOBUFS); | ||||
} | } | ||||
LLE_WLOCK_ASSERT(lle); | LLE_WLOCK_ASSERT(lle); | ||||
/* | /* | ||||
* The first time we send a packet to a neighbor whose entry is | * The first time we send a packet to a neighbor whose entry is | ||||
* STALE, we have to change the state to DELAY and a sets a timer to | * STALE, we have to change the state to DELAY and a sets a timer to | ||||
* expire in DELAY_FIRST_PROBE_TIME seconds to ensure do | * expire in DELAY_FIRST_PROBE_TIME seconds to ensure do | ||||
* neighbor unreachability detection on expiration. | * neighbor unreachability detection on expiration. | ||||
* (RFC 2461 7.3.3) | * (RFC 2461 7.3.3) | ||||
*/ | */ | ||||
if (lle->ln_state == ND6_LLINFO_STALE) | if ((!(lle->la_flags & LLE_CHILD)) && (lle->ln_state == ND6_LLINFO_STALE)) | ||||
nd6_llinfo_setstate(lle, ND6_LLINFO_DELAY); | nd6_llinfo_setstate(lle, ND6_LLINFO_DELAY); | ||||
/* | /* | ||||
* If the neighbor cache entry has a state other than INCOMPLETE | * If the neighbor cache entry has a state other than INCOMPLETE | ||||
* (i.e. its link-layer address is already resolved), just | * (i.e. its link-layer address is already resolved), just | ||||
* send the packet. | * send the packet. | ||||
*/ | */ | ||||
if (lle->ln_state > ND6_LLINFO_INCOMPLETE) { | if (lle->ln_state > ND6_LLINFO_INCOMPLETE) { | ||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | nd6_resolve_slow(struct ifnet *ifp, int family, int flags, struct mbuf *m, | ||||
* If there has been no NS for the neighbor after entering the | * If there has been no NS for the neighbor after entering the | ||||
* INCOMPLETE state, send the first solicitation. | * INCOMPLETE state, send the first solicitation. | ||||
* Note that for newly-created lle la_asked will be 0, | * Note that for newly-created lle la_asked will be 0, | ||||
* so we will transition from ND6_LLINFO_NOSTATE to | * so we will transition from ND6_LLINFO_NOSTATE to | ||||
* ND6_LLINFO_INCOMPLETE state here. | * ND6_LLINFO_INCOMPLETE state here. | ||||
*/ | */ | ||||
psrc = NULL; | psrc = NULL; | ||||
send_ns = 0; | send_ns = 0; | ||||
/* If we have child lle, switch to the parent to send NS */ | |||||
if (lle->la_flags & LLE_CHILD) { | |||||
struct llentry *lle_parent = lle->lle_parent; | |||||
LLE_WUNLOCK(lle); | |||||
lle = lle_parent; | |||||
LLE_WLOCK(lle); | |||||
} | |||||
if (lle->la_asked == 0) { | if (lle->la_asked == 0) { | ||||
lle->la_asked++; | lle->la_asked++; | ||||
send_ns = 1; | send_ns = 1; | ||||
psrc = nd6_llinfo_get_holdsrc(lle, &src); | psrc = nd6_llinfo_get_holdsrc(lle, &src); | ||||
nd6_llinfo_setstate(lle, ND6_LLINFO_INCOMPLETE); | nd6_llinfo_setstate(lle, ND6_LLINFO_INCOMPLETE); | ||||
} | } | ||||
LLE_WUNLOCK(lle); | LLE_WUNLOCK(lle); | ||||
Show All 15 Lines | |||||
*/ | */ | ||||
int | int | ||||
nd6_resolve_addr(struct ifnet *ifp, int flags, const struct sockaddr *dst, | nd6_resolve_addr(struct ifnet *ifp, int flags, const struct sockaddr *dst, | ||||
char *desten, uint32_t *pflags) | char *desten, uint32_t *pflags) | ||||
{ | { | ||||
int error; | int error; | ||||
flags |= LLE_ADDRONLY; | flags |= LLE_ADDRONLY; | ||||
error = nd6_resolve_slow(ifp, flags, NULL, | error = nd6_resolve_slow(ifp, AF_INET6, flags, NULL, | ||||
(const struct sockaddr_in6 *)dst, desten, pflags, NULL); | (const struct sockaddr_in6 *)dst, desten, pflags, NULL); | ||||
return (error); | return (error); | ||||
} | } | ||||
int | int | ||||
nd6_flush_holdchain(struct ifnet *ifp, struct llentry *lle, struct mbuf *chain) | nd6_flush_holdchain(struct ifnet *ifp, struct llentry *lle, struct mbuf *chain) | ||||
{ | { | ||||
struct mbuf *m, *m_head; | struct mbuf *m, *m_head; | ||||
Show All 19 Lines | nd6_flush_holdchain(struct ifnet *ifp, struct llentry *lle, struct mbuf *chain) | ||||
/* | /* | ||||
* XXX | * XXX | ||||
* note that intermediate errors are blindly ignored | * note that intermediate errors are blindly ignored | ||||
*/ | */ | ||||
return (error); | return (error); | ||||
} | } | ||||
__noinline void | |||||
nd6_flush_children_holdchain(struct ifnet *ifp, struct llentry *lle) | |||||
{ | |||||
struct llentry *child_lle; | |||||
struct mbuf *chain; | |||||
NET_EPOCH_ASSERT(); | |||||
CK_SLIST_FOREACH(child_lle, &lle->lle_children, lle_child_next) { | |||||
LLE_WLOCK(child_lle); | |||||
chain = nd6_grab_holdchain(child_lle); | |||||
LLE_WUNLOCK(child_lle); | |||||
nd6_flush_holdchain(ifp, child_lle, chain); | |||||
} | |||||
} | |||||
static int | static int | ||||
nd6_need_cache(struct ifnet *ifp) | nd6_need_cache(struct ifnet *ifp) | ||||
{ | { | ||||
/* | /* | ||||
* XXX: we currently do not make neighbor cache on any interface | * XXX: we currently do not make neighbor cache on any interface | ||||
* other than Ethernet and GIF. | * other than Ethernet and GIF. | ||||
* | * | ||||
* RFC2893 says: | * RFC2893 says: | ||||
Show All 37 Lines | nd6_add_ifa_lle(struct in6_ifaddr *ia) | ||||
dst = (struct sockaddr *)&ia->ia_addr; | dst = (struct sockaddr *)&ia->ia_addr; | ||||
ln = lltable_alloc_entry(LLTABLE6(ifp), LLE_IFADDR, dst); | ln = lltable_alloc_entry(LLTABLE6(ifp), LLE_IFADDR, dst); | ||||
if (ln == NULL) | if (ln == NULL) | ||||
return (ENOBUFS); | return (ENOBUFS); | ||||
IF_AFDATA_WLOCK(ifp); | IF_AFDATA_WLOCK(ifp); | ||||
LLE_WLOCK(ln); | LLE_WLOCK(ln); | ||||
/* Unlink any entry if exists */ | /* Unlink any entry if exists */ | ||||
ln_tmp = lla_lookup(LLTABLE6(ifp), LLE_EXCLUSIVE, dst); | ln_tmp = lla_lookup(LLTABLE6(ifp), LLE_SF(AF_INET6, LLE_EXCLUSIVE), dst); | ||||
if (ln_tmp != NULL) | if (ln_tmp != NULL) | ||||
lltable_unlink_entry(LLTABLE6(ifp), ln_tmp); | lltable_unlink_entry(LLTABLE6(ifp), ln_tmp); | ||||
lltable_link_entry(LLTABLE6(ifp), ln); | lltable_link_entry(LLTABLE6(ifp), ln); | ||||
IF_AFDATA_WUNLOCK(ifp); | IF_AFDATA_WUNLOCK(ifp); | ||||
if (ln_tmp != NULL) | if (ln_tmp != NULL) | ||||
EVENTHANDLER_INVOKE(lle_event, ln_tmp, LLENTRY_EXPIRED); | EVENTHANDLER_INVOKE(lle_event, ln_tmp, LLENTRY_EXPIRED); | ||||
EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_RESOLVED); | EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_RESOLVED); | ||||
▲ Show 20 Lines • Show All 125 Lines • Show Last 20 Lines |
Possible null pointer here. Missing else ?