Fix long-standing issue with incorrect radix mask calculation.
Usual symptoms are messages like rn_delete: inconsistent annotation rn_addmask: mask impossibly already in tree or inability to flush/delete particular prefix in ipfw table. Changes: * Assume 32 bytes as maximum radix key length * Remove rn_init() * Statically allocate rn_ones/rn_zeroes * Make separate mask tree for each "normal" tree instead of system global one * Remove "optimization" on masks reusage and key zeroying * Change rn_addmask() arguments to accept tree pointer (no users in base) PR: kern/182851, kern/169206, kern/135476, kern/134531 Found by: Slawa Olhovchenkov <slw@zxy.spb.ru> MFC after: 2 weeks Reviewed by: glebius Sponsored by: Yandex LLC
This commit is contained in:
parent
716151ab18
commit
65a17d744e
146
sys/net/radix.c
146
sys/net/radix.c
@ -66,27 +66,19 @@ static struct radix_node
|
||||
*rn_search(void *, struct radix_node *),
|
||||
*rn_search_m(void *, struct radix_node *, void *);
|
||||
|
||||
static int max_keylen;
|
||||
static struct radix_mask *rn_mkfreelist;
|
||||
static struct radix_node_head *mask_rnhead;
|
||||
/*
|
||||
* Work area -- the following point to 3 buffers of size max_keylen,
|
||||
* allocated in this order in a block of memory malloc'ed by rn_init.
|
||||
* rn_zeros, rn_ones are set in rn_init and used in readonly afterwards.
|
||||
* addmask_key is used in rn_addmask in rw mode and not thread-safe.
|
||||
*/
|
||||
static char *rn_zeros, *rn_ones, *addmask_key;
|
||||
static void rn_detachhead_internal(void **head);
|
||||
static int rn_inithead_internal(void **head, int off);
|
||||
|
||||
#define MKGet(m) { \
|
||||
if (rn_mkfreelist) { \
|
||||
m = rn_mkfreelist; \
|
||||
rn_mkfreelist = (m)->rm_mklist; \
|
||||
} else \
|
||||
R_Malloc(m, struct radix_mask *, sizeof (struct radix_mask)); }
|
||||
|
||||
#define MKFree(m) { (m)->rm_mklist = rn_mkfreelist; rn_mkfreelist = (m);}
|
||||
#define RADIX_MAX_KEY_LEN 32
|
||||
|
||||
static char rn_zeros[RADIX_MAX_KEY_LEN];
|
||||
static char rn_ones[RADIX_MAX_KEY_LEN] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
};
|
||||
|
||||
#define rn_masktop (mask_rnhead->rnh_treetop)
|
||||
|
||||
static int rn_lexobetter(void *m_arg, void *n_arg);
|
||||
static struct radix_mask *
|
||||
@ -230,7 +222,8 @@ rn_lookup(v_arg, m_arg, head)
|
||||
caddr_t netmask = 0;
|
||||
|
||||
if (m_arg) {
|
||||
x = rn_addmask(m_arg, 1, head->rnh_treetop->rn_offset);
|
||||
x = rn_addmask(m_arg, head->rnh_masks, 1,
|
||||
head->rnh_treetop->rn_offset);
|
||||
if (x == 0)
|
||||
return (0);
|
||||
netmask = x->rn_key;
|
||||
@ -489,53 +482,47 @@ on1:
|
||||
}
|
||||
|
||||
struct radix_node *
|
||||
rn_addmask(n_arg, search, skip)
|
||||
int search, skip;
|
||||
void *n_arg;
|
||||
rn_addmask(void *n_arg, struct radix_node_head *maskhead, int search, int skip)
|
||||
{
|
||||
caddr_t netmask = (caddr_t)n_arg;
|
||||
register struct radix_node *x;
|
||||
register caddr_t cp, cplim;
|
||||
register int b = 0, mlen, j;
|
||||
int maskduplicated, m0, isnormal;
|
||||
int maskduplicated, isnormal;
|
||||
struct radix_node *saved_x;
|
||||
static int last_zeroed = 0;
|
||||
char addmask_key[RADIX_MAX_KEY_LEN];
|
||||
|
||||
if ((mlen = LEN(netmask)) > max_keylen)
|
||||
mlen = max_keylen;
|
||||
if ((mlen = LEN(netmask)) > RADIX_MAX_KEY_LEN)
|
||||
mlen = RADIX_MAX_KEY_LEN;
|
||||
if (skip == 0)
|
||||
skip = 1;
|
||||
if (mlen <= skip)
|
||||
return (mask_rnhead->rnh_nodes);
|
||||
return (maskhead->rnh_nodes);
|
||||
|
||||
bzero(addmask_key, RADIX_MAX_KEY_LEN);
|
||||
if (skip > 1)
|
||||
bcopy(rn_ones + 1, addmask_key + 1, skip - 1);
|
||||
if ((m0 = mlen) > skip)
|
||||
bcopy(netmask + skip, addmask_key + skip, mlen - skip);
|
||||
bcopy(netmask + skip, addmask_key + skip, mlen - skip);
|
||||
/*
|
||||
* Trim trailing zeroes.
|
||||
*/
|
||||
for (cp = addmask_key + mlen; (cp > addmask_key) && cp[-1] == 0;)
|
||||
cp--;
|
||||
mlen = cp - addmask_key;
|
||||
if (mlen <= skip) {
|
||||
if (m0 >= last_zeroed)
|
||||
last_zeroed = mlen;
|
||||
return (mask_rnhead->rnh_nodes);
|
||||
}
|
||||
if (m0 < last_zeroed)
|
||||
bzero(addmask_key + m0, last_zeroed - m0);
|
||||
*addmask_key = last_zeroed = mlen;
|
||||
x = rn_search(addmask_key, rn_masktop);
|
||||
if (mlen <= skip)
|
||||
return (maskhead->rnh_nodes);
|
||||
*addmask_key = mlen;
|
||||
x = rn_search(addmask_key, maskhead->rnh_treetop);
|
||||
if (bcmp(addmask_key, x->rn_key, mlen) != 0)
|
||||
x = 0;
|
||||
if (x || search)
|
||||
return (x);
|
||||
R_Zalloc(x, struct radix_node *, max_keylen + 2 * sizeof (*x));
|
||||
R_Zalloc(x, struct radix_node *, RADIX_MAX_KEY_LEN + 2 * sizeof (*x));
|
||||
if ((saved_x = x) == 0)
|
||||
return (0);
|
||||
netmask = cp = (caddr_t)(x + 2);
|
||||
bcopy(addmask_key, cp, mlen);
|
||||
x = rn_insert(cp, mask_rnhead, &maskduplicated, x);
|
||||
x = rn_insert(cp, maskhead, &maskduplicated, x);
|
||||
if (maskduplicated) {
|
||||
log(LOG_ERR, "rn_addmask: mask impossibly already in tree");
|
||||
Free(saved_x);
|
||||
@ -590,12 +577,12 @@ rn_new_radix_mask(tt, next)
|
||||
{
|
||||
register struct radix_mask *m;
|
||||
|
||||
MKGet(m);
|
||||
R_Malloc(m, struct radix_mask *, sizeof (struct radix_mask));
|
||||
if (m == 0) {
|
||||
log(LOG_ERR, "Mask for route not entered\n");
|
||||
log(LOG_ERR, "Failed to allocate route mask\n");
|
||||
return (0);
|
||||
}
|
||||
bzero(m, sizeof *m);
|
||||
bzero(m, sizeof(*m));
|
||||
m->rm_bit = tt->rn_bit;
|
||||
m->rm_flags = tt->rn_flags;
|
||||
if (tt->rn_flags & RNF_NORMAL)
|
||||
@ -629,7 +616,8 @@ rn_addroute(v_arg, n_arg, head, treenodes)
|
||||
* nodes and possibly save time in calculating indices.
|
||||
*/
|
||||
if (netmask) {
|
||||
if ((x = rn_addmask(netmask, 0, top->rn_offset)) == 0)
|
||||
x = rn_addmask(netmask, head->rnh_masks, 0, top->rn_offset);
|
||||
if (x == NULL)
|
||||
return (0);
|
||||
b_leaf = x->rn_bit;
|
||||
b = -1 - x->rn_bit;
|
||||
@ -808,7 +796,8 @@ rn_delete(v_arg, netmask_arg, head)
|
||||
* Delete our route from mask lists.
|
||||
*/
|
||||
if (netmask) {
|
||||
if ((x = rn_addmask(netmask, 1, head_off)) == 0)
|
||||
x = rn_addmask(netmask, head->rnh_masks, 1, head_off);
|
||||
if (x == NULL)
|
||||
return (0);
|
||||
netmask = x->rn_key;
|
||||
while (tt->rn_mask != netmask)
|
||||
@ -841,7 +830,7 @@ rn_delete(v_arg, netmask_arg, head)
|
||||
for (mp = &x->rn_mklist; (m = *mp); mp = &m->rm_mklist)
|
||||
if (m == saved_m) {
|
||||
*mp = m->rm_mklist;
|
||||
MKFree(m);
|
||||
Free(m);
|
||||
break;
|
||||
}
|
||||
if (m == 0) {
|
||||
@ -932,7 +921,7 @@ on1:
|
||||
struct radix_mask *mm = m->rm_mklist;
|
||||
x->rn_mklist = 0;
|
||||
if (--(m->rm_refs) < 0)
|
||||
MKFree(m);
|
||||
Free(m);
|
||||
m = mm;
|
||||
}
|
||||
if (m)
|
||||
@ -1128,10 +1117,8 @@ rn_walktree(h, f, w)
|
||||
* bits starting at 'off'.
|
||||
* Return 1 on success, 0 on error.
|
||||
*/
|
||||
int
|
||||
rn_inithead(head, off)
|
||||
void **head;
|
||||
int off;
|
||||
static int
|
||||
rn_inithead_internal(void **head, int off)
|
||||
{
|
||||
register struct radix_node_head *rnh;
|
||||
register struct radix_node *t, *tt, *ttt;
|
||||
@ -1163,8 +1150,8 @@ rn_inithead(head, off)
|
||||
return (1);
|
||||
}
|
||||
|
||||
int
|
||||
rn_detachhead(void **head)
|
||||
static void
|
||||
rn_detachhead_internal(void **head)
|
||||
{
|
||||
struct radix_node_head *rnh;
|
||||
|
||||
@ -1176,28 +1163,41 @@ rn_detachhead(void **head)
|
||||
Free(rnh);
|
||||
|
||||
*head = NULL;
|
||||
}
|
||||
|
||||
int
|
||||
rn_inithead(void **head, int off)
|
||||
{
|
||||
struct radix_node_head *rnh;
|
||||
|
||||
if (*head != NULL)
|
||||
return (1);
|
||||
|
||||
if (rn_inithead_internal(head, off) == 0)
|
||||
return (0);
|
||||
|
||||
rnh = (struct radix_node_head *)(*head);
|
||||
|
||||
if (rn_inithead_internal((void **)&rnh->rnh_masks, 0) == 0) {
|
||||
rn_detachhead_internal(head);
|
||||
return (0);
|
||||
}
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
void
|
||||
rn_init(int maxk)
|
||||
int
|
||||
rn_detachhead(void **head)
|
||||
{
|
||||
char *cp, *cplim;
|
||||
struct radix_node_head *rnh;
|
||||
|
||||
max_keylen = maxk;
|
||||
if (max_keylen == 0) {
|
||||
log(LOG_ERR,
|
||||
"rn_init: radix functions require max_keylen be set\n");
|
||||
return;
|
||||
}
|
||||
R_Malloc(rn_zeros, char *, 3 * max_keylen);
|
||||
if (rn_zeros == NULL)
|
||||
panic("rn_init");
|
||||
bzero(rn_zeros, 3 * max_keylen);
|
||||
rn_ones = cp = rn_zeros + max_keylen;
|
||||
addmask_key = cplim = rn_ones + max_keylen;
|
||||
while (cp < cplim)
|
||||
*cp++ = -1;
|
||||
if (rn_inithead((void **)(void *)&mask_rnhead, 0) == 0)
|
||||
panic("rn_init 2");
|
||||
KASSERT((head != NULL && *head != NULL),
|
||||
("%s: head already freed", __func__));
|
||||
|
||||
rnh = *head;
|
||||
|
||||
rn_detachhead_internal((void **)&rnh->rnh_masks);
|
||||
rn_detachhead_internal(head);
|
||||
return (1);
|
||||
}
|
||||
|
||||
|
@ -124,6 +124,7 @@ struct radix_node_head {
|
||||
void (*rnh_close) /* do something when the last ref drops */
|
||||
(struct radix_node *rn, struct radix_node_head *head);
|
||||
struct radix_node rnh_nodes[3]; /* empty tree for common case */
|
||||
struct radix_node_head *rnh_masks; /* Storage for our masks */
|
||||
#ifdef _KERNEL
|
||||
struct rwlock rnh_lock; /* locks entire radix tree */
|
||||
#endif
|
||||
@ -152,12 +153,11 @@ struct radix_node_head {
|
||||
#define RADIX_NODE_HEAD_WLOCK_ASSERT(rnh) rw_assert(&(rnh)->rnh_lock, RA_WLOCKED)
|
||||
#endif /* _KERNEL */
|
||||
|
||||
void rn_init(int);
|
||||
int rn_inithead(void **, int);
|
||||
int rn_detachhead(void **);
|
||||
int rn_refines(void *, void *);
|
||||
struct radix_node
|
||||
*rn_addmask(void *, int, int),
|
||||
*rn_addmask(void *, struct radix_node_head *, int, int),
|
||||
*rn_addroute (void *, void *, struct radix_node_head *,
|
||||
struct radix_node [2]),
|
||||
*rn_delete(void *, void *, struct radix_node_head *),
|
||||
|
@ -183,20 +183,12 @@ rt_tables_get_rnh(int table, int fam)
|
||||
static void
|
||||
route_init(void)
|
||||
{
|
||||
struct domain *dom;
|
||||
int max_keylen = 0;
|
||||
|
||||
/* whack the tunable ints into line. */
|
||||
if (rt_numfibs > RT_MAXFIBS)
|
||||
rt_numfibs = RT_MAXFIBS;
|
||||
if (rt_numfibs == 0)
|
||||
rt_numfibs = 1;
|
||||
|
||||
for (dom = domains; dom; dom = dom->dom_next)
|
||||
if (dom->dom_maxrtkey > max_keylen)
|
||||
max_keylen = dom->dom_maxrtkey;
|
||||
|
||||
rn_init(max_keylen); /* init all zeroes, all ones, mask table */
|
||||
}
|
||||
SYSINIT(route_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, route_init, 0);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user