Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F110674711
D31379.id93952.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
17 KB
Referenced Files
None
Subscribers
None
D31379.id93952.diff
View Options
Index: sys/net/if_ethersubr.c
===================================================================
--- sys/net/if_ethersubr.c
+++ sys/net/if_ethersubr.c
@@ -237,8 +237,8 @@
#ifdef INET6
case AF_INET6:
if ((m->m_flags & M_MCAST) == 0)
- error = nd6_resolve(ifp, 0, m, dst, phdr, &lleflags,
- plle);
+ error = nd6_resolve(ifp, LLE_SF(AF_INET6, 0), m, dst, phdr,
+ &lleflags, plle);
else {
const struct in6_addr *a6;
a6 = &(((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(_fam, _flags) (((_flags) & 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,6 +241,8 @@
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);
@@ -267,6 +276,7 @@
return;
llentry_mark_used(lle);
}
+struct llentry *llentry_lookup_family(struct llentry *lle, int family);
int lla_rt_output(struct rt_msghdr *, struct rt_addrinfo *);
Index: sys/net/if_llatbl.c
===================================================================
--- sys/net/if_llatbl.c
+++ sys/net/if_llatbl.c
@@ -398,6 +398,26 @@
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);
+}
+
/*
* Requests feedback from the datapath.
* First packet using @lle should result in
@@ -407,9 +427,17 @@
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);
+ }
}
/*
@@ -431,8 +459,8 @@
* Return 0 if the entry was not used, relevant time_uptime
* otherwise.
*/
-time_t
-llentry_get_hittime(struct llentry *lle)
+static time_t
+llentry_get_hittime_raw(struct llentry *lle)
{
time_t lle_hittime = 0;
@@ -444,6 +472,23 @@
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.
@@ -585,7 +630,7 @@
ifp = llt->llt_ifp;
IF_AFDATA_WLOCK(ifp);
- lle = lla_lookup(llt, LLE_EXCLUSIVE, l3addr);
+ lle = lla_lookup(llt, LLE_SF(l3addr->sa_family, LLE_EXCLUSIVE), l3addr);
if (lle == NULL) {
IF_AFDATA_WUNLOCK(ifp);
@@ -700,6 +745,25 @@
return (llt->llt_link_entry(llt, lle));
}
+void
+lltable_link_child_entry(struct llentry *lle, struct llentry *child_lle)
+{
+ child_lle->lle_parent = lle;
+ child_lle->lle_tbl = lle->lle_tbl;
+ child_lle->la_flags |= LLE_LINKED;
+ CK_SLIST_INSERT_HEAD(&lle->lle_children, child_lle, lle_child_next);
+}
+
+void
+lltable_unlink_child_entry(struct llentry *child_lle)
+{
+ struct llentry *lle = child_lle->lle_parent;
+
+ child_lle->la_flags &= ~LLE_LINKED;
+ child_lle->lle_parent = NULL;
+ CK_SLIST_REMOVE(&lle->lle_children, child_lle, llentry, lle_child_next);
+}
+
int
lltable_unlink_entry(struct lltable *llt, struct llentry *lle)
{
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, LLE_SF(AF_INET6, 0), ifp);
if (ln == NULL)
goto nolladdropt;
Index: sys/netinet6/in6.c
===================================================================
--- sys/netinet6/in6.c
+++ sys/netinet6/in6.c
@@ -2335,6 +2335,11 @@
lle = in6_lltable_find_dst(llt, &sin6->sin6_addr);
if (lle == NULL)
return (NULL);
+
+ int family = flags >> 16;
+ if (__predict_false(family != AF_INET6))
+ lle = llentry_lookup_family(lle, family);
+
if (flags & LLE_UNLOCKED)
return (lle);
Index: sys/netinet6/nd6.h
===================================================================
--- sys/netinet6/nd6.h
+++ sys/netinet6/nd6.h
@@ -379,6 +379,7 @@
bool nd6_try_set_entry_addr(struct ifnet *ifp, struct llentry *lle, char *lladdr);
struct mbuf *nd6_grab_holdchain(struct llentry *);
int nd6_flush_holdchain(struct ifnet *, struct llentry *, struct mbuf *);
+void nd6_flush_children_holdchain(struct ifnet *, struct llentry *);
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 *,
Index: sys/netinet6/nd6.c
===================================================================
--- sys/netinet6/nd6.c
+++ sys/netinet6/nd6.c
@@ -139,7 +139,7 @@
static void nd6_llinfo_timer(void *);
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 *,
+static int nd6_resolve_slow(struct ifnet *, int, int, struct mbuf *,
const struct sockaddr_in6 *, u_char *, uint32_t *, struct llentry **);
static int nd6_need_cache(struct ifnet *);
@@ -530,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;
@@ -1375,40 +1379,76 @@
* 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, LLE_SF(AF_INET6, 0), ifp)) != NULL) {
LLE_RUNLOCK(lle);
rc = 1;
}
return (rc);
}
+static __noinline void
+nd6_free_children(struct llentry *lle)
+{
+ struct llentry *child_lle;
+
+ NET_EPOCH_ASSERT();
+ LLE_WLOCK_ASSERT(lle);
+
+ while ((child_lle = CK_SLIST_FIRST(&lle->lle_children)) != NULL) {
+ LLE_WLOCK(child_lle);
+ lltable_unlink_child_entry(child_lle);
+ llentry_free(child_lle);
+ }
+}
+
/*
* Tries to update @lle address/prepend data with new @lladdr.
*
* Returns true on success.
* In any case, @lle is returned wlocked.
*/
-bool
-nd6_try_set_entry_addr(struct ifnet *ifp, struct llentry *lle, char *lladdr)
+static __noinline bool
+nd6_try_set_entry_addr_locked(struct ifnet *ifp, struct llentry *lle, char *lladdr)
{
- u_char linkhdr[LLE_MAX_LINKHDR];
- size_t linkhdrsize;
- int lladdr_off;
-
- LLE_WLOCK_ASSERT(lle);
+ u_char buf[LLE_MAX_LINKHDR];
+ int fam, off;
+ size_t sz;
- linkhdrsize = sizeof(linkhdr);
- if (lltable_calc_llheader(ifp, AF_INET6, lladdr,
- linkhdr, &linkhdrsize, &lladdr_off) != 0) {
+ sz = sizeof(buf);
+ if (lltable_calc_llheader(ifp, AF_INET6, lladdr, buf, &sz, &off) != 0)
return (false);
+
+ /* Update data */
+ lltable_set_entry_addr(ifp, lle, buf, sz, off);
+
+ struct llentry *child_lle;
+ CK_SLIST_FOREACH(child_lle, &lle->lle_children, lle_child_next) {
+ LLE_WLOCK(child_lle);
+ fam = child_lle->r_family;
+ sz = sizeof(buf);
+ if (lltable_calc_llheader(ifp, fam, lladdr, buf, &sz, &off) == 0) {
+ /* success */
+ lltable_set_entry_addr(ifp, child_lle, buf, sz, off);
+ child_lle->ln_state = ND6_LLINFO_REACHABLE;
+ }
+ LLE_WUNLOCK(child_lle);
}
+ return (true);
+}
+
+bool
+nd6_try_set_entry_addr(struct ifnet *ifp, struct llentry *lle, char *lladdr)
+{
+ NET_EPOCH_ASSERT();
+ LLE_WLOCK_ASSERT(lle);
+
if (!lltable_acquire_wlock(ifp, lle))
return (false);
- lltable_set_entry_addr(ifp, lle, linkhdr, linkhdrsize, lladdr_off);
+ bool ret = nd6_try_set_entry_addr_locked(ifp, lle, lladdr);
IF_AFDATA_WUNLOCK(ifp);
- return (true);
+ return (ret);
}
/*
@@ -1432,6 +1472,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);
@@ -1553,6 +1595,8 @@
}
IF_AFDATA_UNLOCK(ifp);
+ nd6_free_children(ln);
+
llentry_free(ln);
if (dr != NULL)
defrouter_rele(dr);
@@ -1827,7 +1871,7 @@
return (error);
NET_EPOCH_ENTER(et);
- ln = nd6_lookup(&nb_addr, 0, ifp);
+ ln = nd6_lookup(&nb_addr, LLE_SF(AF_INET6, 0), ifp);
NET_EPOCH_EXIT(et);
if (ln == NULL) {
@@ -1977,7 +2021,7 @@
* description on it in NS section (RFC 2461 7.2.3).
*/
flags = lladdr ? LLE_EXCLUSIVE : 0;
- ln = nd6_lookup(from, flags, ifp);
+ ln = nd6_lookup(from, LLE_SF(AF_INET6, flags), ifp);
is_newentry = 0;
if (ln == NULL) {
flags |= LLE_EXCLUSIVE;
@@ -2001,7 +2045,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, LLE_SF(AF_INET6, LLE_EXCLUSIVE), ifp);
if (ln_tmp == NULL)
lltable_link_entry(LLTABLE6(ifp), ln);
IF_AFDATA_WUNLOCK(ifp);
@@ -2086,6 +2130,8 @@
if (chain != NULL)
nd6_flush_holdchain(ifp, ln, chain);
+ if (do_update)
+ nd6_flush_children_holdchain(ifp, ln);
/*
* When the link-layer address of a router changes, select the
@@ -2227,7 +2273,7 @@
* - other errors (alloc failure, etc)
*/
int
-nd6_resolve(struct ifnet *ifp, int is_gw, struct mbuf *m,
+nd6_resolve(struct ifnet *ifp, int gw_flags, struct mbuf *m,
const struct sockaddr *sa_dst, u_char *desten, uint32_t *pflags,
struct llentry **plle)
{
@@ -2261,8 +2307,9 @@
}
}
- ln = nd6_lookup(&dst6->sin6_addr, plle ? LLE_EXCLUSIVE : LLE_UNLOCKED,
- ifp);
+ int family = gw_flags >> 16;
+ int lookup_flags = plle ? LLE_EXCLUSIVE : LLE_UNLOCKED;
+ ln = nd6_lookup(&dst6->sin6_addr, LLE_SF(family, lookup_flags), ifp);
if (ln != NULL && (ln->r_flags & RLLE_VALID) != 0) {
/* Entry found, let's copy lle info */
bcopy(ln->r_linkdata, desten, ln->r_hdrlen);
@@ -2278,19 +2325,39 @@
} else if (plle && ln)
LLE_WUNLOCK(ln);
- return (nd6_resolve_slow(ifp, 0, m, dst6, desten, pflags, plle));
+ return (nd6_resolve_slow(ifp, family, 0, m, dst6, desten, pflags, plle));
}
/*
- * Finds or creates a new llentry for @addr.
+ * Finds or creates a new llentry for @addr and @family.
* Returns wlocked llentry or NULL.
+ *
+ *
+ * Child LLEs.
+ *
+ * Do not have their own state machine (gets marked as static)
+ * settimer bails out for child LLEs just in case.
+ *
+ * Locking order: parent lle gets locked first, chen goes the child.
*/
static __noinline struct llentry *
-nd6_get_llentry(struct ifnet *ifp, const struct in6_addr *addr)
+nd6_get_llentry(struct ifnet *ifp, const struct in6_addr *addr, int family)
{
+ struct llentry *child_lle = NULL;
struct llentry *lle, *lle_tmp;
lle = nd6_alloc(addr, 0, ifp);
+ if (lle != NULL && family != AF_INET6) {
+ child_lle = nd6_alloc(addr, 0, ifp);
+ if (child_lle == NULL) {
+ lltable_free_entry(LLTABLE6(ifp), lle);
+ return (NULL);
+ }
+ child_lle->r_family = family;
+ child_lle->la_flags |= LLE_CHILD | LLE_STATIC;
+ child_lle->ln_state = ND6_LLINFO_INCOMPLETE;
+ }
+
if (lle == NULL) {
char ip6buf[INET6_ADDRSTRLEN];
log(LOG_DEBUG,
@@ -2303,15 +2370,30 @@
IF_AFDATA_WLOCK(ifp);
LLE_WLOCK(lle);
/* Prefer any existing entry over newly-created one */
- lle_tmp = nd6_lookup(addr, LLE_EXCLUSIVE, ifp);
+ lle_tmp = nd6_lookup(addr, LLE_SF(AF_INET6, LLE_EXCLUSIVE), ifp);
if (lle_tmp == NULL)
lltable_link_entry(LLTABLE6(ifp), lle);
- IF_AFDATA_WUNLOCK(ifp);
- if (lle_tmp != NULL) {
+ else {
lltable_free_entry(LLTABLE6(ifp), lle);
- return (lle_tmp);
- } else
- return (lle);
+ lle = lle_tmp;
+ }
+ if (child_lle != NULL) {
+ /* Check if child lle for the same family exists */
+ lle_tmp = llentry_lookup_family(lle, child_lle->r_family);
+ LLE_WLOCK(child_lle);
+ if (lle_tmp == NULL) {
+ /* Attach */
+ lltable_link_child_entry(lle, child_lle);
+ } else {
+ /* child lle already exists, free newly-created one */
+ lltable_free_entry(LLTABLE6(ifp), child_lle);
+ child_lle = lle_tmp;
+ }
+ LLE_WUNLOCK(lle);
+ lle = child_lle;
+ }
+ IF_AFDATA_WUNLOCK(ifp);
+ return (lle);
}
/*
@@ -2326,7 +2408,7 @@
* Set noinline to be dtrace-friendly
*/
static __noinline int
-nd6_resolve_slow(struct ifnet *ifp, int flags, struct mbuf *m,
+nd6_resolve_slow(struct ifnet *ifp, int family, int flags, struct mbuf *m,
const struct sockaddr_in6 *dst, u_char *desten, uint32_t *pflags,
struct llentry **plle)
{
@@ -2343,14 +2425,14 @@
* At this point, the destination of the packet must be a unicast
* or an anycast address(i.e. not a multicast).
*/
- lle = nd6_lookup(&dst->sin6_addr, LLE_EXCLUSIVE, ifp);
+ lle = nd6_lookup(&dst->sin6_addr, LLE_SF(family, LLE_EXCLUSIVE), ifp);
if ((lle == NULL) && nd6_is_addr_neighbor(dst, ifp)) {
/*
* 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_get_llentry(ifp, &dst->sin6_addr);
+ lle = nd6_get_llentry(ifp, &dst->sin6_addr, family);
}
if (lle == NULL) {
@@ -2367,7 +2449,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);
/*
@@ -2432,6 +2514,14 @@
*/
psrc = NULL;
send_ns = 0;
+
+ /* If we have child lle, switch to the parent to send NS */
+ if (lle->la_flags & LLE_CHILD) {
+ struct llentry *lle_parent = lle->lle_parent;
+ LLE_WUNLOCK(lle);
+ lle = lle_parent;
+ LLE_WLOCK(lle);
+ }
if (lle->la_asked == 0) {
lle->la_asked++;
send_ns = 1;
@@ -2463,7 +2553,7 @@
int error;
flags |= LLE_ADDRONLY;
- error = nd6_resolve_slow(ifp, flags, NULL,
+ error = nd6_resolve_slow(ifp, AF_INET6, flags, NULL,
(const struct sockaddr_in6 *)dst, desten, pflags, NULL);
return (error);
}
@@ -2499,6 +2589,22 @@
return (error);
}
+__noinline void
+nd6_flush_children_holdchain(struct ifnet *ifp, struct llentry *lle)
+{
+ struct llentry *child_lle;
+ struct mbuf *chain;
+
+ NET_EPOCH_ASSERT();
+
+ CK_SLIST_FOREACH(child_lle, &lle->lle_children, lle_child_next) {
+ LLE_WLOCK(child_lle);
+ chain = nd6_grab_holdchain(child_lle);
+ LLE_WUNLOCK(child_lle);
+ nd6_flush_holdchain(ifp, child_lle, chain);
+ }
+}
+
static int
nd6_need_cache(struct ifnet *ifp)
{
@@ -2552,7 +2658,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), LLE_SF(AF_INET6, 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
@@ -630,6 +630,7 @@
size_t linkhdrsize;
int flags, is_override, is_router, is_solicited;
int lladdr_off, lladdrlen, checklink;
+ bool flush_holdchain = false;
NET_EPOCH_ASSERT();
@@ -747,7 +748,7 @@
* If no neighbor cache entry is found, NA SHOULD silently be
* discarded.
*/
- ln = nd6_lookup(&taddr6, LLE_EXCLUSIVE, ifp);
+ ln = nd6_lookup(&taddr6, LLE_SF(AF_INET6, LLE_EXCLUSIVE), ifp);
if (ln == NULL) {
goto freeit;
}
@@ -773,6 +774,7 @@
if (!nd6_try_set_entry_addr(ifp, ln, lladdr))
goto freeit;
+ flush_holdchain = true;
EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_RESOLVED);
if (is_solicited)
nd6_llinfo_setstate(ln, ND6_LLINFO_REACHABLE);
@@ -899,6 +901,8 @@
if (chain != NULL)
nd6_flush_holdchain(ifp, ln, chain);
+ if (flush_holdchain)
+ nd6_flush_children_holdchain(ifp, ln);
if (checklink)
pfxlist_onlink_check();
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Feb 22, 6:50 PM (7 h, 11 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
16777193
Default Alt Text
D31379.id93952.diff (17 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