Partially merge r274887,r275334,r275577,r275578,r275586 to minimize
differences between projects/routing and HEAD. This commit tries to keep code logic the same while changing underlying code to use unified callbacks. * Add llt_foreach_entry method to traverse all entries in given llt * Add llt_dump_entry method to export particular lle entry in sysctl/rtsock format (code is not indented properly to minimize diff). Will be fixed in the next commits. * Add llt_link_entry/llt_unlink_entry methods to link/unlink particular lle. * Add llt_fill_sa_entry method to export address in the lle to sockaddr format. * Add llt_hash method to use in generic hash table support code. * Add llt_free_entry method which is used in llt_prefix_free code. * Prepare for fine-grained locking by separating lle unlink and deletion in lltable_free() and lltable_prefix_free(). * Provide lltable_get<ifp|af>() functions to reduce direct 'struct lltable' access by external callers. * Remove @llt agrument from lle_free() lle callback since it was unused. * Temporarily add L3_CADDR() macro for 'const' sockaddr typecasting. * Switch to per-af hashing code. * Rename LLE_FREE_LOCKED() callback from in[6]_lltable_free() to in_[6]lltable_destroy() to avoid clashing with llt_free_entry() method. Update description from these functions. * Use unified lltable_free_entry() function instead of per-af one. Reviewed by: ae
This commit is contained in:
parent
fa26a29063
commit
4f240a9c31
@ -70,6 +70,35 @@ static void vnet_lltable_init(void);
|
||||
struct rwlock lltable_rwlock;
|
||||
RW_SYSINIT(lltable_rwlock, &lltable_rwlock, "lltable_rwlock");
|
||||
|
||||
static void llentries_unlink(struct lltable *llt, struct llentries *head);
|
||||
|
||||
static void htable_unlink_entry(struct llentry *lle);
|
||||
static void htable_link_entry(struct lltable *llt, struct llentry *lle);
|
||||
static int htable_foreach_lle(struct lltable *llt, llt_foreach_cb_t *f,
|
||||
void *farg);
|
||||
|
||||
/*
|
||||
* Dump lle state for a specific address family.
|
||||
*/
|
||||
static int
|
||||
lltable_dump_af(struct lltable *llt, struct sysctl_req *wr)
|
||||
{
|
||||
int error;
|
||||
|
||||
LLTABLE_LOCK_ASSERT();
|
||||
|
||||
if (llt->llt_ifp->if_flags & IFF_LOOPBACK)
|
||||
return (0);
|
||||
error = 0;
|
||||
|
||||
IF_AFDATA_RLOCK(llt->llt_ifp);
|
||||
error = lltable_foreach_lle(llt,
|
||||
(llt_foreach_cb_t *)llt->llt_dump_entry, wr);
|
||||
IF_AFDATA_RUNLOCK(llt->llt_ifp);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Dump arp state for a specific address family.
|
||||
*/
|
||||
@ -82,7 +111,7 @@ lltable_sysctl_dumparp(int af, struct sysctl_req *wr)
|
||||
LLTABLE_RLOCK();
|
||||
SLIST_FOREACH(llt, &V_lltables, llt_link) {
|
||||
if (llt->llt_af == af) {
|
||||
error = llt->llt_dump(llt, wr);
|
||||
error = lltable_dump_af(llt, wr);
|
||||
if (error != 0)
|
||||
goto done;
|
||||
}
|
||||
@ -93,25 +122,136 @@ lltable_sysctl_dumparp(int af, struct sysctl_req *wr)
|
||||
}
|
||||
|
||||
/*
|
||||
* Deletes an address from the address table.
|
||||
* This function is called by the timer functions
|
||||
* such as arptimer() and nd6_llinfo_timer(), and
|
||||
* the caller does the locking.
|
||||
* Common function helpers for chained hash table.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Runs specified callback for each entry in @llt.
|
||||
* Caller does the locking.
|
||||
*
|
||||
*/
|
||||
static int
|
||||
htable_foreach_lle(struct lltable *llt, llt_foreach_cb_t *f, void *farg)
|
||||
{
|
||||
struct llentry *lle, *next;
|
||||
int i, error;
|
||||
|
||||
error = 0;
|
||||
|
||||
for (i = 0; i < LLTBL_HASHTBL_SIZE; i++) {
|
||||
LIST_FOREACH_SAFE(lle, &llt->lle_head[i], lle_next, next) {
|
||||
error = f(llt, lle, farg);
|
||||
if (error != 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
static void
|
||||
htable_link_entry(struct lltable *llt, struct llentry *lle)
|
||||
{
|
||||
struct llentries *lleh;
|
||||
uint32_t hashidx;
|
||||
|
||||
if ((lle->la_flags & LLE_LINKED) != 0)
|
||||
return;
|
||||
|
||||
IF_AFDATA_WLOCK_ASSERT(llt->llt_ifp);
|
||||
|
||||
hashidx = llt->llt_hash(lle, LLTBL_HASHTBL_SIZE);
|
||||
lleh = &llt->lle_head[hashidx];
|
||||
|
||||
lle->lle_tbl = llt;
|
||||
lle->lle_head = lleh;
|
||||
lle->la_flags |= LLE_LINKED;
|
||||
LIST_INSERT_HEAD(lleh, lle, lle_next);
|
||||
}
|
||||
|
||||
static void
|
||||
htable_unlink_entry(struct llentry *lle)
|
||||
{
|
||||
|
||||
if ((lle->la_flags & LLE_LINKED) != 0) {
|
||||
IF_AFDATA_WLOCK_ASSERT(lle->lle_tbl->llt_ifp);
|
||||
LIST_REMOVE(lle, lle_next);
|
||||
lle->la_flags &= ~(LLE_VALID | LLE_LINKED);
|
||||
#if 0
|
||||
lle->lle_tbl = NULL;
|
||||
lle->lle_head = NULL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
struct prefix_match_data {
|
||||
const struct sockaddr *prefix;
|
||||
const struct sockaddr *mask;
|
||||
struct llentries dchain;
|
||||
u_int flags;
|
||||
};
|
||||
|
||||
static int
|
||||
htable_prefix_free_cb(struct lltable *llt, struct llentry *lle, void *farg)
|
||||
{
|
||||
struct prefix_match_data *pmd;
|
||||
|
||||
pmd = (struct prefix_match_data *)farg;
|
||||
|
||||
if (llt->llt_match_prefix(pmd->prefix, pmd->mask, pmd->flags, lle)) {
|
||||
LLE_WLOCK(lle);
|
||||
LIST_INSERT_HEAD(&pmd->dchain, lle, lle_chain);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
htable_prefix_free(struct lltable *llt, const struct sockaddr *prefix,
|
||||
const struct sockaddr *mask, u_int flags)
|
||||
{
|
||||
struct llentry *lle, *next;
|
||||
struct prefix_match_data pmd;
|
||||
|
||||
bzero(&pmd, sizeof(pmd));
|
||||
pmd.prefix = prefix;
|
||||
pmd.mask = mask;
|
||||
pmd.flags = flags;
|
||||
LIST_INIT(&pmd.dchain);
|
||||
|
||||
IF_AFDATA_WLOCK(llt->llt_ifp);
|
||||
/* Push matching lles to chain */
|
||||
lltable_foreach_lle(llt, htable_prefix_free_cb, &pmd);
|
||||
|
||||
llentries_unlink(llt, &pmd.dchain);
|
||||
IF_AFDATA_WUNLOCK(llt->llt_ifp);
|
||||
|
||||
LIST_FOREACH_SAFE(lle, &pmd.dchain, lle_chain, next)
|
||||
llt->llt_free_entry(llt, lle);
|
||||
}
|
||||
|
||||
static void
|
||||
llentries_unlink(struct lltable *llt, struct llentries *head)
|
||||
{
|
||||
struct llentry *lle, *next;
|
||||
|
||||
LIST_FOREACH_SAFE(lle, head, lle_chain, next)
|
||||
llt->llt_unlink_entry(lle);
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function used to drop all mbufs in hold queue.
|
||||
*
|
||||
* Returns the number of held packets, if any, that were dropped.
|
||||
*/
|
||||
size_t
|
||||
llentry_free(struct llentry *lle)
|
||||
lltable_drop_entry_queue(struct llentry *lle)
|
||||
{
|
||||
size_t pkts_dropped;
|
||||
struct mbuf *next;
|
||||
|
||||
IF_AFDATA_WLOCK_ASSERT(lle->lle_tbl->llt_ifp);
|
||||
LLE_WLOCK_ASSERT(lle);
|
||||
|
||||
LIST_REMOVE(lle, lle_next);
|
||||
lle->la_flags &= ~(LLE_VALID | LLE_LINKED);
|
||||
|
||||
pkts_dropped = 0;
|
||||
while ((lle->la_numheld > 0) && (lle->la_hold != NULL)) {
|
||||
next = lle->la_hold->m_nextpkt;
|
||||
@ -125,6 +265,34 @@ llentry_free(struct llentry *lle)
|
||||
("%s: la_numheld %d > 0, pkts_droped %zd", __func__,
|
||||
lle->la_numheld, pkts_dropped));
|
||||
|
||||
return (pkts_dropped);
|
||||
}
|
||||
|
||||
/*
|
||||
* Deletes an address from the address table.
|
||||
* This function is called by the timer functions
|
||||
* such as arptimer() and nd6_llinfo_timer(), and
|
||||
* the caller does the locking.
|
||||
*
|
||||
* Returns the number of held packets, if any, that were dropped.
|
||||
*/
|
||||
size_t
|
||||
llentry_free(struct llentry *lle)
|
||||
{
|
||||
struct lltable *llt;
|
||||
size_t pkts_dropped;
|
||||
|
||||
LLE_WLOCK_ASSERT(lle);
|
||||
|
||||
if ((lle->la_flags & LLE_LINKED) != 0) {
|
||||
llt = lle->lle_tbl;
|
||||
|
||||
IF_AFDATA_WLOCK_ASSERT(llt->llt_ifp);
|
||||
llt->llt_unlink_entry(lle);
|
||||
}
|
||||
|
||||
pkts_dropped = lltable_drop_entry_queue(lle);
|
||||
|
||||
LLE_FREE_LOCKED(lle);
|
||||
|
||||
return (pkts_dropped);
|
||||
@ -159,6 +327,23 @@ llentry_alloc(struct ifnet *ifp, struct lltable *lt,
|
||||
return (la);
|
||||
}
|
||||
|
||||
/*
|
||||
* Free all entries from given table and free itself.
|
||||
*/
|
||||
|
||||
static int
|
||||
lltable_free_cb(struct lltable *llt, struct llentry *lle, void *farg)
|
||||
{
|
||||
struct llentries *dchain;
|
||||
|
||||
dchain = (struct llentries *)farg;
|
||||
|
||||
LLE_WLOCK(lle);
|
||||
LIST_INSERT_HEAD(dchain, lle, lle_chain);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Free all entries from given table and free itself.
|
||||
*/
|
||||
@ -166,7 +351,7 @@ void
|
||||
lltable_free(struct lltable *llt)
|
||||
{
|
||||
struct llentry *lle, *next;
|
||||
int i;
|
||||
struct llentries dchain;
|
||||
|
||||
KASSERT(llt != NULL, ("%s: llt is NULL", __func__));
|
||||
|
||||
@ -174,17 +359,19 @@ lltable_free(struct lltable *llt)
|
||||
SLIST_REMOVE(&V_lltables, llt, lltable, llt_link);
|
||||
LLTABLE_WUNLOCK();
|
||||
|
||||
LIST_INIT(&dchain);
|
||||
IF_AFDATA_WLOCK(llt->llt_ifp);
|
||||
for (i = 0; i < LLTBL_HASHTBL_SIZE; i++) {
|
||||
LIST_FOREACH_SAFE(lle, &llt->lle_head[i], lle_next, next) {
|
||||
LLE_WLOCK(lle);
|
||||
if (callout_stop(&lle->la_timer))
|
||||
LLE_REMREF(lle);
|
||||
llentry_free(lle);
|
||||
}
|
||||
}
|
||||
/* Push all lles to @dchain */
|
||||
lltable_foreach_lle(llt, lltable_free_cb, &dchain);
|
||||
llentries_unlink(llt, &dchain);
|
||||
IF_AFDATA_WUNLOCK(llt->llt_ifp);
|
||||
|
||||
LIST_FOREACH_SAFE(lle, &dchain, lle_chain, next) {
|
||||
if (callout_stop(&lle->la_timer))
|
||||
LLE_REMREF(lle);
|
||||
llentry_free(lle);
|
||||
}
|
||||
|
||||
free(llt, M_LLTABLE);
|
||||
}
|
||||
|
||||
@ -232,8 +419,6 @@ lltable_prefix_free(int af, struct sockaddr *prefix, struct sockaddr *mask,
|
||||
LLTABLE_RUNLOCK();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Create a new lltable.
|
||||
*/
|
||||
@ -250,6 +435,12 @@ lltable_init(struct ifnet *ifp, int af)
|
||||
for (i = 0; i < LLTBL_HASHTBL_SIZE; i++)
|
||||
LIST_INIT(&llt->lle_head[i]);
|
||||
|
||||
/* Set some default callbacks */
|
||||
llt->llt_link_entry = htable_link_entry;
|
||||
llt->llt_unlink_entry = htable_unlink_entry;
|
||||
llt->llt_prefix_free = htable_prefix_free;
|
||||
llt->llt_foreach_entry = htable_foreach_lle;
|
||||
|
||||
LLTABLE_WLOCK();
|
||||
SLIST_INSERT_HEAD(&V_lltables, llt, llt_link);
|
||||
LLTABLE_WUNLOCK();
|
||||
@ -257,6 +448,54 @@ lltable_init(struct ifnet *ifp, int af)
|
||||
return (llt);
|
||||
}
|
||||
|
||||
/*
|
||||
* External methods used by lltable consumers
|
||||
*/
|
||||
|
||||
int
|
||||
lltable_foreach_lle(struct lltable *llt, llt_foreach_cb_t *f, void *farg)
|
||||
{
|
||||
|
||||
return (llt->llt_foreach_entry(llt, f, farg));
|
||||
}
|
||||
|
||||
void
|
||||
lltable_link_entry(struct lltable *llt, struct llentry *lle)
|
||||
{
|
||||
|
||||
llt->llt_link_entry(llt, lle);
|
||||
}
|
||||
|
||||
void
|
||||
lltable_unlink_entry(struct lltable *llt, struct llentry *lle)
|
||||
{
|
||||
|
||||
llt->llt_unlink_entry(lle);
|
||||
}
|
||||
|
||||
void
|
||||
lltable_fill_sa_entry(const struct llentry *lle, struct sockaddr *sa)
|
||||
{
|
||||
struct lltable *llt;
|
||||
|
||||
llt = lle->lle_tbl;
|
||||
llt->llt_fill_sa_entry(lle, sa);
|
||||
}
|
||||
|
||||
struct ifnet *
|
||||
lltable_get_ifp(const struct lltable *llt)
|
||||
{
|
||||
|
||||
return (llt->llt_ifp);
|
||||
}
|
||||
|
||||
int
|
||||
lltable_get_af(const struct lltable *llt)
|
||||
{
|
||||
|
||||
return (llt->llt_af);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called in route_output when rtm_flags contains RTF_LLDATA.
|
||||
*/
|
||||
|
@ -57,7 +57,7 @@ struct llentry {
|
||||
struct rwlock lle_lock;
|
||||
struct lltable *lle_tbl;
|
||||
struct llentries *lle_head;
|
||||
void (*lle_free)(struct lltable *, struct llentry *);
|
||||
void (*lle_free)(struct llentry *);
|
||||
struct mbuf *la_hold;
|
||||
int la_numheld; /* # of packets currently held */
|
||||
time_t la_expire;
|
||||
@ -76,6 +76,7 @@ struct llentry {
|
||||
uint8_t mac8[20]; /* IB needs 20 bytes. */
|
||||
} ll_addr;
|
||||
|
||||
LIST_ENTRY(llentry) lle_chain; /* chain of deleted items */
|
||||
/* XXX af-private? */
|
||||
union {
|
||||
struct callout ln_timer_ch;
|
||||
@ -114,7 +115,7 @@ struct llentry {
|
||||
|
||||
#define LLE_FREE_LOCKED(lle) do { \
|
||||
if ((lle)->lle_refcnt == 1) \
|
||||
(lle)->lle_free((lle)->lle_tbl, (lle)); \
|
||||
(lle)->lle_free(lle); \
|
||||
else { \
|
||||
LLE_REMREF(lle); \
|
||||
LLE_WUNLOCK(lle); \
|
||||
@ -133,6 +134,7 @@ struct llentry {
|
||||
#define la_timer lle_timer.la_timer
|
||||
|
||||
/* XXX bad name */
|
||||
#define L3_CADDR(lle) ((const struct sockaddr *)(&lle[1]))
|
||||
#define L3_ADDR(lle) ((struct sockaddr *)(&lle[1]))
|
||||
#define L3_ADDR_LEN(lle) (((struct sockaddr *)(&lle[1]))->sa_len)
|
||||
|
||||
@ -152,7 +154,18 @@ typedef int (llt_delete_t)(struct lltable *, u_int flags,
|
||||
const struct sockaddr *l3addr);
|
||||
typedef void (llt_prefix_free_t)(struct lltable *,
|
||||
const struct sockaddr *prefix, const struct sockaddr *mask, u_int flags);
|
||||
typedef int (llt_dump_t)(struct lltable *, struct sysctl_req *);
|
||||
typedef int (llt_dump_entry_t)(struct lltable *, struct llentry *,
|
||||
struct sysctl_req *);
|
||||
typedef uint32_t (llt_hash_t)(const struct llentry *, uint32_t);
|
||||
typedef int (llt_match_prefix_t)(const struct sockaddr *,
|
||||
const struct sockaddr *, u_int, struct llentry *);
|
||||
typedef void (llt_free_entry_t)(struct lltable *, struct llentry *);
|
||||
typedef void (llt_fill_sa_entry_t)(const struct llentry *, struct sockaddr *);
|
||||
typedef void (llt_link_entry_t)(struct lltable *, struct llentry *);
|
||||
typedef void (llt_unlink_entry_t)(struct llentry *);
|
||||
|
||||
typedef int (llt_foreach_cb_t)(struct lltable *, struct llentry *, void *);
|
||||
typedef int (llt_foreach_entry_t)(struct lltable *, llt_foreach_cb_t *, void *);
|
||||
|
||||
struct lltable {
|
||||
SLIST_ENTRY(lltable) llt_link;
|
||||
@ -164,7 +177,14 @@ struct lltable {
|
||||
llt_create_t *llt_create;
|
||||
llt_delete_t *llt_delete;
|
||||
llt_prefix_free_t *llt_prefix_free;
|
||||
llt_dump_t *llt_dump;
|
||||
llt_dump_entry_t *llt_dump_entry;
|
||||
llt_hash_t *llt_hash;
|
||||
llt_match_prefix_t *llt_match_prefix;
|
||||
llt_free_entry_t *llt_free_entry;
|
||||
llt_foreach_entry_t *llt_foreach_entry;
|
||||
llt_link_entry_t *llt_link_entry;
|
||||
llt_unlink_entry_t *llt_unlink_entry;
|
||||
llt_fill_sa_entry_t *llt_fill_sa_entry;
|
||||
};
|
||||
|
||||
MALLOC_DECLARE(M_LLTABLE);
|
||||
@ -197,6 +217,19 @@ size_t llentry_free(struct llentry *);
|
||||
struct llentry *llentry_alloc(struct ifnet *, struct lltable *,
|
||||
struct sockaddr_storage *);
|
||||
|
||||
/* helper functions */
|
||||
size_t lltable_drop_entry_queue(struct llentry *);
|
||||
|
||||
struct llentry *lltable_create_lle(struct lltable *llt, u_int flags,
|
||||
const void *paddr);
|
||||
void lltable_link_entry(struct lltable *llt, struct llentry *lle);
|
||||
void lltable_unlink_entry(struct lltable *llt, struct llentry *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);
|
||||
|
||||
int lltable_foreach_lle(struct lltable *llt, llt_foreach_cb_t *f,
|
||||
void *farg);
|
||||
/*
|
||||
* Generic link layer address lookup function.
|
||||
*/
|
||||
|
178
sys/netinet/in.c
178
sys/netinet/in.c
@ -961,15 +961,19 @@ struct in_llentry {
|
||||
struct sockaddr_in l3_addr4;
|
||||
};
|
||||
|
||||
#define IN_LLTBL_DEFAULT_HSIZE 32
|
||||
#define IN_LLTBL_HASH(k, h) \
|
||||
(((((((k >> 8) ^ k) >> 8) ^ k) >> 8) ^ k) & ((h) - 1))
|
||||
|
||||
/*
|
||||
* Deletes an address from the address table.
|
||||
* This function is called by the timer functions
|
||||
* such as arptimer() and nd6_llinfo_timer(), and
|
||||
* the caller does the locking.
|
||||
* Do actual deallocation of @lle.
|
||||
* Called by LLE_FREE_LOCKED when number of references
|
||||
* drops to zero.
|
||||
*/
|
||||
static void
|
||||
in_lltable_free(struct lltable *llt, struct llentry *lle)
|
||||
in_lltable_destroy_lle(struct llentry *lle)
|
||||
{
|
||||
|
||||
LLE_WUNLOCK(lle);
|
||||
LLE_LOCK_DESTROY(lle);
|
||||
free(lle, M_LLTABLE);
|
||||
@ -991,7 +995,7 @@ in_lltable_new(const struct sockaddr *l3addr, u_int flags)
|
||||
lle->base.la_expire = time_uptime; /* mark expired */
|
||||
lle->l3_addr4 = *(const struct sockaddr_in *)l3addr;
|
||||
lle->base.lle_refcnt = 1;
|
||||
lle->base.lle_free = in_lltable_free;
|
||||
lle->base.lle_free = in_lltable_destroy_lle;
|
||||
LLE_LOCK_INIT(&lle->base);
|
||||
callout_init(&lle->base.la_timer, 1);
|
||||
|
||||
@ -1001,37 +1005,48 @@ in_lltable_new(const struct sockaddr *l3addr, u_int flags)
|
||||
#define IN_ARE_MASKED_ADDR_EQUAL(d, a, m) ( \
|
||||
(((ntohl((d)->sin_addr.s_addr) ^ (a)->sin_addr.s_addr) & (m)->sin_addr.s_addr)) == 0 )
|
||||
|
||||
static void
|
||||
in_lltable_prefix_free(struct lltable *llt, const struct sockaddr *prefix,
|
||||
const struct sockaddr *mask, u_int flags)
|
||||
static int
|
||||
in_lltable_match_prefix(const struct sockaddr *prefix,
|
||||
const struct sockaddr *mask, u_int flags, struct llentry *lle)
|
||||
{
|
||||
const struct sockaddr_in *pfx = (const struct sockaddr_in *)prefix;
|
||||
const struct sockaddr_in *msk = (const struct sockaddr_in *)mask;
|
||||
struct llentry *lle, *next;
|
||||
int i;
|
||||
size_t pkts_dropped;
|
||||
|
||||
IF_AFDATA_WLOCK(llt->llt_ifp);
|
||||
for (i = 0; i < LLTBL_HASHTBL_SIZE; i++) {
|
||||
LIST_FOREACH_SAFE(lle, &llt->lle_head[i], lle_next, next) {
|
||||
/*
|
||||
* (flags & LLE_STATIC) means deleting all entries
|
||||
* including static ARP entries.
|
||||
*/
|
||||
if (IN_ARE_MASKED_ADDR_EQUAL(satosin(L3_ADDR(lle)),
|
||||
pfx, msk) && ((flags & LLE_STATIC) ||
|
||||
!(lle->la_flags & LLE_STATIC))) {
|
||||
LLE_WLOCK(lle);
|
||||
if (callout_stop(&lle->la_timer))
|
||||
LLE_REMREF(lle);
|
||||
pkts_dropped = llentry_free(lle);
|
||||
ARPSTAT_ADD(dropped, pkts_dropped);
|
||||
}
|
||||
}
|
||||
}
|
||||
IF_AFDATA_WUNLOCK(llt->llt_ifp);
|
||||
/*
|
||||
* (flags & LLE_STATIC) means deleting all entries
|
||||
* including static ARP entries.
|
||||
*/
|
||||
if (IN_ARE_MASKED_ADDR_EQUAL(satosin(L3_ADDR(lle)), pfx, msk) &&
|
||||
((flags & LLE_STATIC) || !(lle->la_flags & LLE_STATIC)))
|
||||
return (1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
in_lltable_free_entry(struct lltable *llt, struct llentry *lle)
|
||||
{
|
||||
struct ifnet *ifp;
|
||||
size_t pkts_dropped;
|
||||
|
||||
LLE_WLOCK_ASSERT(lle);
|
||||
KASSERT(llt != NULL, ("lltable is NULL"));
|
||||
|
||||
/* Unlink entry from table if not already */
|
||||
if ((lle->la_flags & LLE_LINKED) != 0) {
|
||||
ifp = llt->llt_ifp;
|
||||
IF_AFDATA_WLOCK_ASSERT(ifp);
|
||||
lltable_unlink_entry(llt, lle);
|
||||
}
|
||||
|
||||
/* cancel timer */
|
||||
if (callout_stop(&lle->la_timer))
|
||||
LLE_REMREF(lle);
|
||||
|
||||
/* Drop hold queue */
|
||||
pkts_dropped = llentry_free(lle);
|
||||
ARPSTAT_ADD(dropped, pkts_dropped);
|
||||
}
|
||||
|
||||
static int
|
||||
in_lltable_rtcheck(struct ifnet *ifp, u_int flags, const struct sockaddr *l3addr)
|
||||
@ -1107,16 +1122,45 @@ in_lltable_rtcheck(struct ifnet *ifp, u_int flags, const struct sockaddr *l3addr
|
||||
return (0);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
in_lltable_hash_dst(const struct in_addr dst, uint32_t hsize)
|
||||
{
|
||||
|
||||
return (IN_LLTBL_HASH(dst.s_addr, hsize));
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
in_lltable_hash(const struct llentry *lle, uint32_t hsize)
|
||||
{
|
||||
const struct sockaddr_in *sin;
|
||||
|
||||
sin = (const struct sockaddr_in *)(L3_CADDR(lle));
|
||||
|
||||
return (in_lltable_hash_dst(sin->sin_addr, hsize));
|
||||
}
|
||||
|
||||
static void
|
||||
in_lltable_fill_sa_entry(const struct llentry *lle, struct sockaddr *sa)
|
||||
{
|
||||
struct sockaddr_in *sin;
|
||||
|
||||
sin = (struct sockaddr_in *)sa;
|
||||
bzero(sin, sizeof(*sin));
|
||||
sin->sin_family = AF_INET;
|
||||
sin->sin_len = sizeof(*sin);
|
||||
sin->sin_addr = ((const struct sockaddr_in *)(L3_CADDR(lle)))->sin_addr;
|
||||
}
|
||||
|
||||
static inline struct llentry *
|
||||
in_lltable_find_dst(struct lltable *llt, struct in_addr dst)
|
||||
{
|
||||
struct llentry *lle;
|
||||
struct llentries *lleh;
|
||||
struct sockaddr_in *sin;
|
||||
u_int hashkey;
|
||||
u_int hashidx;
|
||||
|
||||
hashkey = dst.s_addr;
|
||||
lleh = &llt->lle_head[LLATBL_HASH(hashkey, LLTBL_HASHMASK)];
|
||||
hashidx = in_lltable_hash_dst(dst, LLTBL_HASHTBL_SIZE);
|
||||
lleh = &llt->lle_head[hashidx];
|
||||
LIST_FOREACH(lle, lleh, lle_next) {
|
||||
sin = satosin(L3_ADDR(lle));
|
||||
if (lle->la_flags & LLE_DELETED)
|
||||
@ -1169,8 +1213,6 @@ in_lltable_create(struct lltable *llt, u_int flags, const struct sockaddr *l3add
|
||||
const struct sockaddr_in *sin = (const struct sockaddr_in *)l3addr;
|
||||
struct ifnet *ifp = llt->llt_ifp;
|
||||
struct llentry *lle;
|
||||
struct llentries *lleh;
|
||||
u_int hashkey;
|
||||
|
||||
IF_AFDATA_WLOCK_ASSERT(ifp);
|
||||
KASSERT(l3addr->sa_family == AF_INET,
|
||||
@ -1205,13 +1247,7 @@ in_lltable_create(struct lltable *llt, u_int flags, const struct sockaddr *l3add
|
||||
lle->la_flags |= (LLE_VALID | LLE_STATIC);
|
||||
}
|
||||
|
||||
hashkey = sin->sin_addr.s_addr;
|
||||
lleh = &llt->lle_head[LLATBL_HASH(hashkey, LLTBL_HASHMASK)];
|
||||
|
||||
lle->lle_tbl = llt;
|
||||
lle->lle_head = lleh;
|
||||
lle->la_flags |= LLE_LINKED;
|
||||
LIST_INSERT_HEAD(lleh, lle, lle_next);
|
||||
lltable_link_entry(llt, lle);
|
||||
LLE_WLOCK(lle);
|
||||
|
||||
return (lle);
|
||||
@ -1226,22 +1262,11 @@ in_lltable_lookup(struct lltable *llt, u_int flags, const struct sockaddr *l3add
|
||||
{
|
||||
const struct sockaddr_in *sin = (const struct sockaddr_in *)l3addr;
|
||||
struct llentry *lle;
|
||||
struct llentries *lleh;
|
||||
u_int hashkey;
|
||||
|
||||
IF_AFDATA_LOCK_ASSERT(llt->llt_ifp);
|
||||
KASSERT(l3addr->sa_family == AF_INET,
|
||||
("sin_family %d", l3addr->sa_family));
|
||||
|
||||
hashkey = sin->sin_addr.s_addr;
|
||||
lleh = &llt->lle_head[LLATBL_HASH(hashkey, LLTBL_HASHMASK)];
|
||||
LIST_FOREACH(lle, lleh, lle_next) {
|
||||
struct sockaddr_in *sa2 = satosin(L3_ADDR(lle));
|
||||
if (lle->la_flags & LLE_DELETED)
|
||||
continue;
|
||||
if (sa2->sin_addr.s_addr == sin->sin_addr.s_addr)
|
||||
break;
|
||||
}
|
||||
lle = in_lltable_find_dst(llt, sin->sin_addr);
|
||||
|
||||
if (lle == NULL)
|
||||
return (NULL);
|
||||
@ -1255,47 +1280,39 @@ in_lltable_lookup(struct lltable *llt, u_int flags, const struct sockaddr *l3add
|
||||
}
|
||||
|
||||
static int
|
||||
in_lltable_dump(struct lltable *llt, struct sysctl_req *wr)
|
||||
in_lltable_dump_entry(struct lltable *llt, struct llentry *lle,
|
||||
struct sysctl_req *wr)
|
||||
{
|
||||
#define SIN(lle) ((struct sockaddr_in *) L3_ADDR(lle))
|
||||
struct ifnet *ifp = llt->llt_ifp;
|
||||
struct llentry *lle;
|
||||
/* XXX stack use */
|
||||
struct {
|
||||
struct rt_msghdr rtm;
|
||||
struct sockaddr_in sin;
|
||||
struct sockaddr_dl sdl;
|
||||
} arpc;
|
||||
int error, i;
|
||||
|
||||
LLTABLE_LOCK_ASSERT();
|
||||
|
||||
error = 0;
|
||||
for (i = 0; i < LLTBL_HASHTBL_SIZE; i++) {
|
||||
LIST_FOREACH(lle, &llt->lle_head[i], lle_next) {
|
||||
struct sockaddr_dl *sdl;
|
||||
struct sockaddr_dl *sdl;
|
||||
int error;
|
||||
|
||||
bzero(&arpc, sizeof(arpc));
|
||||
/* skip deleted entries */
|
||||
if ((lle->la_flags & LLE_DELETED) == LLE_DELETED)
|
||||
continue;
|
||||
return (0);
|
||||
/* Skip if jailed and not a valid IP of the prison. */
|
||||
if (prison_if(wr->td->td_ucred, L3_ADDR(lle)) != 0)
|
||||
continue;
|
||||
lltable_fill_sa_entry(lle,(struct sockaddr *)&arpc.sin);
|
||||
if (prison_if(wr->td->td_ucred,
|
||||
(struct sockaddr *)&arpc.sin) != 0)
|
||||
return (0);
|
||||
/*
|
||||
* produce a msg made of:
|
||||
* struct rt_msghdr;
|
||||
* struct sockaddr_in; (IPv4)
|
||||
* struct sockaddr_dl;
|
||||
*/
|
||||
bzero(&arpc, sizeof(arpc));
|
||||
arpc.rtm.rtm_msglen = sizeof(arpc);
|
||||
arpc.rtm.rtm_version = RTM_VERSION;
|
||||
arpc.rtm.rtm_type = RTM_GET;
|
||||
arpc.rtm.rtm_flags = RTF_UP;
|
||||
arpc.rtm.rtm_addrs = RTA_DST | RTA_GATEWAY;
|
||||
arpc.sin.sin_family = AF_INET;
|
||||
arpc.sin.sin_len = sizeof(arpc.sin);
|
||||
arpc.sin.sin_addr.s_addr = SIN(lle)->sin_addr.s_addr;
|
||||
|
||||
/* publish */
|
||||
if (lle->la_flags & LLE_PUB)
|
||||
@ -1321,12 +1338,8 @@ in_lltable_dump(struct lltable *llt, struct sysctl_req *wr)
|
||||
arpc.rtm.rtm_flags |= RTF_STATIC;
|
||||
arpc.rtm.rtm_index = ifp->if_index;
|
||||
error = SYSCTL_OUT(wr, &arpc, sizeof(arpc));
|
||||
if (error)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return error;
|
||||
#undef SIN
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
void *
|
||||
@ -1339,11 +1352,14 @@ in_domifattach(struct ifnet *ifp)
|
||||
|
||||
llt = lltable_init(ifp, AF_INET);
|
||||
if (llt != NULL) {
|
||||
llt->llt_prefix_free = in_lltable_prefix_free;
|
||||
llt->llt_lookup = in_lltable_lookup;
|
||||
llt->llt_create = in_lltable_create;
|
||||
llt->llt_delete = in_lltable_delete;
|
||||
llt->llt_dump = in_lltable_dump;
|
||||
llt->llt_dump_entry = in_lltable_dump_entry;
|
||||
llt->llt_hash = in_lltable_hash;
|
||||
llt->llt_fill_sa_entry = in_lltable_fill_sa_entry;
|
||||
llt->llt_free_entry = in_lltable_free_entry;
|
||||
llt->llt_match_prefix = in_lltable_match_prefix;
|
||||
}
|
||||
ii->ii_llt = llt;
|
||||
|
||||
|
@ -2050,15 +2050,19 @@ struct in6_llentry {
|
||||
struct sockaddr_in6 l3_addr6;
|
||||
};
|
||||
|
||||
#define IN6_LLTBL_DEFAULT_HSIZE 32
|
||||
#define IN6_LLTBL_HASH(k, h) \
|
||||
(((((((k >> 8) ^ k) >> 8) ^ k) >> 8) ^ k) & ((h) - 1))
|
||||
|
||||
/*
|
||||
* Deletes an address from the address table.
|
||||
* This function is called by the timer functions
|
||||
* such as arptimer() and nd6_llinfo_timer(), and
|
||||
* the caller does the locking.
|
||||
* Do actual deallocation of @lle.
|
||||
* Called by LLE_FREE_LOCKED when number of references
|
||||
* drops to zero.
|
||||
*/
|
||||
static void
|
||||
in6_lltable_free(struct lltable *llt, struct llentry *lle)
|
||||
in6_lltable_destroy_lle(struct llentry *lle)
|
||||
{
|
||||
|
||||
LLE_WUNLOCK(lle);
|
||||
LLE_LOCK_DESTROY(lle);
|
||||
free(lle, M_LLTABLE);
|
||||
@ -2075,42 +2079,48 @@ in6_lltable_new(const struct sockaddr *l3addr, u_int flags)
|
||||
|
||||
lle->l3_addr6 = *(const struct sockaddr_in6 *)l3addr;
|
||||
lle->base.lle_refcnt = 1;
|
||||
lle->base.lle_free = in6_lltable_free;
|
||||
lle->base.lle_free = in6_lltable_destroy_lle;
|
||||
LLE_LOCK_INIT(&lle->base);
|
||||
callout_init(&lle->base.ln_timer_ch, 1);
|
||||
|
||||
return (&lle->base);
|
||||
}
|
||||
|
||||
static void
|
||||
in6_lltable_prefix_free(struct lltable *llt, const struct sockaddr *prefix,
|
||||
const struct sockaddr *mask, u_int flags)
|
||||
static int
|
||||
in6_lltable_match_prefix(const struct sockaddr *prefix,
|
||||
const struct sockaddr *mask, u_int flags, struct llentry *lle)
|
||||
{
|
||||
const struct sockaddr_in6 *pfx = (const struct sockaddr_in6 *)prefix;
|
||||
const struct sockaddr_in6 *msk = (const struct sockaddr_in6 *)mask;
|
||||
struct llentry *lle, *next;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* (flags & LLE_STATIC) means deleting all entries
|
||||
* including static ND6 entries.
|
||||
*/
|
||||
IF_AFDATA_WLOCK(llt->llt_ifp);
|
||||
for (i = 0; i < LLTBL_HASHTBL_SIZE; i++) {
|
||||
LIST_FOREACH_SAFE(lle, &llt->lle_head[i], lle_next, next) {
|
||||
if (IN6_ARE_MASKED_ADDR_EQUAL(
|
||||
&satosin6(L3_ADDR(lle))->sin6_addr,
|
||||
&pfx->sin6_addr, &msk->sin6_addr) &&
|
||||
((flags & LLE_STATIC) ||
|
||||
!(lle->la_flags & LLE_STATIC))) {
|
||||
LLE_WLOCK(lle);
|
||||
if (callout_stop(&lle->la_timer))
|
||||
LLE_REMREF(lle);
|
||||
llentry_free(lle);
|
||||
}
|
||||
}
|
||||
if (IN6_ARE_MASKED_ADDR_EQUAL(&satosin6(L3_ADDR(lle))->sin6_addr,
|
||||
&pfx->sin6_addr, &msk->sin6_addr) &&
|
||||
((flags & LLE_STATIC) || !(lle->la_flags & LLE_STATIC)))
|
||||
return (1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
in6_lltable_free_entry(struct lltable *llt, struct llentry *lle)
|
||||
{
|
||||
struct ifnet *ifp;
|
||||
|
||||
LLE_WLOCK_ASSERT(lle);
|
||||
KASSERT(llt != NULL, ("lltable is NULL"));
|
||||
|
||||
/* Unlink entry from table */
|
||||
if ((lle->la_flags & LLE_LINKED) != 0) {
|
||||
|
||||
ifp = llt->llt_ifp;
|
||||
IF_AFDATA_WLOCK_ASSERT(ifp);
|
||||
lltable_unlink_entry(llt, lle);
|
||||
}
|
||||
IF_AFDATA_WUNLOCK(llt->llt_ifp);
|
||||
|
||||
if (callout_stop(&lle->la_timer))
|
||||
LLE_REMREF(lle);
|
||||
|
||||
llentry_free(lle);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -2152,18 +2162,47 @@ in6_lltable_rtcheck(struct ifnet *ifp,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
in6_lltable_hash_dst(const struct in6_addr *dst, uint32_t hsize)
|
||||
{
|
||||
|
||||
return (IN6_LLTBL_HASH(dst->s6_addr32[3], hsize));
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
in6_lltable_hash(const struct llentry *lle, uint32_t hsize)
|
||||
{
|
||||
const struct sockaddr_in6 *sin6;
|
||||
|
||||
sin6 = (const struct sockaddr_in6 *)L3_CADDR(lle);
|
||||
|
||||
return (in6_lltable_hash_dst(&sin6->sin6_addr, hsize));
|
||||
}
|
||||
|
||||
static void
|
||||
in6_lltable_fill_sa_entry(const struct llentry *lle, struct sockaddr *sa)
|
||||
{
|
||||
struct sockaddr_in6 *sin6;
|
||||
|
||||
sin6 = (struct sockaddr_in6 *)sa;
|
||||
bzero(sin6, sizeof(*sin6));
|
||||
sin6->sin6_family = AF_INET6;
|
||||
sin6->sin6_len = sizeof(*sin6);
|
||||
sin6->sin6_addr =((const struct sockaddr_in6*)L3_CADDR(lle))->sin6_addr;
|
||||
}
|
||||
|
||||
static inline struct llentry *
|
||||
in6_lltable_find_dst(struct lltable *llt, const struct in6_addr *dst)
|
||||
{
|
||||
struct llentry *lle;
|
||||
struct llentries *lleh;
|
||||
const struct sockaddr_in6 *sin6;
|
||||
u_int hashkey;
|
||||
u_int hashidx;
|
||||
|
||||
hashkey = dst->s6_addr32[3];
|
||||
lleh = &llt->lle_head[LLATBL_HASH(hashkey, LLTBL_HASHMASK)];
|
||||
hashidx = in6_lltable_hash_dst(dst, LLTBL_HASHTBL_SIZE);
|
||||
lleh = &llt->lle_head[hashidx];
|
||||
LIST_FOREACH(lle, lleh, lle_next) {
|
||||
sin6 = (const struct sockaddr_in6 *)L3_ADDR(lle);
|
||||
sin6 = (const struct sockaddr_in6 *)L3_CADDR(lle);
|
||||
if (lle->la_flags & LLE_DELETED)
|
||||
continue;
|
||||
if (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, dst))
|
||||
@ -2212,8 +2251,6 @@ in6_lltable_create(struct lltable *llt, u_int flags,
|
||||
const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)l3addr;
|
||||
struct ifnet *ifp = llt->llt_ifp;
|
||||
struct llentry *lle;
|
||||
struct llentries *lleh;
|
||||
u_int hashkey;
|
||||
|
||||
IF_AFDATA_WLOCK_ASSERT(ifp);
|
||||
KASSERT(l3addr->sa_family == AF_INET6,
|
||||
@ -2246,13 +2283,7 @@ in6_lltable_create(struct lltable *llt, u_int flags,
|
||||
lle->la_flags |= (LLE_VALID | LLE_STATIC);
|
||||
}
|
||||
|
||||
hashkey = sin6->sin6_addr.s6_addr32[3];
|
||||
lleh = &llt->lle_head[LLATBL_HASH(hashkey, LLTBL_HASHMASK)];
|
||||
|
||||
lle->lle_tbl = llt;
|
||||
lle->lle_head = lleh;
|
||||
lle->la_flags |= LLE_LINKED;
|
||||
LIST_INSERT_HEAD(lleh, lle, lle_next);
|
||||
lltable_link_entry(llt, lle);
|
||||
LLE_WLOCK(lle);
|
||||
|
||||
return (lle);
|
||||
@ -2282,10 +2313,10 @@ in6_lltable_lookup(struct lltable *llt, u_int flags,
|
||||
}
|
||||
|
||||
static int
|
||||
in6_lltable_dump(struct lltable *llt, struct sysctl_req *wr)
|
||||
in6_lltable_dump_entry(struct lltable *llt, struct llentry *lle,
|
||||
struct sysctl_req *wr)
|
||||
{
|
||||
struct ifnet *ifp = llt->llt_ifp;
|
||||
struct llentry *lle;
|
||||
/* XXX stack use */
|
||||
struct {
|
||||
struct rt_msghdr rtm;
|
||||
@ -2298,39 +2329,30 @@ in6_lltable_dump(struct lltable *llt, struct sysctl_req *wr)
|
||||
#endif
|
||||
struct sockaddr_dl sdl;
|
||||
} ndpc;
|
||||
int i, error;
|
||||
struct sockaddr_dl *sdl;
|
||||
int error;
|
||||
|
||||
if (ifp->if_flags & IFF_LOOPBACK)
|
||||
return 0;
|
||||
|
||||
LLTABLE_LOCK_ASSERT();
|
||||
|
||||
error = 0;
|
||||
for (i = 0; i < LLTBL_HASHTBL_SIZE; i++) {
|
||||
LIST_FOREACH(lle, &llt->lle_head[i], lle_next) {
|
||||
struct sockaddr_dl *sdl;
|
||||
|
||||
/* skip deleted or invalid entries */
|
||||
bzero(&ndpc, sizeof(ndpc));
|
||||
/* skip invalid entries */
|
||||
if ((lle->la_flags & (LLE_DELETED|LLE_VALID)) != LLE_VALID)
|
||||
continue;
|
||||
return (0);
|
||||
/* Skip if jailed and not a valid IP of the prison. */
|
||||
if (prison_if(wr->td->td_ucred, L3_ADDR(lle)) != 0)
|
||||
continue;
|
||||
lltable_fill_sa_entry(lle,
|
||||
(struct sockaddr *)&ndpc.sin6);
|
||||
if (prison_if(wr->td->td_ucred,
|
||||
(struct sockaddr *)&ndpc.sin6) != 0)
|
||||
return (0);
|
||||
/*
|
||||
* produce a msg made of:
|
||||
* struct rt_msghdr;
|
||||
* struct sockaddr_in6 (IPv6)
|
||||
* struct sockaddr_dl;
|
||||
*/
|
||||
bzero(&ndpc, sizeof(ndpc));
|
||||
ndpc.rtm.rtm_msglen = sizeof(ndpc);
|
||||
ndpc.rtm.rtm_version = RTM_VERSION;
|
||||
ndpc.rtm.rtm_type = RTM_GET;
|
||||
ndpc.rtm.rtm_flags = RTF_UP;
|
||||
ndpc.rtm.rtm_addrs = RTA_DST | RTA_GATEWAY;
|
||||
ndpc.sin6.sin6_family = AF_INET6;
|
||||
ndpc.sin6.sin6_len = sizeof(ndpc.sin6);
|
||||
bcopy(L3_ADDR(lle), &ndpc.sin6, L3_ADDR_LEN(lle));
|
||||
if (V_deembed_scopeid)
|
||||
sa6_recoverscope(&ndpc.sin6);
|
||||
|
||||
@ -2352,11 +2374,8 @@ in6_lltable_dump(struct lltable *llt, struct sysctl_req *wr)
|
||||
ndpc.rtm.rtm_flags |= RTF_STATIC;
|
||||
ndpc.rtm.rtm_index = ifp->if_index;
|
||||
error = SYSCTL_OUT(wr, &ndpc, sizeof(ndpc));
|
||||
if (error)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return error;
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
void *
|
||||
@ -2389,11 +2408,14 @@ in6_domifattach(struct ifnet *ifp)
|
||||
ext->scope6_id = scope6_ifattach(ifp);
|
||||
ext->lltable = lltable_init(ifp, AF_INET6);
|
||||
if (ext->lltable != NULL) {
|
||||
ext->lltable->llt_prefix_free = in6_lltable_prefix_free;
|
||||
ext->lltable->llt_lookup = in6_lltable_lookup;
|
||||
ext->lltable->llt_create = in6_lltable_create;
|
||||
ext->lltable->llt_delete = in6_lltable_delete;
|
||||
ext->lltable->llt_dump = in6_lltable_dump;
|
||||
ext->lltable->llt_dump_entry = in6_lltable_dump_entry;
|
||||
ext->lltable->llt_hash = in6_lltable_hash;
|
||||
ext->lltable->llt_fill_sa_entry = in6_lltable_fill_sa_entry;
|
||||
ext->lltable->llt_free_entry = in6_lltable_free_entry;
|
||||
ext->lltable->llt_match_prefix = in6_lltable_match_prefix;
|
||||
}
|
||||
|
||||
ext->mld_ifinfo = mld_domifattach(ifp);
|
||||
|
Loading…
Reference in New Issue
Block a user