From owner-svn-src-head@FreeBSD.ORG Wed Oct 16 12:18:45 2013 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTP id 636667BC; Wed, 16 Oct 2013 12:18:45 +0000 (UTC) (envelope-from melifaro@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id 5035C28E4; Wed, 16 Oct 2013 12:18:45 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.7/8.14.7) with ESMTP id r9GCIjn8095468; Wed, 16 Oct 2013 12:18:45 GMT (envelope-from melifaro@svn.freebsd.org) Received: (from melifaro@localhost) by svn.freebsd.org (8.14.7/8.14.5/Submit) id r9GCIiMN095465; Wed, 16 Oct 2013 12:18:44 GMT (envelope-from melifaro@svn.freebsd.org) Message-Id: <201310161218.r9GCIiMN095465@svn.freebsd.org> From: "Alexander V. Chernikov" Date: Wed, 16 Oct 2013 12:18:44 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r256624 - head/sys/net X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 16 Oct 2013 12:18:45 -0000 Author: melifaro Date: Wed Oct 16 12:18:44 2013 New Revision: 256624 URL: http://svnweb.freebsd.org/changeset/base/256624 Log: 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 MFC after: 2 weeks Reviewed by: glebius Sponsored by: Yandex LLC Modified: head/sys/net/radix.c head/sys/net/radix.h head/sys/net/route.c Modified: head/sys/net/radix.c ============================================================================== --- head/sys/net/radix.c Wed Oct 16 12:15:33 2013 (r256623) +++ head/sys/net/radix.c Wed Oct 16 12:18:44 2013 (r256624) @@ -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 RADIX_MAX_KEY_LEN 32 -#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);} +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, treenode * 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); } + Modified: head/sys/net/radix.h ============================================================================== --- head/sys/net/radix.h Wed Oct 16 12:15:33 2013 (r256623) +++ head/sys/net/radix.h Wed Oct 16 12:18:44 2013 (r256624) @@ -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 *), Modified: head/sys/net/route.c ============================================================================== --- head/sys/net/route.c Wed Oct 16 12:15:33 2013 (r256623) +++ head/sys/net/route.c Wed Oct 16 12:18:44 2013 (r256624) @@ -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);