Page MenuHomeFreeBSD

D31379.id93144.diff
No OneTemporary

D31379.id93144.diff

Index: sbin/route/route.c
===================================================================
--- sbin/route/route.c
+++ sbin/route/route.c
@@ -1149,6 +1149,14 @@
}
#endif
+static int
+guess_af(char *str)
+{
+ if (strchr(str, ':'))
+ return (AF_INET6);
+ return (AF_INET);
+}
+
/*
* Interpret an argument as a network address of some kind,
* returning 1 if a host address, 0 if a network address.
@@ -1184,8 +1192,16 @@
#endif
rtm_addrs |= (1 << idx);
sa = (struct sockaddr *)&so[idx];
- sa->sa_family = af;
- sa->sa_len = aflen;
+ switch (guess_af(str)) {
+ case AF_INET6:
+ sa->sa_family = AF_INET6;
+ sa->sa_len = sizeof(struct sockaddr_in6);
+ break;
+ case AF_INET:
+ sa->sa_family = AF_INET;
+ sa->sa_len = sizeof(struct sockaddr_in);
+ break;
+ }
switch (idx) {
case RTAX_GATEWAY:
Index: sys/net/if_ethersubr.c
===================================================================
--- sys/net/if_ethersubr.c
+++ sys/net/if_ethersubr.c
@@ -204,6 +204,7 @@
struct ether_header *eh;
uint32_t lleflags = 0;
int error = 0;
+ int family = RO_GET_FAMILY(ro, dst);
#if defined(INET) || defined(INET6)
uint16_t etype;
#endif
@@ -235,10 +236,11 @@
#endif
#ifdef INET6
case AF_INET6:
- if ((m->m_flags & M_MCAST) == 0)
- error = nd6_resolve(ifp, ro_get_gw_type(ro), m, dst,
- phdr, &lleflags, plle);
- else {
+ if ((m->m_flags & M_MCAST) == 0) {
+ int flags = family << 16;
+ error = nd6_resolve(ifp, flags, m, dst, phdr, &lleflags,
+ plle);
+ } else {
const struct in6_addr *a6;
a6 = &(((const struct sockaddr_in6 *)dst)->sin6_addr);
ETHER_MAP_IPV6_MULTICAST(a6, eh->ether_dhost);
@@ -289,7 +291,6 @@
uint32_t pflags;
struct llentry *lle = NULL;
int addref = 0;
- int af = RO_GET_FAMILY(ro, dst);
phdr = NULL;
pflags = 0;
@@ -353,7 +354,7 @@
if ((pflags & RT_L2_ME) != 0) {
update_mbuf_csumflags(m, m);
- return (if_simloop(ifp, m, af, 0));
+ return (if_simloop(ifp, m, RO_GET_FAMILY(ro, dst), 0));
}
loop_copy = (pflags & RT_MAY_LOOP) != 0;
@@ -371,19 +372,6 @@
if ((pflags & RT_HAS_HEADER) == 0) {
eh = mtod(m, struct ether_header *);
memcpy(eh, phdr, hlen);
-#if defined(INET) && defined(INET6)
- /* XXX phdr might be from lle cache, let's fix the ether_type */
- /* FIXME maybe we can cache phdr in route ??? */
- if (dst->sa_family != af) {
- uint16_t etype = 0;
- if (af == AF_INET)
- etype = htons(ETHERTYPE_IP);
- else if (af == AF_INET6)
- etype = htons(ETHERTYPE_IPV6);
- if (etype != 0)
- memcpy(&eh->ether_type, &etype, sizeof(etype));
- }
-#endif
}
/*
@@ -413,7 +401,7 @@
*/
if ((n = m_dup(m, M_NOWAIT)) != NULL) {
update_mbuf_csumflags(m, n);
- (void)if_simloop(ifp, n, af, hlen);
+ (void)if_simloop(ifp, n, RO_GET_FAMILY(ro, dst), hlen);
} else
if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
}
Index: sys/net/if_fwsubr.c
===================================================================
--- sys/net/if_fwsubr.c
+++ sys/net/if_fwsubr.c
@@ -195,8 +195,8 @@
#ifdef INET6
case AF_INET6:
if (unicast) {
- error = nd6_resolve(fc->fc_ifp, ro_get_gw_type(ro), m,
- dst, (u_char *) destfw, NULL, NULL);
+ error = nd6_resolve(fc->fc_ifp, 0, m, dst,
+ (u_char *) destfw, NULL, NULL);
if (error)
return (error == EWOULDBLOCK ? 0 : error);
}
Index: sys/net/if_infiniband.c
===================================================================
--- sys/net/if_infiniband.c
+++ sys/net/if_infiniband.c
@@ -253,8 +253,8 @@
#ifdef INET6
case AF_INET6:
if ((m->m_flags & M_MCAST) == 0) {
- error = nd6_resolve(ifp, ro_get_gw_type(ro), m, dst,
- phdr, &lleflags, plle);
+ error = nd6_resolve(ifp, 0, m, dst, phdr, &lleflags,
+ plle);
} else {
infiniband_ipv6_multicast_map(
&((const struct sockaddr_in6 *)dst)->sin6_addr,
Index: sys/net/if_llatbl.h
===================================================================
--- sys/net/if_llatbl.h
+++ sys/net/if_llatbl.h
@@ -58,7 +58,8 @@
} r_l3addr;
char r_linkdata[LLE_MAX_LINKHDR]; /* L2 data */
uint8_t r_hdrlen; /* length for LL header */
- uint8_t spare0[3];
+ uint8_t r_family; /* Upper layer proto family */
+ uint8_t spare0[2];
uint16_t r_flags; /* LLE runtime flags */
uint16_t r_skip_req; /* feedback from fast path */
@@ -78,6 +79,9 @@
time_t lle_hittime; /* Time when r_skip_req was unset */
int lle_refcnt;
char *ll_addr; /* link-layer address */
+ CK_SLIST_HEAD(llentry_children_head,llentry) lle_children; /* child encaps */
+ CK_SLIST_ENTRY(llentry) lle_child_next; /* child encaps */
+ struct llentry *lle_parent; /* parent for a child */
CK_LIST_ENTRY(llentry) lle_chain; /* chain of deleted items */
struct callout lle_timer;
@@ -104,6 +108,8 @@
#define LLE_IS_VALID(lle) (((lle) != NULL) && ((lle) != (void *)-1))
+#define LLE_SF(_fl, _fam) (((_fl) & 0xFFFF) | ((_fam) << 16))
+
#define LLE_ADDREF(lle) do { \
LLE_WLOCK_ASSERT(lle); \
KASSERT((lle)->lle_refcnt >= 0, \
@@ -195,6 +201,7 @@
#define LLE_REDIRECT 0x0010 /* installed by redirect; has host rtentry */
#define LLE_PUB 0x0020 /* publish entry ??? */
#define LLE_LINKED 0x0040 /* linked to lookup structure */
+#define LLE_CHILD 0x0080 /* Child LLE storing different AF encap */
/* LLE request flags */
#define LLE_EXCLUSIVE 0x2000 /* return lle xlocked */
#define LLE_UNLOCKED 0x4000 /* return lle unlocked */
@@ -234,10 +241,14 @@
const struct sockaddr *l3addr);
int lltable_link_entry(struct lltable *llt, struct llentry *lle);
int lltable_unlink_entry(struct lltable *llt, struct llentry *lle);
+void lltable_link_child_entry(struct llentry *parent_lle, struct llentry *child_lle);
+void lltable_unlink_child_entry(struct llentry *child_lle);
void lltable_fill_sa_entry(const struct llentry *lle, struct sockaddr *sa);
struct ifnet *lltable_get_ifp(const struct lltable *llt);
int lltable_get_af(const struct lltable *llt);
+bool lltable_acquire_wlock(struct ifnet *ifp, struct llentry *lle);
+
int lltable_foreach_lle(struct lltable *llt, llt_foreach_cb_t *f,
void *farg);
/*
@@ -263,6 +274,11 @@
lle->lle_tbl->llt_mark_used(lle);
}
+struct llentry *llentry_lookup_family(struct llentry *lle, int family);
+void llentry_request_feedback(struct llentry *lle);
+time_t llentry_get_hittime(struct llentry *lle);
+
+
int lla_rt_output(struct rt_msghdr *, struct rt_addrinfo *);
enum {
Index: sys/net/if_llatbl.c
===================================================================
--- sys/net/if_llatbl.c
+++ sys/net/if_llatbl.c
@@ -317,23 +317,13 @@
lle->r_flags |= RLLE_VALID;
}
-/*
- * Tries to update @lle link-level address.
- * Since update requires AFDATA WLOCK, function
- * drops @lle lock, acquires AFDATA lock and then acquires
- * @lle lock to maintain lock order.
- *
- * Returns 1 on success.
- */
-int
-lltable_try_set_entry_addr(struct ifnet *ifp, struct llentry *lle,
- const char *linkhdr, size_t linkhdrsize, int lladdr_off)
+bool
+lltable_acquire_wlock(struct ifnet *ifp, struct llentry *lle)
{
+ NET_EPOCH_ASSERT();
/* Perform real LLE update */
/* use afdata WLOCK to update fields */
- LLE_WLOCK_ASSERT(lle);
- LLE_ADDREF(lle);
LLE_WUNLOCK(lle);
IF_AFDATA_WLOCK(ifp);
LLE_WLOCK(lle);
@@ -344,17 +334,33 @@
*/
if ((lle->la_flags & LLE_DELETED) != 0) {
IF_AFDATA_WUNLOCK(ifp);
- LLE_FREE_LOCKED(lle);
- return (0);
+ return (false);
}
+ return (true);
+}
+
+/*
+ * Tries to update @lle link-level address.
+ * Since update requires AFDATA WLOCK, function
+ * drops @lle lock, acquires AFDATA lock and then acquires
+ * @lle lock to maintain lock order.
+ *
+ * Returns 1 on success.
+ */
+int
+lltable_try_set_entry_addr(struct ifnet *ifp, struct llentry *lle,
+ const char *linkhdr, size_t linkhdrsize, int lladdr_off)
+{
+
+ if (!lltable_acquire_wlock(ifp, lle))
+ return (0);
+
/* Update data */
lltable_set_entry_addr(ifp, lle, linkhdr, linkhdrsize, lladdr_off);
IF_AFDATA_WUNLOCK(ifp);
- LLE_REMREF(lle);
-
return (1);
}
@@ -386,6 +392,74 @@
return (error);
}
+/*
+ * Searches for the child entry matching @family inside @lle.
+ * Returns the entry or NULL.
+ */
+struct llentry *
+llentry_lookup_family(struct llentry *lle, int family)
+{
+ struct llentry *child_lle;
+
+ if (lle == NULL)
+ return (NULL);
+
+ CK_SLIST_FOREACH(child_lle, &lle->lle_children, lle_child_next) {
+ if (child_lle->r_family == family)
+ return (child_lle);
+ }
+
+ return (NULL);
+}
+
+void
+llentry_request_feedback(struct llentry *lle)
+{
+ struct llentry *child_lle;
+
+ LLE_REQ_LOCK(lle);
+ lle->r_skip_req = 1;
+ LLE_REQ_UNLOCK(lle);
+
+ CK_SLIST_FOREACH(child_lle, &lle->lle_children, lle_child_next) {
+ LLE_REQ_LOCK(child_lle);
+ child_lle->r_skip_req = 1;
+ LLE_REQ_UNLOCK(child_lle);
+ }
+
+}
+
+static time_t
+llentry_get_hittime_raw(struct llentry *lle)
+{
+ time_t lle_hittime = 0;
+
+ LLE_REQ_LOCK(lle);
+ if ((lle->r_skip_req == 0) && (lle_hittime < lle->lle_hittime))
+ lle_hittime = lle->lle_hittime;
+ LLE_REQ_UNLOCK(lle);
+
+ return (lle_hittime);
+}
+
+time_t
+llentry_get_hittime(struct llentry *lle)
+{
+ time_t lle_hittime = 0;
+ struct llentry *child_lle;
+
+ lle_hittime = llentry_get_hittime_raw(lle);
+
+ CK_SLIST_FOREACH(child_lle, &lle->lle_children, lle_child_next) {
+ time_t hittime = llentry_get_hittime_raw(child_lle);
+ if (hittime > lle_hittime)
+ lle_hittime = hittime;
+ }
+
+ return (lle_hittime);
+}
+
+
/*
* Update link-layer header for given @lle after
* interface lladdr was changed.
@@ -527,7 +601,7 @@
ifp = llt->llt_ifp;
IF_AFDATA_WLOCK(ifp);
- lle = lla_lookup(llt, LLE_EXCLUSIVE, l3addr);
+ lle = lla_lookup(llt, LLE_SF(LLE_EXCLUSIVE, l3addr->sa_family), l3addr);
if (lle == NULL) {
IF_AFDATA_WUNLOCK(ifp);
@@ -642,6 +716,22 @@
return (llt->llt_link_entry(llt, lle));
}
+void
+lltable_link_child_entry(struct llentry *parent_lle, struct llentry *child_lle)
+{
+ child_lle->lle_parent = parent_lle;
+ child_lle->lle_tbl = parent_lle->lle_tbl;
+ child_lle->la_flags |= LLE_LINKED;
+ CK_SLIST_INSERT_HEAD(&parent_lle->lle_children, child_lle, lle_child_next);
+}
+
+void
+lltable_unlink_child_entry(struct llentry *child_lle)
+{
+ child_lle->la_flags &= ~LLE_LINKED;
+ child_lle->lle_parent = NULL;
+}
+
int
lltable_unlink_entry(struct lltable *llt, struct llentry *lle)
{
Index: sys/net/route.h
===================================================================
--- sys/net/route.h
+++ sys/net/route.h
@@ -230,38 +230,6 @@
#define NHR_COPY 0x100 /* Copy rte data */
#define NHR_UNLOCKED 0x200 /* Do not lock table */
-/* nd6 / arpresolve gateway type */
-/* FIXME need proper place */
-typedef enum {
- GW_NONE = 0,
-#ifdef INET
- GW_IPV4_SRC,
-#endif
-#ifdef INET6
- GW_IPV6_SRC
-#endif
-} gw_type_t;
-
-/* FIXME need proper place */
-static __inline gw_type_t
-ro_get_gw_type(struct route *ro)
-{
- if (ro == NULL || (ro->ro_flags & RT_HAS_GW) == 0)
- return GW_NONE;
- switch(ro->ro_dst.sa_family) {
-#ifdef INET
- case AF_INET:
- return GW_IPV4_SRC;
-#endif
-#ifdef INET6
- case AF_INET6:
- return GW_IPV6_SRC;
-#endif
- default:
- return GW_NONE;
- }
-}
-
/*
* Routing statistics.
*/
Index: sys/netinet6/icmp6.c
===================================================================
--- sys/netinet6/icmp6.c
+++ sys/netinet6/icmp6.c
@@ -2546,7 +2546,7 @@
struct nd_opt_hdr *nd_opt;
char *lladdr;
- ln = nd6_lookup(router_ll6, 0, ifp);
+ ln = nd6_lookup(router_ll6, ND6_SF6(0), ifp);
if (ln == NULL)
goto nolladdropt;
Index: sys/netinet6/in6.c
===================================================================
--- sys/netinet6/in6.c
+++ sys/netinet6/in6.c
@@ -2352,6 +2352,11 @@
("wrong lle request flags: %#x", flags));
lle = in6_lltable_find_dst(llt, &sin6->sin6_addr);
+
+ int family = flags >> 16;
+ if (__predict_false(family != AF_INET6))
+ lle = llentry_lookup_family(lle, family);
+
if (lle == NULL)
return (NULL);
if (flags & LLE_UNLOCKED)
Index: sys/netinet6/nd6.h
===================================================================
--- sys/netinet6/nd6.h
+++ sys/netinet6/nd6.h
@@ -353,6 +353,9 @@
#define nd_opts_last nd_opt_each.last
#define nd_opts_done nd_opt_each.done
+#define ND6_SF(_flags, _family) LLE_SF(_flags, _family)
+#define ND6_SF6(_flags) ND6_SF(_flags, AF_INET6)
+
/* XXX: need nd6_var.h?? */
/* nd6.c */
void nd6_init(void);
@@ -372,19 +375,21 @@
void nd6_purge(struct ifnet *);
int nd6_resolve_addr(struct ifnet *ifp, int flags, const struct sockaddr *dst,
char *desten, uint32_t *pflags);
-int nd6_resolve(struct ifnet *, gw_type_t, struct mbuf *,
+int nd6_resolve(struct ifnet *, int, struct mbuf *,
const struct sockaddr *, u_char *, uint32_t *, struct llentry **);
int nd6_ioctl(u_long, caddr_t, struct ifnet *);
void nd6_cache_lladdr(struct ifnet *, struct in6_addr *,
char *, int, int, int);
void nd6_grab_holdchain(struct llentry *, struct mbuf **,
struct sockaddr_in6 *);
-int nd6_flush_holdchain(struct ifnet *, struct mbuf *,
+int nd6_flush_holdchain(struct ifnet *, struct llentry *, struct mbuf *,
struct sockaddr_in6 *);
int nd6_add_ifa_lle(struct in6_ifaddr *);
void nd6_rem_ifa_lle(struct in6_ifaddr *, int);
int nd6_output_ifp(struct ifnet *, struct ifnet *, struct mbuf *,
struct sockaddr_in6 *, struct route *);
+bool nd6_try_set_entry_addr(struct ifnet *ifp, struct llentry *ln, char *lladdr);
+void nd6_flush_children_holdchain(struct ifnet *ifp, struct llentry *lle);
struct rib_head;
struct rib_cmd_info;
Index: sys/netinet6/nd6.c
===================================================================
--- sys/netinet6/nd6.c
+++ sys/netinet6/nd6.c
@@ -140,11 +140,8 @@
static void nd6_llinfo_settimer_locked(struct llentry *, long);
static void clear_llinfo_pqueue(struct llentry *);
static int nd6_resolve_slow(struct ifnet *, int, struct mbuf *,
- const struct sockaddr_in6 *, u_char *, uint32_t *, struct llentry **,
- gw_type_t);
+ const struct sockaddr_in6 *, u_char *, uint32_t *, struct llentry **);
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);
#define V_nd6_slowtimo_ch VNET(nd6_slowtimo_ch)
@@ -533,6 +530,10 @@
LLE_WLOCK_ASSERT(ln);
+ /* Do not schedule timers for child LLEs */
+ if (ln->la_flags & LLE_CHILD)
+ return;
+
if (tick < 0) {
ln->la_expire = 0;
ln->ln_ntick = 0;
@@ -620,7 +621,7 @@
static int
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;
long delay;
@@ -628,12 +629,9 @@
nd_gctimer = V_nd6_gctimer;
nd_delay = V_nd6_delay;
- LLE_REQ_LOCK(lle);
- r_skip_req = lle->r_skip_req;
- lle_hittime = lle->lle_hittime;
- LLE_REQ_UNLOCK(lle);
+ lle_hittime = llentry_get_hittime(lle);
- if (r_skip_req > 0) {
+ if (lle_hittime == 0) {
/*
* Nonzero r_skip_req value was set upon entering
* STALE state. Since value was not changed, no
@@ -712,9 +710,7 @@
* Notify fast path that we want to know if any packet
* is transmitted by setting r_skip_req.
*/
- LLE_REQ_LOCK(lle);
- lle->r_skip_req = 1;
- LLE_REQ_UNLOCK(lle);
+ llentry_request_feedback(lle);
nd_delay = V_nd6_delay;
nd_gctimer = V_nd6_gctimer;
@@ -1388,13 +1384,81 @@
* Even if the address matches none of our addresses, it might be
* 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);
rc = 1;
}
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.
* Since the function would cause significant changes in the kernel, DO NOT
@@ -1416,6 +1480,8 @@
LLE_WLOCK_ASSERT(ln);
ND6_RLOCK_ASSERT();
+ KASSERT((ln->la_flags & LLE_CHILD) == 0, ("child lle"));
+
ifp = lltable_get_ifp(ln->lle_tbl);
if ((ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV) != 0)
dr = defrouter_lookup_locked(&ln->r_l3addr.addr6, ifp);
@@ -1537,7 +1603,10 @@
}
IF_AFDATA_UNLOCK(ifp);
+ nd6_free_children(ln);
+
llentry_free(ln);
+
if (dr != NULL)
defrouter_rele(dr);
}
@@ -1811,7 +1880,7 @@
return (error);
NET_EPOCH_ENTER(et);
- ln = nd6_lookup(&nb_addr, 0, ifp);
+ ln = nd6_lookup(&nb_addr, ND6_SF6(0), ifp);
NET_EPOCH_EXIT(et);
if (ln == NULL) {
@@ -1961,7 +2030,7 @@
* Spec says nothing in sections for RA, RS and NA. There's small
* 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);
is_newentry = 0;
if (ln == NULL) {
@@ -1986,7 +2055,7 @@
IF_AFDATA_WLOCK(ifp);
LLE_WLOCK(ln);
/* 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)
lltable_link_entry(LLTABLE6(ifp), ln);
IF_AFDATA_WUNLOCK(ifp);
@@ -2041,14 +2110,8 @@
* Record source link-layer address
* XXX is it dependent to ifp->if_type?
*/
- linkhdrsize = sizeof(linkhdr);
- if (lltable_calc_llheader(ifp, AF_INET6, lladdr,
- linkhdr, &linkhdrsize, &lladdr_off) != 0)
- return;
-
- if (lltable_try_set_entry_addr(ifp, ln, linkhdr, linkhdrsize,
- lladdr_off) == 0) {
- /* Entry was deleted */
+ if (!nd6_try_set_entry_addr(ifp, ln, lladdr)) {
+ LLE_WUNLOCK(ln);
return;
}
@@ -2075,7 +2138,9 @@
LLE_RUNLOCK(ln);
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
@@ -2216,7 +2281,7 @@
* - other errors (alloc failure, etc)
*/
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,
struct llentry **plle)
{
@@ -2250,20 +2315,14 @@
}
}
- ln = nd6_lookup(&dst6->sin6_addr, plle ? LLE_EXCLUSIVE : LLE_UNLOCKED,
- ifp);
+ int family = gw_flags >> 16;
+ 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) {
/* Entry found, let's copy lle info */
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)
*pflags = LLE_VALID | (ln->r_flags & RLLE_IFADDR);
/* Check if we have feedback request from nd6 timer */
@@ -2282,8 +2341,69 @@
} else if (plle && ln)
LLE_WUNLOCK(ln);
- return (nd6_resolve_slow(ifp, 0, m, dst6, desten, pflags, plle,
- gw_type));
+ flags = family << 16;
+ 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;
+ 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);
}
/*
@@ -2300,9 +2420,9 @@
static __noinline int
nd6_resolve_slow(struct ifnet *ifp, int flags, struct mbuf *m,
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;
int send_ns, ll_len;
char *lladdr;
@@ -2316,36 +2436,16 @@
* or an anycast address(i.e. not a multicast).
*/
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)) {
/*
* Since nd6_is_addr_neighbor() internally calls nd6_lookup(),
* the condition below is not very efficient. But we believe
* it is tolerable, because this should be a rare case.
*/
- 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);
- }
-
- 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;
- }
+ lle = nd6_get_llentry(ifp, &dst->sin6_addr, family);
}
}
if (lle == NULL) {
@@ -2362,7 +2462,7 @@
* neighbor unreachability detection on expiration.
* (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);
/*
@@ -2379,15 +2479,6 @@
ll_len = lle->r_hdrlen;
}
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)
*pflags = lle->la_flags;
if (plle) {
@@ -2404,13 +2495,6 @@
* packet queue in the mbuf. When it exceeds nd6_maxqueuelen,
* 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) {
struct mbuf *m_hold;
int i;
@@ -2442,6 +2526,13 @@
*/
psrc = NULL;
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) {
lle->la_asked++;
send_ns = 1;
@@ -2474,24 +2565,27 @@
flags |= LLE_ADDRONLY;
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);
}
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 mbuf *m, *m_head;
int error = 0;
+ struct route_in6 ro = {
+ .ro_prepend = lle->r_linkdata,
+ .ro_plen = lle->r_hdrlen,
+ };
m_head = chain;
-
while (m_head) {
m = m_head;
m_head = m_head->m_nextpkt;
m->m_nextpkt = NULL;
- error = output_ifp_one(ifp, m, dst);
+ error = nd6_output_ifp(ifp, ifp, m, dst, (struct route *)&ro);
}
/*
@@ -2501,54 +2595,23 @@
return (error);
}
-#define MTAG_ND6_ENCAP_TYPE 4237446679
-
-static bool
-nd6_attach_encap_tag(struct mbuf *m, gw_type_t type)
+__noinline void
+nd6_flush_children_holdchain(struct ifnet *ifp, struct llentry *lle)
{
- struct m_tag *mtag;
- sa_family_t af = AF_UNSPEC;
-
-#ifdef INET
- 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);
-}
+ struct llentry *child_lle;
+ struct sockaddr_in6 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);
+ NET_EPOCH_ASSERT();
- return (error);
+ 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
@@ -2604,7 +2667,7 @@
IF_AFDATA_WLOCK(ifp);
LLE_WLOCK(ln);
/* 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)
lltable_unlink_entry(LLTABLE6(ifp), ln_tmp);
lltable_link_entry(LLTABLE6(ifp), ln);
Index: sys/netinet6/nd6_nbr.c
===================================================================
--- sys/netinet6/nd6_nbr.c
+++ sys/netinet6/nd6_nbr.c
@@ -625,12 +625,11 @@
struct in6_addr daddr6, taddr6;
struct sockaddr_in6 sin6;
union nd_opts ndopts;
- u_char linkhdr[LLE_MAX_LINKHDR];
char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN];
char *lladdr;
- size_t linkhdrsize;
int flags, is_override, is_router, is_solicited;
- int lladdr_off, lladdrlen, checklink;
+ int lladdrlen, checklink;
+ bool flush_chains = false;
NET_EPOCH_ASSERT();
@@ -748,7 +747,7 @@
* If no neighbor cache entry is found, NA SHOULD silently be
* discarded.
*/
- ln = nd6_lookup(&taddr6, LLE_EXCLUSIVE, ifp);
+ ln = nd6_lookup(&taddr6, ND6_SF6(LLE_EXCLUSIVE), ifp);
if (ln == NULL) {
goto freeit;
}
@@ -771,16 +770,10 @@
/*
* Record link-layer address, and update the state.
*/
- linkhdrsize = sizeof(linkhdr);
- if (lltable_calc_llheader(ifp, AF_INET6, lladdr,
- linkhdr, &linkhdrsize, &lladdr_off) != 0)
- return;
-
- if (lltable_try_set_entry_addr(ifp, ln, linkhdr, linkhdrsize,
- lladdr_off) == 0) {
- ln = NULL;
+ if (!nd6_try_set_entry_addr(ifp, ln, lladdr))
goto freeit;
- }
+
+ flush_chains = true;
EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_RESOLVED);
if (is_solicited)
nd6_llinfo_setstate(ln, ND6_LLINFO_REACHABLE);
@@ -846,15 +839,10 @@
* Update link-local address, if any.
*/
if (lladdr != NULL) {
- linkhdrsize = sizeof(linkhdr);
- if (lltable_calc_llheader(ifp, AF_INET6, lladdr,
- linkhdr, &linkhdrsize, &lladdr_off) != 0)
- goto freeit;
- if (lltable_try_set_entry_addr(ifp, ln, linkhdr,
- linkhdrsize, lladdr_off) == 0) {
- ln = NULL;
+ if (!nd6_try_set_entry_addr(ifp, ln, lladdr))
goto freeit;
- }
+
+ flush_chains = true;
EVENTHANDLER_INVOKE(lle_event, ln,
LLENTRY_RESOLVED);
}
@@ -908,7 +896,9 @@
LLE_WUNLOCK(ln);
if (chain != NULL)
- nd6_flush_holdchain(ifp, chain, &sin6);
+ nd6_flush_holdchain(ifp, ln, chain, &sin6);
+ if (flush_chains)
+ nd6_flush_children_holdchain(ifp, ln);
if (checklink)
pfxlist_onlink_check();
Index: sys/netinet6/nd6_rtr.c
===================================================================
--- sys/netinet6/nd6_rtr.c
+++ sys/netinet6/nd6_rtr.c
@@ -972,7 +972,7 @@
TAILQ_FOREACH(dr, &V_nd6_defrouter, dr_entry) {
NET_EPOCH_ENTER(et);
if (selected_dr == NULL && dr->ifp->if_fib == fibnum &&
- (ln = nd6_lookup(&dr->rtaddr, 0, dr->ifp)) &&
+ (ln = nd6_lookup(&dr->rtaddr, ND6_SF6(0), dr->ifp)) &&
ND6_IS_LLINFO_PROBREACH(ln)) {
selected_dr = dr;
defrouter_ref(selected_dr);
@@ -1022,7 +1022,7 @@
}
} else if (installed_dr != NULL) {
NET_EPOCH_ENTER(et);
- if ((ln = nd6_lookup(&installed_dr->rtaddr, 0,
+ if ((ln = nd6_lookup(&installed_dr->rtaddr, ND6_SF6(0),
installed_dr->ifp)) &&
ND6_IS_LLINFO_PROBREACH(ln) &&
installed_dr->ifp->if_fib == fibnum &&
@@ -1814,7 +1814,7 @@
NET_EPOCH_ENTER(et);
LIST_FOREACH(pfxrtr, &pr->ndpr_advrtrs, pfr_entry) {
- ln = nd6_lookup(&pfxrtr->router->rtaddr, 0, pfxrtr->router->ifp);
+ ln = nd6_lookup(&pfxrtr->router->rtaddr, ND6_SF6(0), pfxrtr->router->ifp);
if (ln == NULL)
continue;
canreach = ND6_IS_LLINFO_PROBREACH(ln);

File Metadata

Mime Type
text/plain
Expires
Tue, Nov 11, 6:53 AM (8 h, 57 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
25146357
Default Alt Text
D31379.id93144.diff (31 KB)

Event Timeline