Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F168835898
D31379.id93100.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
24 KB
Referenced Files
None
Subscribers
None
D31379.id93100.diff
View Options
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
@@ -236,8 +236,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 {
const struct in6_addr *a6;
a6 = &(((const struct sockaddr_in6 *)dst)->sin6_addr);
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;
@@ -195,6 +199,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 +239,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 +272,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.
@@ -642,6 +716,23 @@
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)
+{
+ struct llentry *parent_lle = child_lle->lle_parent;
+
+ CK_SLIST_REMOVE(&parent_lle->lle_children, child_lle, llentry, lle_child_next);
+}
+
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/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 != 0))
+ 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
@@ -372,14 +372,14 @@
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);
Index: sys/netinet6/nd6.c
===================================================================
--- sys/netinet6/nd6.c
+++ sys/netinet6/nd6.c
@@ -140,11 +140,9 @@
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 *);
+static void nd6_flush_children_holdchain(struct ifnet *ifp, struct llentry *lle);
VNET_DEFINE_STATIC(struct callout, nd6_slowtimo_ch);
#define V_nd6_slowtimo_ch VNET(nd6_slowtimo_ch)
@@ -620,7 +618,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 +626,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 +707,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;
@@ -1395,6 +1388,94 @@
return (rc);
}
+static __noinline void
+nd6_free_child_raw(struct llentry *ln)
+{
+
+ NET_EPOCH_ASSERT();
+ LLE_WLOCK_ASSERT(ln);
+
+ /* Guard against race with other llentry_free(). */
+ if (ln->la_flags & LLE_LINKED) {
+
+ LLE_REMREF(ln);
+ ln->la_flags &= ~LLE_LINKED;
+
+ /* Timer will be cancelled here */
+ llentry_free(ln);
+ }
+}
+
+static __noinline void
+nd6_free_child(struct llentry *child_ln)
+{
+
+ NET_EPOCH_ASSERT();
+ LLE_WLOCK_ASSERT(child_ln);
+
+ struct llentry *parent_ln = child_ln->lle_parent;
+ LLE_WUNLOCK(child_ln);
+
+ /* Remove child_ln from the list */
+ LLE_WLOCK(parent_ln);
+ lltable_unlink_child_entry(child_ln);
+ LLE_WUNLOCK(parent_ln);
+
+ LLE_WLOCK(child_ln);
+
+ nd6_free_child_raw(child_ln);
+}
+
+static __noinline void
+nd6_free_children(struct llentry_children_head *head)
+{
+ struct llentry *ln;
+
+ NET_EPOCH_ASSERT();
+
+ CK_SLIST_FOREACH(ln, head, lle_child_next) {
+ LLE_WLOCK(ln);
+ nd6_free_child_raw(ln);
+ }
+}
+
+static __noinline bool
+nd6_try_set_entry_addr(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) {
+ IF_AFDATA_WUNLOCK(ifp);
+ 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 = AF_INET;
+ 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);
+}
+
+
/*
* Free an nd6 llinfo entry.
* Since the function would cause significant changes in the kernel, DO NOT
@@ -1416,6 +1497,12 @@
LLE_WLOCK_ASSERT(ln);
ND6_RLOCK_ASSERT();
+ if (ln->la_flags & LLE_CHILD) {
+ ND6_RUNLOCK();
+ nd6_free_child(ln);
+ return;
+ }
+
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 +1624,14 @@
}
IF_AFDATA_UNLOCK(ifp);
+ /* Free children */
+ struct llentry_children_head head = CK_SLIST_HEAD_INITIALIZER(head);
+ CK_SLIST_SWAP(&head, &ln->lle_children, llentry);
+
llentry_free(ln);
+
+ nd6_free_children(&head);
+
if (dr != NULL)
defrouter_rele(dr);
}
@@ -1938,9 +2032,6 @@
uint16_t router = 0;
struct sockaddr_in6 sin6;
struct mbuf *chain = NULL;
- u_char linkhdr[LLE_MAX_LINKHDR];
- size_t linkhdrsize;
- int lladdr_off;
NET_EPOCH_ASSERT();
IF_AFDATA_UNLOCK_ASSERT(ifp);
@@ -1975,12 +2066,8 @@
* fill it before insertion.
*/
if (lladdr != NULL) {
- linkhdrsize = sizeof(linkhdr);
- if (lltable_calc_llheader(ifp, AF_INET6, lladdr,
- linkhdr, &linkhdrsize, &lladdr_off) != 0)
+ if (nd6_try_set_entry_addr(ifp, ln, lladdr))
return;
- lltable_set_entry_addr(ifp, ln, linkhdr, linkhdrsize,
- lladdr_off);
}
IF_AFDATA_WLOCK(ifp);
@@ -2041,16 +2128,12 @@
* 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)
+ if (!lltable_acquire_wlock(ifp, ln))
return;
-
- if (lltable_try_set_entry_addr(ifp, ln, linkhdr, linkhdrsize,
- lladdr_off) == 0) {
- /* Entry was deleted */
+ bool ret = nd6_try_set_entry_addr(ifp, ln, lladdr);
+ IF_AFDATA_WUNLOCK(ifp);
+ if (!ret)
return;
- }
nd6_llinfo_setstate(ln, ND6_LLINFO_STALE);
@@ -2075,7 +2158,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 +2301,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 +2335,12 @@
}
}
- ln = nd6_lookup(&dst6->sin6_addr, plle ? LLE_EXCLUSIVE : LLE_UNLOCKED,
- ifp);
+ int family = gw_flags >> 16;
+ int flags = (family << 16) | (plle ? LLE_EXCLUSIVE : LLE_UNLOCKED);
+ 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 +2359,67 @@
} 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 != 0) {
+ 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 */
+ lle_tmp = nd6_lookup(addr, LLE_EXCLUSIVE, 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);
+ if (lle_tmp == NULL) {
+ /* Attach */
+ lltable_link_child_entry(parent_lle, child_lle);
+ } else {
+ lltable_free_entry(LLTABLE6(ifp), child_lle);
+ child_lle = lle_tmp;
+ }
+ LLE_WLOCK(child_lle);
+ LLE_WUNLOCK(parent_lle);
+ lle = child_lle;
+ } else
+ lle = parent_lle;
+
+ IF_AFDATA_WUNLOCK(ifp);
+
+ return (lle);
}
/*
@@ -2300,9 +2436,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 +2452,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 = (family << 16) | LLE_EXCLUSIVE;
+ 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 +2478,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 +2495,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 +2511,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 +2542,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 +2581,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 +2611,24 @@
return (error);
}
-#define MTAG_ND6_ENCAP_TYPE 4237446679
-
-static bool
-nd6_attach_encap_tag(struct mbuf *m, gw_type_t type)
-{
- 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);
-}
-
-static int
-output_ifp_one(struct ifnet *ifp, struct mbuf *m, struct sockaddr_in6 *dst)
+static __noinline void
+nd6_flush_children_holdchain(struct ifnet *ifp, struct llentry *lle)
{
- 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);
+ struct llentry *child_lle;
+ struct sockaddr_in6 sin6;
- return (error);
+ NET_EPOCH_ASSERT();
+ /* Assume lle is locked */
+
+ 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
Index: sys/netinet6/nd6_nbr.c
===================================================================
--- sys/netinet6/nd6_nbr.c
+++ sys/netinet6/nd6_nbr.c
@@ -908,7 +908,7 @@
LLE_WUNLOCK(ln);
if (chain != NULL)
- nd6_flush_holdchain(ifp, chain, &sin6);
+ nd6_flush_holdchain(ifp, ln, chain, &sin6);
if (checklink)
pfxlist_onlink_check();
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Aug 31, 9:22 AM (13 h, 31 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
37672893
Default Alt Text
D31379.id93100.diff (24 KB)
Attached To
Mode
D31379: [lltable] Add support for "child" LLEs holding encap for IPv4oIPv6 entries.
Attached
Detach File
Event Timeline
Log In to Comment