xref: /freebsd/sys/netpfil/ipfw/ip_fw_table_algo.c (revision 720ee730c602e624022fa74b9fd61604318a6d41)
19f7d47b0SAlexander V. Chernikov /*-
29f7d47b0SAlexander V. Chernikov  * Copyright (c) 2004 Ruslan Ermilov and Vsevolod Lobko.
39f7d47b0SAlexander V. Chernikov  *
49f7d47b0SAlexander V. Chernikov  * Redistribution and use in source and binary forms, with or without
59f7d47b0SAlexander V. Chernikov  * modification, are permitted provided that the following conditions
69f7d47b0SAlexander V. Chernikov  * are met:
79f7d47b0SAlexander V. Chernikov  * 1. Redistributions of source code must retain the above copyright
89f7d47b0SAlexander V. Chernikov  *    notice, this list of conditions and the following disclaimer.
99f7d47b0SAlexander V. Chernikov  * 2. Redistributions in binary form must reproduce the above copyright
109f7d47b0SAlexander V. Chernikov  *    notice, this list of conditions and the following disclaimer in the
119f7d47b0SAlexander V. Chernikov  *    documentation and/or other materials provided with the distribution.
129f7d47b0SAlexander V. Chernikov  *
139f7d47b0SAlexander V. Chernikov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
149f7d47b0SAlexander V. Chernikov  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
159f7d47b0SAlexander V. Chernikov  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
169f7d47b0SAlexander V. Chernikov  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
179f7d47b0SAlexander V. Chernikov  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
189f7d47b0SAlexander V. Chernikov  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
199f7d47b0SAlexander V. Chernikov  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
209f7d47b0SAlexander V. Chernikov  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
219f7d47b0SAlexander V. Chernikov  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
229f7d47b0SAlexander V. Chernikov  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
239f7d47b0SAlexander V. Chernikov  * SUCH DAMAGE.
249f7d47b0SAlexander V. Chernikov  */
259f7d47b0SAlexander V. Chernikov 
269f7d47b0SAlexander V. Chernikov #include <sys/cdefs.h>
279f7d47b0SAlexander V. Chernikov __FBSDID("$FreeBSD: projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c 267384 2014-06-12 09:59:11Z melifaro $");
289f7d47b0SAlexander V. Chernikov 
299f7d47b0SAlexander V. Chernikov /*
309f7d47b0SAlexander V. Chernikov  * Lookup table algorithms.
319f7d47b0SAlexander V. Chernikov  *
329f7d47b0SAlexander V. Chernikov  */
339f7d47b0SAlexander V. Chernikov 
349f7d47b0SAlexander V. Chernikov #include "opt_ipfw.h"
359f7d47b0SAlexander V. Chernikov #include "opt_inet.h"
369f7d47b0SAlexander V. Chernikov #ifndef INET
379f7d47b0SAlexander V. Chernikov #error IPFIREWALL requires INET.
389f7d47b0SAlexander V. Chernikov #endif /* INET */
399f7d47b0SAlexander V. Chernikov #include "opt_inet6.h"
409f7d47b0SAlexander V. Chernikov 
419f7d47b0SAlexander V. Chernikov #include <sys/param.h>
429f7d47b0SAlexander V. Chernikov #include <sys/systm.h>
439f7d47b0SAlexander V. Chernikov #include <sys/malloc.h>
449f7d47b0SAlexander V. Chernikov #include <sys/kernel.h>
459f7d47b0SAlexander V. Chernikov #include <sys/lock.h>
469f7d47b0SAlexander V. Chernikov #include <sys/rwlock.h>
479f7d47b0SAlexander V. Chernikov #include <sys/socket.h>
489f7d47b0SAlexander V. Chernikov #include <sys/queue.h>
499f7d47b0SAlexander V. Chernikov #include <net/if.h>	/* ip_fw.h requires IFNAMSIZ */
509f7d47b0SAlexander V. Chernikov #include <net/radix.h>
519f7d47b0SAlexander V. Chernikov 
529f7d47b0SAlexander V. Chernikov #include <netinet/in.h>
539f7d47b0SAlexander V. Chernikov #include <netinet/ip_var.h>	/* struct ipfw_rule_ref */
549f7d47b0SAlexander V. Chernikov #include <netinet/ip_fw.h>
559f7d47b0SAlexander V. Chernikov 
569f7d47b0SAlexander V. Chernikov #include <netpfil/ipfw/ip_fw_private.h>
57ea761a5dSAlexander V. Chernikov #include <netpfil/ipfw/ip_fw_table.h>
589f7d47b0SAlexander V. Chernikov 
599f7d47b0SAlexander V. Chernikov static MALLOC_DEFINE(M_IPFW_TBL, "ipfw_tbl", "IpFw tables");
609f7d47b0SAlexander V. Chernikov 
610bce0c23SAlexander V. Chernikov /*
620bce0c23SAlexander V. Chernikov  * Utility structures/functions common to more than one algo
630bce0c23SAlexander V. Chernikov  */
640bce0c23SAlexander V. Chernikov 
650bce0c23SAlexander V. Chernikov struct mod_item {
660bce0c23SAlexander V. Chernikov 	void	*main_ptr;
670bce0c23SAlexander V. Chernikov 	size_t	size;
680bce0c23SAlexander V. Chernikov 	void	*main_ptr6;
690bce0c23SAlexander V. Chernikov 	size_t	size6;
700bce0c23SAlexander V. Chernikov };
710bce0c23SAlexander V. Chernikov 
7268394ec8SAlexander V. Chernikov static int badd(const void *key, void *item, void *base, size_t nmemb,
7368394ec8SAlexander V. Chernikov     size_t size, int (*compar) (const void *, const void *));
7468394ec8SAlexander V. Chernikov static int bdel(const void *key, void *base, size_t nmemb, size_t size,
7568394ec8SAlexander V. Chernikov     int (*compar) (const void *, const void *));
7668394ec8SAlexander V. Chernikov 
7768394ec8SAlexander V. Chernikov 
7868394ec8SAlexander V. Chernikov /*
7968394ec8SAlexander V. Chernikov  * CIDR implementation using radix
8068394ec8SAlexander V. Chernikov  *
8168394ec8SAlexander V. Chernikov  */
8268394ec8SAlexander V. Chernikov 
839f7d47b0SAlexander V. Chernikov /*
849f7d47b0SAlexander V. Chernikov  * The radix code expects addr and mask to be array of bytes,
859f7d47b0SAlexander V. Chernikov  * with the first byte being the length of the array. rn_inithead
869f7d47b0SAlexander V. Chernikov  * is called with the offset in bits of the lookup key within the
879f7d47b0SAlexander V. Chernikov  * array. If we use a sockaddr_in as the underlying type,
889f7d47b0SAlexander V. Chernikov  * sin_len is conveniently located at offset 0, sin_addr is at
899f7d47b0SAlexander V. Chernikov  * offset 4 and normally aligned.
909f7d47b0SAlexander V. Chernikov  * But for portability, let's avoid assumption and make the code explicit
919f7d47b0SAlexander V. Chernikov  */
929f7d47b0SAlexander V. Chernikov #define KEY_LEN(v)	*((uint8_t *)&(v))
939f7d47b0SAlexander V. Chernikov /*
949f7d47b0SAlexander V. Chernikov  * Do not require radix to compare more than actual IPv4/IPv6 address
959f7d47b0SAlexander V. Chernikov  */
969f7d47b0SAlexander V. Chernikov #define KEY_LEN_INET	(offsetof(struct sockaddr_in, sin_addr) + sizeof(in_addr_t))
97e0a8b9eeSAlexander V. Chernikov #define KEY_LEN_INET6	(offsetof(struct sa_in6, sin6_addr) + sizeof(struct in6_addr))
989f7d47b0SAlexander V. Chernikov 
999f7d47b0SAlexander V. Chernikov #define OFF_LEN_INET	(8 * offsetof(struct sockaddr_in, sin_addr))
100e0a8b9eeSAlexander V. Chernikov #define OFF_LEN_INET6	(8 * offsetof(struct sa_in6, sin6_addr))
1019f7d47b0SAlexander V. Chernikov 
102e0a8b9eeSAlexander V. Chernikov struct radix_cidr_entry {
1039f7d47b0SAlexander V. Chernikov 	struct radix_node	rn[2];
104e0a8b9eeSAlexander V. Chernikov 	struct sockaddr_in	addr;
105e0a8b9eeSAlexander V. Chernikov 	uint32_t		value;
106e0a8b9eeSAlexander V. Chernikov 	uint8_t			masklen;
107e0a8b9eeSAlexander V. Chernikov };
108e0a8b9eeSAlexander V. Chernikov 
109e0a8b9eeSAlexander V. Chernikov struct sa_in6 {
110e0a8b9eeSAlexander V. Chernikov 	uint8_t			sin6_len;
111e0a8b9eeSAlexander V. Chernikov 	uint8_t			sin6_family;
112e0a8b9eeSAlexander V. Chernikov 	uint8_t			pad[2];
113e0a8b9eeSAlexander V. Chernikov 	struct in6_addr		sin6_addr;
114e0a8b9eeSAlexander V. Chernikov };
115e0a8b9eeSAlexander V. Chernikov 
116e0a8b9eeSAlexander V. Chernikov struct radix_cidr_xentry {
117e0a8b9eeSAlexander V. Chernikov 	struct radix_node	rn[2];
118e0a8b9eeSAlexander V. Chernikov 	struct sa_in6		addr6;
119e0a8b9eeSAlexander V. Chernikov 	uint32_t		value;
120e0a8b9eeSAlexander V. Chernikov 	uint8_t			masklen;
1219f7d47b0SAlexander V. Chernikov };
1229f7d47b0SAlexander V. Chernikov 
1235f379342SAlexander V. Chernikov struct radix_cfg {
1245f379342SAlexander V. Chernikov 	struct radix_node_head	*head4;
1255f379342SAlexander V. Chernikov 	struct radix_node_head	*head6;
1265f379342SAlexander V. Chernikov 	size_t			count4;
1275f379342SAlexander V. Chernikov 	size_t			count6;
1285f379342SAlexander V. Chernikov };
1295f379342SAlexander V. Chernikov 
1300bce0c23SAlexander V. Chernikov struct ta_buf_cidr
1310bce0c23SAlexander V. Chernikov {
1320bce0c23SAlexander V. Chernikov 	void *ent_ptr;
1330bce0c23SAlexander V. Chernikov 	struct sockaddr	*addr_ptr;
1340bce0c23SAlexander V. Chernikov 	struct sockaddr	*mask_ptr;
1350bce0c23SAlexander V. Chernikov 	union {
1360bce0c23SAlexander V. Chernikov 		struct {
1370bce0c23SAlexander V. Chernikov 			struct sockaddr_in sa;
1380bce0c23SAlexander V. Chernikov 			struct sockaddr_in ma;
1390bce0c23SAlexander V. Chernikov 		} a4;
1400bce0c23SAlexander V. Chernikov 		struct {
1410bce0c23SAlexander V. Chernikov 			struct sa_in6 sa;
1420bce0c23SAlexander V. Chernikov 			struct sa_in6 ma;
1430bce0c23SAlexander V. Chernikov 		} a6;
1440bce0c23SAlexander V. Chernikov 	} addr;
1450bce0c23SAlexander V. Chernikov };
1460bce0c23SAlexander V. Chernikov 
1479f7d47b0SAlexander V. Chernikov static int
1489f7d47b0SAlexander V. Chernikov ta_lookup_radix(struct table_info *ti, void *key, uint32_t keylen,
1499f7d47b0SAlexander V. Chernikov     uint32_t *val)
1509f7d47b0SAlexander V. Chernikov {
1519f7d47b0SAlexander V. Chernikov 	struct radix_node_head *rnh;
1529f7d47b0SAlexander V. Chernikov 
1539f7d47b0SAlexander V. Chernikov 	if (keylen == sizeof(in_addr_t)) {
154e0a8b9eeSAlexander V. Chernikov 		struct radix_cidr_entry *ent;
1559f7d47b0SAlexander V. Chernikov 		struct sockaddr_in sa;
1569f7d47b0SAlexander V. Chernikov 		KEY_LEN(sa) = KEY_LEN_INET;
1579f7d47b0SAlexander V. Chernikov 		sa.sin_addr.s_addr = *((in_addr_t *)key);
1589f7d47b0SAlexander V. Chernikov 		rnh = (struct radix_node_head *)ti->state;
159e0a8b9eeSAlexander V. Chernikov 		ent = (struct radix_cidr_entry *)(rnh->rnh_matchaddr(&sa, rnh));
1609f7d47b0SAlexander V. Chernikov 		if (ent != NULL) {
1619f7d47b0SAlexander V. Chernikov 			*val = ent->value;
1629f7d47b0SAlexander V. Chernikov 			return (1);
1639f7d47b0SAlexander V. Chernikov 		}
1649f7d47b0SAlexander V. Chernikov 	} else {
165e0a8b9eeSAlexander V. Chernikov 		struct radix_cidr_xentry *xent;
166e0a8b9eeSAlexander V. Chernikov 		struct sa_in6 sa6;
1679f7d47b0SAlexander V. Chernikov 		KEY_LEN(sa6) = KEY_LEN_INET6;
1689f7d47b0SAlexander V. Chernikov 		memcpy(&sa6.sin6_addr, key, sizeof(struct in6_addr));
1699f7d47b0SAlexander V. Chernikov 		rnh = (struct radix_node_head *)ti->xstate;
170e0a8b9eeSAlexander V. Chernikov 		xent = (struct radix_cidr_xentry *)(rnh->rnh_matchaddr(&sa6, rnh));
1719f7d47b0SAlexander V. Chernikov 		if (xent != NULL) {
1729f7d47b0SAlexander V. Chernikov 			*val = xent->value;
1739f7d47b0SAlexander V. Chernikov 			return (1);
1749f7d47b0SAlexander V. Chernikov 		}
1759f7d47b0SAlexander V. Chernikov 	}
1769f7d47b0SAlexander V. Chernikov 
1779f7d47b0SAlexander V. Chernikov 	return (0);
1789f7d47b0SAlexander V. Chernikov }
1799f7d47b0SAlexander V. Chernikov 
1809f7d47b0SAlexander V. Chernikov /*
1819f7d47b0SAlexander V. Chernikov  * New table
1829f7d47b0SAlexander V. Chernikov  */
1839f7d47b0SAlexander V. Chernikov static int
18468394ec8SAlexander V. Chernikov ta_init_radix(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
185914bffb6SAlexander V. Chernikov     char *data, uint8_t tflags)
1869f7d47b0SAlexander V. Chernikov {
1875f379342SAlexander V. Chernikov 	struct radix_cfg *cfg;
1889f7d47b0SAlexander V. Chernikov 
1899f7d47b0SAlexander V. Chernikov 	if (!rn_inithead(&ti->state, OFF_LEN_INET))
1909f7d47b0SAlexander V. Chernikov 		return (ENOMEM);
1919f7d47b0SAlexander V. Chernikov 	if (!rn_inithead(&ti->xstate, OFF_LEN_INET6)) {
1929f7d47b0SAlexander V. Chernikov 		rn_detachhead(&ti->state);
1939f7d47b0SAlexander V. Chernikov 		return (ENOMEM);
1949f7d47b0SAlexander V. Chernikov 	}
1959f7d47b0SAlexander V. Chernikov 
1965f379342SAlexander V. Chernikov 	cfg = malloc(sizeof(struct radix_cfg), M_IPFW, M_WAITOK | M_ZERO);
1975f379342SAlexander V. Chernikov 
1985f379342SAlexander V. Chernikov 	*ta_state = cfg;
1999f7d47b0SAlexander V. Chernikov 	ti->lookup = ta_lookup_radix;
2009f7d47b0SAlexander V. Chernikov 
2019f7d47b0SAlexander V. Chernikov 	return (0);
2029f7d47b0SAlexander V. Chernikov }
2039f7d47b0SAlexander V. Chernikov 
2049f7d47b0SAlexander V. Chernikov static int
205a399f8beSAlexander V. Chernikov flush_radix_entry(struct radix_node *rn, void *arg)
2069f7d47b0SAlexander V. Chernikov {
2079f7d47b0SAlexander V. Chernikov 	struct radix_node_head * const rnh = arg;
208e0a8b9eeSAlexander V. Chernikov 	struct radix_cidr_entry *ent;
2099f7d47b0SAlexander V. Chernikov 
210e0a8b9eeSAlexander V. Chernikov 	ent = (struct radix_cidr_entry *)
2119f7d47b0SAlexander V. Chernikov 	    rnh->rnh_deladdr(rn->rn_key, rn->rn_mask, rnh);
2129f7d47b0SAlexander V. Chernikov 	if (ent != NULL)
2139f7d47b0SAlexander V. Chernikov 		free(ent, M_IPFW_TBL);
2149f7d47b0SAlexander V. Chernikov 	return (0);
2159f7d47b0SAlexander V. Chernikov }
2169f7d47b0SAlexander V. Chernikov 
2179f7d47b0SAlexander V. Chernikov static void
2189f7d47b0SAlexander V. Chernikov ta_destroy_radix(void *ta_state, struct table_info *ti)
2199f7d47b0SAlexander V. Chernikov {
2205f379342SAlexander V. Chernikov 	struct radix_cfg *cfg;
2219f7d47b0SAlexander V. Chernikov 	struct radix_node_head *rnh;
2229f7d47b0SAlexander V. Chernikov 
2235f379342SAlexander V. Chernikov 	cfg = (struct radix_cfg *)ta_state;
2245f379342SAlexander V. Chernikov 
2259f7d47b0SAlexander V. Chernikov 	rnh = (struct radix_node_head *)(ti->state);
226a399f8beSAlexander V. Chernikov 	rnh->rnh_walktree(rnh, flush_radix_entry, rnh);
2279f7d47b0SAlexander V. Chernikov 	rn_detachhead(&ti->state);
2289f7d47b0SAlexander V. Chernikov 
2299f7d47b0SAlexander V. Chernikov 	rnh = (struct radix_node_head *)(ti->xstate);
230a399f8beSAlexander V. Chernikov 	rnh->rnh_walktree(rnh, flush_radix_entry, rnh);
2319f7d47b0SAlexander V. Chernikov 	rn_detachhead(&ti->xstate);
2325f379342SAlexander V. Chernikov 
2335f379342SAlexander V. Chernikov 	free(cfg, M_IPFW);
2345f379342SAlexander V. Chernikov }
2355f379342SAlexander V. Chernikov 
2365f379342SAlexander V. Chernikov /*
2375f379342SAlexander V. Chernikov  * Provide algo-specific table info
2385f379342SAlexander V. Chernikov  */
2395f379342SAlexander V. Chernikov static void
2405f379342SAlexander V. Chernikov ta_dump_radix_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo)
2415f379342SAlexander V. Chernikov {
2425f379342SAlexander V. Chernikov 	struct radix_cfg *cfg;
2435f379342SAlexander V. Chernikov 
2445f379342SAlexander V. Chernikov 	cfg = (struct radix_cfg *)ta_state;
2455f379342SAlexander V. Chernikov 
2465f379342SAlexander V. Chernikov 	tinfo->flags = IPFW_TATFLAGS_AFDATA | IPFW_TATFLAGS_AFITEM;
2475f379342SAlexander V. Chernikov 	tinfo->taclass4 = IPFW_TACLASS_RADIX;
2485f379342SAlexander V. Chernikov 	tinfo->count4 = cfg->count4;
2495f379342SAlexander V. Chernikov 	tinfo->itemsize4 = sizeof(struct radix_cidr_entry);
2505f379342SAlexander V. Chernikov 	tinfo->taclass6 = IPFW_TACLASS_RADIX;
2515f379342SAlexander V. Chernikov 	tinfo->count6 = cfg->count6;
2525f379342SAlexander V. Chernikov 	tinfo->itemsize6 = sizeof(struct radix_cidr_xentry);
2539f7d47b0SAlexander V. Chernikov }
2549f7d47b0SAlexander V. Chernikov 
2559f7d47b0SAlexander V. Chernikov static int
25681d3153dSAlexander V. Chernikov ta_dump_radix_tentry(void *ta_state, struct table_info *ti, void *e,
25781d3153dSAlexander V. Chernikov     ipfw_obj_tentry *tent)
2589f7d47b0SAlexander V. Chernikov {
259e0a8b9eeSAlexander V. Chernikov 	struct radix_cidr_entry *n;
260e0a8b9eeSAlexander V. Chernikov 	struct radix_cidr_xentry *xn;
2619f7d47b0SAlexander V. Chernikov 
262e0a8b9eeSAlexander V. Chernikov 	n = (struct radix_cidr_entry *)e;
2639f7d47b0SAlexander V. Chernikov 
2649f7d47b0SAlexander V. Chernikov 	/* Guess IPv4/IPv6 radix by sockaddr family */
2659f7d47b0SAlexander V. Chernikov 	if (n->addr.sin_family == AF_INET) {
26681d3153dSAlexander V. Chernikov 		tent->k.addr.s_addr = n->addr.sin_addr.s_addr;
267e0a8b9eeSAlexander V. Chernikov 		tent->masklen = n->masklen;
26881d3153dSAlexander V. Chernikov 		tent->subtype = AF_INET;
26981d3153dSAlexander V. Chernikov 		tent->value = n->value;
2709f7d47b0SAlexander V. Chernikov #ifdef INET6
2719f7d47b0SAlexander V. Chernikov 	} else {
272e0a8b9eeSAlexander V. Chernikov 		xn = (struct radix_cidr_xentry *)e;
273e0a8b9eeSAlexander V. Chernikov 		memcpy(&tent->k, &xn->addr6.sin6_addr, sizeof(struct in6_addr));
274e0a8b9eeSAlexander V. Chernikov 		tent->masklen = xn->masklen;
27581d3153dSAlexander V. Chernikov 		tent->subtype = AF_INET6;
27681d3153dSAlexander V. Chernikov 		tent->value = xn->value;
2779f7d47b0SAlexander V. Chernikov #endif
2789f7d47b0SAlexander V. Chernikov 	}
2799f7d47b0SAlexander V. Chernikov 
2809f7d47b0SAlexander V. Chernikov 	return (0);
2819f7d47b0SAlexander V. Chernikov }
2829f7d47b0SAlexander V. Chernikov 
28381d3153dSAlexander V. Chernikov static int
284914bffb6SAlexander V. Chernikov ta_find_radix_tentry(void *ta_state, struct table_info *ti,
285914bffb6SAlexander V. Chernikov     ipfw_obj_tentry *tent)
28681d3153dSAlexander V. Chernikov {
28781d3153dSAlexander V. Chernikov 	struct radix_node_head *rnh;
28881d3153dSAlexander V. Chernikov 	void *e;
28981d3153dSAlexander V. Chernikov 
29081d3153dSAlexander V. Chernikov 	e = NULL;
291914bffb6SAlexander V. Chernikov 	if (tent->subtype == AF_INET) {
29281d3153dSAlexander V. Chernikov 		struct sockaddr_in sa;
29381d3153dSAlexander V. Chernikov 		KEY_LEN(sa) = KEY_LEN_INET;
294914bffb6SAlexander V. Chernikov 		sa.sin_addr.s_addr = tent->k.addr.s_addr;
29581d3153dSAlexander V. Chernikov 		rnh = (struct radix_node_head *)ti->state;
29681d3153dSAlexander V. Chernikov 		e = rnh->rnh_matchaddr(&sa, rnh);
29781d3153dSAlexander V. Chernikov 	} else {
298e0a8b9eeSAlexander V. Chernikov 		struct sa_in6 sa6;
29981d3153dSAlexander V. Chernikov 		KEY_LEN(sa6) = KEY_LEN_INET6;
300914bffb6SAlexander V. Chernikov 		memcpy(&sa6.sin6_addr, &tent->k.addr6, sizeof(struct in6_addr));
30181d3153dSAlexander V. Chernikov 		rnh = (struct radix_node_head *)ti->xstate;
30281d3153dSAlexander V. Chernikov 		e = rnh->rnh_matchaddr(&sa6, rnh);
30381d3153dSAlexander V. Chernikov 	}
30481d3153dSAlexander V. Chernikov 
30581d3153dSAlexander V. Chernikov 	if (e != NULL) {
30681d3153dSAlexander V. Chernikov 		ta_dump_radix_tentry(ta_state, ti, e, tent);
30781d3153dSAlexander V. Chernikov 		return (0);
30881d3153dSAlexander V. Chernikov 	}
30981d3153dSAlexander V. Chernikov 
31081d3153dSAlexander V. Chernikov 	return (ENOENT);
31181d3153dSAlexander V. Chernikov }
31281d3153dSAlexander V. Chernikov 
3139f7d47b0SAlexander V. Chernikov static void
3149f7d47b0SAlexander V. Chernikov ta_foreach_radix(void *ta_state, struct table_info *ti, ta_foreach_f *f,
3159f7d47b0SAlexander V. Chernikov     void *arg)
3169f7d47b0SAlexander V. Chernikov {
3179f7d47b0SAlexander V. Chernikov 	struct radix_node_head *rnh;
3189f7d47b0SAlexander V. Chernikov 
3199f7d47b0SAlexander V. Chernikov 	rnh = (struct radix_node_head *)(ti->state);
3209f7d47b0SAlexander V. Chernikov 	rnh->rnh_walktree(rnh, (walktree_f_t *)f, arg);
3219f7d47b0SAlexander V. Chernikov 
3229f7d47b0SAlexander V. Chernikov 	rnh = (struct radix_node_head *)(ti->xstate);
3239f7d47b0SAlexander V. Chernikov 	rnh->rnh_walktree(rnh, (walktree_f_t *)f, arg);
3249f7d47b0SAlexander V. Chernikov }
3259f7d47b0SAlexander V. Chernikov 
3269f7d47b0SAlexander V. Chernikov 
3279f7d47b0SAlexander V. Chernikov #ifdef INET6
3289f7d47b0SAlexander V. Chernikov static inline void
3299f7d47b0SAlexander V. Chernikov ipv6_writemask(struct in6_addr *addr6, uint8_t mask)
3309f7d47b0SAlexander V. Chernikov {
3319f7d47b0SAlexander V. Chernikov 	uint32_t *cp;
3329f7d47b0SAlexander V. Chernikov 
3339f7d47b0SAlexander V. Chernikov 	for (cp = (uint32_t *)addr6; mask >= 32; mask -= 32)
3349f7d47b0SAlexander V. Chernikov 		*cp++ = 0xFFFFFFFF;
3359f7d47b0SAlexander V. Chernikov 	*cp = htonl(mask ? ~((1 << (32 - mask)) - 1) : 0);
3369f7d47b0SAlexander V. Chernikov }
3379f7d47b0SAlexander V. Chernikov #endif
3389f7d47b0SAlexander V. Chernikov 
3392e324d29SAlexander V. Chernikov static void
3402e324d29SAlexander V. Chernikov tei_to_sockaddr_ent(struct tentry_info *tei, struct sockaddr *sa,
3412e324d29SAlexander V. Chernikov     struct sockaddr *ma, int *set_mask)
3422e324d29SAlexander V. Chernikov {
3432e324d29SAlexander V. Chernikov 	int mlen;
3442e324d29SAlexander V. Chernikov 	struct sockaddr_in *addr, *mask;
345*720ee730SAlexander V. Chernikov 	struct sa_in6 *addr6, *mask6;
3462e324d29SAlexander V. Chernikov 	in_addr_t a4;
3472e324d29SAlexander V. Chernikov 
3482e324d29SAlexander V. Chernikov 	mlen = tei->masklen;
3492e324d29SAlexander V. Chernikov 
3502e324d29SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
3512e324d29SAlexander V. Chernikov #ifdef INET
3522e324d29SAlexander V. Chernikov 		addr = (struct sockaddr_in *)sa;
3532e324d29SAlexander V. Chernikov 		mask = (struct sockaddr_in *)ma;
3542e324d29SAlexander V. Chernikov 		/* Set 'total' structure length */
3552e324d29SAlexander V. Chernikov 		KEY_LEN(*addr) = KEY_LEN_INET;
3562e324d29SAlexander V. Chernikov 		KEY_LEN(*mask) = KEY_LEN_INET;
3572e324d29SAlexander V. Chernikov 		addr->sin_family = AF_INET;
3582e324d29SAlexander V. Chernikov 		mask->sin_addr.s_addr =
3592e324d29SAlexander V. Chernikov 		    htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);
3602e324d29SAlexander V. Chernikov 		a4 = *((in_addr_t *)tei->paddr);
3612e324d29SAlexander V. Chernikov 		addr->sin_addr.s_addr = a4 & mask->sin_addr.s_addr;
3622e324d29SAlexander V. Chernikov 		if (mlen != 32)
3632e324d29SAlexander V. Chernikov 			*set_mask = 1;
3642e324d29SAlexander V. Chernikov 		else
3652e324d29SAlexander V. Chernikov 			*set_mask = 0;
3662e324d29SAlexander V. Chernikov #endif
3672e324d29SAlexander V. Chernikov #ifdef INET6
3682e324d29SAlexander V. Chernikov 	} else if (tei->subtype == AF_INET6) {
3692e324d29SAlexander V. Chernikov 		/* IPv6 case */
370*720ee730SAlexander V. Chernikov 		addr6 = (struct sa_in6 *)sa;
371*720ee730SAlexander V. Chernikov 		mask6 = (struct sa_in6 *)ma;
3722e324d29SAlexander V. Chernikov 		/* Set 'total' structure length */
3732e324d29SAlexander V. Chernikov 		KEY_LEN(*addr6) = KEY_LEN_INET6;
3742e324d29SAlexander V. Chernikov 		KEY_LEN(*mask6) = KEY_LEN_INET6;
3752e324d29SAlexander V. Chernikov 		addr6->sin6_family = AF_INET6;
3762e324d29SAlexander V. Chernikov 		ipv6_writemask(&mask6->sin6_addr, mlen);
3772e324d29SAlexander V. Chernikov 		memcpy(&addr6->sin6_addr, tei->paddr, sizeof(struct in6_addr));
3782e324d29SAlexander V. Chernikov 		APPLY_MASK(&addr6->sin6_addr, &mask6->sin6_addr);
3792e324d29SAlexander V. Chernikov 		if (mlen != 128)
3802e324d29SAlexander V. Chernikov 			*set_mask = 1;
3812e324d29SAlexander V. Chernikov 		else
3822e324d29SAlexander V. Chernikov 			*set_mask = 0;
3832e324d29SAlexander V. Chernikov 	}
3842e324d29SAlexander V. Chernikov #endif
3852e324d29SAlexander V. Chernikov }
3869f7d47b0SAlexander V. Chernikov 
3879f7d47b0SAlexander V. Chernikov static int
388a399f8beSAlexander V. Chernikov ta_prepare_add_radix(struct ip_fw_chain *ch, struct tentry_info *tei,
38968394ec8SAlexander V. Chernikov     void *ta_buf)
3909f7d47b0SAlexander V. Chernikov {
3919f7d47b0SAlexander V. Chernikov 	struct ta_buf_cidr *tb;
392e0a8b9eeSAlexander V. Chernikov 	struct radix_cidr_entry *ent;
393e0a8b9eeSAlexander V. Chernikov 	struct radix_cidr_xentry *xent;
3942e324d29SAlexander V. Chernikov 	struct sockaddr *addr, *mask;
3952e324d29SAlexander V. Chernikov 	int mlen, set_mask;
3969f7d47b0SAlexander V. Chernikov 
3979f7d47b0SAlexander V. Chernikov 	tb = (struct ta_buf_cidr *)ta_buf;
3989f7d47b0SAlexander V. Chernikov 
3999f7d47b0SAlexander V. Chernikov 	mlen = tei->masklen;
4002e324d29SAlexander V. Chernikov 	set_mask = 0;
4019f7d47b0SAlexander V. Chernikov 
402ac35ff17SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
4039f7d47b0SAlexander V. Chernikov #ifdef INET
4049f7d47b0SAlexander V. Chernikov 		if (mlen > 32)
4059f7d47b0SAlexander V. Chernikov 			return (EINVAL);
4069f7d47b0SAlexander V. Chernikov 		ent = malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO);
4079f7d47b0SAlexander V. Chernikov 		ent->value = tei->value;
408e0a8b9eeSAlexander V. Chernikov 		ent->masklen = mlen;
4092e324d29SAlexander V. Chernikov 
4102e324d29SAlexander V. Chernikov 		addr = (struct sockaddr *)&ent->addr;
4112e324d29SAlexander V. Chernikov 		mask = (struct sockaddr *)&tb->addr.a4.ma;
4129f7d47b0SAlexander V. Chernikov 		tb->ent_ptr = ent;
4139f7d47b0SAlexander V. Chernikov #endif
4149f7d47b0SAlexander V. Chernikov #ifdef INET6
415ac35ff17SAlexander V. Chernikov 	} else if (tei->subtype == AF_INET6) {
4169f7d47b0SAlexander V. Chernikov 		/* IPv6 case */
4179f7d47b0SAlexander V. Chernikov 		if (mlen > 128)
4189f7d47b0SAlexander V. Chernikov 			return (EINVAL);
4199f7d47b0SAlexander V. Chernikov 		xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO);
4209f7d47b0SAlexander V. Chernikov 		xent->value = tei->value;
421e0a8b9eeSAlexander V. Chernikov 		xent->masklen = mlen;
4222e324d29SAlexander V. Chernikov 
4232e324d29SAlexander V. Chernikov 		addr = (struct sockaddr *)&xent->addr6;
4242e324d29SAlexander V. Chernikov 		mask = (struct sockaddr *)&tb->addr.a6.ma;
4259f7d47b0SAlexander V. Chernikov 		tb->ent_ptr = xent;
4269f7d47b0SAlexander V. Chernikov #endif
4279f7d47b0SAlexander V. Chernikov 	} else {
4289f7d47b0SAlexander V. Chernikov 		/* Unknown CIDR type */
4299f7d47b0SAlexander V. Chernikov 		return (EINVAL);
4309f7d47b0SAlexander V. Chernikov 	}
4319f7d47b0SAlexander V. Chernikov 
4322e324d29SAlexander V. Chernikov 	tei_to_sockaddr_ent(tei, addr, mask, &set_mask);
4332e324d29SAlexander V. Chernikov 	/* Set pointers */
4342e324d29SAlexander V. Chernikov 	tb->addr_ptr = addr;
4352e324d29SAlexander V. Chernikov 	if (set_mask != 0)
4362e324d29SAlexander V. Chernikov 		tb->mask_ptr = mask;
4372e324d29SAlexander V. Chernikov 
4389f7d47b0SAlexander V. Chernikov 	return (0);
4399f7d47b0SAlexander V. Chernikov }
4409f7d47b0SAlexander V. Chernikov 
4419f7d47b0SAlexander V. Chernikov static int
442a399f8beSAlexander V. Chernikov ta_add_radix(void *ta_state, struct table_info *ti, struct tentry_info *tei,
443b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
4449f7d47b0SAlexander V. Chernikov {
4455f379342SAlexander V. Chernikov 	struct radix_cfg *cfg;
4469f7d47b0SAlexander V. Chernikov 	struct radix_node_head *rnh;
4479f7d47b0SAlexander V. Chernikov 	struct radix_node *rn;
4489f7d47b0SAlexander V. Chernikov 	struct ta_buf_cidr *tb;
449648e8380SAlexander V. Chernikov 	uint32_t *old_value, value;
4509f7d47b0SAlexander V. Chernikov 
4515f379342SAlexander V. Chernikov 	cfg = (struct radix_cfg *)ta_state;
4529f7d47b0SAlexander V. Chernikov 	tb = (struct ta_buf_cidr *)ta_buf;
4539f7d47b0SAlexander V. Chernikov 
454ac35ff17SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
4559f7d47b0SAlexander V. Chernikov 		rnh = ti->state;
4569f7d47b0SAlexander V. Chernikov 	else
4579f7d47b0SAlexander V. Chernikov 		rnh = ti->xstate;
4589f7d47b0SAlexander V. Chernikov 
4594c0c07a5SAlexander V. Chernikov 	/* Search for an entry first */
4604c0c07a5SAlexander V. Chernikov 	rn = rnh->rnh_lookup(tb->addr_ptr, tb->mask_ptr, rnh);
4614c0c07a5SAlexander V. Chernikov 	if (rn != NULL) {
462ac35ff17SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
4639f7d47b0SAlexander V. Chernikov 			return (EEXIST);
464ac35ff17SAlexander V. Chernikov 		/* Record already exists. Update value if we're asked to */
465648e8380SAlexander V. Chernikov 		if (tei->subtype == AF_INET)
466648e8380SAlexander V. Chernikov 			old_value = &((struct radix_cidr_entry *)rn)->value;
467648e8380SAlexander V. Chernikov 		else
468648e8380SAlexander V. Chernikov 			old_value = &((struct radix_cidr_xentry *)rn)->value;
469648e8380SAlexander V. Chernikov 
470648e8380SAlexander V. Chernikov 		value = *old_value;
471648e8380SAlexander V. Chernikov 		*old_value = tei->value;
472648e8380SAlexander V. Chernikov 		tei->value = value;
473ac35ff17SAlexander V. Chernikov 
474ac35ff17SAlexander V. Chernikov 		/* Indicate that update has happened instead of addition */
475ac35ff17SAlexander V. Chernikov 		tei->flags |= TEI_FLAGS_UPDATED;
476adea6201SAlexander V. Chernikov 		*pnum = 0;
477e0a8b9eeSAlexander V. Chernikov 
478e0a8b9eeSAlexander V. Chernikov 		return (0);
479ac35ff17SAlexander V. Chernikov 	}
480ac35ff17SAlexander V. Chernikov 
4814c0c07a5SAlexander V. Chernikov 	if ((tei->flags & TEI_FLAGS_DONTADD) != 0)
4824c0c07a5SAlexander V. Chernikov 		return (EFBIG);
4834c0c07a5SAlexander V. Chernikov 
4844c0c07a5SAlexander V. Chernikov 	rn = rnh->rnh_addaddr(tb->addr_ptr, tb->mask_ptr, rnh, tb->ent_ptr);
4854c0c07a5SAlexander V. Chernikov 	if (rn == NULL) {
4864c0c07a5SAlexander V. Chernikov 		/* Unknown error */
4874c0c07a5SAlexander V. Chernikov 		return (EINVAL);
4884c0c07a5SAlexander V. Chernikov 	}
4894c0c07a5SAlexander V. Chernikov 
4905f379342SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
4915f379342SAlexander V. Chernikov 		cfg->count4++;
4925f379342SAlexander V. Chernikov 	else
4935f379342SAlexander V. Chernikov 		cfg->count6++;
494ac35ff17SAlexander V. Chernikov 	tb->ent_ptr = NULL;
495adea6201SAlexander V. Chernikov 	*pnum = 1;
4969f7d47b0SAlexander V. Chernikov 
4979f7d47b0SAlexander V. Chernikov 	return (0);
4989f7d47b0SAlexander V. Chernikov }
4999f7d47b0SAlexander V. Chernikov 
5009f7d47b0SAlexander V. Chernikov static int
501a399f8beSAlexander V. Chernikov ta_prepare_del_radix(struct ip_fw_chain *ch, struct tentry_info *tei,
50268394ec8SAlexander V. Chernikov     void *ta_buf)
5039f7d47b0SAlexander V. Chernikov {
5049f7d47b0SAlexander V. Chernikov 	struct ta_buf_cidr *tb;
5052e324d29SAlexander V. Chernikov 	struct sockaddr *addr, *mask;
5062e324d29SAlexander V. Chernikov 	int mlen, set_mask;
5079f7d47b0SAlexander V. Chernikov 
5089f7d47b0SAlexander V. Chernikov 	tb = (struct ta_buf_cidr *)ta_buf;
5099f7d47b0SAlexander V. Chernikov 
5109f7d47b0SAlexander V. Chernikov 	mlen = tei->masklen;
5112e324d29SAlexander V. Chernikov 	set_mask = 0;
5129f7d47b0SAlexander V. Chernikov 
513ac35ff17SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
514e0a8b9eeSAlexander V. Chernikov 		if (mlen > 32)
515e0a8b9eeSAlexander V. Chernikov 			return (EINVAL);
5162e324d29SAlexander V. Chernikov 
5172e324d29SAlexander V. Chernikov 		addr = (struct sockaddr *)&tb->addr.a4.sa;
5182e324d29SAlexander V. Chernikov 		mask = (struct sockaddr *)&tb->addr.a4.ma;
5199f7d47b0SAlexander V. Chernikov #ifdef INET6
520ac35ff17SAlexander V. Chernikov 	} else if (tei->subtype == AF_INET6) {
5219f7d47b0SAlexander V. Chernikov 		if (mlen > 128)
5229f7d47b0SAlexander V. Chernikov 			return (EINVAL);
5232e324d29SAlexander V. Chernikov 
5242e324d29SAlexander V. Chernikov 		addr = (struct sockaddr *)&tb->addr.a6.sa;
5252e324d29SAlexander V. Chernikov 		mask = (struct sockaddr *)&tb->addr.a6.ma;
5269f7d47b0SAlexander V. Chernikov #endif
5279f7d47b0SAlexander V. Chernikov 	} else
5289f7d47b0SAlexander V. Chernikov 		return (EINVAL);
5299f7d47b0SAlexander V. Chernikov 
5302e324d29SAlexander V. Chernikov 	tei_to_sockaddr_ent(tei, addr, mask, &set_mask);
5312e324d29SAlexander V. Chernikov 	tb->addr_ptr = addr;
5322e324d29SAlexander V. Chernikov 	if (set_mask != 0)
5332e324d29SAlexander V. Chernikov 		tb->mask_ptr = mask;
5342e324d29SAlexander V. Chernikov 
5359f7d47b0SAlexander V. Chernikov 	return (0);
5369f7d47b0SAlexander V. Chernikov }
5379f7d47b0SAlexander V. Chernikov 
5389f7d47b0SAlexander V. Chernikov static int
539a399f8beSAlexander V. Chernikov ta_del_radix(void *ta_state, struct table_info *ti, struct tentry_info *tei,
540b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
5419f7d47b0SAlexander V. Chernikov {
5425f379342SAlexander V. Chernikov 	struct radix_cfg *cfg;
5439f7d47b0SAlexander V. Chernikov 	struct radix_node_head *rnh;
5449f7d47b0SAlexander V. Chernikov 	struct radix_node *rn;
5459f7d47b0SAlexander V. Chernikov 	struct ta_buf_cidr *tb;
5469f7d47b0SAlexander V. Chernikov 
5475f379342SAlexander V. Chernikov 	cfg = (struct radix_cfg *)ta_state;
5489f7d47b0SAlexander V. Chernikov 	tb = (struct ta_buf_cidr *)ta_buf;
5499f7d47b0SAlexander V. Chernikov 
550ac35ff17SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
5519f7d47b0SAlexander V. Chernikov 		rnh = ti->state;
5529f7d47b0SAlexander V. Chernikov 	else
5539f7d47b0SAlexander V. Chernikov 		rnh = ti->xstate;
5549f7d47b0SAlexander V. Chernikov 
5559f7d47b0SAlexander V. Chernikov 	rn = rnh->rnh_deladdr(tb->addr_ptr, tb->mask_ptr, rnh);
5569f7d47b0SAlexander V. Chernikov 
557648e8380SAlexander V. Chernikov 	/* Save entry value to @tei */
558648e8380SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
559648e8380SAlexander V. Chernikov 		tei->value = ((struct radix_cidr_entry *)rn)->value;
560648e8380SAlexander V. Chernikov 	else
561648e8380SAlexander V. Chernikov 		tei->value = ((struct radix_cidr_xentry *)rn)->value;
562648e8380SAlexander V. Chernikov 
5639f7d47b0SAlexander V. Chernikov 	tb->ent_ptr = rn;
5649f7d47b0SAlexander V. Chernikov 
5659f7d47b0SAlexander V. Chernikov 	if (rn == NULL)
56681d3153dSAlexander V. Chernikov 		return (ENOENT);
5679f7d47b0SAlexander V. Chernikov 
5685f379342SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
5695f379342SAlexander V. Chernikov 		cfg->count4--;
5705f379342SAlexander V. Chernikov 	else
5715f379342SAlexander V. Chernikov 		cfg->count6--;
572adea6201SAlexander V. Chernikov 	*pnum = 1;
573adea6201SAlexander V. Chernikov 
5749f7d47b0SAlexander V. Chernikov 	return (0);
5759f7d47b0SAlexander V. Chernikov }
5769f7d47b0SAlexander V. Chernikov 
5779f7d47b0SAlexander V. Chernikov static void
578a399f8beSAlexander V. Chernikov ta_flush_radix_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
57968394ec8SAlexander V. Chernikov     void *ta_buf)
5809f7d47b0SAlexander V. Chernikov {
5819f7d47b0SAlexander V. Chernikov 	struct ta_buf_cidr *tb;
5829f7d47b0SAlexander V. Chernikov 
5839f7d47b0SAlexander V. Chernikov 	tb = (struct ta_buf_cidr *)ta_buf;
5849f7d47b0SAlexander V. Chernikov 
585ac35ff17SAlexander V. Chernikov 	if (tb->ent_ptr != NULL)
5869f7d47b0SAlexander V. Chernikov 		free(tb->ent_ptr, M_IPFW_TBL);
5879f7d47b0SAlexander V. Chernikov }
5889f7d47b0SAlexander V. Chernikov 
589b6ee846eSAlexander V. Chernikov static int
590b6ee846eSAlexander V. Chernikov ta_has_space_radix(void *ta_state, struct table_info *ti, uint32_t count,
591b6ee846eSAlexander V. Chernikov     uint64_t *pflags)
592b6ee846eSAlexander V. Chernikov {
593b6ee846eSAlexander V. Chernikov 
594b6ee846eSAlexander V. Chernikov 	/*
5950bce0c23SAlexander V. Chernikov 	 * radix does not require additional memory allocations
596b6ee846eSAlexander V. Chernikov 	 * other than nodes itself. Adding new masks to the tree do
597b6ee846eSAlexander V. Chernikov 	 * but we don't have any API to call (and we don't known which
598b6ee846eSAlexander V. Chernikov 	 * sizes do we need).
599b6ee846eSAlexander V. Chernikov 	 */
600b6ee846eSAlexander V. Chernikov 	return (1);
601b6ee846eSAlexander V. Chernikov }
602b6ee846eSAlexander V. Chernikov 
60374b941f0SAlexander V. Chernikov struct table_algo cidr_radix = {
604adea6201SAlexander V. Chernikov 	.name		= "cidr:radix",
6059d099b4fSAlexander V. Chernikov 	.type		= IPFW_TABLE_CIDR,
60657a1cf95SAlexander V. Chernikov 	.flags		= TA_FLAG_DEFAULT,
60757a1cf95SAlexander V. Chernikov 	.ta_buf_size	= sizeof(struct ta_buf_cidr),
6089f7d47b0SAlexander V. Chernikov 	.init		= ta_init_radix,
6099f7d47b0SAlexander V. Chernikov 	.destroy	= ta_destroy_radix,
610a399f8beSAlexander V. Chernikov 	.prepare_add	= ta_prepare_add_radix,
611a399f8beSAlexander V. Chernikov 	.prepare_del	= ta_prepare_del_radix,
612a399f8beSAlexander V. Chernikov 	.add		= ta_add_radix,
613a399f8beSAlexander V. Chernikov 	.del		= ta_del_radix,
614a399f8beSAlexander V. Chernikov 	.flush_entry	= ta_flush_radix_entry,
6159f7d47b0SAlexander V. Chernikov 	.foreach	= ta_foreach_radix,
61681d3153dSAlexander V. Chernikov 	.dump_tentry	= ta_dump_radix_tentry,
61781d3153dSAlexander V. Chernikov 	.find_tentry	= ta_find_radix_tentry,
6185f379342SAlexander V. Chernikov 	.dump_tinfo	= ta_dump_radix_tinfo,
619b6ee846eSAlexander V. Chernikov 	.has_space	= ta_has_space_radix,
6209f7d47b0SAlexander V. Chernikov };
6219f7d47b0SAlexander V. Chernikov 
6229f7d47b0SAlexander V. Chernikov 
6239f7d47b0SAlexander V. Chernikov /*
62474b941f0SAlexander V. Chernikov  * cidr:hash cmds
62574b941f0SAlexander V. Chernikov  *
62674b941f0SAlexander V. Chernikov  *
62774b941f0SAlexander V. Chernikov  * ti->data:
62874b941f0SAlexander V. Chernikov  * [inv.mask4][inv.mask6][log2hsize4][log2hsize6]
62974b941f0SAlexander V. Chernikov  * [        8][        8[          8][         8]
63074b941f0SAlexander V. Chernikov  *
63174b941f0SAlexander V. Chernikov  * inv.mask4: 32 - mask
63274b941f0SAlexander V. Chernikov  * inv.mask6:
63374b941f0SAlexander V. Chernikov  * 1) _slow lookup: mask
63474b941f0SAlexander V. Chernikov  * 2) _aligned: (128 - mask) / 8
63574b941f0SAlexander V. Chernikov  * 3) _64: 8
636ce2817b5SAlexander V. Chernikov  *
637ce2817b5SAlexander V. Chernikov  *
638ce2817b5SAlexander V. Chernikov  * pflags:
639ce2817b5SAlexander V. Chernikov  * [v4=1/v6=0][hsize]
640ce2817b5SAlexander V. Chernikov  * [       32][   32]
64174b941f0SAlexander V. Chernikov  */
64274b941f0SAlexander V. Chernikov 
64374b941f0SAlexander V. Chernikov struct chashentry;
64474b941f0SAlexander V. Chernikov 
64574b941f0SAlexander V. Chernikov SLIST_HEAD(chashbhead, chashentry);
64674b941f0SAlexander V. Chernikov 
64774b941f0SAlexander V. Chernikov struct chash_cfg {
64874b941f0SAlexander V. Chernikov 	struct chashbhead *head4;
64974b941f0SAlexander V. Chernikov 	struct chashbhead *head6;
65074b941f0SAlexander V. Chernikov 	size_t	size4;
65174b941f0SAlexander V. Chernikov 	size_t	size6;
652ce2817b5SAlexander V. Chernikov 	size_t	items4;
653ce2817b5SAlexander V. Chernikov 	size_t	items6;
65474b941f0SAlexander V. Chernikov 	uint8_t	mask4;
65574b941f0SAlexander V. Chernikov 	uint8_t	mask6;
65674b941f0SAlexander V. Chernikov };
65774b941f0SAlexander V. Chernikov 
65874b941f0SAlexander V. Chernikov struct chashentry {
65974b941f0SAlexander V. Chernikov 	SLIST_ENTRY(chashentry)	next;
66074b941f0SAlexander V. Chernikov 	uint32_t	value;
66174b941f0SAlexander V. Chernikov 	uint32_t	type;
66274b941f0SAlexander V. Chernikov 	union {
66374b941f0SAlexander V. Chernikov 		uint32_t	a4;	/* Host format */
66474b941f0SAlexander V. Chernikov 		struct in6_addr	a6;	/* Network format */
66574b941f0SAlexander V. Chernikov 	} a;
66674b941f0SAlexander V. Chernikov };
66774b941f0SAlexander V. Chernikov 
6680bce0c23SAlexander V. Chernikov struct ta_buf_chash
6690bce0c23SAlexander V. Chernikov {
6700bce0c23SAlexander V. Chernikov 	void *ent_ptr;
6710bce0c23SAlexander V. Chernikov 	struct chashentry ent;
6720bce0c23SAlexander V. Chernikov };
6730bce0c23SAlexander V. Chernikov 
6740bce0c23SAlexander V. Chernikov 
67574b941f0SAlexander V. Chernikov static __inline uint32_t
67674b941f0SAlexander V. Chernikov hash_ip(uint32_t addr, int hsize)
67774b941f0SAlexander V. Chernikov {
67874b941f0SAlexander V. Chernikov 
67974b941f0SAlexander V. Chernikov 	return (addr % (hsize - 1));
68074b941f0SAlexander V. Chernikov }
68174b941f0SAlexander V. Chernikov 
68274b941f0SAlexander V. Chernikov static __inline uint32_t
68374b941f0SAlexander V. Chernikov hash_ip6(struct in6_addr *addr6, int hsize)
68474b941f0SAlexander V. Chernikov {
68574b941f0SAlexander V. Chernikov 	uint32_t i;
68674b941f0SAlexander V. Chernikov 
68774b941f0SAlexander V. Chernikov 	i = addr6->s6_addr32[0] ^ addr6->s6_addr32[1] ^
68874b941f0SAlexander V. Chernikov 	    addr6->s6_addr32[2] ^ addr6->s6_addr32[3];
68974b941f0SAlexander V. Chernikov 
69074b941f0SAlexander V. Chernikov 	return (i % (hsize - 1));
69174b941f0SAlexander V. Chernikov }
69274b941f0SAlexander V. Chernikov 
69374b941f0SAlexander V. Chernikov 
69474b941f0SAlexander V. Chernikov static __inline uint16_t
69574b941f0SAlexander V. Chernikov hash_ip64(struct in6_addr *addr6, int hsize)
69674b941f0SAlexander V. Chernikov {
69774b941f0SAlexander V. Chernikov 	uint32_t i;
69874b941f0SAlexander V. Chernikov 
69974b941f0SAlexander V. Chernikov 	i = addr6->s6_addr32[0] ^ addr6->s6_addr32[1];
70074b941f0SAlexander V. Chernikov 
70174b941f0SAlexander V. Chernikov 	return (i % (hsize - 1));
70274b941f0SAlexander V. Chernikov }
70374b941f0SAlexander V. Chernikov 
70474b941f0SAlexander V. Chernikov 
70574b941f0SAlexander V. Chernikov static __inline uint32_t
70674b941f0SAlexander V. Chernikov hash_ip6_slow(struct in6_addr *addr6, void *key, int mask, int hsize)
70774b941f0SAlexander V. Chernikov {
70874b941f0SAlexander V. Chernikov 	struct in6_addr mask6;
70974b941f0SAlexander V. Chernikov 
71074b941f0SAlexander V. Chernikov 	ipv6_writemask(&mask6, mask);
71174b941f0SAlexander V. Chernikov 	memcpy(addr6, key, sizeof(struct in6_addr));
71274b941f0SAlexander V. Chernikov 	APPLY_MASK(addr6, &mask6);
71374b941f0SAlexander V. Chernikov 	return (hash_ip6(addr6, hsize));
71474b941f0SAlexander V. Chernikov }
71574b941f0SAlexander V. Chernikov 
71674b941f0SAlexander V. Chernikov static __inline uint32_t
71774b941f0SAlexander V. Chernikov hash_ip6_al(struct in6_addr *addr6, void *key, int mask, int hsize)
71874b941f0SAlexander V. Chernikov {
71974b941f0SAlexander V. Chernikov 	uint64_t *paddr;
72074b941f0SAlexander V. Chernikov 
72174b941f0SAlexander V. Chernikov 	paddr = (uint64_t *)addr6;
72274b941f0SAlexander V. Chernikov 	*paddr = 0;
72374b941f0SAlexander V. Chernikov 	*(paddr + 1) = 0;
72474b941f0SAlexander V. Chernikov 	memcpy(addr6, key, mask);
72574b941f0SAlexander V. Chernikov 	return (hash_ip6(addr6, hsize));
72674b941f0SAlexander V. Chernikov }
72774b941f0SAlexander V. Chernikov 
72874b941f0SAlexander V. Chernikov static int
72974b941f0SAlexander V. Chernikov ta_lookup_chash_slow(struct table_info *ti, void *key, uint32_t keylen,
73074b941f0SAlexander V. Chernikov     uint32_t *val)
73174b941f0SAlexander V. Chernikov {
73274b941f0SAlexander V. Chernikov 	struct chashbhead *head;
73374b941f0SAlexander V. Chernikov 	struct chashentry *ent;
73474b941f0SAlexander V. Chernikov 	uint16_t hash, hsize;
73574b941f0SAlexander V. Chernikov 	uint8_t imask;
73674b941f0SAlexander V. Chernikov 
73774b941f0SAlexander V. Chernikov 	if (keylen == sizeof(in_addr_t)) {
73874b941f0SAlexander V. Chernikov 		head = (struct chashbhead *)ti->state;
73974b941f0SAlexander V. Chernikov 		imask = ti->data >> 24;
74074b941f0SAlexander V. Chernikov 		hsize = 1 << ((ti->data & 0xFFFF) >> 8);
74174b941f0SAlexander V. Chernikov 		uint32_t a;
74274b941f0SAlexander V. Chernikov 		a = ntohl(*((in_addr_t *)key));
74374b941f0SAlexander V. Chernikov 		a = a >> imask;
74474b941f0SAlexander V. Chernikov 		hash = hash_ip(a, hsize);
74574b941f0SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
74674b941f0SAlexander V. Chernikov 			if (ent->a.a4 == a) {
74774b941f0SAlexander V. Chernikov 				*val = ent->value;
74874b941f0SAlexander V. Chernikov 				return (1);
74974b941f0SAlexander V. Chernikov 			}
75074b941f0SAlexander V. Chernikov 		}
75174b941f0SAlexander V. Chernikov 	} else {
75274b941f0SAlexander V. Chernikov 		/* IPv6: worst scenario: non-round mask */
75374b941f0SAlexander V. Chernikov 		struct in6_addr addr6;
75474b941f0SAlexander V. Chernikov 		head = (struct chashbhead *)ti->xstate;
75574b941f0SAlexander V. Chernikov 		imask = (ti->data & 0xFF0000) >> 16;
75674b941f0SAlexander V. Chernikov 		hsize = 1 << (ti->data & 0xFF);
75774b941f0SAlexander V. Chernikov 		hash = hash_ip6_slow(&addr6, key, imask, hsize);
75874b941f0SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
75974b941f0SAlexander V. Chernikov 			if (memcmp(&ent->a.a6, &addr6, 16) == 0) {
76074b941f0SAlexander V. Chernikov 				*val = ent->value;
76174b941f0SAlexander V. Chernikov 				return (1);
76274b941f0SAlexander V. Chernikov 			}
76374b941f0SAlexander V. Chernikov 		}
76474b941f0SAlexander V. Chernikov 	}
76574b941f0SAlexander V. Chernikov 
76674b941f0SAlexander V. Chernikov 	return (0);
76774b941f0SAlexander V. Chernikov }
76874b941f0SAlexander V. Chernikov 
76974b941f0SAlexander V. Chernikov static int
77074b941f0SAlexander V. Chernikov ta_lookup_chash_aligned(struct table_info *ti, void *key, uint32_t keylen,
77174b941f0SAlexander V. Chernikov     uint32_t *val)
77274b941f0SAlexander V. Chernikov {
77374b941f0SAlexander V. Chernikov 	struct chashbhead *head;
77474b941f0SAlexander V. Chernikov 	struct chashentry *ent;
77574b941f0SAlexander V. Chernikov 	uint16_t hash, hsize;
77674b941f0SAlexander V. Chernikov 	uint8_t imask;
77774b941f0SAlexander V. Chernikov 
77874b941f0SAlexander V. Chernikov 	if (keylen == sizeof(in_addr_t)) {
77974b941f0SAlexander V. Chernikov 		head = (struct chashbhead *)ti->state;
78074b941f0SAlexander V. Chernikov 		imask = ti->data >> 24;
78174b941f0SAlexander V. Chernikov 		hsize = 1 << ((ti->data & 0xFFFF) >> 8);
78274b941f0SAlexander V. Chernikov 		uint32_t a;
78374b941f0SAlexander V. Chernikov 		a = ntohl(*((in_addr_t *)key));
78474b941f0SAlexander V. Chernikov 		a = a >> imask;
78574b941f0SAlexander V. Chernikov 		hash = hash_ip(a, hsize);
78674b941f0SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
78774b941f0SAlexander V. Chernikov 			if (ent->a.a4 == a) {
78874b941f0SAlexander V. Chernikov 				*val = ent->value;
78974b941f0SAlexander V. Chernikov 				return (1);
79074b941f0SAlexander V. Chernikov 			}
79174b941f0SAlexander V. Chernikov 		}
79274b941f0SAlexander V. Chernikov 	} else {
79374b941f0SAlexander V. Chernikov 		/* IPv6: aligned to 8bit mask */
79474b941f0SAlexander V. Chernikov 		struct in6_addr addr6;
79574b941f0SAlexander V. Chernikov 		uint64_t *paddr, *ptmp;
79674b941f0SAlexander V. Chernikov 		head = (struct chashbhead *)ti->xstate;
79774b941f0SAlexander V. Chernikov 		imask = (ti->data & 0xFF0000) >> 16;
79874b941f0SAlexander V. Chernikov 		hsize = 1 << (ti->data & 0xFF);
79974b941f0SAlexander V. Chernikov 
80074b941f0SAlexander V. Chernikov 		hash = hash_ip6_al(&addr6, key, imask, hsize);
80174b941f0SAlexander V. Chernikov 		paddr = (uint64_t *)&addr6;
80274b941f0SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
80374b941f0SAlexander V. Chernikov 			ptmp = (uint64_t *)&ent->a.a6;
80474b941f0SAlexander V. Chernikov 			if (paddr[0] == ptmp[0] && paddr[1] == ptmp[1]) {
80574b941f0SAlexander V. Chernikov 				*val = ent->value;
80674b941f0SAlexander V. Chernikov 				return (1);
80774b941f0SAlexander V. Chernikov 			}
80874b941f0SAlexander V. Chernikov 		}
80974b941f0SAlexander V. Chernikov 	}
81074b941f0SAlexander V. Chernikov 
81174b941f0SAlexander V. Chernikov 	return (0);
81274b941f0SAlexander V. Chernikov }
81374b941f0SAlexander V. Chernikov 
81474b941f0SAlexander V. Chernikov static int
81574b941f0SAlexander V. Chernikov ta_lookup_chash_64(struct table_info *ti, void *key, uint32_t keylen,
81674b941f0SAlexander V. Chernikov     uint32_t *val)
81774b941f0SAlexander V. Chernikov {
81874b941f0SAlexander V. Chernikov 	struct chashbhead *head;
81974b941f0SAlexander V. Chernikov 	struct chashentry *ent;
82074b941f0SAlexander V. Chernikov 	uint16_t hash, hsize;
82174b941f0SAlexander V. Chernikov 	uint8_t imask;
82274b941f0SAlexander V. Chernikov 
82374b941f0SAlexander V. Chernikov 	if (keylen == sizeof(in_addr_t)) {
82474b941f0SAlexander V. Chernikov 		head = (struct chashbhead *)ti->state;
82574b941f0SAlexander V. Chernikov 		imask = ti->data >> 24;
82674b941f0SAlexander V. Chernikov 		hsize = 1 << ((ti->data & 0xFFFF) >> 8);
82774b941f0SAlexander V. Chernikov 		uint32_t a;
82874b941f0SAlexander V. Chernikov 		a = ntohl(*((in_addr_t *)key));
82974b941f0SAlexander V. Chernikov 		a = a >> imask;
83074b941f0SAlexander V. Chernikov 		hash = hash_ip(a, hsize);
83174b941f0SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
83274b941f0SAlexander V. Chernikov 			if (ent->a.a4 == a) {
83374b941f0SAlexander V. Chernikov 				*val = ent->value;
83474b941f0SAlexander V. Chernikov 				return (1);
83574b941f0SAlexander V. Chernikov 			}
83674b941f0SAlexander V. Chernikov 		}
83774b941f0SAlexander V. Chernikov 	} else {
83874b941f0SAlexander V. Chernikov 		/* IPv6: /64 */
83974b941f0SAlexander V. Chernikov 		uint64_t a6, *paddr;
84074b941f0SAlexander V. Chernikov 		head = (struct chashbhead *)ti->xstate;
84174b941f0SAlexander V. Chernikov 		paddr = (uint64_t *)key;
84274b941f0SAlexander V. Chernikov 		hsize = 1 << (ti->data & 0xFF);
84374b941f0SAlexander V. Chernikov 		a6 = *paddr;
84474b941f0SAlexander V. Chernikov 		hash = hash_ip64((struct in6_addr *)key, hsize);
84574b941f0SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
84674b941f0SAlexander V. Chernikov 			paddr = (uint64_t *)&ent->a.a6;
84774b941f0SAlexander V. Chernikov 			if (a6 == *paddr) {
84874b941f0SAlexander V. Chernikov 				*val = ent->value;
84974b941f0SAlexander V. Chernikov 				return (1);
85074b941f0SAlexander V. Chernikov 			}
85174b941f0SAlexander V. Chernikov 		}
85274b941f0SAlexander V. Chernikov 	}
85374b941f0SAlexander V. Chernikov 
85474b941f0SAlexander V. Chernikov 	return (0);
85574b941f0SAlexander V. Chernikov }
85674b941f0SAlexander V. Chernikov 
85774b941f0SAlexander V. Chernikov static int
8580bce0c23SAlexander V. Chernikov chash_parse_opts(struct chash_cfg *cfg, char *data)
85974b941f0SAlexander V. Chernikov {
86074b941f0SAlexander V. Chernikov 	char *pdel, *pend, *s;
86174b941f0SAlexander V. Chernikov 	int mask4, mask6;
86274b941f0SAlexander V. Chernikov 
8630bce0c23SAlexander V. Chernikov 	mask4 = cfg->mask4;
8640bce0c23SAlexander V. Chernikov 	mask6 = cfg->mask6;
86574b941f0SAlexander V. Chernikov 
86674b941f0SAlexander V. Chernikov 	if (data == NULL)
86774b941f0SAlexander V. Chernikov 		return (0);
86874b941f0SAlexander V. Chernikov 	if ((pdel = strchr(data, ' ')) == NULL)
86974b941f0SAlexander V. Chernikov 		return (0);
87074b941f0SAlexander V. Chernikov 	while (*pdel == ' ')
87174b941f0SAlexander V. Chernikov 		pdel++;
87274b941f0SAlexander V. Chernikov 	if (strncmp(pdel, "masks=", 6) != 0)
87374b941f0SAlexander V. Chernikov 		return (EINVAL);
87474b941f0SAlexander V. Chernikov 	if ((s = strchr(pdel, ' ')) != NULL)
87574b941f0SAlexander V. Chernikov 		*s++ = '\0';
87674b941f0SAlexander V. Chernikov 
87774b941f0SAlexander V. Chernikov 	pdel += 6;
87874b941f0SAlexander V. Chernikov 	/* Need /XX[,/YY] */
87974b941f0SAlexander V. Chernikov 	if (*pdel++ != '/')
88074b941f0SAlexander V. Chernikov 		return (EINVAL);
88174b941f0SAlexander V. Chernikov 	mask4 = strtol(pdel, &pend, 10);
88274b941f0SAlexander V. Chernikov 	if (*pend == ',') {
88374b941f0SAlexander V. Chernikov 		/* ,/YY */
88474b941f0SAlexander V. Chernikov 		pdel = pend + 1;
88574b941f0SAlexander V. Chernikov 		if (*pdel++ != '/')
88674b941f0SAlexander V. Chernikov 			return (EINVAL);
88774b941f0SAlexander V. Chernikov 		mask6 = strtol(pdel, &pend, 10);
88874b941f0SAlexander V. Chernikov 		if (*pend != '\0')
88974b941f0SAlexander V. Chernikov 			return (EINVAL);
89074b941f0SAlexander V. Chernikov 	} else if (*pend != '\0')
89174b941f0SAlexander V. Chernikov 		return (EINVAL);
89274b941f0SAlexander V. Chernikov 
89374b941f0SAlexander V. Chernikov 	if (mask4 < 0 || mask4 > 32 || mask6 < 0 || mask6 > 128)
89474b941f0SAlexander V. Chernikov 		return (EINVAL);
89574b941f0SAlexander V. Chernikov 
8960bce0c23SAlexander V. Chernikov 	cfg->mask4 = mask4;
8970bce0c23SAlexander V. Chernikov 	cfg->mask6 = mask6;
89874b941f0SAlexander V. Chernikov 
89974b941f0SAlexander V. Chernikov 	return (0);
90074b941f0SAlexander V. Chernikov }
90174b941f0SAlexander V. Chernikov 
90274b941f0SAlexander V. Chernikov static void
90374b941f0SAlexander V. Chernikov ta_print_chash_config(void *ta_state, struct table_info *ti, char *buf,
90474b941f0SAlexander V. Chernikov     size_t bufsize)
90574b941f0SAlexander V. Chernikov {
9060bce0c23SAlexander V. Chernikov 	struct chash_cfg *cfg;
90774b941f0SAlexander V. Chernikov 
9080bce0c23SAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
90974b941f0SAlexander V. Chernikov 
9100bce0c23SAlexander V. Chernikov 	if (cfg->mask4 != 32 || cfg->mask6 != 128)
91174b941f0SAlexander V. Chernikov 		snprintf(buf, bufsize, "%s masks=/%d,/%d", "cidr:hash",
9120bce0c23SAlexander V. Chernikov 		    cfg->mask4, cfg->mask6);
91374b941f0SAlexander V. Chernikov 	else
91474b941f0SAlexander V. Chernikov 		snprintf(buf, bufsize, "%s", "cidr:hash");
91574b941f0SAlexander V. Chernikov }
91674b941f0SAlexander V. Chernikov 
917914bffb6SAlexander V. Chernikov static int
918914bffb6SAlexander V. Chernikov log2(uint32_t v)
919914bffb6SAlexander V. Chernikov {
920914bffb6SAlexander V. Chernikov 	uint32_t r;
921914bffb6SAlexander V. Chernikov 
922914bffb6SAlexander V. Chernikov 	r = 0;
923914bffb6SAlexander V. Chernikov 	while (v >>= 1)
924914bffb6SAlexander V. Chernikov 		r++;
925914bffb6SAlexander V. Chernikov 
926914bffb6SAlexander V. Chernikov 	return (r);
927914bffb6SAlexander V. Chernikov }
92874b941f0SAlexander V. Chernikov 
92974b941f0SAlexander V. Chernikov /*
93074b941f0SAlexander V. Chernikov  * New table.
93174b941f0SAlexander V. Chernikov  * We assume 'data' to be either NULL or the following format:
93274b941f0SAlexander V. Chernikov  * 'cidr:hash [masks=/32[,/128]]'
93374b941f0SAlexander V. Chernikov  */
93474b941f0SAlexander V. Chernikov static int
93574b941f0SAlexander V. Chernikov ta_init_chash(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
936914bffb6SAlexander V. Chernikov     char *data, uint8_t tflags)
93774b941f0SAlexander V. Chernikov {
93874b941f0SAlexander V. Chernikov 	int error, i;
939914bffb6SAlexander V. Chernikov 	uint32_t hsize;
9400bce0c23SAlexander V. Chernikov 	struct chash_cfg *cfg;
94174b941f0SAlexander V. Chernikov 
9420bce0c23SAlexander V. Chernikov 	cfg = malloc(sizeof(struct chash_cfg), M_IPFW, M_WAITOK | M_ZERO);
94374b941f0SAlexander V. Chernikov 
9440bce0c23SAlexander V. Chernikov 	cfg->mask4 = 32;
9450bce0c23SAlexander V. Chernikov 	cfg->mask6 = 128;
94674b941f0SAlexander V. Chernikov 
9470bce0c23SAlexander V. Chernikov 	if ((error = chash_parse_opts(cfg, data)) != 0) {
9480bce0c23SAlexander V. Chernikov 		free(cfg, M_IPFW);
94974b941f0SAlexander V. Chernikov 		return (error);
95074b941f0SAlexander V. Chernikov 	}
95174b941f0SAlexander V. Chernikov 
9520bce0c23SAlexander V. Chernikov 	cfg->size4 = 128;
9530bce0c23SAlexander V. Chernikov 	cfg->size6 = 128;
95474b941f0SAlexander V. Chernikov 
9550bce0c23SAlexander V. Chernikov 	cfg->head4 = malloc(sizeof(struct chashbhead) * cfg->size4, M_IPFW,
95674b941f0SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
9570bce0c23SAlexander V. Chernikov 	cfg->head6 = malloc(sizeof(struct chashbhead) * cfg->size6, M_IPFW,
95874b941f0SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
9590bce0c23SAlexander V. Chernikov 	for (i = 0; i < cfg->size4; i++)
9600bce0c23SAlexander V. Chernikov 		SLIST_INIT(&cfg->head4[i]);
9610bce0c23SAlexander V. Chernikov 	for (i = 0; i < cfg->size6; i++)
9620bce0c23SAlexander V. Chernikov 		SLIST_INIT(&cfg->head6[i]);
96374b941f0SAlexander V. Chernikov 
96474b941f0SAlexander V. Chernikov 
9650bce0c23SAlexander V. Chernikov 	*ta_state = cfg;
9660bce0c23SAlexander V. Chernikov 	ti->state = cfg->head4;
9670bce0c23SAlexander V. Chernikov 	ti->xstate = cfg->head6;
96874b941f0SAlexander V. Chernikov 
96974b941f0SAlexander V. Chernikov 	/* Store data depending on v6 mask length */
9700bce0c23SAlexander V. Chernikov 	hsize = log2(cfg->size4) << 8 | log2(cfg->size6);
9710bce0c23SAlexander V. Chernikov 	if (cfg->mask6 == 64) {
9720bce0c23SAlexander V. Chernikov 		ti->data = (32 - cfg->mask4) << 24 | (128 - cfg->mask6) << 16|
973914bffb6SAlexander V. Chernikov 		    hsize;
97474b941f0SAlexander V. Chernikov 		ti->lookup = ta_lookup_chash_64;
9750bce0c23SAlexander V. Chernikov 	} else if ((cfg->mask6  % 8) == 0) {
9760bce0c23SAlexander V. Chernikov 		ti->data = (32 - cfg->mask4) << 24 |
9770bce0c23SAlexander V. Chernikov 		    cfg->mask6 << 13 | hsize;
97874b941f0SAlexander V. Chernikov 		ti->lookup = ta_lookup_chash_aligned;
97974b941f0SAlexander V. Chernikov 	} else {
98074b941f0SAlexander V. Chernikov 		/* don't do that! */
9810bce0c23SAlexander V. Chernikov 		ti->data = (32 - cfg->mask4) << 24 |
9820bce0c23SAlexander V. Chernikov 		    cfg->mask6 << 16 | hsize;
98374b941f0SAlexander V. Chernikov 		ti->lookup = ta_lookup_chash_slow;
98474b941f0SAlexander V. Chernikov 	}
98574b941f0SAlexander V. Chernikov 
98674b941f0SAlexander V. Chernikov 	return (0);
98774b941f0SAlexander V. Chernikov }
98874b941f0SAlexander V. Chernikov 
98974b941f0SAlexander V. Chernikov static void
99074b941f0SAlexander V. Chernikov ta_destroy_chash(void *ta_state, struct table_info *ti)
99174b941f0SAlexander V. Chernikov {
9920bce0c23SAlexander V. Chernikov 	struct chash_cfg *cfg;
99374b941f0SAlexander V. Chernikov 	struct chashentry *ent, *ent_next;
99474b941f0SAlexander V. Chernikov 	int i;
99574b941f0SAlexander V. Chernikov 
9960bce0c23SAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
99774b941f0SAlexander V. Chernikov 
9980bce0c23SAlexander V. Chernikov 	for (i = 0; i < cfg->size4; i++)
9990bce0c23SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &cfg->head4[i], next, ent_next)
100074b941f0SAlexander V. Chernikov 			free(ent, M_IPFW_TBL);
100174b941f0SAlexander V. Chernikov 
10020bce0c23SAlexander V. Chernikov 	for (i = 0; i < cfg->size6; i++)
10030bce0c23SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &cfg->head6[i], next, ent_next)
100474b941f0SAlexander V. Chernikov 			free(ent, M_IPFW_TBL);
100574b941f0SAlexander V. Chernikov 
10060bce0c23SAlexander V. Chernikov 	free(cfg->head4, M_IPFW);
10070bce0c23SAlexander V. Chernikov 	free(cfg->head6, M_IPFW);
1008ce2817b5SAlexander V. Chernikov 
10090bce0c23SAlexander V. Chernikov 	free(cfg, M_IPFW);
101074b941f0SAlexander V. Chernikov }
101174b941f0SAlexander V. Chernikov 
10125f379342SAlexander V. Chernikov static void
10135f379342SAlexander V. Chernikov ta_dump_chash_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo)
10145f379342SAlexander V. Chernikov {
10155f379342SAlexander V. Chernikov 	struct chash_cfg *cfg;
10165f379342SAlexander V. Chernikov 
10175f379342SAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
10185f379342SAlexander V. Chernikov 
10195f379342SAlexander V. Chernikov 	tinfo->flags = IPFW_TATFLAGS_AFDATA | IPFW_TATFLAGS_AFITEM;
10205f379342SAlexander V. Chernikov 	tinfo->taclass4 = IPFW_TACLASS_HASH;
10215f379342SAlexander V. Chernikov 	tinfo->size4 = cfg->size4;
10225f379342SAlexander V. Chernikov 	tinfo->count4 = cfg->items4;
10235f379342SAlexander V. Chernikov 	tinfo->itemsize4 = sizeof(struct chashentry);
10245f379342SAlexander V. Chernikov 	tinfo->taclass6 = IPFW_TACLASS_HASH;
10255f379342SAlexander V. Chernikov 	tinfo->size6 = cfg->size6;
10265f379342SAlexander V. Chernikov 	tinfo->count6 = cfg->items6;
10275f379342SAlexander V. Chernikov 	tinfo->itemsize6 = sizeof(struct chashentry);
10285f379342SAlexander V. Chernikov }
10295f379342SAlexander V. Chernikov 
103074b941f0SAlexander V. Chernikov static int
103174b941f0SAlexander V. Chernikov ta_dump_chash_tentry(void *ta_state, struct table_info *ti, void *e,
103274b941f0SAlexander V. Chernikov     ipfw_obj_tentry *tent)
103374b941f0SAlexander V. Chernikov {
10340bce0c23SAlexander V. Chernikov 	struct chash_cfg *cfg;
103574b941f0SAlexander V. Chernikov 	struct chashentry *ent;
103674b941f0SAlexander V. Chernikov 
10370bce0c23SAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
103874b941f0SAlexander V. Chernikov 	ent = (struct chashentry *)e;
103974b941f0SAlexander V. Chernikov 
104074b941f0SAlexander V. Chernikov 	if (ent->type == AF_INET) {
10410bce0c23SAlexander V. Chernikov 		tent->k.addr.s_addr = htonl(ent->a.a4 << (32 - cfg->mask4));
10420bce0c23SAlexander V. Chernikov 		tent->masklen = cfg->mask4;
104374b941f0SAlexander V. Chernikov 		tent->subtype = AF_INET;
104474b941f0SAlexander V. Chernikov 		tent->value = ent->value;
104574b941f0SAlexander V. Chernikov #ifdef INET6
104674b941f0SAlexander V. Chernikov 	} else {
104774b941f0SAlexander V. Chernikov 		memcpy(&tent->k, &ent->a.a6, sizeof(struct in6_addr));
10480bce0c23SAlexander V. Chernikov 		tent->masklen = cfg->mask6;
104974b941f0SAlexander V. Chernikov 		tent->subtype = AF_INET6;
105074b941f0SAlexander V. Chernikov 		tent->value = ent->value;
105174b941f0SAlexander V. Chernikov #endif
105274b941f0SAlexander V. Chernikov 	}
105374b941f0SAlexander V. Chernikov 
105474b941f0SAlexander V. Chernikov 	return (0);
105574b941f0SAlexander V. Chernikov }
105674b941f0SAlexander V. Chernikov 
1057ce2817b5SAlexander V. Chernikov static uint32_t
1058ce2817b5SAlexander V. Chernikov hash_ent(struct chashentry *ent, int af, int mlen, uint32_t size)
1059ce2817b5SAlexander V. Chernikov {
1060ce2817b5SAlexander V. Chernikov 	uint32_t hash;
1061ce2817b5SAlexander V. Chernikov 
1062ce2817b5SAlexander V. Chernikov 	if (af == AF_INET) {
1063ce2817b5SAlexander V. Chernikov 		hash = hash_ip(ent->a.a4, size);
1064ce2817b5SAlexander V. Chernikov 	} else {
1065ce2817b5SAlexander V. Chernikov 		if (mlen == 64)
1066ce2817b5SAlexander V. Chernikov 			hash = hash_ip64(&ent->a.a6, size);
1067ce2817b5SAlexander V. Chernikov 		else
1068ce2817b5SAlexander V. Chernikov 			hash = hash_ip6(&ent->a.a6, size);
1069ce2817b5SAlexander V. Chernikov 	}
1070ce2817b5SAlexander V. Chernikov 
1071ce2817b5SAlexander V. Chernikov 	return (hash);
1072ce2817b5SAlexander V. Chernikov }
1073ce2817b5SAlexander V. Chernikov 
1074ce2817b5SAlexander V. Chernikov static int
1075ce2817b5SAlexander V. Chernikov tei_to_chash_ent(struct tentry_info *tei, struct chashentry *ent)
1076ce2817b5SAlexander V. Chernikov {
1077ce2817b5SAlexander V. Chernikov 	struct in6_addr mask6;
1078ce2817b5SAlexander V. Chernikov 	int mlen;
1079ce2817b5SAlexander V. Chernikov 
1080ce2817b5SAlexander V. Chernikov 
1081ce2817b5SAlexander V. Chernikov 	mlen = tei->masklen;
1082ce2817b5SAlexander V. Chernikov 
1083ce2817b5SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
1084ce2817b5SAlexander V. Chernikov #ifdef INET
1085ce2817b5SAlexander V. Chernikov 		if (mlen > 32)
1086ce2817b5SAlexander V. Chernikov 			return (EINVAL);
1087ce2817b5SAlexander V. Chernikov 		ent->type = AF_INET;
1088ce2817b5SAlexander V. Chernikov 
1089ce2817b5SAlexander V. Chernikov 		/* Calculate masked address */
1090ce2817b5SAlexander V. Chernikov 		ent->a.a4 = ntohl(*((in_addr_t *)tei->paddr)) >> (32 - mlen);
1091ce2817b5SAlexander V. Chernikov #endif
1092ce2817b5SAlexander V. Chernikov #ifdef INET6
1093ce2817b5SAlexander V. Chernikov 	} else if (tei->subtype == AF_INET6) {
1094ce2817b5SAlexander V. Chernikov 		/* IPv6 case */
1095ce2817b5SAlexander V. Chernikov 		if (mlen > 128)
1096ce2817b5SAlexander V. Chernikov 			return (EINVAL);
1097ce2817b5SAlexander V. Chernikov 		ent->type = AF_INET6;
1098ce2817b5SAlexander V. Chernikov 
1099ce2817b5SAlexander V. Chernikov 		ipv6_writemask(&mask6, mlen);
1100ce2817b5SAlexander V. Chernikov 		memcpy(&ent->a.a6, tei->paddr, sizeof(struct in6_addr));
1101ce2817b5SAlexander V. Chernikov 		APPLY_MASK(&ent->a.a6, &mask6);
1102ce2817b5SAlexander V. Chernikov #endif
1103ce2817b5SAlexander V. Chernikov 	} else {
1104ce2817b5SAlexander V. Chernikov 		/* Unknown CIDR type */
1105ce2817b5SAlexander V. Chernikov 		return (EINVAL);
1106ce2817b5SAlexander V. Chernikov 	}
1107ce2817b5SAlexander V. Chernikov 	ent->value = tei->value;
1108ce2817b5SAlexander V. Chernikov 
1109ce2817b5SAlexander V. Chernikov 	return (0);
1110ce2817b5SAlexander V. Chernikov }
1111ce2817b5SAlexander V. Chernikov 
111274b941f0SAlexander V. Chernikov static int
1113914bffb6SAlexander V. Chernikov ta_find_chash_tentry(void *ta_state, struct table_info *ti,
1114914bffb6SAlexander V. Chernikov     ipfw_obj_tentry *tent)
111574b941f0SAlexander V. Chernikov {
11160bce0c23SAlexander V. Chernikov 	struct chash_cfg *cfg;
1117ce2817b5SAlexander V. Chernikov 	struct chashbhead *head;
1118ce2817b5SAlexander V. Chernikov 	struct chashentry ent, *tmp;
1119ce2817b5SAlexander V. Chernikov 	struct tentry_info tei;
1120ce2817b5SAlexander V. Chernikov 	int error;
1121ce2817b5SAlexander V. Chernikov 	uint32_t hash;
112274b941f0SAlexander V. Chernikov 
11230bce0c23SAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
1124ce2817b5SAlexander V. Chernikov 
1125ce2817b5SAlexander V. Chernikov 	memset(&ent, 0, sizeof(ent));
1126ce2817b5SAlexander V. Chernikov 	memset(&tei, 0, sizeof(tei));
1127ce2817b5SAlexander V. Chernikov 
1128914bffb6SAlexander V. Chernikov 	if (tent->subtype == AF_INET) {
1129914bffb6SAlexander V. Chernikov 		tei.paddr = &tent->k.addr;
11300bce0c23SAlexander V. Chernikov 		tei.masklen = cfg->mask4;
1131ce2817b5SAlexander V. Chernikov 		tei.subtype = AF_INET;
113274b941f0SAlexander V. Chernikov 
1133ce2817b5SAlexander V. Chernikov 		if ((error = tei_to_chash_ent(&tei, &ent)) != 0)
1134ce2817b5SAlexander V. Chernikov 			return (error);
1135ce2817b5SAlexander V. Chernikov 
11360bce0c23SAlexander V. Chernikov 		head = cfg->head4;
11370bce0c23SAlexander V. Chernikov 		hash = hash_ent(&ent, AF_INET, cfg->mask4, cfg->size4);
1138ce2817b5SAlexander V. Chernikov 		/* Check for existence */
1139ce2817b5SAlexander V. Chernikov 		SLIST_FOREACH(tmp, &head[hash], next) {
1140ce2817b5SAlexander V. Chernikov 			if (tmp->a.a4 != ent.a.a4)
1141ce2817b5SAlexander V. Chernikov 				continue;
1142ce2817b5SAlexander V. Chernikov 
1143ce2817b5SAlexander V. Chernikov 			ta_dump_chash_tentry(ta_state, ti, tmp, tent);
114474b941f0SAlexander V. Chernikov 			return (0);
114574b941f0SAlexander V. Chernikov 		}
1146ce2817b5SAlexander V. Chernikov 	} else {
1147914bffb6SAlexander V. Chernikov 		tei.paddr = &tent->k.addr6;
11480bce0c23SAlexander V. Chernikov 		tei.masklen = cfg->mask6;
1149ce2817b5SAlexander V. Chernikov 		tei.subtype = AF_INET6;
1150ce2817b5SAlexander V. Chernikov 
1151ce2817b5SAlexander V. Chernikov 		if ((error = tei_to_chash_ent(&tei, &ent)) != 0)
1152ce2817b5SAlexander V. Chernikov 			return (error);
1153ce2817b5SAlexander V. Chernikov 
11540bce0c23SAlexander V. Chernikov 		head = cfg->head6;
11550bce0c23SAlexander V. Chernikov 		hash = hash_ent(&ent, AF_INET6, cfg->mask6, cfg->size6);
1156ce2817b5SAlexander V. Chernikov 		/* Check for existence */
1157ce2817b5SAlexander V. Chernikov 		SLIST_FOREACH(tmp, &head[hash], next) {
1158ce2817b5SAlexander V. Chernikov 			if (memcmp(&tmp->a.a6, &ent.a.a6, 16) != 0)
1159ce2817b5SAlexander V. Chernikov 				continue;
1160ce2817b5SAlexander V. Chernikov 			ta_dump_chash_tentry(ta_state, ti, tmp, tent);
1161ce2817b5SAlexander V. Chernikov 			return (0);
1162ce2817b5SAlexander V. Chernikov 		}
1163ce2817b5SAlexander V. Chernikov 	}
1164ce2817b5SAlexander V. Chernikov 
116574b941f0SAlexander V. Chernikov 	return (ENOENT);
116674b941f0SAlexander V. Chernikov }
116774b941f0SAlexander V. Chernikov 
116874b941f0SAlexander V. Chernikov static void
116974b941f0SAlexander V. Chernikov ta_foreach_chash(void *ta_state, struct table_info *ti, ta_foreach_f *f,
117074b941f0SAlexander V. Chernikov     void *arg)
117174b941f0SAlexander V. Chernikov {
11720bce0c23SAlexander V. Chernikov 	struct chash_cfg *cfg;
117374b941f0SAlexander V. Chernikov 	struct chashentry *ent, *ent_next;
117474b941f0SAlexander V. Chernikov 	int i;
117574b941f0SAlexander V. Chernikov 
11760bce0c23SAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
117774b941f0SAlexander V. Chernikov 
11780bce0c23SAlexander V. Chernikov 	for (i = 0; i < cfg->size4; i++)
11790bce0c23SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &cfg->head4[i], next, ent_next)
118074b941f0SAlexander V. Chernikov 			f(ent, arg);
118174b941f0SAlexander V. Chernikov 
11820bce0c23SAlexander V. Chernikov 	for (i = 0; i < cfg->size6; i++)
11830bce0c23SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &cfg->head6[i], next, ent_next)
118474b941f0SAlexander V. Chernikov 			f(ent, arg);
118574b941f0SAlexander V. Chernikov }
118674b941f0SAlexander V. Chernikov 
118774b941f0SAlexander V. Chernikov static int
118874b941f0SAlexander V. Chernikov ta_prepare_add_chash(struct ip_fw_chain *ch, struct tentry_info *tei,
118974b941f0SAlexander V. Chernikov     void *ta_buf)
119074b941f0SAlexander V. Chernikov {
119174b941f0SAlexander V. Chernikov 	struct ta_buf_chash *tb;
119274b941f0SAlexander V. Chernikov 	struct chashentry *ent;
1193ce2817b5SAlexander V. Chernikov 	int error;
119474b941f0SAlexander V. Chernikov 
119574b941f0SAlexander V. Chernikov 	tb = (struct ta_buf_chash *)ta_buf;
119674b941f0SAlexander V. Chernikov 
119774b941f0SAlexander V. Chernikov 	ent = malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO);
119874b941f0SAlexander V. Chernikov 
1199ce2817b5SAlexander V. Chernikov 	error = tei_to_chash_ent(tei, ent);
1200ce2817b5SAlexander V. Chernikov 	if (error != 0) {
1201ce2817b5SAlexander V. Chernikov 		free(ent, M_IPFW_TBL);
1202ce2817b5SAlexander V. Chernikov 		return (error);
120374b941f0SAlexander V. Chernikov 	}
1204ce2817b5SAlexander V. Chernikov 	tb->ent_ptr = ent;
120574b941f0SAlexander V. Chernikov 
120674b941f0SAlexander V. Chernikov 	return (0);
120774b941f0SAlexander V. Chernikov }
120874b941f0SAlexander V. Chernikov 
120974b941f0SAlexander V. Chernikov static int
121074b941f0SAlexander V. Chernikov ta_add_chash(void *ta_state, struct table_info *ti, struct tentry_info *tei,
1211b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
121274b941f0SAlexander V. Chernikov {
12130bce0c23SAlexander V. Chernikov 	struct chash_cfg *cfg;
121474b941f0SAlexander V. Chernikov 	struct chashbhead *head;
121574b941f0SAlexander V. Chernikov 	struct chashentry *ent, *tmp;
121674b941f0SAlexander V. Chernikov 	struct ta_buf_chash *tb;
121774b941f0SAlexander V. Chernikov 	int exists;
1218648e8380SAlexander V. Chernikov 	uint32_t hash, value;
121974b941f0SAlexander V. Chernikov 
12200bce0c23SAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
122174b941f0SAlexander V. Chernikov 	tb = (struct ta_buf_chash *)ta_buf;
122274b941f0SAlexander V. Chernikov 	ent = (struct chashentry *)tb->ent_ptr;
122374b941f0SAlexander V. Chernikov 	hash = 0;
122474b941f0SAlexander V. Chernikov 	exists = 0;
122574b941f0SAlexander V. Chernikov 
122674b941f0SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
12270bce0c23SAlexander V. Chernikov 		if (tei->masklen != cfg->mask4)
122874b941f0SAlexander V. Chernikov 			return (EINVAL);
12290bce0c23SAlexander V. Chernikov 		head = cfg->head4;
12300bce0c23SAlexander V. Chernikov 		hash = hash_ent(ent, AF_INET, cfg->mask4, cfg->size4);
1231ce2817b5SAlexander V. Chernikov 
123274b941f0SAlexander V. Chernikov 		/* Check for existence */
123374b941f0SAlexander V. Chernikov 		SLIST_FOREACH(tmp, &head[hash], next) {
123474b941f0SAlexander V. Chernikov 			if (tmp->a.a4 == ent->a.a4) {
123574b941f0SAlexander V. Chernikov 				exists = 1;
123674b941f0SAlexander V. Chernikov 				break;
123774b941f0SAlexander V. Chernikov 			}
123874b941f0SAlexander V. Chernikov 		}
123974b941f0SAlexander V. Chernikov 	} else {
12400bce0c23SAlexander V. Chernikov 		if (tei->masklen != cfg->mask6)
124174b941f0SAlexander V. Chernikov 			return (EINVAL);
12420bce0c23SAlexander V. Chernikov 		head = cfg->head6;
12430bce0c23SAlexander V. Chernikov 		hash = hash_ent(ent, AF_INET6, cfg->mask6, cfg->size6);
124474b941f0SAlexander V. Chernikov 		/* Check for existence */
124574b941f0SAlexander V. Chernikov 		SLIST_FOREACH(tmp, &head[hash], next) {
1246ce2817b5SAlexander V. Chernikov 			if (memcmp(&tmp->a.a6, &ent->a.a6, 16) == 0) {
124774b941f0SAlexander V. Chernikov 				exists = 1;
124874b941f0SAlexander V. Chernikov 				break;
124974b941f0SAlexander V. Chernikov 			}
125074b941f0SAlexander V. Chernikov 		}
125174b941f0SAlexander V. Chernikov 	}
125274b941f0SAlexander V. Chernikov 
125374b941f0SAlexander V. Chernikov 	if (exists == 1) {
125474b941f0SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
125574b941f0SAlexander V. Chernikov 			return (EEXIST);
125674b941f0SAlexander V. Chernikov 		/* Record already exists. Update value if we're asked to */
1257648e8380SAlexander V. Chernikov 		value = tmp->value;
125874b941f0SAlexander V. Chernikov 		tmp->value = tei->value;
1259648e8380SAlexander V. Chernikov 		tei->value = value;
126074b941f0SAlexander V. Chernikov 		/* Indicate that update has happened instead of addition */
126174b941f0SAlexander V. Chernikov 		tei->flags |= TEI_FLAGS_UPDATED;
126274b941f0SAlexander V. Chernikov 		*pnum = 0;
126374b941f0SAlexander V. Chernikov 	} else {
12644c0c07a5SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_DONTADD) != 0)
12654c0c07a5SAlexander V. Chernikov 			return (EFBIG);
126674b941f0SAlexander V. Chernikov 		SLIST_INSERT_HEAD(&head[hash], ent, next);
126774b941f0SAlexander V. Chernikov 		tb->ent_ptr = NULL;
126874b941f0SAlexander V. Chernikov 		*pnum = 1;
1269ce2817b5SAlexander V. Chernikov 
1270b6ee846eSAlexander V. Chernikov 		/* Update counters */
1271b6ee846eSAlexander V. Chernikov 		if (tei->subtype == AF_INET)
12720bce0c23SAlexander V. Chernikov 			cfg->items4++;
1273b6ee846eSAlexander V. Chernikov 		else
12740bce0c23SAlexander V. Chernikov 			cfg->items6++;
127574b941f0SAlexander V. Chernikov 	}
127674b941f0SAlexander V. Chernikov 
127774b941f0SAlexander V. Chernikov 	return (0);
127874b941f0SAlexander V. Chernikov }
127974b941f0SAlexander V. Chernikov 
128074b941f0SAlexander V. Chernikov static int
128174b941f0SAlexander V. Chernikov ta_prepare_del_chash(struct ip_fw_chain *ch, struct tentry_info *tei,
128274b941f0SAlexander V. Chernikov     void *ta_buf)
128374b941f0SAlexander V. Chernikov {
128474b941f0SAlexander V. Chernikov 	struct ta_buf_chash *tb;
128574b941f0SAlexander V. Chernikov 
128674b941f0SAlexander V. Chernikov 	tb = (struct ta_buf_chash *)ta_buf;
128774b941f0SAlexander V. Chernikov 
1288ce2817b5SAlexander V. Chernikov 	return (tei_to_chash_ent(tei, &tb->ent));
128974b941f0SAlexander V. Chernikov }
129074b941f0SAlexander V. Chernikov 
129174b941f0SAlexander V. Chernikov static int
129274b941f0SAlexander V. Chernikov ta_del_chash(void *ta_state, struct table_info *ti, struct tentry_info *tei,
1293b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
129474b941f0SAlexander V. Chernikov {
12950bce0c23SAlexander V. Chernikov 	struct chash_cfg *cfg;
129674b941f0SAlexander V. Chernikov 	struct chashbhead *head;
12970bce0c23SAlexander V. Chernikov 	struct chashentry *tmp, *tmp_next, *ent;
129874b941f0SAlexander V. Chernikov 	struct ta_buf_chash *tb;
129974b941f0SAlexander V. Chernikov 	uint32_t hash;
130074b941f0SAlexander V. Chernikov 
13010bce0c23SAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
130274b941f0SAlexander V. Chernikov 	tb = (struct ta_buf_chash *)ta_buf;
13030bce0c23SAlexander V. Chernikov 	ent = &tb->ent;
130474b941f0SAlexander V. Chernikov 
130574b941f0SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
13060bce0c23SAlexander V. Chernikov 		if (tei->masklen != cfg->mask4)
130774b941f0SAlexander V. Chernikov 			return (EINVAL);
13080bce0c23SAlexander V. Chernikov 		head = cfg->head4;
13090bce0c23SAlexander V. Chernikov 		hash = hash_ent(ent, AF_INET, cfg->mask4, cfg->size4);
131074b941f0SAlexander V. Chernikov 
13110bce0c23SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(tmp, &head[hash], next, tmp_next) {
13120bce0c23SAlexander V. Chernikov 			if (tmp->a.a4 != ent->a.a4)
1313648e8380SAlexander V. Chernikov 				continue;
1314648e8380SAlexander V. Chernikov 
13150bce0c23SAlexander V. Chernikov 			SLIST_REMOVE(&head[hash], tmp, chashentry, next);
13160bce0c23SAlexander V. Chernikov 			cfg->items4--;
13170bce0c23SAlexander V. Chernikov 			tb->ent_ptr = tmp;
13180bce0c23SAlexander V. Chernikov 			tei->value = tmp->value;
1319648e8380SAlexander V. Chernikov 			*pnum = 1;
132074b941f0SAlexander V. Chernikov 			return (0);
132174b941f0SAlexander V. Chernikov 		}
132274b941f0SAlexander V. Chernikov 	} else {
13230bce0c23SAlexander V. Chernikov 		if (tei->masklen != cfg->mask6)
132474b941f0SAlexander V. Chernikov 			return (EINVAL);
13250bce0c23SAlexander V. Chernikov 		head = cfg->head6;
13260bce0c23SAlexander V. Chernikov 		hash = hash_ent(ent, AF_INET6, cfg->mask6, cfg->size6);
13270bce0c23SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(tmp, &head[hash], next, tmp_next) {
13280bce0c23SAlexander V. Chernikov 			if (memcmp(&tmp->a.a6, &ent->a.a6, 16) != 0)
1329648e8380SAlexander V. Chernikov 				continue;
1330648e8380SAlexander V. Chernikov 
13310bce0c23SAlexander V. Chernikov 			SLIST_REMOVE(&head[hash], tmp, chashentry, next);
13320bce0c23SAlexander V. Chernikov 			cfg->items6--;
13330bce0c23SAlexander V. Chernikov 			tb->ent_ptr = tmp;
13340bce0c23SAlexander V. Chernikov 			tei->value = tmp->value;
133574b941f0SAlexander V. Chernikov 			*pnum = 1;
133674b941f0SAlexander V. Chernikov 			return (0);
133774b941f0SAlexander V. Chernikov 		}
133874b941f0SAlexander V. Chernikov 	}
133974b941f0SAlexander V. Chernikov 
134074b941f0SAlexander V. Chernikov 	return (ENOENT);
134174b941f0SAlexander V. Chernikov }
134274b941f0SAlexander V. Chernikov 
134374b941f0SAlexander V. Chernikov static void
134474b941f0SAlexander V. Chernikov ta_flush_chash_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
134574b941f0SAlexander V. Chernikov     void *ta_buf)
134674b941f0SAlexander V. Chernikov {
134774b941f0SAlexander V. Chernikov 	struct ta_buf_chash *tb;
134874b941f0SAlexander V. Chernikov 
134974b941f0SAlexander V. Chernikov 	tb = (struct ta_buf_chash *)ta_buf;
135074b941f0SAlexander V. Chernikov 
135174b941f0SAlexander V. Chernikov 	if (tb->ent_ptr != NULL)
135274b941f0SAlexander V. Chernikov 		free(tb->ent_ptr, M_IPFW_TBL);
135374b941f0SAlexander V. Chernikov }
135474b941f0SAlexander V. Chernikov 
1355ce2817b5SAlexander V. Chernikov /*
1356ce2817b5SAlexander V. Chernikov  * Hash growing callbacks.
1357ce2817b5SAlexander V. Chernikov  */
1358ce2817b5SAlexander V. Chernikov 
1359b6ee846eSAlexander V. Chernikov static int
1360b6ee846eSAlexander V. Chernikov ta_has_space_chash(void *ta_state, struct table_info *ti, uint32_t count,
1361b6ee846eSAlexander V. Chernikov     uint64_t *pflags)
1362b6ee846eSAlexander V. Chernikov {
1363b6ee846eSAlexander V. Chernikov 	struct chash_cfg *cfg;
1364b6ee846eSAlexander V. Chernikov 	uint64_t data;
1365b6ee846eSAlexander V. Chernikov 
1366b6ee846eSAlexander V. Chernikov 	/*
1367b6ee846eSAlexander V. Chernikov 	 * Since we don't know exact number of IPv4/IPv6 records in @count,
1368b6ee846eSAlexander V. Chernikov 	 * ignore non-zero @count value at all. Check current hash sizes
1369b6ee846eSAlexander V. Chernikov 	 * and return appropriate data.
1370b6ee846eSAlexander V. Chernikov 	 */
1371b6ee846eSAlexander V. Chernikov 
1372b6ee846eSAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
1373b6ee846eSAlexander V. Chernikov 
1374b6ee846eSAlexander V. Chernikov 	data = 0;
1375b6ee846eSAlexander V. Chernikov 	if (cfg->items4 > cfg->size4 && cfg->size4 < 65536)
1376b6ee846eSAlexander V. Chernikov 		data |= (cfg->size4 * 2) << 16;
1377b6ee846eSAlexander V. Chernikov 	if (cfg->items6 > cfg->size6 && cfg->size6 < 65536)
1378b6ee846eSAlexander V. Chernikov 		data |= cfg->size6 * 2;
1379b6ee846eSAlexander V. Chernikov 
1380b6ee846eSAlexander V. Chernikov 	if (data != 0) {
1381b6ee846eSAlexander V. Chernikov 		*pflags = data;
1382b6ee846eSAlexander V. Chernikov 		return (0);
1383b6ee846eSAlexander V. Chernikov 	}
1384b6ee846eSAlexander V. Chernikov 
1385b6ee846eSAlexander V. Chernikov 	return (1);
1386b6ee846eSAlexander V. Chernikov }
1387b6ee846eSAlexander V. Chernikov 
1388ce2817b5SAlexander V. Chernikov /*
1389ce2817b5SAlexander V. Chernikov  * Allocate new, larger chash.
1390ce2817b5SAlexander V. Chernikov  */
1391ce2817b5SAlexander V. Chernikov static int
1392ce2817b5SAlexander V. Chernikov ta_prepare_mod_chash(void *ta_buf, uint64_t *pflags)
1393ce2817b5SAlexander V. Chernikov {
1394ce2817b5SAlexander V. Chernikov 	struct mod_item *mi;
1395ce2817b5SAlexander V. Chernikov 	struct chashbhead *head;
1396ce2817b5SAlexander V. Chernikov 	int i;
1397ce2817b5SAlexander V. Chernikov 
1398ce2817b5SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
1399ce2817b5SAlexander V. Chernikov 
1400ce2817b5SAlexander V. Chernikov 	memset(mi, 0, sizeof(struct mod_item));
1401b6ee846eSAlexander V. Chernikov 	mi->size = (*pflags >> 16) & 0xFFFF;
1402b6ee846eSAlexander V. Chernikov 	mi->size6 = *pflags & 0xFFFF;
1403b6ee846eSAlexander V. Chernikov 	if (mi->size > 0) {
1404b6ee846eSAlexander V. Chernikov 		head = malloc(sizeof(struct chashbhead) * mi->size,
1405b6ee846eSAlexander V. Chernikov 		    M_IPFW, M_WAITOK | M_ZERO);
1406ce2817b5SAlexander V. Chernikov 		for (i = 0; i < mi->size; i++)
1407ce2817b5SAlexander V. Chernikov 			SLIST_INIT(&head[i]);
1408ce2817b5SAlexander V. Chernikov 		mi->main_ptr = head;
1409b6ee846eSAlexander V. Chernikov 	}
1410b6ee846eSAlexander V. Chernikov 
1411b6ee846eSAlexander V. Chernikov 	if (mi->size6 > 0) {
1412b6ee846eSAlexander V. Chernikov 		head = malloc(sizeof(struct chashbhead) * mi->size6,
1413b6ee846eSAlexander V. Chernikov 		    M_IPFW, M_WAITOK | M_ZERO);
1414b6ee846eSAlexander V. Chernikov 		for (i = 0; i < mi->size6; i++)
1415b6ee846eSAlexander V. Chernikov 			SLIST_INIT(&head[i]);
1416b6ee846eSAlexander V. Chernikov 		mi->main_ptr6 = head;
1417b6ee846eSAlexander V. Chernikov 	}
1418ce2817b5SAlexander V. Chernikov 
1419ce2817b5SAlexander V. Chernikov 	return (0);
1420ce2817b5SAlexander V. Chernikov }
1421ce2817b5SAlexander V. Chernikov 
1422ce2817b5SAlexander V. Chernikov /*
1423ce2817b5SAlexander V. Chernikov  * Copy data from old runtime array to new one.
1424ce2817b5SAlexander V. Chernikov  */
1425ce2817b5SAlexander V. Chernikov static int
1426ce2817b5SAlexander V. Chernikov ta_fill_mod_chash(void *ta_state, struct table_info *ti, void *ta_buf,
1427ce2817b5SAlexander V. Chernikov     uint64_t *pflags)
1428ce2817b5SAlexander V. Chernikov {
1429ce2817b5SAlexander V. Chernikov 
1430ce2817b5SAlexander V. Chernikov 	/* In is not possible to do rehash if we're not holidng WLOCK. */
1431ce2817b5SAlexander V. Chernikov 	return (0);
1432ce2817b5SAlexander V. Chernikov }
1433ce2817b5SAlexander V. Chernikov 
1434ce2817b5SAlexander V. Chernikov /*
1435ce2817b5SAlexander V. Chernikov  * Switch old & new arrays.
1436ce2817b5SAlexander V. Chernikov  */
1437ce2817b5SAlexander V. Chernikov static int
1438ce2817b5SAlexander V. Chernikov ta_modify_chash(void *ta_state, struct table_info *ti, void *ta_buf,
1439ce2817b5SAlexander V. Chernikov     uint64_t pflags)
1440ce2817b5SAlexander V. Chernikov {
1441ce2817b5SAlexander V. Chernikov 	struct mod_item *mi;
1442b6ee846eSAlexander V. Chernikov 	struct chash_cfg *cfg;
1443ce2817b5SAlexander V. Chernikov 	struct chashbhead *old_head, *new_head;
1444ce2817b5SAlexander V. Chernikov 	struct chashentry *ent, *ent_next;
1445ce2817b5SAlexander V. Chernikov 	int af, i, mlen;
1446ce2817b5SAlexander V. Chernikov 	uint32_t nhash;
1447b6ee846eSAlexander V. Chernikov 	size_t old_size, new_size;
1448ce2817b5SAlexander V. Chernikov 
1449ce2817b5SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
1450b6ee846eSAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
1451ce2817b5SAlexander V. Chernikov 
1452ce2817b5SAlexander V. Chernikov 	/* Check which hash we need to grow and do we still need that */
1453b6ee846eSAlexander V. Chernikov 	if (mi->size > 0 && cfg->size4 < mi->size) {
1454ce2817b5SAlexander V. Chernikov 		new_head = (struct chashbhead *)mi->main_ptr;
1455b6ee846eSAlexander V. Chernikov 		new_size = mi->size;
1456b6ee846eSAlexander V. Chernikov 		old_size = cfg->size4;
1457b6ee846eSAlexander V. Chernikov 		old_head = ti->state;
1458b6ee846eSAlexander V. Chernikov 		mlen = cfg->mask4;
1459b6ee846eSAlexander V. Chernikov 		af = AF_INET;
1460b6ee846eSAlexander V. Chernikov 
1461ce2817b5SAlexander V. Chernikov 		for (i = 0; i < old_size; i++) {
1462ce2817b5SAlexander V. Chernikov 			SLIST_FOREACH_SAFE(ent, &old_head[i], next, ent_next) {
1463b6ee846eSAlexander V. Chernikov 				nhash = hash_ent(ent, af, mlen, new_size);
1464ce2817b5SAlexander V. Chernikov 				SLIST_INSERT_HEAD(&new_head[nhash], ent, next);
1465ce2817b5SAlexander V. Chernikov 			}
1466ce2817b5SAlexander V. Chernikov 		}
1467ce2817b5SAlexander V. Chernikov 
1468ce2817b5SAlexander V. Chernikov 		ti->state = new_head;
1469b6ee846eSAlexander V. Chernikov 		cfg->head4 = new_head;
1470b6ee846eSAlexander V. Chernikov 		cfg->size4 = mi->size;
1471b6ee846eSAlexander V. Chernikov 		mi->main_ptr = old_head;
1472ce2817b5SAlexander V. Chernikov 	}
1473ce2817b5SAlexander V. Chernikov 
1474b6ee846eSAlexander V. Chernikov 	if (mi->size6 > 0 && cfg->size6 < mi->size6) {
1475b6ee846eSAlexander V. Chernikov 		new_head = (struct chashbhead *)mi->main_ptr6;
1476b6ee846eSAlexander V. Chernikov 		new_size = mi->size6;
1477b6ee846eSAlexander V. Chernikov 		old_size = cfg->size6;
1478b6ee846eSAlexander V. Chernikov 		old_head = ti->xstate;
1479b6ee846eSAlexander V. Chernikov 		mlen = cfg->mask6;
1480b6ee846eSAlexander V. Chernikov 		af = AF_INET6;
1481914bffb6SAlexander V. Chernikov 
1482b6ee846eSAlexander V. Chernikov 		for (i = 0; i < old_size; i++) {
1483b6ee846eSAlexander V. Chernikov 			SLIST_FOREACH_SAFE(ent, &old_head[i], next, ent_next) {
1484b6ee846eSAlexander V. Chernikov 				nhash = hash_ent(ent, af, mlen, new_size);
1485b6ee846eSAlexander V. Chernikov 				SLIST_INSERT_HEAD(&new_head[nhash], ent, next);
1486b6ee846eSAlexander V. Chernikov 			}
1487b6ee846eSAlexander V. Chernikov 		}
1488b6ee846eSAlexander V. Chernikov 
1489b6ee846eSAlexander V. Chernikov 		ti->xstate = new_head;
1490b6ee846eSAlexander V. Chernikov 		cfg->head6 = new_head;
1491b6ee846eSAlexander V. Chernikov 		cfg->size6 = mi->size6;
1492b6ee846eSAlexander V. Chernikov 		mi->main_ptr6 = old_head;
1493b6ee846eSAlexander V. Chernikov 	}
1494b6ee846eSAlexander V. Chernikov 
1495b6ee846eSAlexander V. Chernikov 	/* Update lower 32 bits with new values */
1496b6ee846eSAlexander V. Chernikov 	ti->data &= 0xFFFFFFFF00000000;
1497b6ee846eSAlexander V. Chernikov 	ti->data |= log2(cfg->size4) << 8 | log2(cfg->size6);
1498ce2817b5SAlexander V. Chernikov 
1499ce2817b5SAlexander V. Chernikov 	return (0);
1500ce2817b5SAlexander V. Chernikov }
1501ce2817b5SAlexander V. Chernikov 
1502ce2817b5SAlexander V. Chernikov /*
1503ce2817b5SAlexander V. Chernikov  * Free unneded array.
1504ce2817b5SAlexander V. Chernikov  */
1505ce2817b5SAlexander V. Chernikov static void
1506ce2817b5SAlexander V. Chernikov ta_flush_mod_chash(void *ta_buf)
1507ce2817b5SAlexander V. Chernikov {
1508ce2817b5SAlexander V. Chernikov 	struct mod_item *mi;
1509ce2817b5SAlexander V. Chernikov 
1510ce2817b5SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
1511ce2817b5SAlexander V. Chernikov 	if (mi->main_ptr != NULL)
1512ce2817b5SAlexander V. Chernikov 		free(mi->main_ptr, M_IPFW);
1513b6ee846eSAlexander V. Chernikov 	if (mi->main_ptr6 != NULL)
1514b6ee846eSAlexander V. Chernikov 		free(mi->main_ptr6, M_IPFW);
1515ce2817b5SAlexander V. Chernikov }
1516ce2817b5SAlexander V. Chernikov 
151774b941f0SAlexander V. Chernikov struct table_algo cidr_hash = {
151874b941f0SAlexander V. Chernikov 	.name		= "cidr:hash",
15199d099b4fSAlexander V. Chernikov 	.type		= IPFW_TABLE_CIDR,
152057a1cf95SAlexander V. Chernikov 	.ta_buf_size	= sizeof(struct ta_buf_chash),
152174b941f0SAlexander V. Chernikov 	.init		= ta_init_chash,
152274b941f0SAlexander V. Chernikov 	.destroy	= ta_destroy_chash,
152374b941f0SAlexander V. Chernikov 	.prepare_add	= ta_prepare_add_chash,
152474b941f0SAlexander V. Chernikov 	.prepare_del	= ta_prepare_del_chash,
152574b941f0SAlexander V. Chernikov 	.add		= ta_add_chash,
152674b941f0SAlexander V. Chernikov 	.del		= ta_del_chash,
152774b941f0SAlexander V. Chernikov 	.flush_entry	= ta_flush_chash_entry,
152874b941f0SAlexander V. Chernikov 	.foreach	= ta_foreach_chash,
152974b941f0SAlexander V. Chernikov 	.dump_tentry	= ta_dump_chash_tentry,
153074b941f0SAlexander V. Chernikov 	.find_tentry	= ta_find_chash_tentry,
153174b941f0SAlexander V. Chernikov 	.print_config	= ta_print_chash_config,
15325f379342SAlexander V. Chernikov 	.dump_tinfo	= ta_dump_chash_tinfo,
1533b6ee846eSAlexander V. Chernikov 	.has_space	= ta_has_space_chash,
1534ce2817b5SAlexander V. Chernikov 	.prepare_mod	= ta_prepare_mod_chash,
1535ce2817b5SAlexander V. Chernikov 	.fill_mod	= ta_fill_mod_chash,
1536ce2817b5SAlexander V. Chernikov 	.modify		= ta_modify_chash,
1537ce2817b5SAlexander V. Chernikov 	.flush_mod	= ta_flush_mod_chash,
153874b941f0SAlexander V. Chernikov };
153974b941f0SAlexander V. Chernikov 
154074b941f0SAlexander V. Chernikov 
154174b941f0SAlexander V. Chernikov /*
154268394ec8SAlexander V. Chernikov  * Iface table cmds.
154368394ec8SAlexander V. Chernikov  *
154468394ec8SAlexander V. Chernikov  * Implementation:
154568394ec8SAlexander V. Chernikov  *
154668394ec8SAlexander V. Chernikov  * Runtime part:
154768394ec8SAlexander V. Chernikov  * - sorted array of "struct ifidx" pointed by ti->state.
1548b23d5de9SAlexander V. Chernikov  *   Array is allocated with rounding up to IFIDX_CHUNK. Only existing
154968394ec8SAlexander V. Chernikov  *   interfaces are stored in array, however its allocated size is
155068394ec8SAlexander V. Chernikov  *   sufficient to hold all table records if needed.
155168394ec8SAlexander V. Chernikov  * - current array size is stored in ti->data
155268394ec8SAlexander V. Chernikov  *
155368394ec8SAlexander V. Chernikov  * Table data:
155468394ec8SAlexander V. Chernikov  * - "struct iftable_cfg" is allocated to store table state (ta_state).
155568394ec8SAlexander V. Chernikov  * - All table records are stored inside namedobj instance.
15569f7d47b0SAlexander V. Chernikov  *
15579f7d47b0SAlexander V. Chernikov  */
15589f7d47b0SAlexander V. Chernikov 
155968394ec8SAlexander V. Chernikov struct ifidx {
156068394ec8SAlexander V. Chernikov 	uint16_t	kidx;
156168394ec8SAlexander V. Chernikov 	uint16_t	spare;
156268394ec8SAlexander V. Chernikov 	uint32_t	value;
156368394ec8SAlexander V. Chernikov };
156468394ec8SAlexander V. Chernikov 
156568394ec8SAlexander V. Chernikov struct iftable_cfg;
156668394ec8SAlexander V. Chernikov 
156768394ec8SAlexander V. Chernikov struct ifentry {
156868394ec8SAlexander V. Chernikov 	struct named_object	no;
156968394ec8SAlexander V. Chernikov 	struct ipfw_ifc		ic;
157068394ec8SAlexander V. Chernikov 	struct iftable_cfg	*icfg;
157168394ec8SAlexander V. Chernikov 	uint32_t		value;
157268394ec8SAlexander V. Chernikov 	int			linked;
157368394ec8SAlexander V. Chernikov };
157468394ec8SAlexander V. Chernikov 
157568394ec8SAlexander V. Chernikov struct iftable_cfg {
157668394ec8SAlexander V. Chernikov 	struct namedobj_instance	*ii;
157768394ec8SAlexander V. Chernikov 	struct ip_fw_chain	*ch;
157868394ec8SAlexander V. Chernikov 	struct table_info	*ti;
157968394ec8SAlexander V. Chernikov 	void	*main_ptr;
158068394ec8SAlexander V. Chernikov 	size_t	size;	/* Number of items allocated in array */
158168394ec8SAlexander V. Chernikov 	size_t	count;	/* Number of all items */
158268394ec8SAlexander V. Chernikov 	size_t	used;	/* Number of items _active_ now */
158368394ec8SAlexander V. Chernikov };
158468394ec8SAlexander V. Chernikov 
15850bce0c23SAlexander V. Chernikov struct ta_buf_ifidx
15860bce0c23SAlexander V. Chernikov {
15870bce0c23SAlexander V. Chernikov 	struct ifentry *ife;
15880bce0c23SAlexander V. Chernikov 	uint32_t value;
15890bce0c23SAlexander V. Chernikov };
159068394ec8SAlexander V. Chernikov 
159168394ec8SAlexander V. Chernikov int compare_ifidx(const void *k, const void *v);
159268394ec8SAlexander V. Chernikov static void if_notifier(struct ip_fw_chain *ch, void *cbdata, uint16_t ifindex);
159368394ec8SAlexander V. Chernikov 
159468394ec8SAlexander V. Chernikov int
159568394ec8SAlexander V. Chernikov compare_ifidx(const void *k, const void *v)
159668394ec8SAlexander V. Chernikov {
159768394ec8SAlexander V. Chernikov 	struct ifidx *ifidx;
159868394ec8SAlexander V. Chernikov 	uint16_t key;
159968394ec8SAlexander V. Chernikov 
160068394ec8SAlexander V. Chernikov 	key = *((uint16_t *)k);
160168394ec8SAlexander V. Chernikov 	ifidx = (struct ifidx *)v;
160268394ec8SAlexander V. Chernikov 
160368394ec8SAlexander V. Chernikov 	if (key < ifidx->kidx)
160468394ec8SAlexander V. Chernikov 		return (-1);
160568394ec8SAlexander V. Chernikov 	else if (key > ifidx->kidx)
160668394ec8SAlexander V. Chernikov 		return (1);
160768394ec8SAlexander V. Chernikov 
160868394ec8SAlexander V. Chernikov 	return (0);
160968394ec8SAlexander V. Chernikov }
161068394ec8SAlexander V. Chernikov 
161168394ec8SAlexander V. Chernikov /*
161268394ec8SAlexander V. Chernikov  * Adds item @item with key @key into ascending-sorted array @base.
161368394ec8SAlexander V. Chernikov  * Assumes @base has enough additional storage.
161468394ec8SAlexander V. Chernikov  *
161568394ec8SAlexander V. Chernikov  * Returns 1 on success, 0 on duplicate key.
161668394ec8SAlexander V. Chernikov  */
16179f7d47b0SAlexander V. Chernikov static int
161868394ec8SAlexander V. Chernikov badd(const void *key, void *item, void *base, size_t nmemb,
161968394ec8SAlexander V. Chernikov     size_t size, int (*compar) (const void *, const void *))
162068394ec8SAlexander V. Chernikov {
162168394ec8SAlexander V. Chernikov 	int min, max, mid, shift, res;
162268394ec8SAlexander V. Chernikov 	caddr_t paddr;
162368394ec8SAlexander V. Chernikov 
162468394ec8SAlexander V. Chernikov 	if (nmemb == 0) {
162568394ec8SAlexander V. Chernikov 		memcpy(base, item, size);
162668394ec8SAlexander V. Chernikov 		return (1);
162768394ec8SAlexander V. Chernikov 	}
162868394ec8SAlexander V. Chernikov 
162968394ec8SAlexander V. Chernikov 	/* Binary search */
163068394ec8SAlexander V. Chernikov 	min = 0;
163168394ec8SAlexander V. Chernikov 	max = nmemb - 1;
163268394ec8SAlexander V. Chernikov 	mid = 0;
163368394ec8SAlexander V. Chernikov 	while (min <= max) {
163468394ec8SAlexander V. Chernikov 		mid = (min + max) / 2;
163568394ec8SAlexander V. Chernikov 		res = compar(key, (const void *)((caddr_t)base + mid * size));
163668394ec8SAlexander V. Chernikov 		if (res == 0)
163768394ec8SAlexander V. Chernikov 			return (0);
163868394ec8SAlexander V. Chernikov 
163968394ec8SAlexander V. Chernikov 		if (res > 0)
164068394ec8SAlexander V. Chernikov 			min = mid + 1;
164168394ec8SAlexander V. Chernikov 		else
164268394ec8SAlexander V. Chernikov 			max = mid - 1;
164368394ec8SAlexander V. Chernikov 	}
164468394ec8SAlexander V. Chernikov 
164568394ec8SAlexander V. Chernikov 	/* Item not found. */
164668394ec8SAlexander V. Chernikov 	res = compar(key, (const void *)((caddr_t)base + mid * size));
164768394ec8SAlexander V. Chernikov 	if (res > 0)
164868394ec8SAlexander V. Chernikov 		shift = mid + 1;
164968394ec8SAlexander V. Chernikov 	else
165068394ec8SAlexander V. Chernikov 		shift = mid;
165168394ec8SAlexander V. Chernikov 
165268394ec8SAlexander V. Chernikov 	paddr = (caddr_t)base + shift * size;
165368394ec8SAlexander V. Chernikov 	if (nmemb > shift)
165468394ec8SAlexander V. Chernikov 		memmove(paddr + size, paddr, (nmemb - shift) * size);
165568394ec8SAlexander V. Chernikov 
165668394ec8SAlexander V. Chernikov 	memcpy(paddr, item, size);
165768394ec8SAlexander V. Chernikov 
165868394ec8SAlexander V. Chernikov 	return (1);
165968394ec8SAlexander V. Chernikov }
166068394ec8SAlexander V. Chernikov 
166168394ec8SAlexander V. Chernikov /*
166268394ec8SAlexander V. Chernikov  * Deletes item with key @key from ascending-sorted array @base.
166368394ec8SAlexander V. Chernikov  *
166468394ec8SAlexander V. Chernikov  * Returns 1 on success, 0 for non-existent key.
166568394ec8SAlexander V. Chernikov  */
166668394ec8SAlexander V. Chernikov static int
166768394ec8SAlexander V. Chernikov bdel(const void *key, void *base, size_t nmemb, size_t size,
166868394ec8SAlexander V. Chernikov     int (*compar) (const void *, const void *))
166968394ec8SAlexander V. Chernikov {
167068394ec8SAlexander V. Chernikov 	caddr_t item;
167168394ec8SAlexander V. Chernikov 	size_t sz;
167268394ec8SAlexander V. Chernikov 
167368394ec8SAlexander V. Chernikov 	item = (caddr_t)bsearch(key, base, nmemb, size, compar);
167468394ec8SAlexander V. Chernikov 
167568394ec8SAlexander V. Chernikov 	if (item == NULL)
167668394ec8SAlexander V. Chernikov 		return (0);
167768394ec8SAlexander V. Chernikov 
167868394ec8SAlexander V. Chernikov 	sz = (caddr_t)base + nmemb * size - item;
167968394ec8SAlexander V. Chernikov 
168068394ec8SAlexander V. Chernikov 	if (sz > 0)
168168394ec8SAlexander V. Chernikov 		memmove(item, item + size, sz);
168268394ec8SAlexander V. Chernikov 
168368394ec8SAlexander V. Chernikov 	return (1);
168468394ec8SAlexander V. Chernikov }
168568394ec8SAlexander V. Chernikov 
168668394ec8SAlexander V. Chernikov static struct ifidx *
168768394ec8SAlexander V. Chernikov ifidx_find(struct table_info *ti, void *key)
168868394ec8SAlexander V. Chernikov {
168968394ec8SAlexander V. Chernikov 	struct ifidx *ifi;
169068394ec8SAlexander V. Chernikov 
169168394ec8SAlexander V. Chernikov 	ifi = bsearch(key, ti->state, ti->data, sizeof(struct ifidx),
169268394ec8SAlexander V. Chernikov 	    compare_ifidx);
169368394ec8SAlexander V. Chernikov 
169468394ec8SAlexander V. Chernikov 	return (ifi);
169568394ec8SAlexander V. Chernikov }
169668394ec8SAlexander V. Chernikov 
169768394ec8SAlexander V. Chernikov static int
169868394ec8SAlexander V. Chernikov ta_lookup_ifidx(struct table_info *ti, void *key, uint32_t keylen,
16999f7d47b0SAlexander V. Chernikov     uint32_t *val)
17009f7d47b0SAlexander V. Chernikov {
170168394ec8SAlexander V. Chernikov 	struct ifidx *ifi;
17029f7d47b0SAlexander V. Chernikov 
170368394ec8SAlexander V. Chernikov 	ifi = ifidx_find(ti, key);
17049f7d47b0SAlexander V. Chernikov 
170568394ec8SAlexander V. Chernikov 	if (ifi != NULL) {
170668394ec8SAlexander V. Chernikov 		*val = ifi->value;
17079f7d47b0SAlexander V. Chernikov 		return (1);
17089f7d47b0SAlexander V. Chernikov 	}
17099f7d47b0SAlexander V. Chernikov 
17109f7d47b0SAlexander V. Chernikov 	return (0);
17119f7d47b0SAlexander V. Chernikov }
17129f7d47b0SAlexander V. Chernikov 
17139f7d47b0SAlexander V. Chernikov static int
171468394ec8SAlexander V. Chernikov ta_init_ifidx(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
1715914bffb6SAlexander V. Chernikov     char *data, uint8_t tflags)
17169f7d47b0SAlexander V. Chernikov {
171768394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
17189f7d47b0SAlexander V. Chernikov 
171968394ec8SAlexander V. Chernikov 	icfg = malloc(sizeof(struct iftable_cfg), M_IPFW, M_WAITOK | M_ZERO);
17209f7d47b0SAlexander V. Chernikov 
172168394ec8SAlexander V. Chernikov 	icfg->ii = ipfw_objhash_create(16);
17220bce0c23SAlexander V. Chernikov 	icfg->size = 16;
17230bce0c23SAlexander V. Chernikov 	icfg->main_ptr = malloc(sizeof(struct ifidx) * icfg->size, M_IPFW,
172468394ec8SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
172568394ec8SAlexander V. Chernikov 	icfg->ch = ch;
17269f7d47b0SAlexander V. Chernikov 
172768394ec8SAlexander V. Chernikov 	*ta_state = icfg;
172868394ec8SAlexander V. Chernikov 	ti->state = icfg->main_ptr;
172968394ec8SAlexander V. Chernikov 	ti->lookup = ta_lookup_ifidx;
17309f7d47b0SAlexander V. Chernikov 
17319f7d47b0SAlexander V. Chernikov 	return (0);
17329f7d47b0SAlexander V. Chernikov }
17339f7d47b0SAlexander V. Chernikov 
173468394ec8SAlexander V. Chernikov /*
173568394ec8SAlexander V. Chernikov  * Handle tableinfo @ti pointer change (on table array resize).
173668394ec8SAlexander V. Chernikov  */
173768394ec8SAlexander V. Chernikov static void
173868394ec8SAlexander V. Chernikov ta_change_ti_ifidx(void *ta_state, struct table_info *ti)
173968394ec8SAlexander V. Chernikov {
174068394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
174168394ec8SAlexander V. Chernikov 
174268394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
174368394ec8SAlexander V. Chernikov 	icfg->ti = ti;
174468394ec8SAlexander V. Chernikov }
17459f7d47b0SAlexander V. Chernikov 
17469f7d47b0SAlexander V. Chernikov static void
174768394ec8SAlexander V. Chernikov destroy_ifidx_locked(struct namedobj_instance *ii, struct named_object *no,
174868394ec8SAlexander V. Chernikov     void *arg)
17499f7d47b0SAlexander V. Chernikov {
175068394ec8SAlexander V. Chernikov 	struct ifentry *ife;
175168394ec8SAlexander V. Chernikov 	struct ip_fw_chain *ch;
17529f7d47b0SAlexander V. Chernikov 
175368394ec8SAlexander V. Chernikov 	ch = (struct ip_fw_chain *)arg;
175468394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)no;
175568394ec8SAlexander V. Chernikov 
175668394ec8SAlexander V. Chernikov 	ipfw_iface_del_notify(ch, &ife->ic);
175768394ec8SAlexander V. Chernikov 	free(ife, M_IPFW_TBL);
17589f7d47b0SAlexander V. Chernikov }
17599f7d47b0SAlexander V. Chernikov 
176068394ec8SAlexander V. Chernikov 
176168394ec8SAlexander V. Chernikov /*
176268394ec8SAlexander V. Chernikov  * Destroys table @ti
176368394ec8SAlexander V. Chernikov  */
176468394ec8SAlexander V. Chernikov static void
176568394ec8SAlexander V. Chernikov ta_destroy_ifidx(void *ta_state, struct table_info *ti)
17669f7d47b0SAlexander V. Chernikov {
176768394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
176868394ec8SAlexander V. Chernikov 	struct ip_fw_chain *ch;
176968394ec8SAlexander V. Chernikov 
177068394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
177168394ec8SAlexander V. Chernikov 	ch = icfg->ch;
177268394ec8SAlexander V. Chernikov 
177368394ec8SAlexander V. Chernikov 	if (icfg->main_ptr != NULL)
177468394ec8SAlexander V. Chernikov 		free(icfg->main_ptr, M_IPFW);
177568394ec8SAlexander V. Chernikov 
177668394ec8SAlexander V. Chernikov 	ipfw_objhash_foreach(icfg->ii, destroy_ifidx_locked, ch);
177768394ec8SAlexander V. Chernikov 
177868394ec8SAlexander V. Chernikov 	ipfw_objhash_destroy(icfg->ii);
177968394ec8SAlexander V. Chernikov 
178068394ec8SAlexander V. Chernikov 	free(icfg, M_IPFW);
178168394ec8SAlexander V. Chernikov }
178268394ec8SAlexander V. Chernikov 
178368394ec8SAlexander V. Chernikov /*
17845f379342SAlexander V. Chernikov  * Provide algo-specific table info
17855f379342SAlexander V. Chernikov  */
17865f379342SAlexander V. Chernikov static void
17875f379342SAlexander V. Chernikov ta_dump_ifidx_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo)
17885f379342SAlexander V. Chernikov {
17895f379342SAlexander V. Chernikov 	struct iftable_cfg *cfg;
17905f379342SAlexander V. Chernikov 
17915f379342SAlexander V. Chernikov 	cfg = (struct iftable_cfg *)ta_state;
17925f379342SAlexander V. Chernikov 
17935f379342SAlexander V. Chernikov 	tinfo->taclass4 = IPFW_TACLASS_ARRAY;
17945f379342SAlexander V. Chernikov 	tinfo->size4 = cfg->size;
17955f379342SAlexander V. Chernikov 	tinfo->count4 = cfg->used;
17965f379342SAlexander V. Chernikov 	tinfo->itemsize4 = sizeof(struct ifidx);
17975f379342SAlexander V. Chernikov }
17985f379342SAlexander V. Chernikov 
17995f379342SAlexander V. Chernikov /*
180068394ec8SAlexander V. Chernikov  * Prepare state to add to the table:
180168394ec8SAlexander V. Chernikov  * allocate ifentry and reference needed interface.
180268394ec8SAlexander V. Chernikov  */
18039f7d47b0SAlexander V. Chernikov static int
180468394ec8SAlexander V. Chernikov ta_prepare_add_ifidx(struct ip_fw_chain *ch, struct tentry_info *tei,
180568394ec8SAlexander V. Chernikov     void *ta_buf)
180668394ec8SAlexander V. Chernikov {
180768394ec8SAlexander V. Chernikov 	struct ta_buf_ifidx *tb;
180868394ec8SAlexander V. Chernikov 	char *ifname;
180968394ec8SAlexander V. Chernikov 	struct ifentry *ife;
181068394ec8SAlexander V. Chernikov 
181168394ec8SAlexander V. Chernikov 	tb = (struct ta_buf_ifidx *)ta_buf;
181268394ec8SAlexander V. Chernikov 
181368394ec8SAlexander V. Chernikov 	/* Check if string is terminated */
181468394ec8SAlexander V. Chernikov 	ifname = (char *)tei->paddr;
181568394ec8SAlexander V. Chernikov 	if (strnlen(ifname, IF_NAMESIZE) == IF_NAMESIZE)
181668394ec8SAlexander V. Chernikov 		return (EINVAL);
181768394ec8SAlexander V. Chernikov 
181868394ec8SAlexander V. Chernikov 	ife = malloc(sizeof(struct ifentry), M_IPFW_TBL, M_WAITOK | M_ZERO);
181968394ec8SAlexander V. Chernikov 	ife->value = tei->value;
182068394ec8SAlexander V. Chernikov 	ife->ic.cb = if_notifier;
182168394ec8SAlexander V. Chernikov 	ife->ic.cbdata = ife;
182268394ec8SAlexander V. Chernikov 
182368394ec8SAlexander V. Chernikov 	if (ipfw_iface_ref(ch, ifname, &ife->ic) != 0)
182468394ec8SAlexander V. Chernikov 		return (EINVAL);
182568394ec8SAlexander V. Chernikov 
182668394ec8SAlexander V. Chernikov 	/* Use ipfw_iface 'ifname' field as stable storage */
182768394ec8SAlexander V. Chernikov 	ife->no.name = ife->ic.iface->ifname;
182868394ec8SAlexander V. Chernikov 
182968394ec8SAlexander V. Chernikov 	tb->ife = ife;
183068394ec8SAlexander V. Chernikov 
183168394ec8SAlexander V. Chernikov 	return (0);
183268394ec8SAlexander V. Chernikov }
183368394ec8SAlexander V. Chernikov 
183468394ec8SAlexander V. Chernikov static int
1835adea6201SAlexander V. Chernikov ta_add_ifidx(void *ta_state, struct table_info *ti, struct tentry_info *tei,
1836b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
183768394ec8SAlexander V. Chernikov {
183868394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
183968394ec8SAlexander V. Chernikov 	struct ifentry *ife, *tmp;
184068394ec8SAlexander V. Chernikov 	struct ta_buf_ifidx *tb;
184168394ec8SAlexander V. Chernikov 	struct ipfw_iface *iif;
184268394ec8SAlexander V. Chernikov 	struct ifidx *ifi;
184368394ec8SAlexander V. Chernikov 	char *ifname;
1844648e8380SAlexander V. Chernikov 	uint32_t value;
184568394ec8SAlexander V. Chernikov 
184668394ec8SAlexander V. Chernikov 	tb = (struct ta_buf_ifidx *)ta_buf;
184768394ec8SAlexander V. Chernikov 	ifname = (char *)tei->paddr;
184868394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
184968394ec8SAlexander V. Chernikov 	ife = tb->ife;
185068394ec8SAlexander V. Chernikov 
185168394ec8SAlexander V. Chernikov 	ife->icfg = icfg;
185268394ec8SAlexander V. Chernikov 
185368394ec8SAlexander V. Chernikov 	tmp = (struct ifentry *)ipfw_objhash_lookup_name(icfg->ii, 0, ifname);
185468394ec8SAlexander V. Chernikov 
185568394ec8SAlexander V. Chernikov 	if (tmp != NULL) {
185668394ec8SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
185768394ec8SAlexander V. Chernikov 			return (EEXIST);
185868394ec8SAlexander V. Chernikov 
1859648e8380SAlexander V. Chernikov 		/* Exchange values in @tmp and @tei */
1860648e8380SAlexander V. Chernikov 		value = tmp->value;
1861648e8380SAlexander V. Chernikov 		tmp->value = tei->value;
1862648e8380SAlexander V. Chernikov 		tei->value = value;
186368394ec8SAlexander V. Chernikov 
1864648e8380SAlexander V. Chernikov 		iif = tmp->ic.iface;
186568394ec8SAlexander V. Chernikov 		if (iif->resolved != 0) {
1866648e8380SAlexander V. Chernikov 			/* We have to update runtime value, too */
186768394ec8SAlexander V. Chernikov 			ifi = ifidx_find(ti, &iif->ifindex);
186868394ec8SAlexander V. Chernikov 			ifi->value = ife->value;
186968394ec8SAlexander V. Chernikov 		}
187068394ec8SAlexander V. Chernikov 
187168394ec8SAlexander V. Chernikov 		/* Indicate that update has happened instead of addition */
187268394ec8SAlexander V. Chernikov 		tei->flags |= TEI_FLAGS_UPDATED;
1873adea6201SAlexander V. Chernikov 		*pnum = 0;
187468394ec8SAlexander V. Chernikov 		return (0);
187568394ec8SAlexander V. Chernikov 	}
187668394ec8SAlexander V. Chernikov 
18774c0c07a5SAlexander V. Chernikov 	if ((tei->flags & TEI_FLAGS_DONTADD) != 0)
18784c0c07a5SAlexander V. Chernikov 		return (EFBIG);
18794c0c07a5SAlexander V. Chernikov 
188068394ec8SAlexander V. Chernikov 	/* Link to internal list */
188168394ec8SAlexander V. Chernikov 	ipfw_objhash_add(icfg->ii, &ife->no);
188268394ec8SAlexander V. Chernikov 
188368394ec8SAlexander V. Chernikov 	/* Link notifier (possible running its callback) */
188468394ec8SAlexander V. Chernikov 	ipfw_iface_add_notify(icfg->ch, &ife->ic);
188568394ec8SAlexander V. Chernikov 	icfg->count++;
188668394ec8SAlexander V. Chernikov 
188768394ec8SAlexander V. Chernikov 	tb->ife = NULL;
1888adea6201SAlexander V. Chernikov 	*pnum = 1;
188968394ec8SAlexander V. Chernikov 
189068394ec8SAlexander V. Chernikov 	return (0);
189168394ec8SAlexander V. Chernikov }
189268394ec8SAlexander V. Chernikov 
189368394ec8SAlexander V. Chernikov /*
189468394ec8SAlexander V. Chernikov  * Prepare to delete key from table.
189568394ec8SAlexander V. Chernikov  * Do basic interface name checks.
189668394ec8SAlexander V. Chernikov  */
189768394ec8SAlexander V. Chernikov static int
189868394ec8SAlexander V. Chernikov ta_prepare_del_ifidx(struct ip_fw_chain *ch, struct tentry_info *tei,
189968394ec8SAlexander V. Chernikov     void *ta_buf)
19009f7d47b0SAlexander V. Chernikov {
190174b941f0SAlexander V. Chernikov 	struct ta_buf_ifidx *tb;
1902e0a8b9eeSAlexander V. Chernikov 	char *ifname;
19039f7d47b0SAlexander V. Chernikov 
190474b941f0SAlexander V. Chernikov 	tb = (struct ta_buf_ifidx *)ta_buf;
19059f7d47b0SAlexander V. Chernikov 
19069f7d47b0SAlexander V. Chernikov 	/* Check if string is terminated */
1907e0a8b9eeSAlexander V. Chernikov 	ifname = (char *)tei->paddr;
1908e0a8b9eeSAlexander V. Chernikov 	if (strnlen(ifname, IF_NAMESIZE) == IF_NAMESIZE)
19099f7d47b0SAlexander V. Chernikov 		return (EINVAL);
19109f7d47b0SAlexander V. Chernikov 
19119f7d47b0SAlexander V. Chernikov 	return (0);
19129f7d47b0SAlexander V. Chernikov }
19139f7d47b0SAlexander V. Chernikov 
191468394ec8SAlexander V. Chernikov /*
191568394ec8SAlexander V. Chernikov  * Remove key from both configuration list and
191668394ec8SAlexander V. Chernikov  * runtime array. Removed interface notification.
191768394ec8SAlexander V. Chernikov  */
19189f7d47b0SAlexander V. Chernikov static int
1919adea6201SAlexander V. Chernikov ta_del_ifidx(void *ta_state, struct table_info *ti, struct tentry_info *tei,
1920b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
19219f7d47b0SAlexander V. Chernikov {
192268394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
192368394ec8SAlexander V. Chernikov 	struct ifentry *ife;
192468394ec8SAlexander V. Chernikov 	struct ta_buf_ifidx *tb;
192568394ec8SAlexander V. Chernikov 	char *ifname;
192668394ec8SAlexander V. Chernikov 	uint16_t ifindex;
192768394ec8SAlexander V. Chernikov 	int res;
19289f7d47b0SAlexander V. Chernikov 
192968394ec8SAlexander V. Chernikov 	tb = (struct ta_buf_ifidx *)ta_buf;
193068394ec8SAlexander V. Chernikov 	ifname = (char *)tei->paddr;
193168394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
193268394ec8SAlexander V. Chernikov 	ife = tb->ife;
19339f7d47b0SAlexander V. Chernikov 
193468394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)ipfw_objhash_lookup_name(icfg->ii, 0, ifname);
19359f7d47b0SAlexander V. Chernikov 
193668394ec8SAlexander V. Chernikov 	if (ife == NULL)
193781d3153dSAlexander V. Chernikov 		return (ENOENT);
19389f7d47b0SAlexander V. Chernikov 
193968394ec8SAlexander V. Chernikov 	if (ife->linked != 0) {
194068394ec8SAlexander V. Chernikov 		/* We have to remove item from runtime */
194168394ec8SAlexander V. Chernikov 		ifindex = ife->ic.iface->ifindex;
194268394ec8SAlexander V. Chernikov 
194368394ec8SAlexander V. Chernikov 		res = bdel(&ifindex, icfg->main_ptr, icfg->used,
194468394ec8SAlexander V. Chernikov 		    sizeof(struct ifidx), compare_ifidx);
194568394ec8SAlexander V. Chernikov 
194668394ec8SAlexander V. Chernikov 		KASSERT(res == 1, ("index %d does not exist", ifindex));
194768394ec8SAlexander V. Chernikov 		icfg->used--;
194868394ec8SAlexander V. Chernikov 		ti->data = icfg->used;
194968394ec8SAlexander V. Chernikov 		ife->linked = 0;
195068394ec8SAlexander V. Chernikov 	}
195168394ec8SAlexander V. Chernikov 
195268394ec8SAlexander V. Chernikov 	/* Unlink from local list */
195368394ec8SAlexander V. Chernikov 	ipfw_objhash_del(icfg->ii, &ife->no);
195468394ec8SAlexander V. Chernikov 	/* Unlink notifier */
195568394ec8SAlexander V. Chernikov 	ipfw_iface_del_notify(icfg->ch, &ife->ic);
195668394ec8SAlexander V. Chernikov 
195768394ec8SAlexander V. Chernikov 	icfg->count--;
1958648e8380SAlexander V. Chernikov 	tei->value = ife->value;
195968394ec8SAlexander V. Chernikov 
196068394ec8SAlexander V. Chernikov 	tb->ife = ife;
1961adea6201SAlexander V. Chernikov 	*pnum = 1;
196268394ec8SAlexander V. Chernikov 
19639f7d47b0SAlexander V. Chernikov 	return (0);
19649f7d47b0SAlexander V. Chernikov }
19659f7d47b0SAlexander V. Chernikov 
196668394ec8SAlexander V. Chernikov /*
196768394ec8SAlexander V. Chernikov  * Flush deleted entry.
196868394ec8SAlexander V. Chernikov  * Drops interface reference and frees entry.
196968394ec8SAlexander V. Chernikov  */
19709f7d47b0SAlexander V. Chernikov static void
197168394ec8SAlexander V. Chernikov ta_flush_ifidx_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
197268394ec8SAlexander V. Chernikov     void *ta_buf)
19739f7d47b0SAlexander V. Chernikov {
197468394ec8SAlexander V. Chernikov 	struct ta_buf_ifidx *tb;
19759f7d47b0SAlexander V. Chernikov 
197668394ec8SAlexander V. Chernikov 	tb = (struct ta_buf_ifidx *)ta_buf;
19779f7d47b0SAlexander V. Chernikov 
197868394ec8SAlexander V. Chernikov 	if (tb->ife != NULL) {
197968394ec8SAlexander V. Chernikov 		/* Unlink first */
198068394ec8SAlexander V. Chernikov 		ipfw_iface_unref(ch, &tb->ife->ic);
198168394ec8SAlexander V. Chernikov 		free(tb->ife, M_IPFW_TBL);
198268394ec8SAlexander V. Chernikov 	}
198368394ec8SAlexander V. Chernikov }
198468394ec8SAlexander V. Chernikov 
198568394ec8SAlexander V. Chernikov 
198668394ec8SAlexander V. Chernikov /*
198768394ec8SAlexander V. Chernikov  * Handle interface announce/withdrawal for particular table.
198868394ec8SAlexander V. Chernikov  * Every real runtime array modification happens here.
198968394ec8SAlexander V. Chernikov  */
199068394ec8SAlexander V. Chernikov static void
199168394ec8SAlexander V. Chernikov if_notifier(struct ip_fw_chain *ch, void *cbdata, uint16_t ifindex)
199268394ec8SAlexander V. Chernikov {
199368394ec8SAlexander V. Chernikov 	struct ifentry *ife;
199468394ec8SAlexander V. Chernikov 	struct ifidx ifi;
199568394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
199668394ec8SAlexander V. Chernikov 	struct table_info *ti;
199768394ec8SAlexander V. Chernikov 	int res;
199868394ec8SAlexander V. Chernikov 
199968394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)cbdata;
200068394ec8SAlexander V. Chernikov 	icfg = ife->icfg;
200168394ec8SAlexander V. Chernikov 	ti = icfg->ti;
200268394ec8SAlexander V. Chernikov 
200368394ec8SAlexander V. Chernikov 	KASSERT(ti != NULL, ("ti=NULL, check change_ti handler"));
200468394ec8SAlexander V. Chernikov 
200568394ec8SAlexander V. Chernikov 	if (ife->linked == 0 && ifindex != 0) {
200668394ec8SAlexander V. Chernikov 		/* Interface announce */
200768394ec8SAlexander V. Chernikov 		ifi.kidx = ifindex;
200868394ec8SAlexander V. Chernikov 		ifi.spare = 0;
200968394ec8SAlexander V. Chernikov 		ifi.value = ife->value;
201068394ec8SAlexander V. Chernikov 		res = badd(&ifindex, &ifi, icfg->main_ptr, icfg->used,
201168394ec8SAlexander V. Chernikov 		    sizeof(struct ifidx), compare_ifidx);
201268394ec8SAlexander V. Chernikov 		KASSERT(res == 1, ("index %d already exists", ifindex));
201368394ec8SAlexander V. Chernikov 		icfg->used++;
201468394ec8SAlexander V. Chernikov 		ti->data = icfg->used;
201568394ec8SAlexander V. Chernikov 		ife->linked = 1;
201668394ec8SAlexander V. Chernikov 	} else if (ife->linked != 0 && ifindex == 0) {
201768394ec8SAlexander V. Chernikov 		/* Interface withdrawal */
201868394ec8SAlexander V. Chernikov 		ifindex = ife->ic.iface->ifindex;
201968394ec8SAlexander V. Chernikov 
202068394ec8SAlexander V. Chernikov 		res = bdel(&ifindex, icfg->main_ptr, icfg->used,
202168394ec8SAlexander V. Chernikov 		    sizeof(struct ifidx), compare_ifidx);
202268394ec8SAlexander V. Chernikov 
202368394ec8SAlexander V. Chernikov 		KASSERT(res == 1, ("index %d does not exist", ifindex));
202468394ec8SAlexander V. Chernikov 		icfg->used--;
202568394ec8SAlexander V. Chernikov 		ti->data = icfg->used;
202668394ec8SAlexander V. Chernikov 		ife->linked = 0;
202768394ec8SAlexander V. Chernikov 	}
202868394ec8SAlexander V. Chernikov }
202968394ec8SAlexander V. Chernikov 
203068394ec8SAlexander V. Chernikov 
203168394ec8SAlexander V. Chernikov /*
203268394ec8SAlexander V. Chernikov  * Table growing callbacks.
203368394ec8SAlexander V. Chernikov  */
203468394ec8SAlexander V. Chernikov 
2035b6ee846eSAlexander V. Chernikov static int
2036b6ee846eSAlexander V. Chernikov ta_has_space_ifidx(void *ta_state, struct table_info *ti, uint32_t count,
2037b6ee846eSAlexander V. Chernikov     uint64_t *pflags)
2038b6ee846eSAlexander V. Chernikov {
2039b6ee846eSAlexander V. Chernikov 	struct iftable_cfg *cfg;
20400bce0c23SAlexander V. Chernikov 	uint32_t size;
2041b6ee846eSAlexander V. Chernikov 
2042b6ee846eSAlexander V. Chernikov 	cfg = (struct iftable_cfg *)ta_state;
2043b6ee846eSAlexander V. Chernikov 
20440bce0c23SAlexander V. Chernikov 	size = cfg->size;
20450bce0c23SAlexander V. Chernikov 	while (size < cfg->count + count)
20460bce0c23SAlexander V. Chernikov 		size *= 2;
20470bce0c23SAlexander V. Chernikov 
20480bce0c23SAlexander V. Chernikov 	if (size != cfg->size) {
20490bce0c23SAlexander V. Chernikov 		*pflags = size;
2050b6ee846eSAlexander V. Chernikov 		return (0);
2051b6ee846eSAlexander V. Chernikov 	}
2052b6ee846eSAlexander V. Chernikov 
2053b6ee846eSAlexander V. Chernikov 	return (1);
2054b6ee846eSAlexander V. Chernikov }
2055b6ee846eSAlexander V. Chernikov 
205668394ec8SAlexander V. Chernikov /*
205768394ec8SAlexander V. Chernikov  * Allocate ned, larger runtime ifidx array.
205868394ec8SAlexander V. Chernikov  */
205968394ec8SAlexander V. Chernikov static int
206068394ec8SAlexander V. Chernikov ta_prepare_mod_ifidx(void *ta_buf, uint64_t *pflags)
206168394ec8SAlexander V. Chernikov {
20620bce0c23SAlexander V. Chernikov 	struct mod_item *mi;
206368394ec8SAlexander V. Chernikov 
20640bce0c23SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
206568394ec8SAlexander V. Chernikov 
20660bce0c23SAlexander V. Chernikov 	memset(mi, 0, sizeof(struct mod_item));
206768394ec8SAlexander V. Chernikov 	mi->size = *pflags;
206868394ec8SAlexander V. Chernikov 	mi->main_ptr = malloc(sizeof(struct ifidx) * mi->size, M_IPFW,
206968394ec8SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
207068394ec8SAlexander V. Chernikov 
207168394ec8SAlexander V. Chernikov 	return (0);
207268394ec8SAlexander V. Chernikov }
207368394ec8SAlexander V. Chernikov 
207468394ec8SAlexander V. Chernikov /*
207568394ec8SAlexander V. Chernikov  * Copy data from old runtime array to new one.
207668394ec8SAlexander V. Chernikov  */
207768394ec8SAlexander V. Chernikov static int
207868394ec8SAlexander V. Chernikov ta_fill_mod_ifidx(void *ta_state, struct table_info *ti, void *ta_buf,
207968394ec8SAlexander V. Chernikov     uint64_t *pflags)
208068394ec8SAlexander V. Chernikov {
20810bce0c23SAlexander V. Chernikov 	struct mod_item *mi;
208268394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
208368394ec8SAlexander V. Chernikov 
20840bce0c23SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
208568394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
208668394ec8SAlexander V. Chernikov 
208768394ec8SAlexander V. Chernikov 	/* Check if we still need to grow array */
208868394ec8SAlexander V. Chernikov 	if (icfg->size >= mi->size) {
208968394ec8SAlexander V. Chernikov 		*pflags = 0;
209068394ec8SAlexander V. Chernikov 		return (0);
209168394ec8SAlexander V. Chernikov 	}
209268394ec8SAlexander V. Chernikov 
209368394ec8SAlexander V. Chernikov 	memcpy(mi->main_ptr, icfg->main_ptr, icfg->used * sizeof(struct ifidx));
209468394ec8SAlexander V. Chernikov 
209568394ec8SAlexander V. Chernikov 	return (0);
209668394ec8SAlexander V. Chernikov }
209768394ec8SAlexander V. Chernikov 
209868394ec8SAlexander V. Chernikov /*
209968394ec8SAlexander V. Chernikov  * Switch old & new arrays.
210068394ec8SAlexander V. Chernikov  */
210168394ec8SAlexander V. Chernikov static int
210268394ec8SAlexander V. Chernikov ta_modify_ifidx(void *ta_state, struct table_info *ti, void *ta_buf,
210368394ec8SAlexander V. Chernikov     uint64_t pflags)
210468394ec8SAlexander V. Chernikov {
21050bce0c23SAlexander V. Chernikov 	struct mod_item *mi;
210668394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
210768394ec8SAlexander V. Chernikov 	void *old_ptr;
210868394ec8SAlexander V. Chernikov 
21090bce0c23SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
211068394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
211168394ec8SAlexander V. Chernikov 
211268394ec8SAlexander V. Chernikov 	old_ptr = icfg->main_ptr;
211368394ec8SAlexander V. Chernikov 	icfg->main_ptr = mi->main_ptr;
211468394ec8SAlexander V. Chernikov 	icfg->size = mi->size;
211568394ec8SAlexander V. Chernikov 	ti->state = icfg->main_ptr;
211668394ec8SAlexander V. Chernikov 
211768394ec8SAlexander V. Chernikov 	mi->main_ptr = old_ptr;
211868394ec8SAlexander V. Chernikov 
211968394ec8SAlexander V. Chernikov 	return (0);
212068394ec8SAlexander V. Chernikov }
212168394ec8SAlexander V. Chernikov 
212268394ec8SAlexander V. Chernikov /*
212368394ec8SAlexander V. Chernikov  * Free unneded array.
212468394ec8SAlexander V. Chernikov  */
212568394ec8SAlexander V. Chernikov static void
212668394ec8SAlexander V. Chernikov ta_flush_mod_ifidx(void *ta_buf)
212768394ec8SAlexander V. Chernikov {
21280bce0c23SAlexander V. Chernikov 	struct mod_item *mi;
212968394ec8SAlexander V. Chernikov 
21300bce0c23SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
213168394ec8SAlexander V. Chernikov 	if (mi->main_ptr != NULL)
213268394ec8SAlexander V. Chernikov 		free(mi->main_ptr, M_IPFW);
21339f7d47b0SAlexander V. Chernikov }
21349f7d47b0SAlexander V. Chernikov 
21359f7d47b0SAlexander V. Chernikov static int
213668394ec8SAlexander V. Chernikov ta_dump_ifidx_tentry(void *ta_state, struct table_info *ti, void *e,
213781d3153dSAlexander V. Chernikov     ipfw_obj_tentry *tent)
21389f7d47b0SAlexander V. Chernikov {
213968394ec8SAlexander V. Chernikov 	struct ifentry *ife;
21409f7d47b0SAlexander V. Chernikov 
214168394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)e;
214268394ec8SAlexander V. Chernikov 
214381d3153dSAlexander V. Chernikov 	tent->masklen = 8 * IF_NAMESIZE;
214468394ec8SAlexander V. Chernikov 	memcpy(&tent->k, ife->no.name, IF_NAMESIZE);
214568394ec8SAlexander V. Chernikov 	tent->value = ife->value;
21469f7d47b0SAlexander V. Chernikov 
21479f7d47b0SAlexander V. Chernikov 	return (0);
21489f7d47b0SAlexander V. Chernikov }
21499f7d47b0SAlexander V. Chernikov 
215081d3153dSAlexander V. Chernikov static int
2151914bffb6SAlexander V. Chernikov ta_find_ifidx_tentry(void *ta_state, struct table_info *ti,
2152914bffb6SAlexander V. Chernikov     ipfw_obj_tentry *tent)
215381d3153dSAlexander V. Chernikov {
215468394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
215568394ec8SAlexander V. Chernikov 	struct ifentry *ife;
215668394ec8SAlexander V. Chernikov 	char *ifname;
215781d3153dSAlexander V. Chernikov 
215868394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
2159914bffb6SAlexander V. Chernikov 	ifname = tent->k.iface;
216081d3153dSAlexander V. Chernikov 
216168394ec8SAlexander V. Chernikov 	if (strnlen(ifname, IF_NAMESIZE) == IF_NAMESIZE)
216268394ec8SAlexander V. Chernikov 		return (EINVAL);
216381d3153dSAlexander V. Chernikov 
216468394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)ipfw_objhash_lookup_name(icfg->ii, 0, ifname);
216568394ec8SAlexander V. Chernikov 
216668394ec8SAlexander V. Chernikov 	if (ife != NULL) {
216768394ec8SAlexander V. Chernikov 		ta_dump_ifidx_tentry(ta_state, ti, ife, tent);
216881d3153dSAlexander V. Chernikov 		return (0);
216981d3153dSAlexander V. Chernikov 	}
217081d3153dSAlexander V. Chernikov 
217181d3153dSAlexander V. Chernikov 	return (ENOENT);
217281d3153dSAlexander V. Chernikov }
217381d3153dSAlexander V. Chernikov 
217468394ec8SAlexander V. Chernikov struct wa_ifidx {
217568394ec8SAlexander V. Chernikov 	ta_foreach_f	*f;
217668394ec8SAlexander V. Chernikov 	void		*arg;
217768394ec8SAlexander V. Chernikov };
217868394ec8SAlexander V. Chernikov 
21799f7d47b0SAlexander V. Chernikov static void
218068394ec8SAlexander V. Chernikov foreach_ifidx(struct namedobj_instance *ii, struct named_object *no,
21819f7d47b0SAlexander V. Chernikov     void *arg)
21829f7d47b0SAlexander V. Chernikov {
218368394ec8SAlexander V. Chernikov 	struct ifentry *ife;
218468394ec8SAlexander V. Chernikov 	struct wa_ifidx *wa;
21859f7d47b0SAlexander V. Chernikov 
218668394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)no;
218768394ec8SAlexander V. Chernikov 	wa = (struct wa_ifidx *)arg;
218868394ec8SAlexander V. Chernikov 
218968394ec8SAlexander V. Chernikov 	wa->f(ife, wa->arg);
21909f7d47b0SAlexander V. Chernikov }
21919f7d47b0SAlexander V. Chernikov 
219268394ec8SAlexander V. Chernikov static void
219368394ec8SAlexander V. Chernikov ta_foreach_ifidx(void *ta_state, struct table_info *ti, ta_foreach_f *f,
219468394ec8SAlexander V. Chernikov     void *arg)
219568394ec8SAlexander V. Chernikov {
219668394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
219768394ec8SAlexander V. Chernikov 	struct wa_ifidx wa;
219868394ec8SAlexander V. Chernikov 
219968394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
220068394ec8SAlexander V. Chernikov 
220168394ec8SAlexander V. Chernikov 	wa.f = f;
220268394ec8SAlexander V. Chernikov 	wa.arg = arg;
220368394ec8SAlexander V. Chernikov 
220468394ec8SAlexander V. Chernikov 	ipfw_objhash_foreach(icfg->ii, foreach_ifidx, &wa);
220568394ec8SAlexander V. Chernikov }
220668394ec8SAlexander V. Chernikov 
220774b941f0SAlexander V. Chernikov struct table_algo iface_idx = {
2208adea6201SAlexander V. Chernikov 	.name		= "iface:array",
22099d099b4fSAlexander V. Chernikov 	.type		= IPFW_TABLE_INTERFACE,
221057a1cf95SAlexander V. Chernikov 	.flags		= TA_FLAG_DEFAULT,
221157a1cf95SAlexander V. Chernikov 	.ta_buf_size	= sizeof(struct ta_buf_ifidx),
221268394ec8SAlexander V. Chernikov 	.init		= ta_init_ifidx,
221368394ec8SAlexander V. Chernikov 	.destroy	= ta_destroy_ifidx,
221468394ec8SAlexander V. Chernikov 	.prepare_add	= ta_prepare_add_ifidx,
221568394ec8SAlexander V. Chernikov 	.prepare_del	= ta_prepare_del_ifidx,
221668394ec8SAlexander V. Chernikov 	.add		= ta_add_ifidx,
221768394ec8SAlexander V. Chernikov 	.del		= ta_del_ifidx,
221868394ec8SAlexander V. Chernikov 	.flush_entry	= ta_flush_ifidx_entry,
221968394ec8SAlexander V. Chernikov 	.foreach	= ta_foreach_ifidx,
222068394ec8SAlexander V. Chernikov 	.dump_tentry	= ta_dump_ifidx_tentry,
222168394ec8SAlexander V. Chernikov 	.find_tentry	= ta_find_ifidx_tentry,
22225f379342SAlexander V. Chernikov 	.dump_tinfo	= ta_dump_ifidx_tinfo,
2223b6ee846eSAlexander V. Chernikov 	.has_space	= ta_has_space_ifidx,
222468394ec8SAlexander V. Chernikov 	.prepare_mod	= ta_prepare_mod_ifidx,
222568394ec8SAlexander V. Chernikov 	.fill_mod	= ta_fill_mod_ifidx,
222668394ec8SAlexander V. Chernikov 	.modify		= ta_modify_ifidx,
222768394ec8SAlexander V. Chernikov 	.flush_mod	= ta_flush_mod_ifidx,
222868394ec8SAlexander V. Chernikov 	.change_ti	= ta_change_ti_ifidx,
22299f7d47b0SAlexander V. Chernikov };
22309f7d47b0SAlexander V. Chernikov 
2231b23d5de9SAlexander V. Chernikov /*
2232b23d5de9SAlexander V. Chernikov  * Number array cmds.
2233b23d5de9SAlexander V. Chernikov  *
2234b23d5de9SAlexander V. Chernikov  * Implementation:
2235b23d5de9SAlexander V. Chernikov  *
2236b23d5de9SAlexander V. Chernikov  * Runtime part:
2237b23d5de9SAlexander V. Chernikov  * - sorted array of "struct numarray" pointed by ti->state.
2238b23d5de9SAlexander V. Chernikov  *   Array is allocated with rounding up to NUMARRAY_CHUNK.
2239b23d5de9SAlexander V. Chernikov  * - current array size is stored in ti->data
2240b23d5de9SAlexander V. Chernikov  *
2241b23d5de9SAlexander V. Chernikov  */
2242b23d5de9SAlexander V. Chernikov 
2243b23d5de9SAlexander V. Chernikov struct numarray {
2244b23d5de9SAlexander V. Chernikov 	uint32_t	number;
2245b23d5de9SAlexander V. Chernikov 	uint32_t	value;
2246b23d5de9SAlexander V. Chernikov };
2247b23d5de9SAlexander V. Chernikov 
2248b23d5de9SAlexander V. Chernikov struct numarray_cfg {
2249b23d5de9SAlexander V. Chernikov 	void	*main_ptr;
2250b23d5de9SAlexander V. Chernikov 	size_t	size;	/* Number of items allocated in array */
2251b23d5de9SAlexander V. Chernikov 	size_t	used;	/* Number of items _active_ now */
2252b23d5de9SAlexander V. Chernikov };
2253b23d5de9SAlexander V. Chernikov 
22540bce0c23SAlexander V. Chernikov struct ta_buf_numarray
22550bce0c23SAlexander V. Chernikov {
22560bce0c23SAlexander V. Chernikov 	struct numarray na;
22570bce0c23SAlexander V. Chernikov };
2258b23d5de9SAlexander V. Chernikov 
2259b23d5de9SAlexander V. Chernikov int compare_numarray(const void *k, const void *v);
2260b23d5de9SAlexander V. Chernikov 
2261b23d5de9SAlexander V. Chernikov int
2262b23d5de9SAlexander V. Chernikov compare_numarray(const void *k, const void *v)
2263b23d5de9SAlexander V. Chernikov {
2264b23d5de9SAlexander V. Chernikov 	struct numarray *na;
2265b23d5de9SAlexander V. Chernikov 	uint32_t key;
2266b23d5de9SAlexander V. Chernikov 
2267b23d5de9SAlexander V. Chernikov 	key = *((uint32_t *)k);
2268b23d5de9SAlexander V. Chernikov 	na = (struct numarray *)v;
2269b23d5de9SAlexander V. Chernikov 
2270b23d5de9SAlexander V. Chernikov 	if (key < na->number)
2271b23d5de9SAlexander V. Chernikov 		return (-1);
2272b23d5de9SAlexander V. Chernikov 	else if (key > na->number)
2273b23d5de9SAlexander V. Chernikov 		return (1);
2274b23d5de9SAlexander V. Chernikov 
2275b23d5de9SAlexander V. Chernikov 	return (0);
2276b23d5de9SAlexander V. Chernikov }
2277b23d5de9SAlexander V. Chernikov 
2278b23d5de9SAlexander V. Chernikov static struct numarray *
2279b23d5de9SAlexander V. Chernikov numarray_find(struct table_info *ti, void *key)
2280b23d5de9SAlexander V. Chernikov {
2281b23d5de9SAlexander V. Chernikov 	struct numarray *ri;
2282b23d5de9SAlexander V. Chernikov 
2283b23d5de9SAlexander V. Chernikov 	ri = bsearch(key, ti->state, ti->data, sizeof(struct numarray),
2284b23d5de9SAlexander V. Chernikov 	    compare_ifidx);
2285b23d5de9SAlexander V. Chernikov 
2286b23d5de9SAlexander V. Chernikov 	return (ri);
2287b23d5de9SAlexander V. Chernikov }
2288b23d5de9SAlexander V. Chernikov 
2289b23d5de9SAlexander V. Chernikov static int
2290b23d5de9SAlexander V. Chernikov ta_lookup_numarray(struct table_info *ti, void *key, uint32_t keylen,
2291b23d5de9SAlexander V. Chernikov     uint32_t *val)
2292b23d5de9SAlexander V. Chernikov {
2293b23d5de9SAlexander V. Chernikov 	struct numarray *ri;
2294b23d5de9SAlexander V. Chernikov 
2295b23d5de9SAlexander V. Chernikov 	ri = numarray_find(ti, key);
2296b23d5de9SAlexander V. Chernikov 
2297b23d5de9SAlexander V. Chernikov 	if (ri != NULL) {
2298b23d5de9SAlexander V. Chernikov 		*val = ri->value;
2299b23d5de9SAlexander V. Chernikov 		return (1);
2300b23d5de9SAlexander V. Chernikov 	}
2301b23d5de9SAlexander V. Chernikov 
2302b23d5de9SAlexander V. Chernikov 	return (0);
2303b23d5de9SAlexander V. Chernikov }
2304b23d5de9SAlexander V. Chernikov 
2305b23d5de9SAlexander V. Chernikov static int
2306b23d5de9SAlexander V. Chernikov ta_init_numarray(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
2307914bffb6SAlexander V. Chernikov     char *data, uint8_t tflags)
2308b23d5de9SAlexander V. Chernikov {
2309b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2310b23d5de9SAlexander V. Chernikov 
2311b23d5de9SAlexander V. Chernikov 	cfg = malloc(sizeof(*cfg), M_IPFW, M_WAITOK | M_ZERO);
2312b23d5de9SAlexander V. Chernikov 
23130bce0c23SAlexander V. Chernikov 	cfg->size = 16;
2314b23d5de9SAlexander V. Chernikov 	cfg->main_ptr = malloc(sizeof(struct numarray) * cfg->size, M_IPFW,
2315b23d5de9SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
2316b23d5de9SAlexander V. Chernikov 
2317b23d5de9SAlexander V. Chernikov 	*ta_state = cfg;
2318b23d5de9SAlexander V. Chernikov 	ti->state = cfg->main_ptr;
2319b23d5de9SAlexander V. Chernikov 	ti->lookup = ta_lookup_numarray;
2320b23d5de9SAlexander V. Chernikov 
2321b23d5de9SAlexander V. Chernikov 	return (0);
2322b23d5de9SAlexander V. Chernikov }
2323b23d5de9SAlexander V. Chernikov 
2324b23d5de9SAlexander V. Chernikov /*
2325b23d5de9SAlexander V. Chernikov  * Destroys table @ti
2326b23d5de9SAlexander V. Chernikov  */
2327b23d5de9SAlexander V. Chernikov static void
2328b23d5de9SAlexander V. Chernikov ta_destroy_numarray(void *ta_state, struct table_info *ti)
2329b23d5de9SAlexander V. Chernikov {
2330b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2331b23d5de9SAlexander V. Chernikov 
2332b23d5de9SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2333b23d5de9SAlexander V. Chernikov 
2334b23d5de9SAlexander V. Chernikov 	if (cfg->main_ptr != NULL)
2335b23d5de9SAlexander V. Chernikov 		free(cfg->main_ptr, M_IPFW);
2336b23d5de9SAlexander V. Chernikov 
2337b23d5de9SAlexander V. Chernikov 	free(cfg, M_IPFW);
2338b23d5de9SAlexander V. Chernikov }
2339b23d5de9SAlexander V. Chernikov 
2340b23d5de9SAlexander V. Chernikov /*
23415f379342SAlexander V. Chernikov  * Provide algo-specific table info
23425f379342SAlexander V. Chernikov  */
23435f379342SAlexander V. Chernikov static void
23445f379342SAlexander V. Chernikov ta_dump_numarray_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo)
23455f379342SAlexander V. Chernikov {
23465f379342SAlexander V. Chernikov 	struct numarray_cfg *cfg;
23475f379342SAlexander V. Chernikov 
23485f379342SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
23495f379342SAlexander V. Chernikov 
23505f379342SAlexander V. Chernikov 	tinfo->taclass4 = IPFW_TACLASS_ARRAY;
23515f379342SAlexander V. Chernikov 	tinfo->size4 = cfg->size;
23525f379342SAlexander V. Chernikov 	tinfo->count4 = cfg->used;
23535f379342SAlexander V. Chernikov 	tinfo->itemsize4 = sizeof(struct numarray);
23545f379342SAlexander V. Chernikov }
23555f379342SAlexander V. Chernikov 
23565f379342SAlexander V. Chernikov /*
2357b23d5de9SAlexander V. Chernikov  * Prepare for addition/deletion to an array.
2358b23d5de9SAlexander V. Chernikov  */
2359b23d5de9SAlexander V. Chernikov static int
2360b23d5de9SAlexander V. Chernikov ta_prepare_add_numarray(struct ip_fw_chain *ch, struct tentry_info *tei,
2361b23d5de9SAlexander V. Chernikov     void *ta_buf)
2362b23d5de9SAlexander V. Chernikov {
2363b23d5de9SAlexander V. Chernikov 	struct ta_buf_numarray *tb;
2364b23d5de9SAlexander V. Chernikov 
2365b23d5de9SAlexander V. Chernikov 	tb = (struct ta_buf_numarray *)ta_buf;
2366b23d5de9SAlexander V. Chernikov 
2367b23d5de9SAlexander V. Chernikov 	tb->na.number = *((uint32_t *)tei->paddr);
2368b23d5de9SAlexander V. Chernikov 	tb->na.value = tei->value;
2369b23d5de9SAlexander V. Chernikov 
2370b23d5de9SAlexander V. Chernikov 	return (0);
2371b23d5de9SAlexander V. Chernikov }
2372b23d5de9SAlexander V. Chernikov 
2373b23d5de9SAlexander V. Chernikov static int
2374b23d5de9SAlexander V. Chernikov ta_add_numarray(void *ta_state, struct table_info *ti, struct tentry_info *tei,
2375b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
2376b23d5de9SAlexander V. Chernikov {
2377b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2378b23d5de9SAlexander V. Chernikov 	struct ta_buf_numarray *tb;
2379b23d5de9SAlexander V. Chernikov 	struct numarray *ri;
2380b23d5de9SAlexander V. Chernikov 	int res;
2381648e8380SAlexander V. Chernikov 	uint32_t value;
2382b23d5de9SAlexander V. Chernikov 
2383b23d5de9SAlexander V. Chernikov 	tb = (struct ta_buf_numarray *)ta_buf;
2384b23d5de9SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2385b23d5de9SAlexander V. Chernikov 
2386b23d5de9SAlexander V. Chernikov 	ri = numarray_find(ti, &tb->na.number);
2387b23d5de9SAlexander V. Chernikov 
2388b23d5de9SAlexander V. Chernikov 	if (ri != NULL) {
2389b23d5de9SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
2390b23d5de9SAlexander V. Chernikov 			return (EEXIST);
2391b23d5de9SAlexander V. Chernikov 
2392648e8380SAlexander V. Chernikov 		/* Exchange values between ri and @tei */
2393648e8380SAlexander V. Chernikov 		value = ri->value;
2394648e8380SAlexander V. Chernikov 		ri->value = tei->value;
2395648e8380SAlexander V. Chernikov 		tei->value = value;
2396b23d5de9SAlexander V. Chernikov 		/* Indicate that update has happened instead of addition */
2397b23d5de9SAlexander V. Chernikov 		tei->flags |= TEI_FLAGS_UPDATED;
2398b23d5de9SAlexander V. Chernikov 		*pnum = 0;
2399b23d5de9SAlexander V. Chernikov 		return (0);
2400b23d5de9SAlexander V. Chernikov 	}
2401b23d5de9SAlexander V. Chernikov 
24024c0c07a5SAlexander V. Chernikov 	if ((tei->flags & TEI_FLAGS_DONTADD) != 0)
24034c0c07a5SAlexander V. Chernikov 		return (EFBIG);
24044c0c07a5SAlexander V. Chernikov 
2405b23d5de9SAlexander V. Chernikov 	res = badd(&tb->na.number, &tb->na, cfg->main_ptr, cfg->used,
2406b23d5de9SAlexander V. Chernikov 	    sizeof(struct numarray), compare_numarray);
2407b23d5de9SAlexander V. Chernikov 
2408b23d5de9SAlexander V. Chernikov 	KASSERT(res == 1, ("number %d already exists", tb->na.number));
2409b23d5de9SAlexander V. Chernikov 	cfg->used++;
2410b23d5de9SAlexander V. Chernikov 	ti->data = cfg->used;
2411b23d5de9SAlexander V. Chernikov 	*pnum = 1;
2412b23d5de9SAlexander V. Chernikov 
2413b23d5de9SAlexander V. Chernikov 	return (0);
2414b23d5de9SAlexander V. Chernikov }
2415b23d5de9SAlexander V. Chernikov 
2416b23d5de9SAlexander V. Chernikov /*
2417b23d5de9SAlexander V. Chernikov  * Remove key from both configuration list and
2418b23d5de9SAlexander V. Chernikov  * runtime array. Removed interface notification.
2419b23d5de9SAlexander V. Chernikov  */
2420b23d5de9SAlexander V. Chernikov static int
2421b23d5de9SAlexander V. Chernikov ta_del_numarray(void *ta_state, struct table_info *ti, struct tentry_info *tei,
2422b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
2423b23d5de9SAlexander V. Chernikov {
2424b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2425b23d5de9SAlexander V. Chernikov 	struct ta_buf_numarray *tb;
2426b23d5de9SAlexander V. Chernikov 	struct numarray *ri;
2427b23d5de9SAlexander V. Chernikov 	int res;
2428b23d5de9SAlexander V. Chernikov 
2429b23d5de9SAlexander V. Chernikov 	tb = (struct ta_buf_numarray *)ta_buf;
2430b23d5de9SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2431b23d5de9SAlexander V. Chernikov 
2432b23d5de9SAlexander V. Chernikov 	ri = numarray_find(ti, &tb->na.number);
2433b23d5de9SAlexander V. Chernikov 	if (ri == NULL)
2434b23d5de9SAlexander V. Chernikov 		return (ENOENT);
2435b23d5de9SAlexander V. Chernikov 
2436648e8380SAlexander V. Chernikov 	tei->value = ri->value;
2437648e8380SAlexander V. Chernikov 
2438b23d5de9SAlexander V. Chernikov 	res = bdel(&tb->na.number, cfg->main_ptr, cfg->used,
2439b23d5de9SAlexander V. Chernikov 	    sizeof(struct numarray), compare_numarray);
2440b23d5de9SAlexander V. Chernikov 
2441b23d5de9SAlexander V. Chernikov 	KASSERT(res == 1, ("number %u does not exist", tb->na.number));
2442b23d5de9SAlexander V. Chernikov 	cfg->used--;
2443b23d5de9SAlexander V. Chernikov 	ti->data = cfg->used;
2444b23d5de9SAlexander V. Chernikov 	*pnum = 1;
2445b23d5de9SAlexander V. Chernikov 
2446b23d5de9SAlexander V. Chernikov 	return (0);
2447b23d5de9SAlexander V. Chernikov }
2448b23d5de9SAlexander V. Chernikov 
2449b23d5de9SAlexander V. Chernikov static void
2450b23d5de9SAlexander V. Chernikov ta_flush_numarray_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
2451b23d5de9SAlexander V. Chernikov     void *ta_buf)
2452b23d5de9SAlexander V. Chernikov {
2453b23d5de9SAlexander V. Chernikov 
24540bce0c23SAlexander V. Chernikov 	/* We don't have any state, do nothing */
2455b23d5de9SAlexander V. Chernikov }
2456b23d5de9SAlexander V. Chernikov 
2457b23d5de9SAlexander V. Chernikov 
2458b23d5de9SAlexander V. Chernikov /*
2459b23d5de9SAlexander V. Chernikov  * Table growing callbacks.
2460b23d5de9SAlexander V. Chernikov  */
2461b23d5de9SAlexander V. Chernikov 
2462b6ee846eSAlexander V. Chernikov static int
2463b6ee846eSAlexander V. Chernikov ta_has_space_numarray(void *ta_state, struct table_info *ti, uint32_t count,
2464b6ee846eSAlexander V. Chernikov     uint64_t *pflags)
2465b6ee846eSAlexander V. Chernikov {
2466b6ee846eSAlexander V. Chernikov 	struct numarray_cfg *cfg;
24670bce0c23SAlexander V. Chernikov 	size_t size;
2468b6ee846eSAlexander V. Chernikov 
2469b6ee846eSAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2470b6ee846eSAlexander V. Chernikov 
24710bce0c23SAlexander V. Chernikov 	size = cfg->size;
24720bce0c23SAlexander V. Chernikov 	while (size < cfg->used + count)
24730bce0c23SAlexander V. Chernikov 		size *= 2;
24740bce0c23SAlexander V. Chernikov 
24750bce0c23SAlexander V. Chernikov 	if (size != cfg->size) {
24760bce0c23SAlexander V. Chernikov 		*pflags = size;
2477b6ee846eSAlexander V. Chernikov 		return (0);
2478b6ee846eSAlexander V. Chernikov 	}
2479b6ee846eSAlexander V. Chernikov 
2480b6ee846eSAlexander V. Chernikov 	return (1);
2481b6ee846eSAlexander V. Chernikov }
2482b6ee846eSAlexander V. Chernikov 
2483b23d5de9SAlexander V. Chernikov /*
2484b6ee846eSAlexander V. Chernikov  * Allocate new, larger runtime array.
2485b23d5de9SAlexander V. Chernikov  */
2486b23d5de9SAlexander V. Chernikov static int
2487b23d5de9SAlexander V. Chernikov ta_prepare_mod_numarray(void *ta_buf, uint64_t *pflags)
2488b23d5de9SAlexander V. Chernikov {
2489b23d5de9SAlexander V. Chernikov 	struct mod_item *mi;
2490b23d5de9SAlexander V. Chernikov 
2491b23d5de9SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
2492b23d5de9SAlexander V. Chernikov 
2493b23d5de9SAlexander V. Chernikov 	memset(mi, 0, sizeof(struct mod_item));
2494b23d5de9SAlexander V. Chernikov 	mi->size = *pflags;
2495b23d5de9SAlexander V. Chernikov 	mi->main_ptr = malloc(sizeof(struct numarray) * mi->size, M_IPFW,
2496b23d5de9SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
2497b23d5de9SAlexander V. Chernikov 
2498b23d5de9SAlexander V. Chernikov 	return (0);
2499b23d5de9SAlexander V. Chernikov }
2500b23d5de9SAlexander V. Chernikov 
2501b23d5de9SAlexander V. Chernikov /*
2502b23d5de9SAlexander V. Chernikov  * Copy data from old runtime array to new one.
2503b23d5de9SAlexander V. Chernikov  */
2504b23d5de9SAlexander V. Chernikov static int
2505b23d5de9SAlexander V. Chernikov ta_fill_mod_numarray(void *ta_state, struct table_info *ti, void *ta_buf,
2506b23d5de9SAlexander V. Chernikov     uint64_t *pflags)
2507b23d5de9SAlexander V. Chernikov {
2508b23d5de9SAlexander V. Chernikov 	struct mod_item *mi;
2509b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2510b23d5de9SAlexander V. Chernikov 
2511b23d5de9SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
2512b23d5de9SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2513b23d5de9SAlexander V. Chernikov 
2514b23d5de9SAlexander V. Chernikov 	/* Check if we still need to grow array */
2515b23d5de9SAlexander V. Chernikov 	if (cfg->size >= mi->size) {
2516b23d5de9SAlexander V. Chernikov 		*pflags = 0;
2517b23d5de9SAlexander V. Chernikov 		return (0);
2518b23d5de9SAlexander V. Chernikov 	}
2519b23d5de9SAlexander V. Chernikov 
2520b23d5de9SAlexander V. Chernikov 	memcpy(mi->main_ptr, cfg->main_ptr, cfg->used * sizeof(struct numarray));
2521b23d5de9SAlexander V. Chernikov 
2522b23d5de9SAlexander V. Chernikov 	return (0);
2523b23d5de9SAlexander V. Chernikov }
2524b23d5de9SAlexander V. Chernikov 
2525b23d5de9SAlexander V. Chernikov /*
2526b23d5de9SAlexander V. Chernikov  * Switch old & new arrays.
2527b23d5de9SAlexander V. Chernikov  */
2528b23d5de9SAlexander V. Chernikov static int
2529b23d5de9SAlexander V. Chernikov ta_modify_numarray(void *ta_state, struct table_info *ti, void *ta_buf,
2530b23d5de9SAlexander V. Chernikov     uint64_t pflags)
2531b23d5de9SAlexander V. Chernikov {
2532b23d5de9SAlexander V. Chernikov 	struct mod_item *mi;
2533b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2534b23d5de9SAlexander V. Chernikov 	void *old_ptr;
2535b23d5de9SAlexander V. Chernikov 
2536b23d5de9SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
2537b23d5de9SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2538b23d5de9SAlexander V. Chernikov 
2539b23d5de9SAlexander V. Chernikov 	old_ptr = cfg->main_ptr;
2540b23d5de9SAlexander V. Chernikov 	cfg->main_ptr = mi->main_ptr;
2541b23d5de9SAlexander V. Chernikov 	cfg->size = mi->size;
2542b23d5de9SAlexander V. Chernikov 	ti->state = cfg->main_ptr;
2543b23d5de9SAlexander V. Chernikov 
2544b23d5de9SAlexander V. Chernikov 	mi->main_ptr = old_ptr;
2545b23d5de9SAlexander V. Chernikov 
2546b23d5de9SAlexander V. Chernikov 	return (0);
2547b23d5de9SAlexander V. Chernikov }
2548b23d5de9SAlexander V. Chernikov 
2549b23d5de9SAlexander V. Chernikov /*
2550b23d5de9SAlexander V. Chernikov  * Free unneded array.
2551b23d5de9SAlexander V. Chernikov  */
2552b23d5de9SAlexander V. Chernikov static void
2553b23d5de9SAlexander V. Chernikov ta_flush_mod_numarray(void *ta_buf)
2554b23d5de9SAlexander V. Chernikov {
2555b23d5de9SAlexander V. Chernikov 	struct mod_item *mi;
2556b23d5de9SAlexander V. Chernikov 
2557b23d5de9SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
2558b23d5de9SAlexander V. Chernikov 	if (mi->main_ptr != NULL)
2559b23d5de9SAlexander V. Chernikov 		free(mi->main_ptr, M_IPFW);
2560b23d5de9SAlexander V. Chernikov }
2561b23d5de9SAlexander V. Chernikov 
2562b23d5de9SAlexander V. Chernikov static int
2563b23d5de9SAlexander V. Chernikov ta_dump_numarray_tentry(void *ta_state, struct table_info *ti, void *e,
2564b23d5de9SAlexander V. Chernikov     ipfw_obj_tentry *tent)
2565b23d5de9SAlexander V. Chernikov {
2566b23d5de9SAlexander V. Chernikov 	struct numarray *na;
2567b23d5de9SAlexander V. Chernikov 
2568b23d5de9SAlexander V. Chernikov 	na = (struct numarray *)e;
2569b23d5de9SAlexander V. Chernikov 
2570b23d5de9SAlexander V. Chernikov 	tent->k.key = na->number;
2571b23d5de9SAlexander V. Chernikov 	tent->value = na->value;
2572b23d5de9SAlexander V. Chernikov 
2573b23d5de9SAlexander V. Chernikov 	return (0);
2574b23d5de9SAlexander V. Chernikov }
2575b23d5de9SAlexander V. Chernikov 
2576b23d5de9SAlexander V. Chernikov static int
2577914bffb6SAlexander V. Chernikov ta_find_numarray_tentry(void *ta_state, struct table_info *ti,
2578914bffb6SAlexander V. Chernikov     ipfw_obj_tentry *tent)
2579b23d5de9SAlexander V. Chernikov {
2580b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2581b23d5de9SAlexander V. Chernikov 	struct numarray *ri;
2582b23d5de9SAlexander V. Chernikov 
2583b23d5de9SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2584b23d5de9SAlexander V. Chernikov 
2585914bffb6SAlexander V. Chernikov 	ri = numarray_find(ti, &tent->k.key);
2586b23d5de9SAlexander V. Chernikov 
2587b23d5de9SAlexander V. Chernikov 	if (ri != NULL) {
2588b23d5de9SAlexander V. Chernikov 		ta_dump_numarray_tentry(ta_state, ti, ri, tent);
2589b23d5de9SAlexander V. Chernikov 		return (0);
2590b23d5de9SAlexander V. Chernikov 	}
2591b23d5de9SAlexander V. Chernikov 
2592b23d5de9SAlexander V. Chernikov 	return (ENOENT);
2593b23d5de9SAlexander V. Chernikov }
2594b23d5de9SAlexander V. Chernikov 
2595b23d5de9SAlexander V. Chernikov static void
2596b23d5de9SAlexander V. Chernikov ta_foreach_numarray(void *ta_state, struct table_info *ti, ta_foreach_f *f,
2597b23d5de9SAlexander V. Chernikov     void *arg)
2598b23d5de9SAlexander V. Chernikov {
2599b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2600b23d5de9SAlexander V. Chernikov 	struct numarray *array;
2601b23d5de9SAlexander V. Chernikov 	int i;
2602b23d5de9SAlexander V. Chernikov 
2603b23d5de9SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2604b23d5de9SAlexander V. Chernikov 	array = cfg->main_ptr;
2605b23d5de9SAlexander V. Chernikov 
2606b23d5de9SAlexander V. Chernikov 	for (i = 0; i < cfg->used; i++)
2607b23d5de9SAlexander V. Chernikov 		f(&array[i], arg);
2608b23d5de9SAlexander V. Chernikov }
2609b23d5de9SAlexander V. Chernikov 
2610b23d5de9SAlexander V. Chernikov struct table_algo number_array = {
2611b23d5de9SAlexander V. Chernikov 	.name		= "number:array",
2612b23d5de9SAlexander V. Chernikov 	.type		= IPFW_TABLE_NUMBER,
261357a1cf95SAlexander V. Chernikov 	.ta_buf_size	= sizeof(struct ta_buf_numarray),
2614b23d5de9SAlexander V. Chernikov 	.init		= ta_init_numarray,
2615b23d5de9SAlexander V. Chernikov 	.destroy	= ta_destroy_numarray,
2616b23d5de9SAlexander V. Chernikov 	.prepare_add	= ta_prepare_add_numarray,
2617b23d5de9SAlexander V. Chernikov 	.prepare_del	= ta_prepare_add_numarray,
2618b23d5de9SAlexander V. Chernikov 	.add		= ta_add_numarray,
2619b23d5de9SAlexander V. Chernikov 	.del		= ta_del_numarray,
2620b23d5de9SAlexander V. Chernikov 	.flush_entry	= ta_flush_numarray_entry,
2621b23d5de9SAlexander V. Chernikov 	.foreach	= ta_foreach_numarray,
2622b23d5de9SAlexander V. Chernikov 	.dump_tentry	= ta_dump_numarray_tentry,
2623b23d5de9SAlexander V. Chernikov 	.find_tentry	= ta_find_numarray_tentry,
26245f379342SAlexander V. Chernikov 	.dump_tinfo	= ta_dump_numarray_tinfo,
2625b6ee846eSAlexander V. Chernikov 	.has_space	= ta_has_space_numarray,
2626b23d5de9SAlexander V. Chernikov 	.prepare_mod	= ta_prepare_mod_numarray,
2627b23d5de9SAlexander V. Chernikov 	.fill_mod	= ta_fill_mod_numarray,
2628b23d5de9SAlexander V. Chernikov 	.modify		= ta_modify_numarray,
2629b23d5de9SAlexander V. Chernikov 	.flush_mod	= ta_flush_mod_numarray,
2630b23d5de9SAlexander V. Chernikov };
2631b23d5de9SAlexander V. Chernikov 
2632914bffb6SAlexander V. Chernikov /*
2633914bffb6SAlexander V. Chernikov  * flow:hash cmds
2634914bffb6SAlexander V. Chernikov  *
2635914bffb6SAlexander V. Chernikov  *
2636914bffb6SAlexander V. Chernikov  * ti->data:
2637914bffb6SAlexander V. Chernikov  * [inv.mask4][inv.mask6][log2hsize4][log2hsize6]
2638914bffb6SAlexander V. Chernikov  * [        8][        8[          8][         8]
2639914bffb6SAlexander V. Chernikov  *
2640914bffb6SAlexander V. Chernikov  * inv.mask4: 32 - mask
2641914bffb6SAlexander V. Chernikov  * inv.mask6:
2642914bffb6SAlexander V. Chernikov  * 1) _slow lookup: mask
2643914bffb6SAlexander V. Chernikov  * 2) _aligned: (128 - mask) / 8
2644914bffb6SAlexander V. Chernikov  * 3) _64: 8
2645914bffb6SAlexander V. Chernikov  *
2646914bffb6SAlexander V. Chernikov  *
2647914bffb6SAlexander V. Chernikov  * pflags:
2648b6ee846eSAlexander V. Chernikov  * [hsize4][hsize6]
2649b6ee846eSAlexander V. Chernikov  * [    16][    16]
2650914bffb6SAlexander V. Chernikov  */
2651914bffb6SAlexander V. Chernikov 
2652914bffb6SAlexander V. Chernikov struct fhashentry;
2653914bffb6SAlexander V. Chernikov 
2654914bffb6SAlexander V. Chernikov SLIST_HEAD(fhashbhead, fhashentry);
2655914bffb6SAlexander V. Chernikov 
2656914bffb6SAlexander V. Chernikov struct fhashentry {
2657914bffb6SAlexander V. Chernikov 	SLIST_ENTRY(fhashentry)	next;
2658914bffb6SAlexander V. Chernikov 	uint8_t		af;
2659914bffb6SAlexander V. Chernikov 	uint8_t		proto;
2660914bffb6SAlexander V. Chernikov 	uint16_t	spare0;
2661914bffb6SAlexander V. Chernikov 	uint16_t	dport;
2662914bffb6SAlexander V. Chernikov 	uint16_t	sport;
2663914bffb6SAlexander V. Chernikov 	uint32_t	value;
2664914bffb6SAlexander V. Chernikov 	uint32_t	spare1;
2665914bffb6SAlexander V. Chernikov };
2666914bffb6SAlexander V. Chernikov 
2667914bffb6SAlexander V. Chernikov struct fhashentry4 {
2668914bffb6SAlexander V. Chernikov 	struct fhashentry	e;
2669914bffb6SAlexander V. Chernikov 	struct in_addr		dip;
2670914bffb6SAlexander V. Chernikov 	struct in_addr		sip;
2671914bffb6SAlexander V. Chernikov };
2672914bffb6SAlexander V. Chernikov 
2673914bffb6SAlexander V. Chernikov struct fhashentry6 {
2674914bffb6SAlexander V. Chernikov 	struct fhashentry	e;
2675914bffb6SAlexander V. Chernikov 	struct in6_addr		dip6;
2676914bffb6SAlexander V. Chernikov 	struct in6_addr		sip6;
2677914bffb6SAlexander V. Chernikov };
2678914bffb6SAlexander V. Chernikov 
2679914bffb6SAlexander V. Chernikov struct fhash_cfg {
2680914bffb6SAlexander V. Chernikov 	struct fhashbhead	*head;
2681914bffb6SAlexander V. Chernikov 	size_t			size;
2682914bffb6SAlexander V. Chernikov 	size_t			items;
2683914bffb6SAlexander V. Chernikov 	struct fhashentry4	fe4;
2684914bffb6SAlexander V. Chernikov 	struct fhashentry6	fe6;
2685914bffb6SAlexander V. Chernikov };
2686914bffb6SAlexander V. Chernikov 
26873fe2ef91SAlexander V. Chernikov struct ta_buf_fhash {
26880bce0c23SAlexander V. Chernikov 	void	*ent_ptr;
26890bce0c23SAlexander V. Chernikov 	struct fhashentry6 fe6;
26900bce0c23SAlexander V. Chernikov };
26910bce0c23SAlexander V. Chernikov 
2692914bffb6SAlexander V. Chernikov static __inline int
2693914bffb6SAlexander V. Chernikov cmp_flow_ent(struct fhashentry *a, struct fhashentry *b, size_t sz)
2694914bffb6SAlexander V. Chernikov {
2695914bffb6SAlexander V. Chernikov 	uint64_t *ka, *kb;
2696914bffb6SAlexander V. Chernikov 
2697914bffb6SAlexander V. Chernikov 	ka = (uint64_t *)(&a->next + 1);
2698914bffb6SAlexander V. Chernikov 	kb = (uint64_t *)(&b->next + 1);
2699914bffb6SAlexander V. Chernikov 
2700914bffb6SAlexander V. Chernikov 	if (*ka == *kb && (memcmp(a + 1, b + 1, sz) == 0))
2701914bffb6SAlexander V. Chernikov 		return (1);
2702914bffb6SAlexander V. Chernikov 
2703914bffb6SAlexander V. Chernikov 	return (0);
2704914bffb6SAlexander V. Chernikov }
2705914bffb6SAlexander V. Chernikov 
2706914bffb6SAlexander V. Chernikov static __inline uint32_t
2707914bffb6SAlexander V. Chernikov hash_flow4(struct fhashentry4 *f, int hsize)
2708914bffb6SAlexander V. Chernikov {
2709914bffb6SAlexander V. Chernikov 	uint32_t i;
2710914bffb6SAlexander V. Chernikov 
2711914bffb6SAlexander V. Chernikov 	i = (f->dip.s_addr) ^ (f->sip.s_addr) ^ (f->e.dport) ^ (f->e.sport);
2712914bffb6SAlexander V. Chernikov 
2713914bffb6SAlexander V. Chernikov 	return (i % (hsize - 1));
2714914bffb6SAlexander V. Chernikov }
2715914bffb6SAlexander V. Chernikov 
2716914bffb6SAlexander V. Chernikov static __inline uint32_t
2717914bffb6SAlexander V. Chernikov hash_flow6(struct fhashentry6 *f, int hsize)
2718914bffb6SAlexander V. Chernikov {
2719914bffb6SAlexander V. Chernikov 	uint32_t i;
2720914bffb6SAlexander V. Chernikov 
2721914bffb6SAlexander V. Chernikov 	i = (f->dip6.__u6_addr.__u6_addr32[2]) ^
2722914bffb6SAlexander V. Chernikov 	    (f->dip6.__u6_addr.__u6_addr32[3]) ^
2723914bffb6SAlexander V. Chernikov 	    (f->sip6.__u6_addr.__u6_addr32[2]) ^
2724914bffb6SAlexander V. Chernikov 	    (f->sip6.__u6_addr.__u6_addr32[3]) ^
2725914bffb6SAlexander V. Chernikov 	    (f->e.dport) ^ (f->e.sport);
2726914bffb6SAlexander V. Chernikov 
2727914bffb6SAlexander V. Chernikov 	return (i % (hsize - 1));
2728914bffb6SAlexander V. Chernikov }
2729914bffb6SAlexander V. Chernikov 
2730914bffb6SAlexander V. Chernikov static uint32_t
2731914bffb6SAlexander V. Chernikov hash_flow_ent(struct fhashentry *ent, uint32_t size)
2732914bffb6SAlexander V. Chernikov {
2733914bffb6SAlexander V. Chernikov 	uint32_t hash;
2734914bffb6SAlexander V. Chernikov 
2735914bffb6SAlexander V. Chernikov 	if (ent->af == AF_INET) {
2736914bffb6SAlexander V. Chernikov 		hash = hash_flow4((struct fhashentry4 *)ent, size);
2737914bffb6SAlexander V. Chernikov 	} else {
2738914bffb6SAlexander V. Chernikov 		hash = hash_flow6((struct fhashentry6 *)ent, size);
2739914bffb6SAlexander V. Chernikov 	}
2740914bffb6SAlexander V. Chernikov 
2741914bffb6SAlexander V. Chernikov 	return (hash);
2742914bffb6SAlexander V. Chernikov }
2743914bffb6SAlexander V. Chernikov 
2744914bffb6SAlexander V. Chernikov static int
2745914bffb6SAlexander V. Chernikov ta_lookup_fhash(struct table_info *ti, void *key, uint32_t keylen,
2746914bffb6SAlexander V. Chernikov     uint32_t *val)
2747914bffb6SAlexander V. Chernikov {
2748914bffb6SAlexander V. Chernikov 	struct fhashbhead *head;
2749914bffb6SAlexander V. Chernikov 	struct fhashentry *ent;
2750914bffb6SAlexander V. Chernikov 	struct fhashentry4 *m4;
2751914bffb6SAlexander V. Chernikov 	struct ipfw_flow_id *id;
2752914bffb6SAlexander V. Chernikov 	uint16_t hash, hsize;
2753914bffb6SAlexander V. Chernikov 
2754914bffb6SAlexander V. Chernikov 	id = (struct ipfw_flow_id *)key;
2755914bffb6SAlexander V. Chernikov 	head = (struct fhashbhead *)ti->state;
2756914bffb6SAlexander V. Chernikov 	hsize = ti->data;
2757914bffb6SAlexander V. Chernikov 	m4 = (struct fhashentry4 *)ti->xstate;
2758914bffb6SAlexander V. Chernikov 
2759914bffb6SAlexander V. Chernikov 	if (id->addr_type == 4) {
2760914bffb6SAlexander V. Chernikov 		struct fhashentry4 f;
2761914bffb6SAlexander V. Chernikov 
2762914bffb6SAlexander V. Chernikov 		/* Copy hash mask */
2763914bffb6SAlexander V. Chernikov 		f = *m4;
2764914bffb6SAlexander V. Chernikov 
2765914bffb6SAlexander V. Chernikov 		f.dip.s_addr &= id->dst_ip;
2766914bffb6SAlexander V. Chernikov 		f.sip.s_addr &= id->src_ip;
2767914bffb6SAlexander V. Chernikov 		f.e.dport &= id->dst_port;
2768914bffb6SAlexander V. Chernikov 		f.e.sport &= id->src_port;
2769914bffb6SAlexander V. Chernikov 		f.e.proto &= id->proto;
2770914bffb6SAlexander V. Chernikov 		hash = hash_flow4(&f, hsize);
2771914bffb6SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
2772914bffb6SAlexander V. Chernikov 			if (cmp_flow_ent(ent, &f.e, 2 * 4) != 0) {
2773914bffb6SAlexander V. Chernikov 				*val = ent->value;
2774914bffb6SAlexander V. Chernikov 				return (1);
2775914bffb6SAlexander V. Chernikov 			}
2776914bffb6SAlexander V. Chernikov 		}
2777914bffb6SAlexander V. Chernikov 	} else if (id->addr_type == 6) {
2778914bffb6SAlexander V. Chernikov 		struct fhashentry6 f;
2779914bffb6SAlexander V. Chernikov 		uint64_t *fp, *idp;
2780914bffb6SAlexander V. Chernikov 
2781914bffb6SAlexander V. Chernikov 		/* Copy hash mask */
2782914bffb6SAlexander V. Chernikov 		f = *((struct fhashentry6 *)(m4 + 1));
2783914bffb6SAlexander V. Chernikov 
2784914bffb6SAlexander V. Chernikov 		/* Handle lack of __u6_addr.__u6_addr64 */
2785914bffb6SAlexander V. Chernikov 		fp = (uint64_t *)&f.dip6;
2786914bffb6SAlexander V. Chernikov 		idp = (uint64_t *)&id->dst_ip6;
2787914bffb6SAlexander V. Chernikov 		/* src IPv6 is stored after dst IPv6 */
2788914bffb6SAlexander V. Chernikov 		*fp++ &= *idp++;
2789914bffb6SAlexander V. Chernikov 		*fp++ &= *idp++;
2790914bffb6SAlexander V. Chernikov 		*fp++ &= *idp++;
2791914bffb6SAlexander V. Chernikov 		*fp &= *idp;
2792914bffb6SAlexander V. Chernikov 		f.e.dport &= id->dst_port;
2793914bffb6SAlexander V. Chernikov 		f.e.sport &= id->src_port;
2794914bffb6SAlexander V. Chernikov 		f.e.proto &= id->proto;
2795914bffb6SAlexander V. Chernikov 		hash = hash_flow6(&f, hsize);
2796914bffb6SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
2797914bffb6SAlexander V. Chernikov 			if (cmp_flow_ent(ent, &f.e, 2 * 16) != 0) {
2798914bffb6SAlexander V. Chernikov 				*val = ent->value;
2799914bffb6SAlexander V. Chernikov 				return (1);
2800914bffb6SAlexander V. Chernikov 			}
2801914bffb6SAlexander V. Chernikov 		}
2802914bffb6SAlexander V. Chernikov 	}
2803914bffb6SAlexander V. Chernikov 
2804914bffb6SAlexander V. Chernikov 	return (0);
2805914bffb6SAlexander V. Chernikov }
2806914bffb6SAlexander V. Chernikov 
2807914bffb6SAlexander V. Chernikov /*
2808914bffb6SAlexander V. Chernikov  * New table.
2809914bffb6SAlexander V. Chernikov  */
2810914bffb6SAlexander V. Chernikov static int
2811914bffb6SAlexander V. Chernikov ta_init_fhash(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
2812914bffb6SAlexander V. Chernikov     char *data, uint8_t tflags)
2813914bffb6SAlexander V. Chernikov {
2814914bffb6SAlexander V. Chernikov 	int i;
2815914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
2816914bffb6SAlexander V. Chernikov 	struct fhashentry4 *fe4;
2817914bffb6SAlexander V. Chernikov 	struct fhashentry6 *fe6;
2818914bffb6SAlexander V. Chernikov 
2819914bffb6SAlexander V. Chernikov 	cfg = malloc(sizeof(struct fhash_cfg), M_IPFW, M_WAITOK | M_ZERO);
2820914bffb6SAlexander V. Chernikov 
2821914bffb6SAlexander V. Chernikov 	cfg->size = 512;
2822914bffb6SAlexander V. Chernikov 
2823914bffb6SAlexander V. Chernikov 	cfg->head = malloc(sizeof(struct fhashbhead) * cfg->size, M_IPFW,
2824914bffb6SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
2825914bffb6SAlexander V. Chernikov 	for (i = 0; i < cfg->size; i++)
2826914bffb6SAlexander V. Chernikov 		SLIST_INIT(&cfg->head[i]);
2827914bffb6SAlexander V. Chernikov 
2828914bffb6SAlexander V. Chernikov 	/* Fill in fe masks based on @tflags */
2829914bffb6SAlexander V. Chernikov 	fe4 = &cfg->fe4;
2830914bffb6SAlexander V. Chernikov 	fe6 = &cfg->fe6;
2831914bffb6SAlexander V. Chernikov 	if (tflags & IPFW_TFFLAG_SRCIP) {
2832914bffb6SAlexander V. Chernikov 		memset(&fe4->sip, 0xFF, sizeof(fe4->sip));
2833914bffb6SAlexander V. Chernikov 		memset(&fe6->sip6, 0xFF, sizeof(fe6->sip6));
2834914bffb6SAlexander V. Chernikov 	}
2835914bffb6SAlexander V. Chernikov 	if (tflags & IPFW_TFFLAG_DSTIP) {
2836914bffb6SAlexander V. Chernikov 		memset(&fe4->dip, 0xFF, sizeof(fe4->dip));
2837914bffb6SAlexander V. Chernikov 		memset(&fe6->dip6, 0xFF, sizeof(fe6->dip6));
2838914bffb6SAlexander V. Chernikov 	}
2839914bffb6SAlexander V. Chernikov 	if (tflags & IPFW_TFFLAG_SRCPORT) {
2840914bffb6SAlexander V. Chernikov 		memset(&fe4->e.sport, 0xFF, sizeof(fe4->e.sport));
2841914bffb6SAlexander V. Chernikov 		memset(&fe6->e.sport, 0xFF, sizeof(fe6->e.sport));
2842914bffb6SAlexander V. Chernikov 	}
2843914bffb6SAlexander V. Chernikov 	if (tflags & IPFW_TFFLAG_DSTPORT) {
2844914bffb6SAlexander V. Chernikov 		memset(&fe4->e.dport, 0xFF, sizeof(fe4->e.dport));
2845914bffb6SAlexander V. Chernikov 		memset(&fe6->e.dport, 0xFF, sizeof(fe6->e.dport));
2846914bffb6SAlexander V. Chernikov 	}
2847914bffb6SAlexander V. Chernikov 	if (tflags & IPFW_TFFLAG_PROTO) {
2848914bffb6SAlexander V. Chernikov 		memset(&fe4->e.proto, 0xFF, sizeof(fe4->e.proto));
2849914bffb6SAlexander V. Chernikov 		memset(&fe6->e.proto, 0xFF, sizeof(fe6->e.proto));
2850914bffb6SAlexander V. Chernikov 	}
2851914bffb6SAlexander V. Chernikov 
2852914bffb6SAlexander V. Chernikov 	fe4->e.af = AF_INET;
2853914bffb6SAlexander V. Chernikov 	fe6->e.af = AF_INET6;
2854914bffb6SAlexander V. Chernikov 
2855914bffb6SAlexander V. Chernikov 	*ta_state = cfg;
2856914bffb6SAlexander V. Chernikov 	ti->state = cfg->head;
2857914bffb6SAlexander V. Chernikov 	ti->xstate = &cfg->fe4;
2858914bffb6SAlexander V. Chernikov 	ti->data = cfg->size;
2859914bffb6SAlexander V. Chernikov 	ti->lookup = ta_lookup_fhash;
2860914bffb6SAlexander V. Chernikov 
2861914bffb6SAlexander V. Chernikov 	return (0);
2862914bffb6SAlexander V. Chernikov }
2863914bffb6SAlexander V. Chernikov 
2864914bffb6SAlexander V. Chernikov static void
2865914bffb6SAlexander V. Chernikov ta_destroy_fhash(void *ta_state, struct table_info *ti)
2866914bffb6SAlexander V. Chernikov {
2867914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
2868914bffb6SAlexander V. Chernikov 	struct fhashentry *ent, *ent_next;
2869914bffb6SAlexander V. Chernikov 	int i;
2870914bffb6SAlexander V. Chernikov 
2871914bffb6SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
2872914bffb6SAlexander V. Chernikov 
2873914bffb6SAlexander V. Chernikov 	for (i = 0; i < cfg->size; i++)
2874914bffb6SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &cfg->head[i], next, ent_next)
2875914bffb6SAlexander V. Chernikov 			free(ent, M_IPFW_TBL);
2876914bffb6SAlexander V. Chernikov 
2877914bffb6SAlexander V. Chernikov 	free(cfg->head, M_IPFW);
2878914bffb6SAlexander V. Chernikov 	free(cfg, M_IPFW);
2879914bffb6SAlexander V. Chernikov }
2880914bffb6SAlexander V. Chernikov 
28815f379342SAlexander V. Chernikov /*
28825f379342SAlexander V. Chernikov  * Provide algo-specific table info
28835f379342SAlexander V. Chernikov  */
28845f379342SAlexander V. Chernikov static void
28855f379342SAlexander V. Chernikov ta_dump_fhash_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo)
28865f379342SAlexander V. Chernikov {
28875f379342SAlexander V. Chernikov 	struct fhash_cfg *cfg;
28885f379342SAlexander V. Chernikov 
28895f379342SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
28905f379342SAlexander V. Chernikov 
28915f379342SAlexander V. Chernikov 	tinfo->flags = IPFW_TATFLAGS_AFITEM;
28925f379342SAlexander V. Chernikov 	tinfo->taclass4 = IPFW_TACLASS_HASH;
28935f379342SAlexander V. Chernikov 	tinfo->size4 = cfg->size;
28945f379342SAlexander V. Chernikov 	tinfo->count4 = cfg->items;
28955f379342SAlexander V. Chernikov 	tinfo->itemsize4 = sizeof(struct fhashentry4);
28965f379342SAlexander V. Chernikov 	tinfo->itemsize6 = sizeof(struct fhashentry6);
28975f379342SAlexander V. Chernikov }
28985f379342SAlexander V. Chernikov 
2899914bffb6SAlexander V. Chernikov static int
2900914bffb6SAlexander V. Chernikov ta_dump_fhash_tentry(void *ta_state, struct table_info *ti, void *e,
2901914bffb6SAlexander V. Chernikov     ipfw_obj_tentry *tent)
2902914bffb6SAlexander V. Chernikov {
2903914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
2904914bffb6SAlexander V. Chernikov 	struct fhashentry *ent;
2905914bffb6SAlexander V. Chernikov 	struct fhashentry4 *fe4;
2906914bffb6SAlexander V. Chernikov 	struct fhashentry6 *fe6;
2907914bffb6SAlexander V. Chernikov 	struct tflow_entry *tfe;
2908914bffb6SAlexander V. Chernikov 
2909914bffb6SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
2910914bffb6SAlexander V. Chernikov 	ent = (struct fhashentry *)e;
2911914bffb6SAlexander V. Chernikov 	tfe = &tent->k.flow;
2912914bffb6SAlexander V. Chernikov 
2913914bffb6SAlexander V. Chernikov 	tfe->af = ent->af;
2914914bffb6SAlexander V. Chernikov 	tfe->proto = ent->proto;
2915914bffb6SAlexander V. Chernikov 	tfe->dport = htons(ent->dport);
2916914bffb6SAlexander V. Chernikov 	tfe->sport = htons(ent->sport);
2917914bffb6SAlexander V. Chernikov 	tent->value = ent->value;
2918914bffb6SAlexander V. Chernikov 	tent->subtype = ent->af;
2919914bffb6SAlexander V. Chernikov 
2920914bffb6SAlexander V. Chernikov 	if (ent->af == AF_INET) {
2921914bffb6SAlexander V. Chernikov 		fe4 = (struct fhashentry4 *)ent;
2922914bffb6SAlexander V. Chernikov 		tfe->a.a4.sip.s_addr = htonl(fe4->sip.s_addr);
2923914bffb6SAlexander V. Chernikov 		tfe->a.a4.dip.s_addr = htonl(fe4->dip.s_addr);
2924914bffb6SAlexander V. Chernikov 		tent->masklen = 32;
2925914bffb6SAlexander V. Chernikov #ifdef INET6
2926914bffb6SAlexander V. Chernikov 	} else {
2927914bffb6SAlexander V. Chernikov 		fe6 = (struct fhashentry6 *)ent;
2928914bffb6SAlexander V. Chernikov 		tfe->a.a6.sip6 = fe6->sip6;
2929914bffb6SAlexander V. Chernikov 		tfe->a.a6.dip6 = fe6->dip6;
2930914bffb6SAlexander V. Chernikov 		tent->masklen = 128;
2931914bffb6SAlexander V. Chernikov #endif
2932914bffb6SAlexander V. Chernikov 	}
2933914bffb6SAlexander V. Chernikov 
2934914bffb6SAlexander V. Chernikov 	return (0);
2935914bffb6SAlexander V. Chernikov }
2936914bffb6SAlexander V. Chernikov 
2937914bffb6SAlexander V. Chernikov static int
2938914bffb6SAlexander V. Chernikov tei_to_fhash_ent(struct tentry_info *tei, struct fhashentry *ent)
2939914bffb6SAlexander V. Chernikov {
2940914bffb6SAlexander V. Chernikov 	struct fhashentry4 *fe4;
2941914bffb6SAlexander V. Chernikov 	struct fhashentry6 *fe6;
2942914bffb6SAlexander V. Chernikov 	struct tflow_entry *tfe;
2943914bffb6SAlexander V. Chernikov 
2944914bffb6SAlexander V. Chernikov 	tfe = (struct tflow_entry *)tei->paddr;
2945914bffb6SAlexander V. Chernikov 
2946914bffb6SAlexander V. Chernikov 	ent->af = tei->subtype;
2947914bffb6SAlexander V. Chernikov 	ent->proto = tfe->proto;
2948914bffb6SAlexander V. Chernikov 	ent->value = tei->value;
2949914bffb6SAlexander V. Chernikov 	ent->dport = ntohs(tfe->dport);
2950914bffb6SAlexander V. Chernikov 	ent->sport = ntohs(tfe->sport);
2951914bffb6SAlexander V. Chernikov 
2952914bffb6SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
2953914bffb6SAlexander V. Chernikov #ifdef INET
2954914bffb6SAlexander V. Chernikov 		fe4 = (struct fhashentry4 *)ent;
2955914bffb6SAlexander V. Chernikov 		fe4->sip.s_addr = ntohl(tfe->a.a4.sip.s_addr);
2956914bffb6SAlexander V. Chernikov 		fe4->dip.s_addr = ntohl(tfe->a.a4.dip.s_addr);
2957914bffb6SAlexander V. Chernikov #endif
2958914bffb6SAlexander V. Chernikov #ifdef INET6
2959914bffb6SAlexander V. Chernikov 	} else if (tei->subtype == AF_INET6) {
2960914bffb6SAlexander V. Chernikov 		fe6 = (struct fhashentry6 *)ent;
2961914bffb6SAlexander V. Chernikov 		fe6->sip6 = tfe->a.a6.sip6;
2962914bffb6SAlexander V. Chernikov 		fe6->dip6 = tfe->a.a6.dip6;
2963914bffb6SAlexander V. Chernikov #endif
2964914bffb6SAlexander V. Chernikov 	} else {
2965914bffb6SAlexander V. Chernikov 		/* Unknown CIDR type */
2966914bffb6SAlexander V. Chernikov 		return (EINVAL);
2967914bffb6SAlexander V. Chernikov 	}
2968914bffb6SAlexander V. Chernikov 
2969914bffb6SAlexander V. Chernikov 	return (0);
2970914bffb6SAlexander V. Chernikov }
2971914bffb6SAlexander V. Chernikov 
2972914bffb6SAlexander V. Chernikov 
2973914bffb6SAlexander V. Chernikov static int
2974914bffb6SAlexander V. Chernikov ta_find_fhash_tentry(void *ta_state, struct table_info *ti,
2975914bffb6SAlexander V. Chernikov     ipfw_obj_tentry *tent)
2976914bffb6SAlexander V. Chernikov {
2977914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
2978914bffb6SAlexander V. Chernikov 	struct fhashbhead *head;
2979914bffb6SAlexander V. Chernikov 	struct fhashentry *ent, *tmp;
2980914bffb6SAlexander V. Chernikov 	struct fhashentry6 fe6;
2981914bffb6SAlexander V. Chernikov 	struct tentry_info tei;
2982914bffb6SAlexander V. Chernikov 	int error;
2983914bffb6SAlexander V. Chernikov 	uint32_t hash;
2984914bffb6SAlexander V. Chernikov 	size_t sz;
2985914bffb6SAlexander V. Chernikov 
2986914bffb6SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
2987914bffb6SAlexander V. Chernikov 
2988914bffb6SAlexander V. Chernikov 	ent = &fe6.e;
2989914bffb6SAlexander V. Chernikov 
2990914bffb6SAlexander V. Chernikov 	memset(&fe6, 0, sizeof(fe6));
2991914bffb6SAlexander V. Chernikov 	memset(&tei, 0, sizeof(tei));
2992914bffb6SAlexander V. Chernikov 
2993914bffb6SAlexander V. Chernikov 	tei.paddr = &tent->k.flow;
2994914bffb6SAlexander V. Chernikov 	tei.subtype = tent->subtype;
2995914bffb6SAlexander V. Chernikov 
2996914bffb6SAlexander V. Chernikov 	if ((error = tei_to_fhash_ent(&tei, ent)) != 0)
2997914bffb6SAlexander V. Chernikov 		return (error);
2998914bffb6SAlexander V. Chernikov 
2999914bffb6SAlexander V. Chernikov 	head = cfg->head;
3000914bffb6SAlexander V. Chernikov 	hash = hash_flow_ent(ent, cfg->size);
3001914bffb6SAlexander V. Chernikov 
3002914bffb6SAlexander V. Chernikov 	if (tei.subtype == AF_INET)
3003914bffb6SAlexander V. Chernikov 		sz = 2 * sizeof(struct in_addr);
3004914bffb6SAlexander V. Chernikov 	else
3005914bffb6SAlexander V. Chernikov 		sz = 2 * sizeof(struct in6_addr);
3006914bffb6SAlexander V. Chernikov 
3007914bffb6SAlexander V. Chernikov 	/* Check for existence */
3008914bffb6SAlexander V. Chernikov 	SLIST_FOREACH(tmp, &head[hash], next) {
3009914bffb6SAlexander V. Chernikov 		if (cmp_flow_ent(tmp, ent, sz) != 0) {
3010914bffb6SAlexander V. Chernikov 			ta_dump_fhash_tentry(ta_state, ti, tmp, tent);
3011914bffb6SAlexander V. Chernikov 			return (0);
3012914bffb6SAlexander V. Chernikov 		}
3013914bffb6SAlexander V. Chernikov 	}
3014914bffb6SAlexander V. Chernikov 
3015914bffb6SAlexander V. Chernikov 	return (ENOENT);
3016914bffb6SAlexander V. Chernikov }
3017914bffb6SAlexander V. Chernikov 
3018914bffb6SAlexander V. Chernikov static void
3019914bffb6SAlexander V. Chernikov ta_foreach_fhash(void *ta_state, struct table_info *ti, ta_foreach_f *f,
3020914bffb6SAlexander V. Chernikov     void *arg)
3021914bffb6SAlexander V. Chernikov {
3022914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
3023914bffb6SAlexander V. Chernikov 	struct fhashentry *ent, *ent_next;
3024914bffb6SAlexander V. Chernikov 	int i;
3025914bffb6SAlexander V. Chernikov 
3026914bffb6SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
3027914bffb6SAlexander V. Chernikov 
3028914bffb6SAlexander V. Chernikov 	for (i = 0; i < cfg->size; i++)
3029914bffb6SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &cfg->head[i], next, ent_next)
3030914bffb6SAlexander V. Chernikov 			f(ent, arg);
3031914bffb6SAlexander V. Chernikov }
3032914bffb6SAlexander V. Chernikov 
3033914bffb6SAlexander V. Chernikov static int
3034914bffb6SAlexander V. Chernikov ta_prepare_add_fhash(struct ip_fw_chain *ch, struct tentry_info *tei,
3035914bffb6SAlexander V. Chernikov     void *ta_buf)
3036914bffb6SAlexander V. Chernikov {
3037914bffb6SAlexander V. Chernikov 	struct ta_buf_fhash *tb;
3038914bffb6SAlexander V. Chernikov 	struct fhashentry *ent;
3039914bffb6SAlexander V. Chernikov 	size_t sz;
3040914bffb6SAlexander V. Chernikov 	int error;
3041914bffb6SAlexander V. Chernikov 
3042914bffb6SAlexander V. Chernikov 	tb = (struct ta_buf_fhash *)ta_buf;
3043914bffb6SAlexander V. Chernikov 
3044914bffb6SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
3045914bffb6SAlexander V. Chernikov 		sz = sizeof(struct fhashentry4);
3046914bffb6SAlexander V. Chernikov 	else if (tei->subtype == AF_INET6)
3047914bffb6SAlexander V. Chernikov 		sz = sizeof(struct fhashentry6);
3048914bffb6SAlexander V. Chernikov 	else
3049914bffb6SAlexander V. Chernikov 		return (EINVAL);
3050914bffb6SAlexander V. Chernikov 
3051914bffb6SAlexander V. Chernikov 	ent = malloc(sz, M_IPFW_TBL, M_WAITOK | M_ZERO);
3052914bffb6SAlexander V. Chernikov 
3053914bffb6SAlexander V. Chernikov 	error = tei_to_fhash_ent(tei, ent);
3054914bffb6SAlexander V. Chernikov 	if (error != 0) {
3055914bffb6SAlexander V. Chernikov 		free(ent, M_IPFW_TBL);
3056914bffb6SAlexander V. Chernikov 		return (error);
3057914bffb6SAlexander V. Chernikov 	}
3058914bffb6SAlexander V. Chernikov 	tb->ent_ptr = ent;
3059914bffb6SAlexander V. Chernikov 
3060914bffb6SAlexander V. Chernikov 	return (0);
3061914bffb6SAlexander V. Chernikov }
3062914bffb6SAlexander V. Chernikov 
3063914bffb6SAlexander V. Chernikov static int
3064914bffb6SAlexander V. Chernikov ta_add_fhash(void *ta_state, struct table_info *ti, struct tentry_info *tei,
3065b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
3066914bffb6SAlexander V. Chernikov {
3067914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
3068914bffb6SAlexander V. Chernikov 	struct fhashbhead *head;
3069914bffb6SAlexander V. Chernikov 	struct fhashentry *ent, *tmp;
3070914bffb6SAlexander V. Chernikov 	struct ta_buf_fhash *tb;
3071914bffb6SAlexander V. Chernikov 	int exists;
3072648e8380SAlexander V. Chernikov 	uint32_t hash, value;
3073914bffb6SAlexander V. Chernikov 	size_t sz;
3074914bffb6SAlexander V. Chernikov 
3075914bffb6SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
3076914bffb6SAlexander V. Chernikov 	tb = (struct ta_buf_fhash *)ta_buf;
3077914bffb6SAlexander V. Chernikov 	ent = (struct fhashentry *)tb->ent_ptr;
3078914bffb6SAlexander V. Chernikov 	exists = 0;
3079914bffb6SAlexander V. Chernikov 
3080914bffb6SAlexander V. Chernikov 	head = cfg->head;
3081914bffb6SAlexander V. Chernikov 	hash = hash_flow_ent(ent, cfg->size);
3082914bffb6SAlexander V. Chernikov 
3083914bffb6SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
3084914bffb6SAlexander V. Chernikov 		sz = 2 * sizeof(struct in_addr);
3085914bffb6SAlexander V. Chernikov 	else
3086914bffb6SAlexander V. Chernikov 		sz = 2 * sizeof(struct in6_addr);
3087914bffb6SAlexander V. Chernikov 
3088914bffb6SAlexander V. Chernikov 	/* Check for existence */
3089914bffb6SAlexander V. Chernikov 	SLIST_FOREACH(tmp, &head[hash], next) {
3090914bffb6SAlexander V. Chernikov 		if (cmp_flow_ent(tmp, ent, sz) != 0) {
3091914bffb6SAlexander V. Chernikov 			exists = 1;
3092914bffb6SAlexander V. Chernikov 			break;
3093914bffb6SAlexander V. Chernikov 		}
3094914bffb6SAlexander V. Chernikov 	}
3095914bffb6SAlexander V. Chernikov 
3096914bffb6SAlexander V. Chernikov 	if (exists == 1) {
3097914bffb6SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
3098914bffb6SAlexander V. Chernikov 			return (EEXIST);
3099914bffb6SAlexander V. Chernikov 		/* Record already exists. Update value if we're asked to */
3100648e8380SAlexander V. Chernikov 		/* Exchange values between tmp and @tei */
3101648e8380SAlexander V. Chernikov 		value = tmp->value;
3102914bffb6SAlexander V. Chernikov 		tmp->value = tei->value;
3103648e8380SAlexander V. Chernikov 		tei->value = value;
3104914bffb6SAlexander V. Chernikov 		/* Indicate that update has happened instead of addition */
3105914bffb6SAlexander V. Chernikov 		tei->flags |= TEI_FLAGS_UPDATED;
3106914bffb6SAlexander V. Chernikov 		*pnum = 0;
3107914bffb6SAlexander V. Chernikov 	} else {
31084c0c07a5SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_DONTADD) != 0)
31094c0c07a5SAlexander V. Chernikov 			return (EFBIG);
31104c0c07a5SAlexander V. Chernikov 
3111914bffb6SAlexander V. Chernikov 		SLIST_INSERT_HEAD(&head[hash], ent, next);
3112914bffb6SAlexander V. Chernikov 		tb->ent_ptr = NULL;
3113914bffb6SAlexander V. Chernikov 		*pnum = 1;
3114914bffb6SAlexander V. Chernikov 
3115914bffb6SAlexander V. Chernikov 		/* Update counters and check if we need to grow hash */
3116914bffb6SAlexander V. Chernikov 		cfg->items++;
3117914bffb6SAlexander V. Chernikov 	}
3118914bffb6SAlexander V. Chernikov 
3119914bffb6SAlexander V. Chernikov 	return (0);
3120914bffb6SAlexander V. Chernikov }
3121914bffb6SAlexander V. Chernikov 
3122914bffb6SAlexander V. Chernikov static int
3123914bffb6SAlexander V. Chernikov ta_prepare_del_fhash(struct ip_fw_chain *ch, struct tentry_info *tei,
3124914bffb6SAlexander V. Chernikov     void *ta_buf)
3125914bffb6SAlexander V. Chernikov {
3126914bffb6SAlexander V. Chernikov 	struct ta_buf_fhash *tb;
3127914bffb6SAlexander V. Chernikov 
3128914bffb6SAlexander V. Chernikov 	tb = (struct ta_buf_fhash *)ta_buf;
3129914bffb6SAlexander V. Chernikov 
3130914bffb6SAlexander V. Chernikov 	return (tei_to_fhash_ent(tei, &tb->fe6.e));
3131914bffb6SAlexander V. Chernikov }
3132914bffb6SAlexander V. Chernikov 
3133914bffb6SAlexander V. Chernikov static int
3134914bffb6SAlexander V. Chernikov ta_del_fhash(void *ta_state, struct table_info *ti, struct tentry_info *tei,
3135b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
3136914bffb6SAlexander V. Chernikov {
3137914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
3138914bffb6SAlexander V. Chernikov 	struct fhashbhead *head;
3139914bffb6SAlexander V. Chernikov 	struct fhashentry *ent, *tmp;
3140914bffb6SAlexander V. Chernikov 	struct ta_buf_fhash *tb;
3141914bffb6SAlexander V. Chernikov 	uint32_t hash;
3142914bffb6SAlexander V. Chernikov 	size_t sz;
3143914bffb6SAlexander V. Chernikov 
3144914bffb6SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
3145914bffb6SAlexander V. Chernikov 	tb = (struct ta_buf_fhash *)ta_buf;
3146914bffb6SAlexander V. Chernikov 	ent = &tb->fe6.e;
3147914bffb6SAlexander V. Chernikov 
3148914bffb6SAlexander V. Chernikov 	head = cfg->head;
3149914bffb6SAlexander V. Chernikov 	hash = hash_flow_ent(ent, cfg->size);
3150914bffb6SAlexander V. Chernikov 
3151914bffb6SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
3152914bffb6SAlexander V. Chernikov 		sz = 2 * sizeof(struct in_addr);
3153914bffb6SAlexander V. Chernikov 	else
3154914bffb6SAlexander V. Chernikov 		sz = 2 * sizeof(struct in6_addr);
3155914bffb6SAlexander V. Chernikov 
3156914bffb6SAlexander V. Chernikov 	/* Check for existence */
3157914bffb6SAlexander V. Chernikov 	SLIST_FOREACH(tmp, &head[hash], next) {
3158648e8380SAlexander V. Chernikov 		if (cmp_flow_ent(tmp, ent, sz) == 0)
3159648e8380SAlexander V. Chernikov 			continue;
3160648e8380SAlexander V. Chernikov 
3161914bffb6SAlexander V. Chernikov 		SLIST_REMOVE(&head[hash], tmp, fhashentry, next);
3162648e8380SAlexander V. Chernikov 		tei->value = tmp->value;
3163914bffb6SAlexander V. Chernikov 		*pnum = 1;
3164914bffb6SAlexander V. Chernikov 		cfg->items--;
3165648e8380SAlexander V. Chernikov 		tb->ent_ptr = tmp;
3166914bffb6SAlexander V. Chernikov 		return (0);
3167914bffb6SAlexander V. Chernikov 	}
3168914bffb6SAlexander V. Chernikov 
3169914bffb6SAlexander V. Chernikov 	return (ENOENT);
3170914bffb6SAlexander V. Chernikov }
3171914bffb6SAlexander V. Chernikov 
3172914bffb6SAlexander V. Chernikov static void
3173914bffb6SAlexander V. Chernikov ta_flush_fhash_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
3174914bffb6SAlexander V. Chernikov     void *ta_buf)
3175914bffb6SAlexander V. Chernikov {
3176914bffb6SAlexander V. Chernikov 	struct ta_buf_fhash *tb;
3177914bffb6SAlexander V. Chernikov 
3178914bffb6SAlexander V. Chernikov 	tb = (struct ta_buf_fhash *)ta_buf;
3179914bffb6SAlexander V. Chernikov 
3180914bffb6SAlexander V. Chernikov 	if (tb->ent_ptr != NULL)
3181914bffb6SAlexander V. Chernikov 		free(tb->ent_ptr, M_IPFW_TBL);
3182914bffb6SAlexander V. Chernikov }
3183914bffb6SAlexander V. Chernikov 
3184914bffb6SAlexander V. Chernikov /*
3185914bffb6SAlexander V. Chernikov  * Hash growing callbacks.
3186914bffb6SAlexander V. Chernikov  */
3187914bffb6SAlexander V. Chernikov 
3188b6ee846eSAlexander V. Chernikov static int
3189b6ee846eSAlexander V. Chernikov ta_has_space_fhash(void *ta_state, struct table_info *ti, uint32_t count,
3190b6ee846eSAlexander V. Chernikov     uint64_t *pflags)
3191b6ee846eSAlexander V. Chernikov {
3192b6ee846eSAlexander V. Chernikov 	struct fhash_cfg *cfg;
3193b6ee846eSAlexander V. Chernikov 
3194b6ee846eSAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
3195b6ee846eSAlexander V. Chernikov 
3196b6ee846eSAlexander V. Chernikov 	if (cfg->items > cfg->size && cfg->size < 65536) {
3197b6ee846eSAlexander V. Chernikov 		*pflags = cfg->size * 2;
3198b6ee846eSAlexander V. Chernikov 		return (0);
3199b6ee846eSAlexander V. Chernikov 	}
3200b6ee846eSAlexander V. Chernikov 
3201b6ee846eSAlexander V. Chernikov 	return (1);
3202b6ee846eSAlexander V. Chernikov }
3203b6ee846eSAlexander V. Chernikov 
3204914bffb6SAlexander V. Chernikov /*
3205914bffb6SAlexander V. Chernikov  * Allocate new, larger fhash.
3206914bffb6SAlexander V. Chernikov  */
3207914bffb6SAlexander V. Chernikov static int
3208914bffb6SAlexander V. Chernikov ta_prepare_mod_fhash(void *ta_buf, uint64_t *pflags)
3209914bffb6SAlexander V. Chernikov {
3210914bffb6SAlexander V. Chernikov 	struct mod_item *mi;
3211914bffb6SAlexander V. Chernikov 	struct fhashbhead *head;
3212914bffb6SAlexander V. Chernikov 	int i;
3213914bffb6SAlexander V. Chernikov 
3214914bffb6SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
3215914bffb6SAlexander V. Chernikov 
3216914bffb6SAlexander V. Chernikov 	memset(mi, 0, sizeof(struct mod_item));
3217914bffb6SAlexander V. Chernikov 	mi->size = *pflags;
3218914bffb6SAlexander V. Chernikov 	head = malloc(sizeof(struct fhashbhead) * mi->size, M_IPFW,
3219914bffb6SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
3220914bffb6SAlexander V. Chernikov 	for (i = 0; i < mi->size; i++)
3221914bffb6SAlexander V. Chernikov 		SLIST_INIT(&head[i]);
3222914bffb6SAlexander V. Chernikov 
3223914bffb6SAlexander V. Chernikov 	mi->main_ptr = head;
3224914bffb6SAlexander V. Chernikov 
3225914bffb6SAlexander V. Chernikov 	return (0);
3226914bffb6SAlexander V. Chernikov }
3227914bffb6SAlexander V. Chernikov 
3228914bffb6SAlexander V. Chernikov /*
3229914bffb6SAlexander V. Chernikov  * Copy data from old runtime array to new one.
3230914bffb6SAlexander V. Chernikov  */
3231914bffb6SAlexander V. Chernikov static int
3232914bffb6SAlexander V. Chernikov ta_fill_mod_fhash(void *ta_state, struct table_info *ti, void *ta_buf,
3233914bffb6SAlexander V. Chernikov     uint64_t *pflags)
3234914bffb6SAlexander V. Chernikov {
3235914bffb6SAlexander V. Chernikov 
3236914bffb6SAlexander V. Chernikov 	/* In is not possible to do rehash if we're not holidng WLOCK. */
3237914bffb6SAlexander V. Chernikov 	return (0);
3238914bffb6SAlexander V. Chernikov }
3239914bffb6SAlexander V. Chernikov 
3240914bffb6SAlexander V. Chernikov /*
3241914bffb6SAlexander V. Chernikov  * Switch old & new arrays.
3242914bffb6SAlexander V. Chernikov  */
3243914bffb6SAlexander V. Chernikov static int
3244914bffb6SAlexander V. Chernikov ta_modify_fhash(void *ta_state, struct table_info *ti, void *ta_buf,
3245914bffb6SAlexander V. Chernikov     uint64_t pflags)
3246914bffb6SAlexander V. Chernikov {
3247914bffb6SAlexander V. Chernikov 	struct mod_item *mi;
3248914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
3249914bffb6SAlexander V. Chernikov 	struct fhashbhead *old_head, *new_head;
3250914bffb6SAlexander V. Chernikov 	struct fhashentry *ent, *ent_next;
3251914bffb6SAlexander V. Chernikov 	int i;
3252914bffb6SAlexander V. Chernikov 	uint32_t nhash;
3253914bffb6SAlexander V. Chernikov 	size_t old_size;
3254914bffb6SAlexander V. Chernikov 
3255914bffb6SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
3256914bffb6SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
3257914bffb6SAlexander V. Chernikov 
3258914bffb6SAlexander V. Chernikov 	/* Check which hash we need to grow and do we still need that */
3259914bffb6SAlexander V. Chernikov 	old_size = cfg->size;
3260914bffb6SAlexander V. Chernikov 	old_head = ti->state;
3261914bffb6SAlexander V. Chernikov 
3262914bffb6SAlexander V. Chernikov 	if (old_size >= mi->size)
3263914bffb6SAlexander V. Chernikov 		return (0);
3264914bffb6SAlexander V. Chernikov 
3265914bffb6SAlexander V. Chernikov 	new_head = (struct fhashbhead *)mi->main_ptr;
3266914bffb6SAlexander V. Chernikov 	for (i = 0; i < old_size; i++) {
3267914bffb6SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &old_head[i], next, ent_next) {
3268914bffb6SAlexander V. Chernikov 			nhash = hash_flow_ent(ent, mi->size);
3269914bffb6SAlexander V. Chernikov 			SLIST_INSERT_HEAD(&new_head[nhash], ent, next);
3270914bffb6SAlexander V. Chernikov 		}
3271914bffb6SAlexander V. Chernikov 	}
3272914bffb6SAlexander V. Chernikov 
3273914bffb6SAlexander V. Chernikov 	ti->state = new_head;
3274914bffb6SAlexander V. Chernikov 	ti->data = mi->size;
3275914bffb6SAlexander V. Chernikov 	cfg->head = new_head;
3276914bffb6SAlexander V. Chernikov 	cfg->size = mi->size;
3277914bffb6SAlexander V. Chernikov 
3278914bffb6SAlexander V. Chernikov 	mi->main_ptr = old_head;
3279914bffb6SAlexander V. Chernikov 
3280914bffb6SAlexander V. Chernikov 	return (0);
3281914bffb6SAlexander V. Chernikov }
3282914bffb6SAlexander V. Chernikov 
3283914bffb6SAlexander V. Chernikov /*
3284914bffb6SAlexander V. Chernikov  * Free unneded array.
3285914bffb6SAlexander V. Chernikov  */
3286914bffb6SAlexander V. Chernikov static void
3287914bffb6SAlexander V. Chernikov ta_flush_mod_fhash(void *ta_buf)
3288914bffb6SAlexander V. Chernikov {
3289914bffb6SAlexander V. Chernikov 	struct mod_item *mi;
3290914bffb6SAlexander V. Chernikov 
3291914bffb6SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
3292914bffb6SAlexander V. Chernikov 	if (mi->main_ptr != NULL)
3293914bffb6SAlexander V. Chernikov 		free(mi->main_ptr, M_IPFW);
3294914bffb6SAlexander V. Chernikov }
3295914bffb6SAlexander V. Chernikov 
3296914bffb6SAlexander V. Chernikov struct table_algo flow_hash = {
3297914bffb6SAlexander V. Chernikov 	.name		= "flow:hash",
3298914bffb6SAlexander V. Chernikov 	.type		= IPFW_TABLE_FLOW,
329957a1cf95SAlexander V. Chernikov 	.flags		= TA_FLAG_DEFAULT,
330057a1cf95SAlexander V. Chernikov 	.ta_buf_size	= sizeof(struct ta_buf_fhash),
3301914bffb6SAlexander V. Chernikov 	.init		= ta_init_fhash,
3302914bffb6SAlexander V. Chernikov 	.destroy	= ta_destroy_fhash,
3303914bffb6SAlexander V. Chernikov 	.prepare_add	= ta_prepare_add_fhash,
3304914bffb6SAlexander V. Chernikov 	.prepare_del	= ta_prepare_del_fhash,
3305914bffb6SAlexander V. Chernikov 	.add		= ta_add_fhash,
3306914bffb6SAlexander V. Chernikov 	.del		= ta_del_fhash,
3307914bffb6SAlexander V. Chernikov 	.flush_entry	= ta_flush_fhash_entry,
3308914bffb6SAlexander V. Chernikov 	.foreach	= ta_foreach_fhash,
3309914bffb6SAlexander V. Chernikov 	.dump_tentry	= ta_dump_fhash_tentry,
3310914bffb6SAlexander V. Chernikov 	.find_tentry	= ta_find_fhash_tentry,
33115f379342SAlexander V. Chernikov 	.dump_tinfo	= ta_dump_fhash_tinfo,
3312b6ee846eSAlexander V. Chernikov 	.has_space	= ta_has_space_fhash,
3313914bffb6SAlexander V. Chernikov 	.prepare_mod	= ta_prepare_mod_fhash,
3314914bffb6SAlexander V. Chernikov 	.fill_mod	= ta_fill_mod_fhash,
3315914bffb6SAlexander V. Chernikov 	.modify		= ta_modify_fhash,
3316914bffb6SAlexander V. Chernikov 	.flush_mod	= ta_flush_mod_fhash,
3317914bffb6SAlexander V. Chernikov };
33183fe2ef91SAlexander V. Chernikov 
33199f7d47b0SAlexander V. Chernikov void
33200b565ac0SAlexander V. Chernikov ipfw_table_algo_init(struct ip_fw_chain *ch)
33219f7d47b0SAlexander V. Chernikov {
33220b565ac0SAlexander V. Chernikov 	size_t sz;
33230b565ac0SAlexander V. Chernikov 
33249f7d47b0SAlexander V. Chernikov 	/*
33259f7d47b0SAlexander V. Chernikov 	 * Register all algorithms presented here.
33269f7d47b0SAlexander V. Chernikov 	 */
33270b565ac0SAlexander V. Chernikov 	sz = sizeof(struct table_algo);
33280b565ac0SAlexander V. Chernikov 	ipfw_add_table_algo(ch, &cidr_radix, sz, &cidr_radix.idx);
33290b565ac0SAlexander V. Chernikov 	ipfw_add_table_algo(ch, &cidr_hash, sz, &cidr_hash.idx);
33300b565ac0SAlexander V. Chernikov 	ipfw_add_table_algo(ch, &iface_idx, sz, &iface_idx.idx);
3331b23d5de9SAlexander V. Chernikov 	ipfw_add_table_algo(ch, &number_array, sz, &number_array.idx);
3332914bffb6SAlexander V. Chernikov 	ipfw_add_table_algo(ch, &flow_hash, sz, &flow_hash.idx);
33339f7d47b0SAlexander V. Chernikov }
33349f7d47b0SAlexander V. Chernikov 
33359f7d47b0SAlexander V. Chernikov void
33360b565ac0SAlexander V. Chernikov ipfw_table_algo_destroy(struct ip_fw_chain *ch)
33379f7d47b0SAlexander V. Chernikov {
33380b565ac0SAlexander V. Chernikov 
33390b565ac0SAlexander V. Chernikov 	ipfw_del_table_algo(ch, cidr_radix.idx);
33400b565ac0SAlexander V. Chernikov 	ipfw_del_table_algo(ch, cidr_hash.idx);
33410b565ac0SAlexander V. Chernikov 	ipfw_del_table_algo(ch, iface_idx.idx);
3342b23d5de9SAlexander V. Chernikov 	ipfw_del_table_algo(ch, number_array.idx);
3343914bffb6SAlexander V. Chernikov 	ipfw_del_table_algo(ch, flow_hash.idx);
33449f7d47b0SAlexander V. Chernikov }
33459f7d47b0SAlexander V. Chernikov 
33469f7d47b0SAlexander V. Chernikov 
3347