Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F135490086
D31379.id93144.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
31 KB
Referenced Files
None
Subscribers
None
D31379.id93144.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
@@ -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
Details
Attached
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)
Attached To
Mode
D31379: [lltable] Add support for "child" LLEs holding encap for IPv4oIPv6 entries.
Attached
Detach File
Event Timeline
Log In to Comment