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:
Alexander V. Chernikov 2015-08-10 12:03:59 +00:00
parent 0702ce1a52
commit 11cdad9873
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=286577
4 changed files with 487 additions and 177 deletions

View File

@ -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.
*/

View File

@ -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.
*/

View File

@ -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;

View File

@ -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);