Changeset View
Changeset View
Standalone View
Standalone View
sys/netinet6/nd6.c
Show First 20 Lines • Show All 134 Lines • ▼ Show 20 Lines | |||||
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, struct mbuf *, | ||||
const struct sockaddr_in6 *, u_char *, uint32_t *, struct llentry **, | const struct sockaddr_in6 *, u_char *, uint32_t *, struct llentry **); | ||||
gw_type_t); | |||||
static int nd6_need_cache(struct ifnet *); | static int nd6_need_cache(struct ifnet *); | ||||
static bool nd6_attach_encap_tag(struct mbuf *, gw_type_t); | |||||
static int output_ifp_one(struct ifnet *, struct mbuf *, struct sockaddr_in6 *); | |||||
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) | ||||
SYSCTL_DECL(_net_inet6_icmp6); | SYSCTL_DECL(_net_inet6_icmp6); | ||||
▲ Show 20 Lines • Show All 372 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 71 Lines • ▼ Show 20 Lines | |||||
* the next timer interval in @pdelay). | * the next timer interval in @pdelay). | ||||
* | * | ||||
* Returns zero value if original timer expired or we need to switch to | * Returns zero value if original timer expired or we need to switch to | ||||
* PROBE (store that in @do_switch variable). | * PROBE (store that in @do_switch variable). | ||||
*/ | */ | ||||
static int | static int | ||||
nd6_is_stale(struct llentry *lle, long *pdelay, int *do_switch) | nd6_is_stale(struct llentry *lle, long *pdelay, int *do_switch) | ||||
{ | { | ||||
int nd_delay, nd_gctimer, r_skip_req; | int nd_delay, nd_gctimer; | ||||
time_t lle_hittime; | time_t lle_hittime; | ||||
long delay; | long delay; | ||||
*do_switch = 0; | *do_switch = 0; | ||||
nd_gctimer = V_nd6_gctimer; | nd_gctimer = V_nd6_gctimer; | ||||
nd_delay = V_nd6_delay; | nd_delay = V_nd6_delay; | ||||
LLE_REQ_LOCK(lle); | lle_hittime = llentry_get_hittime(lle); | ||||
r_skip_req = lle->r_skip_req; | |||||
lle_hittime = lle->lle_hittime; | |||||
LLE_REQ_UNLOCK(lle); | |||||
if (r_skip_req > 0) { | if (lle_hittime == 0) { | ||||
/* | /* | ||||
* Nonzero r_skip_req value was set upon entering | * Nonzero r_skip_req value was set upon entering | ||||
* STALE state. Since value was not changed, no | * STALE state. Since value was not changed, no | ||||
* packets were passed using this lle. Ask for | * packets were passed using this lle. Ask for | ||||
* timer reschedule and keep STALE state. | * timer reschedule and keep STALE state. | ||||
*/ | */ | ||||
delay = (long)(MIN(nd_gctimer, nd_delay)); | delay = (long)(MIN(nd_gctimer, nd_delay)); | ||||
delay *= hz; | delay *= hz; | ||||
▲ Show 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | case ND6_LLINFO_REACHABLE: | ||||
} | } | ||||
break; | break; | ||||
case ND6_LLINFO_STALE: | case ND6_LLINFO_STALE: | ||||
/* | /* | ||||
* Notify fast path that we want to know if any packet | * Notify fast path that we want to know if any packet | ||||
* is transmitted by setting r_skip_req. | * is transmitted by setting r_skip_req. | ||||
*/ | */ | ||||
LLE_REQ_LOCK(lle); | llentry_request_feedback(lle); | ||||
lle->r_skip_req = 1; | |||||
LLE_REQ_UNLOCK(lle); | |||||
nd_delay = V_nd6_delay; | nd_delay = V_nd6_delay; | ||||
nd_gctimer = V_nd6_gctimer; | nd_gctimer = V_nd6_gctimer; | ||||
delay = (long)(MIN(nd_gctimer, nd_delay)) * hz; | delay = (long)(MIN(nd_gctimer, nd_delay)) * hz; | ||||
remtime = (long)nd_gctimer * hz - delay; | remtime = (long)nd_gctimer * hz - delay; | ||||
break; | break; | ||||
case ND6_LLINFO_DELAY: | case ND6_LLINFO_DELAY: | ||||
lle->la_asked = 0; | lle->la_asked = 0; | ||||
▲ Show 20 Lines • Show All 657 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, ND6_SF6(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, *tmp; | |||||
struct llentry_children_head head = CK_SLIST_HEAD_INITIALIZER(head); | |||||
NET_EPOCH_ASSERT(); | |||||
LLE_WLOCK_ASSERT(lle); | |||||
CK_SLIST_SWAP(&head, &lle->lle_children, llentry); | |||||
CK_SLIST_FOREACH_SAFE(child_lle, &head, lle_child_next, tmp) { | |||||
LLE_WLOCK(child_lle); | |||||
lltable_unlink_child_entry(child_lle); | |||||
llentry_free(child_lle); | |||||
} | |||||
} | |||||
static __noinline bool | |||||
nd6_try_set_entry_addr_unlocked(struct ifnet *ifp, struct llentry *ln, char *lladdr) | |||||
{ | |||||
u_char buf[LLE_MAX_LINKHDR]; | |||||
size_t sz; | |||||
int fam, off; | |||||
NET_EPOCH_ASSERT(); | |||||
/* Assumes ln is WLOCKED or not yet linked */ | |||||
/* Assumes AFDATA lock is held IF updating linked lle */ | |||||
sz = sizeof(buf); | |||||
if (lltable_calc_llheader(ifp, AF_INET6, lladdr, buf, &sz, &off) != 0) | |||||
return (false); | |||||
/* Update data */ | |||||
lltable_set_entry_addr(ifp, ln, buf, sz, off); | |||||
struct llentry *child_ln; | |||||
CK_SLIST_FOREACH(child_ln, &ln->lle_children, lle_child_next) { | |||||
LLE_WLOCK(child_ln); | |||||
fam = child_ln->r_family; | |||||
sz = sizeof(buf); | |||||
if (lltable_calc_llheader(ifp, fam, lladdr, buf, &sz, &off) == 0) { | |||||
/* success */ | |||||
lltable_set_entry_addr(ifp, child_ln, buf, sz, off); | |||||
child_ln->ln_state = ND6_LLINFO_REACHABLE; | |||||
} | |||||
LLE_WUNLOCK(child_ln); | |||||
} | |||||
return (true); | |||||
} | |||||
bool | |||||
nd6_try_set_entry_addr(struct ifnet *ifp, struct llentry *ln, char *lladdr) | |||||
{ | |||||
NET_EPOCH_ASSERT(); | |||||
LLE_WLOCK_ASSERT(ln); | |||||
if (!lltable_acquire_wlock(ifp, ln)) | |||||
return (false); | |||||
bool ret = nd6_try_set_entry_addr_unlocked(ifp, ln, lladdr); | |||||
IF_AFDATA_WUNLOCK(ifp); | |||||
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 257 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, ND6_SF6(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, | ||||
* Validation about ifp->if_addrlen and lladdrlen must be done in | * Validation about ifp->if_addrlen and lladdrlen must be done in | ||||
* 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 = ND6_SF6(lladdr ? LLE_EXCLUSIVE : 0); | ||||
ln = nd6_lookup(from, flags, ifp); | ln = nd6_lookup(from, 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, ND6_SF6(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 All 38 Lines | nd6_cache_lladdr(struct ifnet *ifp, struct in6_addr *from, char *lladdr, | ||||
do_update = 0; | do_update = 0; | ||||
if (is_newentry == 0 && llchange != 0) { | if (is_newentry == 0 && llchange != 0) { | ||||
do_update = 1; /* (3,5) */ | do_update = 1; /* (3,5) */ | ||||
/* | /* | ||||
* Record source link-layer address | * Record source link-layer address | ||||
* XXX is it dependent to ifp->if_type? | * XXX is it dependent to ifp->if_type? | ||||
*/ | */ | ||||
linkhdrsize = sizeof(linkhdr); | if (!nd6_try_set_entry_addr(ifp, ln, lladdr)) { | ||||
if (lltable_calc_llheader(ifp, AF_INET6, lladdr, | LLE_WUNLOCK(ln); | ||||
linkhdr, &linkhdrsize, &lladdr_off) != 0) | |||||
return; | return; | ||||
if (lltable_try_set_entry_addr(ifp, ln, linkhdr, linkhdrsize, | |||||
lladdr_off) == 0) { | |||||
/* Entry was deleted */ | |||||
return; | |||||
} | } | ||||
nd6_llinfo_setstate(ln, ND6_LLINFO_STALE); | nd6_llinfo_setstate(ln, ND6_LLINFO_STALE); | ||||
EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_RESOLVED); | EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_RESOLVED); | ||||
if (ln->la_hold != NULL) | if (ln->la_hold != NULL) | ||||
nd6_grab_holdchain(ln, &chain, &sin6); | nd6_grab_holdchain(ln, &chain, &sin6); | ||||
Show All 9 Lines | if ((type & 0xFF) == ND_REDIRECT && code != ND_REDIRECT_ROUTER) | ||||
ln->la_flags |= LLE_REDIRECT; | ln->la_flags |= LLE_REDIRECT; | ||||
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, chain, &sin6); | nd6_flush_holdchain(ifp, ln, chain, &sin6); | ||||
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 124 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, gw_type_t gw_type, 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); | if (family == 0) | ||||
family = AF_INET6; | |||||
int flags = ND6_SF(plle ? LLE_EXCLUSIVE : LLE_UNLOCKED, family); | |||||
ln = nd6_lookup(&dst6->sin6_addr, 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); | ||||
#ifdef INET | |||||
/* XXX fix ether type */ | |||||
if (gw_type == GW_IPV4_SRC) { | |||||
struct ether_header *eh; | |||||
uint16_t etype = htons(ETHERTYPE_IP); | |||||
eh = (struct ether_header *)desten; | |||||
bcopy(&etype, &eh->ether_type, sizeof(etype)); | |||||
} | |||||
#endif | |||||
if (pflags != NULL) | if (pflags != NULL) | ||||
*pflags = LLE_VALID | (ln->r_flags & RLLE_IFADDR); | *pflags = LLE_VALID | (ln->r_flags & RLLE_IFADDR); | ||||
/* Check if we have feedback request from nd6 timer */ | /* Check if we have feedback request from nd6 timer */ | ||||
if (ln->r_skip_req != 0) { | if (ln->r_skip_req != 0) { | ||||
LLE_REQ_LOCK(ln); | LLE_REQ_LOCK(ln); | ||||
ln->r_skip_req = 0; /* Notify that entry was used */ | ln->r_skip_req = 0; /* Notify that entry was used */ | ||||
ln->lle_hittime = time_uptime; | ln->lle_hittime = time_uptime; | ||||
LLE_REQ_UNLOCK(ln); | LLE_REQ_UNLOCK(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, | flags = family << 16; | ||||
gw_type)); | return (nd6_resolve_slow(ifp, flags, m, dst6, desten, pflags, plle)); | ||||
} | } | ||||
static __noinline struct llentry * | |||||
nd6_get_llentry(struct ifnet *ifp, const struct in6_addr *addr, int family) | |||||
{ | |||||
struct llentry *parent_lle = NULL, *child_lle = NULL; | |||||
struct llentry *lle = NULL, *lle_tmp; | |||||
parent_lle = nd6_alloc(addr, 0, ifp); | |||||
if (parent_lle != NULL && family != AF_INET6) { | |||||
child_lle = nd6_alloc(addr, 0, ifp); | |||||
if (child_lle == NULL) { | |||||
lltable_free_entry(LLTABLE6(ifp), parent_lle); | |||||
parent_lle = NULL; | |||||
} | |||||
child_lle->r_family = family; | |||||
zlei: Possible null pointer here. Missing `else` ? | |||||
melifaroAuthorUnsubmitted Done Inline ActionsWhoops, good catch! melifaro: Whoops, good catch! | |||||
child_lle->la_flags |= LLE_CHILD | LLE_STATIC; | |||||
child_lle->ln_state = ND6_LLINFO_INCOMPLETE; | |||||
} | |||||
if (parent_lle == NULL) { | |||||
char ip6buf[INET6_ADDRSTRLEN]; | |||||
log(LOG_DEBUG, "nd6_output: can't allocate llinfo for %s " | |||||
"(ln=NULL)\n", ip6_sprintf(ip6buf, addr)); | |||||
return (NULL); | |||||
} | |||||
IF_AFDATA_WLOCK(ifp); | |||||
LLE_WLOCK(parent_lle); | |||||
/* Prefer any existing entry over newly-created one */ | |||||
/* Search for parent lle first */ | |||||
int flags = ND6_SF6(LLE_EXCLUSIVE); | |||||
lle_tmp = nd6_lookup(addr, flags, ifp); | |||||
if (lle_tmp == NULL) | |||||
lltable_link_entry(LLTABLE6(ifp), parent_lle); | |||||
else { | |||||
lltable_free_entry(LLTABLE6(ifp), parent_lle); | |||||
parent_lle = lle_tmp; | |||||
} | |||||
/* parent_lle is the needed lle and is WLOCKED */ | |||||
if (child_lle != NULL) { | |||||
/* Check if child lle for the same family exists */ | |||||
lle_tmp = llentry_lookup_family(parent_lle, child_lle->r_family); | |||||
LLE_WLOCK(child_lle); | |||||
if (lle_tmp == NULL) { | |||||
/* Attach */ | |||||
lltable_link_child_entry(parent_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(parent_lle); | |||||
lle = child_lle; | |||||
} else | |||||
lle = parent_lle; | |||||
IF_AFDATA_WUNLOCK(ifp); | |||||
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 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, gw_type_t gw_type) | struct llentry **plle) | ||||
{ | { | ||||
struct llentry *lle = NULL, *lle_tmp; | 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). | ||||
*/ | */ | ||||
if (lle == NULL) { | if (lle == NULL) { | ||||
lle = nd6_lookup(&dst->sin6_addr, LLE_EXCLUSIVE, ifp); | int family = flags >> 16; | ||||
int nd6_flags = ND6_SF(LLE_EXCLUSIVE, family); | |||||
lle = nd6_lookup(&dst->sin6_addr, nd6_flags, 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_alloc(&dst->sin6_addr, 0, ifp); | lle = nd6_get_llentry(ifp, &dst->sin6_addr, family); | ||||
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); | |||||
} | } | ||||
IF_AFDATA_WLOCK(ifp); | |||||
LLE_WLOCK(lle); | |||||
/* Prefer any existing entry over newly-created one */ | |||||
lle_tmp = nd6_lookup(&dst->sin6_addr, LLE_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 (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) { | ||||
if (flags & LLE_ADDRONLY) { | if (flags & LLE_ADDRONLY) { | ||||
lladdr = lle->ll_addr; | lladdr = lle->ll_addr; | ||||
ll_len = ifp->if_addrlen; | ll_len = ifp->if_addrlen; | ||||
} else { | } else { | ||||
lladdr = lle->r_linkdata; | lladdr = lle->r_linkdata; | ||||
ll_len = lle->r_hdrlen; | ll_len = lle->r_hdrlen; | ||||
} | } | ||||
bcopy(lladdr, desten, ll_len); | bcopy(lladdr, desten, ll_len); | ||||
#ifdef INET | |||||
/* XXX fix ether type */ | |||||
if (gw_type == GW_IPV4_SRC) { | |||||
struct ether_header *eh; | |||||
uint16_t etype = htons(ETHERTYPE_IP); | |||||
eh = (struct ether_header *)desten; | |||||
bcopy(&etype, &eh->ether_type, sizeof(etype)); | |||||
} | |||||
#endif | |||||
if (pflags != NULL) | if (pflags != NULL) | ||||
*pflags = lle->la_flags; | *pflags = lle->la_flags; | ||||
if (plle) { | if (plle) { | ||||
LLE_ADDREF(lle); | LLE_ADDREF(lle); | ||||
*plle = lle; | *plle = lle; | ||||
} | } | ||||
LLE_WUNLOCK(lle); | LLE_WUNLOCK(lle); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* There is a neighbor cache entry, but no ethernet address | * There is a neighbor cache entry, but no ethernet address | ||||
* response yet. Append this latest packet to the end of the | * response yet. Append this latest packet to the end of the | ||||
* packet queue in the mbuf. When it exceeds nd6_maxqueuelen, | * packet queue in the mbuf. When it exceeds nd6_maxqueuelen, | ||||
* the oldest packet in the queue will be removed. | * the oldest packet in the queue will be removed. | ||||
*/ | */ | ||||
if (gw_type != GW_NONE && gw_type != GW_IPV6_SRC && | |||||
!nd6_attach_encap_tag(m, gw_type)) { | |||||
LLE_WUNLOCK(lle); | |||||
m_freem(m); | |||||
return (ENOMEM); | |||||
} | |||||
if (lle->la_hold != NULL) { | if (lle->la_hold != NULL) { | ||||
struct mbuf *m_hold; | struct mbuf *m_hold; | ||||
int i; | int i; | ||||
i = 0; | i = 0; | ||||
for (m_hold = lle->la_hold; m_hold; m_hold = m_hold->m_nextpkt){ | for (m_hold = lle->la_hold; m_hold; m_hold = m_hold->m_nextpkt){ | ||||
i++; | i++; | ||||
if (m_hold->m_nextpkt == NULL) { | if (m_hold->m_nextpkt == NULL) { | ||||
Show All 15 Lines | nd6_resolve_slow(struct ifnet *ifp, 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 (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 16 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, flags, NULL, | ||||
(const struct sockaddr_in6 *)dst, desten, pflags, NULL, GW_NONE); | (const struct sockaddr_in6 *)dst, desten, pflags, NULL); | ||||
return (error); | return (error); | ||||
} | } | ||||
int | int | ||||
nd6_flush_holdchain(struct ifnet *ifp, struct mbuf *chain, | nd6_flush_holdchain(struct ifnet *ifp, struct llentry *lle, struct mbuf *chain, | ||||
struct sockaddr_in6 *dst) | struct sockaddr_in6 *dst) | ||||
{ | { | ||||
struct mbuf *m, *m_head; | struct mbuf *m, *m_head; | ||||
int error = 0; | int error = 0; | ||||
struct route_in6 ro = { | |||||
.ro_prepend = lle->r_linkdata, | |||||
.ro_plen = lle->r_hdrlen, | |||||
}; | |||||
m_head = chain; | m_head = chain; | ||||
while (m_head) { | while (m_head) { | ||||
m = m_head; | m = m_head; | ||||
m_head = m_head->m_nextpkt; | m_head = m_head->m_nextpkt; | ||||
m->m_nextpkt = NULL; | m->m_nextpkt = NULL; | ||||
error = output_ifp_one(ifp, m, dst); | error = nd6_output_ifp(ifp, ifp, m, dst, (struct route *)&ro); | ||||
} | } | ||||
/* | /* | ||||
* XXX | * XXX | ||||
* note that intermediate errors are blindly ignored | * note that intermediate errors are blindly ignored | ||||
*/ | */ | ||||
return (error); | return (error); | ||||
} | } | ||||
#define MTAG_ND6_ENCAP_TYPE 4237446679 | __noinline void | ||||
nd6_flush_children_holdchain(struct ifnet *ifp, struct llentry *lle) | |||||
static bool | |||||
nd6_attach_encap_tag(struct mbuf *m, gw_type_t type) | |||||
{ | { | ||||
struct m_tag *mtag; | struct llentry *child_lle; | ||||
sa_family_t af = AF_UNSPEC; | struct sockaddr_in6 sin6; | ||||
#ifdef INET | NET_EPOCH_ASSERT(); | ||||
if (type == GW_IPV4_SRC) | |||||
af = AF_INET; | |||||
#endif | |||||
if (af == AF_UNSPEC) | |||||
return (true); | |||||
mtag = m_tag_alloc(MTAG_ND6_ENCAP_TYPE, 0, sizeof(sa_family_t), | |||||
M_NOWAIT); | |||||
if (mtag == NULL) | |||||
return (false); | |||||
*(sa_family_t *)(mtag + 1) = af; | |||||
m_tag_prepend(m, mtag); | |||||
return (true); | CK_SLIST_FOREACH(child_lle, &lle->lle_children, lle_child_next) { | ||||
struct mbuf *chain = NULL; | |||||
LLE_WLOCK(child_lle); | |||||
if (child_lle->la_hold != NULL) | |||||
nd6_grab_holdchain(child_lle, &chain, &sin6); | |||||
LLE_WUNLOCK(child_lle); | |||||
if (chain != NULL) | |||||
nd6_flush_holdchain(ifp, child_lle, chain, &sin6); | |||||
} | } | ||||
static int | |||||
output_ifp_one(struct ifnet *ifp, struct mbuf *m, struct sockaddr_in6 *dst) | |||||
{ | |||||
int error; | |||||
struct m_tag *mtag; | |||||
sa_family_t af; | |||||
struct route ro; | |||||
mtag = m_tag_locate(m, MTAG_ND6_ENCAP_TYPE, 0, NULL); | |||||
if (mtag != NULL) { | |||||
af = *(sa_family_t *)(mtag + 1); | |||||
#ifdef INET | |||||
KASSERT(af == AF_INET, ("Unexpected af %d", af)); | |||||
#endif | |||||
bzero(&ro, sizeof(struct route)); | |||||
ro.ro_flags = RT_HAS_GW; | |||||
ro.ro_dst.sa_family = af; /* XXX only restore af */ | |||||
/* XXX no ro.ro_dst.sa_len */ | |||||
error = (*ifp->if_output)(ifp, m, | |||||
(const struct sockaddr *)dst, &ro); | |||||
} else | |||||
error = nd6_output_ifp(ifp, ifp, m, dst, NULL); | |||||
return (error); | |||||
} | } | ||||
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. | ||||
Show All 39 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), ND6_SF6(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 ?