xref: /freebsd/sys/netpfil/ipfw/ip_fw_table_algo.c (revision 2e324d29310062061eb52be2e972209070c99173)
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 #include <net/route.h>
529f7d47b0SAlexander V. Chernikov #include <net/vnet.h>
539f7d47b0SAlexander V. Chernikov 
549f7d47b0SAlexander V. Chernikov #include <netinet/in.h>
559f7d47b0SAlexander V. Chernikov #include <netinet/ip_var.h>	/* struct ipfw_rule_ref */
569f7d47b0SAlexander V. Chernikov #include <netinet/ip_fw.h>
579f7d47b0SAlexander V. Chernikov 
589f7d47b0SAlexander V. Chernikov #include <netpfil/ipfw/ip_fw_private.h>
59ea761a5dSAlexander V. Chernikov #include <netpfil/ipfw/ip_fw_table.h>
609f7d47b0SAlexander V. Chernikov 
619f7d47b0SAlexander V. Chernikov static MALLOC_DEFINE(M_IPFW_TBL, "ipfw_tbl", "IpFw tables");
629f7d47b0SAlexander V. Chernikov 
6368394ec8SAlexander V. Chernikov static int badd(const void *key, void *item, void *base, size_t nmemb,
6468394ec8SAlexander V. Chernikov     size_t size, int (*compar) (const void *, const void *));
6568394ec8SAlexander V. Chernikov static int bdel(const void *key, void *base, size_t nmemb, size_t size,
6668394ec8SAlexander V. Chernikov     int (*compar) (const void *, const void *));
6768394ec8SAlexander V. Chernikov 
6868394ec8SAlexander V. Chernikov 
6968394ec8SAlexander V. Chernikov /*
7068394ec8SAlexander V. Chernikov  * CIDR implementation using radix
7168394ec8SAlexander V. Chernikov  *
7268394ec8SAlexander V. Chernikov  */
7368394ec8SAlexander V. Chernikov 
749f7d47b0SAlexander V. Chernikov /*
759f7d47b0SAlexander V. Chernikov  * The radix code expects addr and mask to be array of bytes,
769f7d47b0SAlexander V. Chernikov  * with the first byte being the length of the array. rn_inithead
779f7d47b0SAlexander V. Chernikov  * is called with the offset in bits of the lookup key within the
789f7d47b0SAlexander V. Chernikov  * array. If we use a sockaddr_in as the underlying type,
799f7d47b0SAlexander V. Chernikov  * sin_len is conveniently located at offset 0, sin_addr is at
809f7d47b0SAlexander V. Chernikov  * offset 4 and normally aligned.
819f7d47b0SAlexander V. Chernikov  * But for portability, let's avoid assumption and make the code explicit
829f7d47b0SAlexander V. Chernikov  */
839f7d47b0SAlexander V. Chernikov #define KEY_LEN(v)	*((uint8_t *)&(v))
849f7d47b0SAlexander V. Chernikov /*
859f7d47b0SAlexander V. Chernikov  * Do not require radix to compare more than actual IPv4/IPv6 address
869f7d47b0SAlexander V. Chernikov  */
879f7d47b0SAlexander V. Chernikov #define KEY_LEN_INET	(offsetof(struct sockaddr_in, sin_addr) + sizeof(in_addr_t))
88e0a8b9eeSAlexander V. Chernikov #define KEY_LEN_INET6	(offsetof(struct sa_in6, sin6_addr) + sizeof(struct in6_addr))
899f7d47b0SAlexander V. Chernikov 
909f7d47b0SAlexander V. Chernikov #define OFF_LEN_INET	(8 * offsetof(struct sockaddr_in, sin_addr))
91e0a8b9eeSAlexander V. Chernikov #define OFF_LEN_INET6	(8 * offsetof(struct sa_in6, sin6_addr))
929f7d47b0SAlexander V. Chernikov 
93e0a8b9eeSAlexander V. Chernikov struct radix_cidr_entry {
949f7d47b0SAlexander V. Chernikov 	struct radix_node	rn[2];
95e0a8b9eeSAlexander V. Chernikov 	struct sockaddr_in	addr;
96e0a8b9eeSAlexander V. Chernikov 	uint32_t		value;
97e0a8b9eeSAlexander V. Chernikov 	uint8_t			masklen;
98e0a8b9eeSAlexander V. Chernikov };
99e0a8b9eeSAlexander V. Chernikov 
100e0a8b9eeSAlexander V. Chernikov struct sa_in6 {
101e0a8b9eeSAlexander V. Chernikov 	uint8_t			sin6_len;
102e0a8b9eeSAlexander V. Chernikov 	uint8_t			sin6_family;
103e0a8b9eeSAlexander V. Chernikov 	uint8_t			pad[2];
104e0a8b9eeSAlexander V. Chernikov 	struct in6_addr		sin6_addr;
105e0a8b9eeSAlexander V. Chernikov };
106e0a8b9eeSAlexander V. Chernikov 
107e0a8b9eeSAlexander V. Chernikov struct radix_cidr_xentry {
108e0a8b9eeSAlexander V. Chernikov 	struct radix_node	rn[2];
109e0a8b9eeSAlexander V. Chernikov 	struct sa_in6		addr6;
110e0a8b9eeSAlexander V. Chernikov 	uint32_t		value;
111e0a8b9eeSAlexander V. Chernikov 	uint8_t			masklen;
1129f7d47b0SAlexander V. Chernikov };
1139f7d47b0SAlexander V. Chernikov 
1149f7d47b0SAlexander V. Chernikov static int
1159f7d47b0SAlexander V. Chernikov ta_lookup_radix(struct table_info *ti, void *key, uint32_t keylen,
1169f7d47b0SAlexander V. Chernikov     uint32_t *val)
1179f7d47b0SAlexander V. Chernikov {
1189f7d47b0SAlexander V. Chernikov 	struct radix_node_head *rnh;
1199f7d47b0SAlexander V. Chernikov 
1209f7d47b0SAlexander V. Chernikov 	if (keylen == sizeof(in_addr_t)) {
121e0a8b9eeSAlexander V. Chernikov 		struct radix_cidr_entry *ent;
1229f7d47b0SAlexander V. Chernikov 		struct sockaddr_in sa;
1239f7d47b0SAlexander V. Chernikov 		KEY_LEN(sa) = KEY_LEN_INET;
1249f7d47b0SAlexander V. Chernikov 		sa.sin_addr.s_addr = *((in_addr_t *)key);
1259f7d47b0SAlexander V. Chernikov 		rnh = (struct radix_node_head *)ti->state;
126e0a8b9eeSAlexander V. Chernikov 		ent = (struct radix_cidr_entry *)(rnh->rnh_matchaddr(&sa, rnh));
1279f7d47b0SAlexander V. Chernikov 		if (ent != NULL) {
1289f7d47b0SAlexander V. Chernikov 			*val = ent->value;
1299f7d47b0SAlexander V. Chernikov 			return (1);
1309f7d47b0SAlexander V. Chernikov 		}
1319f7d47b0SAlexander V. Chernikov 	} else {
132e0a8b9eeSAlexander V. Chernikov 		struct radix_cidr_xentry *xent;
133e0a8b9eeSAlexander V. Chernikov 		struct sa_in6 sa6;
1349f7d47b0SAlexander V. Chernikov 		KEY_LEN(sa6) = KEY_LEN_INET6;
1359f7d47b0SAlexander V. Chernikov 		memcpy(&sa6.sin6_addr, key, sizeof(struct in6_addr));
1369f7d47b0SAlexander V. Chernikov 		rnh = (struct radix_node_head *)ti->xstate;
137e0a8b9eeSAlexander V. Chernikov 		xent = (struct radix_cidr_xentry *)(rnh->rnh_matchaddr(&sa6, rnh));
1389f7d47b0SAlexander V. Chernikov 		if (xent != NULL) {
1399f7d47b0SAlexander V. Chernikov 			*val = xent->value;
1409f7d47b0SAlexander V. Chernikov 			return (1);
1419f7d47b0SAlexander V. Chernikov 		}
1429f7d47b0SAlexander V. Chernikov 	}
1439f7d47b0SAlexander V. Chernikov 
1449f7d47b0SAlexander V. Chernikov 	return (0);
1459f7d47b0SAlexander V. Chernikov }
1469f7d47b0SAlexander V. Chernikov 
1479f7d47b0SAlexander V. Chernikov /*
1489f7d47b0SAlexander V. Chernikov  * New table
1499f7d47b0SAlexander V. Chernikov  */
1509f7d47b0SAlexander V. Chernikov static int
15168394ec8SAlexander V. Chernikov ta_init_radix(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
152914bffb6SAlexander V. Chernikov     char *data, uint8_t tflags)
1539f7d47b0SAlexander V. Chernikov {
1549f7d47b0SAlexander V. Chernikov 
1559f7d47b0SAlexander V. Chernikov 	if (!rn_inithead(&ti->state, OFF_LEN_INET))
1569f7d47b0SAlexander V. Chernikov 		return (ENOMEM);
1579f7d47b0SAlexander V. Chernikov 	if (!rn_inithead(&ti->xstate, OFF_LEN_INET6)) {
1589f7d47b0SAlexander V. Chernikov 		rn_detachhead(&ti->state);
1599f7d47b0SAlexander V. Chernikov 		return (ENOMEM);
1609f7d47b0SAlexander V. Chernikov 	}
1619f7d47b0SAlexander V. Chernikov 
1629f7d47b0SAlexander V. Chernikov 	*ta_state = NULL;
1639f7d47b0SAlexander V. Chernikov 	ti->lookup = ta_lookup_radix;
1649f7d47b0SAlexander V. Chernikov 
1659f7d47b0SAlexander V. Chernikov 	return (0);
1669f7d47b0SAlexander V. Chernikov }
1679f7d47b0SAlexander V. Chernikov 
1689f7d47b0SAlexander V. Chernikov static int
1699f7d47b0SAlexander V. Chernikov flush_table_entry(struct radix_node *rn, void *arg)
1709f7d47b0SAlexander V. Chernikov {
1719f7d47b0SAlexander V. Chernikov 	struct radix_node_head * const rnh = arg;
172e0a8b9eeSAlexander V. Chernikov 	struct radix_cidr_entry *ent;
1739f7d47b0SAlexander V. Chernikov 
174e0a8b9eeSAlexander V. Chernikov 	ent = (struct radix_cidr_entry *)
1759f7d47b0SAlexander V. Chernikov 	    rnh->rnh_deladdr(rn->rn_key, rn->rn_mask, rnh);
1769f7d47b0SAlexander V. Chernikov 	if (ent != NULL)
1779f7d47b0SAlexander V. Chernikov 		free(ent, M_IPFW_TBL);
1789f7d47b0SAlexander V. Chernikov 	return (0);
1799f7d47b0SAlexander V. Chernikov }
1809f7d47b0SAlexander V. Chernikov 
1819f7d47b0SAlexander V. Chernikov static void
1829f7d47b0SAlexander V. Chernikov ta_destroy_radix(void *ta_state, struct table_info *ti)
1839f7d47b0SAlexander V. Chernikov {
1849f7d47b0SAlexander V. Chernikov 	struct radix_node_head *rnh;
1859f7d47b0SAlexander V. Chernikov 
1869f7d47b0SAlexander V. Chernikov 	rnh = (struct radix_node_head *)(ti->state);
1879f7d47b0SAlexander V. Chernikov 	rnh->rnh_walktree(rnh, flush_table_entry, rnh);
1889f7d47b0SAlexander V. Chernikov 	rn_detachhead(&ti->state);
1899f7d47b0SAlexander V. Chernikov 
1909f7d47b0SAlexander V. Chernikov 	rnh = (struct radix_node_head *)(ti->xstate);
1919f7d47b0SAlexander V. Chernikov 	rnh->rnh_walktree(rnh, flush_table_entry, rnh);
1929f7d47b0SAlexander V. Chernikov 	rn_detachhead(&ti->xstate);
1939f7d47b0SAlexander V. Chernikov }
1949f7d47b0SAlexander V. Chernikov 
1959f7d47b0SAlexander V. Chernikov static int
19681d3153dSAlexander V. Chernikov ta_dump_radix_tentry(void *ta_state, struct table_info *ti, void *e,
19781d3153dSAlexander V. Chernikov     ipfw_obj_tentry *tent)
1989f7d47b0SAlexander V. Chernikov {
199e0a8b9eeSAlexander V. Chernikov 	struct radix_cidr_entry *n;
200e0a8b9eeSAlexander V. Chernikov 	struct radix_cidr_xentry *xn;
2019f7d47b0SAlexander V. Chernikov 
202e0a8b9eeSAlexander V. Chernikov 	n = (struct radix_cidr_entry *)e;
2039f7d47b0SAlexander V. Chernikov 
2049f7d47b0SAlexander V. Chernikov 	/* Guess IPv4/IPv6 radix by sockaddr family */
2059f7d47b0SAlexander V. Chernikov 	if (n->addr.sin_family == AF_INET) {
20681d3153dSAlexander V. Chernikov 		tent->k.addr.s_addr = n->addr.sin_addr.s_addr;
207e0a8b9eeSAlexander V. Chernikov 		tent->masklen = n->masklen;
20881d3153dSAlexander V. Chernikov 		tent->subtype = AF_INET;
20981d3153dSAlexander V. Chernikov 		tent->value = n->value;
2109f7d47b0SAlexander V. Chernikov #ifdef INET6
2119f7d47b0SAlexander V. Chernikov 	} else {
212e0a8b9eeSAlexander V. Chernikov 		xn = (struct radix_cidr_xentry *)e;
213e0a8b9eeSAlexander V. Chernikov 		memcpy(&tent->k, &xn->addr6.sin6_addr, sizeof(struct in6_addr));
214e0a8b9eeSAlexander V. Chernikov 		tent->masklen = xn->masklen;
21581d3153dSAlexander V. Chernikov 		tent->subtype = AF_INET6;
21681d3153dSAlexander V. Chernikov 		tent->value = xn->value;
2179f7d47b0SAlexander V. Chernikov #endif
2189f7d47b0SAlexander V. Chernikov 	}
2199f7d47b0SAlexander V. Chernikov 
2209f7d47b0SAlexander V. Chernikov 	return (0);
2219f7d47b0SAlexander V. Chernikov }
2229f7d47b0SAlexander V. Chernikov 
22381d3153dSAlexander V. Chernikov static int
224914bffb6SAlexander V. Chernikov ta_find_radix_tentry(void *ta_state, struct table_info *ti,
225914bffb6SAlexander V. Chernikov     ipfw_obj_tentry *tent)
22681d3153dSAlexander V. Chernikov {
22781d3153dSAlexander V. Chernikov 	struct radix_node_head *rnh;
22881d3153dSAlexander V. Chernikov 	void *e;
22981d3153dSAlexander V. Chernikov 
23081d3153dSAlexander V. Chernikov 	e = NULL;
231914bffb6SAlexander V. Chernikov 	if (tent->subtype == AF_INET) {
23281d3153dSAlexander V. Chernikov 		struct sockaddr_in sa;
23381d3153dSAlexander V. Chernikov 		KEY_LEN(sa) = KEY_LEN_INET;
234914bffb6SAlexander V. Chernikov 		sa.sin_addr.s_addr = tent->k.addr.s_addr;
23581d3153dSAlexander V. Chernikov 		rnh = (struct radix_node_head *)ti->state;
23681d3153dSAlexander V. Chernikov 		e = rnh->rnh_matchaddr(&sa, rnh);
23781d3153dSAlexander V. Chernikov 	} else {
238e0a8b9eeSAlexander V. Chernikov 		struct sa_in6 sa6;
23981d3153dSAlexander V. Chernikov 		KEY_LEN(sa6) = KEY_LEN_INET6;
240914bffb6SAlexander V. Chernikov 		memcpy(&sa6.sin6_addr, &tent->k.addr6, sizeof(struct in6_addr));
24181d3153dSAlexander V. Chernikov 		rnh = (struct radix_node_head *)ti->xstate;
24281d3153dSAlexander V. Chernikov 		e = rnh->rnh_matchaddr(&sa6, rnh);
24381d3153dSAlexander V. Chernikov 	}
24481d3153dSAlexander V. Chernikov 
24581d3153dSAlexander V. Chernikov 	if (e != NULL) {
24681d3153dSAlexander V. Chernikov 		ta_dump_radix_tentry(ta_state, ti, e, tent);
24781d3153dSAlexander V. Chernikov 		return (0);
24881d3153dSAlexander V. Chernikov 	}
24981d3153dSAlexander V. Chernikov 
25081d3153dSAlexander V. Chernikov 	return (ENOENT);
25181d3153dSAlexander V. Chernikov }
25281d3153dSAlexander V. Chernikov 
2539f7d47b0SAlexander V. Chernikov static void
2549f7d47b0SAlexander V. Chernikov ta_foreach_radix(void *ta_state, struct table_info *ti, ta_foreach_f *f,
2559f7d47b0SAlexander V. Chernikov     void *arg)
2569f7d47b0SAlexander V. Chernikov {
2579f7d47b0SAlexander V. Chernikov 	struct radix_node_head *rnh;
2589f7d47b0SAlexander V. Chernikov 
2599f7d47b0SAlexander V. Chernikov 	rnh = (struct radix_node_head *)(ti->state);
2609f7d47b0SAlexander V. Chernikov 	rnh->rnh_walktree(rnh, (walktree_f_t *)f, arg);
2619f7d47b0SAlexander V. Chernikov 
2629f7d47b0SAlexander V. Chernikov 	rnh = (struct radix_node_head *)(ti->xstate);
2639f7d47b0SAlexander V. Chernikov 	rnh->rnh_walktree(rnh, (walktree_f_t *)f, arg);
2649f7d47b0SAlexander V. Chernikov }
2659f7d47b0SAlexander V. Chernikov 
2669f7d47b0SAlexander V. Chernikov 
2679f7d47b0SAlexander V. Chernikov struct ta_buf_cidr
2689f7d47b0SAlexander V. Chernikov {
2699f7d47b0SAlexander V. Chernikov 	struct sockaddr	*addr_ptr;
2709f7d47b0SAlexander V. Chernikov 	struct sockaddr	*mask_ptr;
2719f7d47b0SAlexander V. Chernikov 	void *ent_ptr;
2729f7d47b0SAlexander V. Chernikov 	union {
2739f7d47b0SAlexander V. Chernikov 		struct {
2749f7d47b0SAlexander V. Chernikov 			struct sockaddr_in sa;
2759f7d47b0SAlexander V. Chernikov 			struct sockaddr_in ma;
2769f7d47b0SAlexander V. Chernikov 		} a4;
2779f7d47b0SAlexander V. Chernikov 		struct {
278e0a8b9eeSAlexander V. Chernikov 			struct sa_in6 sa;
279e0a8b9eeSAlexander V. Chernikov 			struct sa_in6 ma;
2809f7d47b0SAlexander V. Chernikov 		} a6;
2819f7d47b0SAlexander V. Chernikov 	} addr;
2829f7d47b0SAlexander V. Chernikov };
2839f7d47b0SAlexander V. Chernikov 
2849f7d47b0SAlexander V. Chernikov #ifdef INET6
2859f7d47b0SAlexander V. Chernikov static inline void
2869f7d47b0SAlexander V. Chernikov ipv6_writemask(struct in6_addr *addr6, uint8_t mask)
2879f7d47b0SAlexander V. Chernikov {
2889f7d47b0SAlexander V. Chernikov 	uint32_t *cp;
2899f7d47b0SAlexander V. Chernikov 
2909f7d47b0SAlexander V. Chernikov 	for (cp = (uint32_t *)addr6; mask >= 32; mask -= 32)
2919f7d47b0SAlexander V. Chernikov 		*cp++ = 0xFFFFFFFF;
2929f7d47b0SAlexander V. Chernikov 	*cp = htonl(mask ? ~((1 << (32 - mask)) - 1) : 0);
2939f7d47b0SAlexander V. Chernikov }
2949f7d47b0SAlexander V. Chernikov #endif
2959f7d47b0SAlexander V. Chernikov 
296*2e324d29SAlexander V. Chernikov static void
297*2e324d29SAlexander V. Chernikov tei_to_sockaddr_ent(struct tentry_info *tei, struct sockaddr *sa,
298*2e324d29SAlexander V. Chernikov     struct sockaddr *ma, int *set_mask)
299*2e324d29SAlexander V. Chernikov {
300*2e324d29SAlexander V. Chernikov 	int mlen;
301*2e324d29SAlexander V. Chernikov 	struct sockaddr_in *addr, *mask;
302*2e324d29SAlexander V. Chernikov 	struct sockaddr_in6 *addr6, *mask6;
303*2e324d29SAlexander V. Chernikov 	in_addr_t a4;
304*2e324d29SAlexander V. Chernikov 
305*2e324d29SAlexander V. Chernikov 	mlen = tei->masklen;
306*2e324d29SAlexander V. Chernikov 
307*2e324d29SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
308*2e324d29SAlexander V. Chernikov #ifdef INET
309*2e324d29SAlexander V. Chernikov 		addr = (struct sockaddr_in *)sa;
310*2e324d29SAlexander V. Chernikov 		mask = (struct sockaddr_in *)ma;
311*2e324d29SAlexander V. Chernikov 		/* Set 'total' structure length */
312*2e324d29SAlexander V. Chernikov 		KEY_LEN(*addr) = KEY_LEN_INET;
313*2e324d29SAlexander V. Chernikov 		KEY_LEN(*mask) = KEY_LEN_INET;
314*2e324d29SAlexander V. Chernikov 		addr->sin_family = AF_INET;
315*2e324d29SAlexander V. Chernikov 		mask->sin_addr.s_addr =
316*2e324d29SAlexander V. Chernikov 		    htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);
317*2e324d29SAlexander V. Chernikov 		a4 = *((in_addr_t *)tei->paddr);
318*2e324d29SAlexander V. Chernikov 		addr->sin_addr.s_addr = a4 & mask->sin_addr.s_addr;
319*2e324d29SAlexander V. Chernikov 		if (mlen != 32)
320*2e324d29SAlexander V. Chernikov 			*set_mask = 1;
321*2e324d29SAlexander V. Chernikov 		else
322*2e324d29SAlexander V. Chernikov 			*set_mask = 0;
323*2e324d29SAlexander V. Chernikov #endif
324*2e324d29SAlexander V. Chernikov #ifdef INET6
325*2e324d29SAlexander V. Chernikov 	} else if (tei->subtype == AF_INET6) {
326*2e324d29SAlexander V. Chernikov 		/* IPv6 case */
327*2e324d29SAlexander V. Chernikov 		addr6 = (struct sockaddr_in6 *)sa;
328*2e324d29SAlexander V. Chernikov 		mask6 = (struct sockaddr_in6 *)ma;
329*2e324d29SAlexander V. Chernikov 		/* Set 'total' structure length */
330*2e324d29SAlexander V. Chernikov 		KEY_LEN(*addr6) = KEY_LEN_INET6;
331*2e324d29SAlexander V. Chernikov 		KEY_LEN(*mask6) = KEY_LEN_INET6;
332*2e324d29SAlexander V. Chernikov 		addr6->sin6_family = AF_INET6;
333*2e324d29SAlexander V. Chernikov 		ipv6_writemask(&mask6->sin6_addr, mlen);
334*2e324d29SAlexander V. Chernikov 		memcpy(&addr6->sin6_addr, tei->paddr, sizeof(struct in6_addr));
335*2e324d29SAlexander V. Chernikov 		APPLY_MASK(&addr6->sin6_addr, &mask6->sin6_addr);
336*2e324d29SAlexander V. Chernikov 		if (mlen != 128)
337*2e324d29SAlexander V. Chernikov 			*set_mask = 1;
338*2e324d29SAlexander V. Chernikov 		else
339*2e324d29SAlexander V. Chernikov 			*set_mask = 0;
340*2e324d29SAlexander V. Chernikov 	}
341*2e324d29SAlexander V. Chernikov #endif
342*2e324d29SAlexander V. Chernikov }
3439f7d47b0SAlexander V. Chernikov 
3449f7d47b0SAlexander V. Chernikov static int
34568394ec8SAlexander V. Chernikov ta_prepare_add_cidr(struct ip_fw_chain *ch, struct tentry_info *tei,
34668394ec8SAlexander V. Chernikov     void *ta_buf)
3479f7d47b0SAlexander V. Chernikov {
3489f7d47b0SAlexander V. Chernikov 	struct ta_buf_cidr *tb;
349e0a8b9eeSAlexander V. Chernikov 	struct radix_cidr_entry *ent;
350e0a8b9eeSAlexander V. Chernikov 	struct radix_cidr_xentry *xent;
351*2e324d29SAlexander V. Chernikov 	struct sockaddr *addr, *mask;
352*2e324d29SAlexander V. Chernikov 	int mlen, set_mask;
3539f7d47b0SAlexander V. Chernikov 
3549f7d47b0SAlexander V. Chernikov 	tb = (struct ta_buf_cidr *)ta_buf;
3559f7d47b0SAlexander V. Chernikov 	memset(tb, 0, sizeof(struct ta_buf_cidr));
3569f7d47b0SAlexander V. Chernikov 
3579f7d47b0SAlexander V. Chernikov 	mlen = tei->masklen;
358*2e324d29SAlexander V. Chernikov 	set_mask = 0;
3599f7d47b0SAlexander V. Chernikov 
360ac35ff17SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
3619f7d47b0SAlexander V. Chernikov #ifdef INET
3629f7d47b0SAlexander V. Chernikov 		if (mlen > 32)
3639f7d47b0SAlexander V. Chernikov 			return (EINVAL);
3649f7d47b0SAlexander V. Chernikov 		ent = malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO);
3659f7d47b0SAlexander V. Chernikov 		ent->value = tei->value;
366e0a8b9eeSAlexander V. Chernikov 		ent->masklen = mlen;
367*2e324d29SAlexander V. Chernikov 
368*2e324d29SAlexander V. Chernikov 		addr = (struct sockaddr *)&ent->addr;
369*2e324d29SAlexander V. Chernikov 		mask = (struct sockaddr *)&tb->addr.a4.ma;
3709f7d47b0SAlexander V. Chernikov 		tb->ent_ptr = ent;
3719f7d47b0SAlexander V. Chernikov #endif
3729f7d47b0SAlexander V. Chernikov #ifdef INET6
373ac35ff17SAlexander V. Chernikov 	} else if (tei->subtype == AF_INET6) {
3749f7d47b0SAlexander V. Chernikov 		/* IPv6 case */
3759f7d47b0SAlexander V. Chernikov 		if (mlen > 128)
3769f7d47b0SAlexander V. Chernikov 			return (EINVAL);
3779f7d47b0SAlexander V. Chernikov 		xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO);
3789f7d47b0SAlexander V. Chernikov 		xent->value = tei->value;
379e0a8b9eeSAlexander V. Chernikov 		xent->masklen = mlen;
380*2e324d29SAlexander V. Chernikov 
381*2e324d29SAlexander V. Chernikov 		addr = (struct sockaddr *)&xent->addr6;
382*2e324d29SAlexander V. Chernikov 		mask = (struct sockaddr *)&tb->addr.a6.ma;
3839f7d47b0SAlexander V. Chernikov 		tb->ent_ptr = xent;
3849f7d47b0SAlexander V. Chernikov #endif
3859f7d47b0SAlexander V. Chernikov 	} else {
3869f7d47b0SAlexander V. Chernikov 		/* Unknown CIDR type */
3879f7d47b0SAlexander V. Chernikov 		return (EINVAL);
3889f7d47b0SAlexander V. Chernikov 	}
3899f7d47b0SAlexander V. Chernikov 
390*2e324d29SAlexander V. Chernikov 	tei_to_sockaddr_ent(tei, addr, mask, &set_mask);
391*2e324d29SAlexander V. Chernikov 	/* Set pointers */
392*2e324d29SAlexander V. Chernikov 	tb->addr_ptr = addr;
393*2e324d29SAlexander V. Chernikov 	if (set_mask != 0)
394*2e324d29SAlexander V. Chernikov 		tb->mask_ptr = mask;
395*2e324d29SAlexander V. Chernikov 
3969f7d47b0SAlexander V. Chernikov 	return (0);
3979f7d47b0SAlexander V. Chernikov }
3989f7d47b0SAlexander V. Chernikov 
3999f7d47b0SAlexander V. Chernikov static int
400adea6201SAlexander V. Chernikov ta_add_cidr(void *ta_state, struct table_info *ti, struct tentry_info *tei,
401adea6201SAlexander V. Chernikov     void *ta_buf, uint64_t *pflags, uint32_t *pnum)
4029f7d47b0SAlexander V. Chernikov {
4039f7d47b0SAlexander V. Chernikov 	struct radix_node_head *rnh;
4049f7d47b0SAlexander V. Chernikov 	struct radix_node *rn;
4059f7d47b0SAlexander V. Chernikov 	struct ta_buf_cidr *tb;
406ac35ff17SAlexander V. Chernikov 	uint32_t value;
4079f7d47b0SAlexander V. Chernikov 
4089f7d47b0SAlexander V. Chernikov 	tb = (struct ta_buf_cidr *)ta_buf;
4099f7d47b0SAlexander V. Chernikov 
410ac35ff17SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
4119f7d47b0SAlexander V. Chernikov 		rnh = ti->state;
4129f7d47b0SAlexander V. Chernikov 	else
4139f7d47b0SAlexander V. Chernikov 		rnh = ti->xstate;
4149f7d47b0SAlexander V. Chernikov 
4159f7d47b0SAlexander V. Chernikov 	rn = rnh->rnh_addaddr(tb->addr_ptr, tb->mask_ptr, rnh, tb->ent_ptr);
4169f7d47b0SAlexander V. Chernikov 
417ac35ff17SAlexander V. Chernikov 	if (rn == NULL) {
418ac35ff17SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
4199f7d47b0SAlexander V. Chernikov 			return (EEXIST);
420ac35ff17SAlexander V. Chernikov 		/* Record already exists. Update value if we're asked to */
421ac35ff17SAlexander V. Chernikov 		rn = rnh->rnh_lookup(tb->addr_ptr, tb->mask_ptr, rnh);
422ac35ff17SAlexander V. Chernikov 		if (rn == NULL) {
423ac35ff17SAlexander V. Chernikov 
424ac35ff17SAlexander V. Chernikov 			/*
425ac35ff17SAlexander V. Chernikov 			 * Radix may have failed addition for other reasons
426ac35ff17SAlexander V. Chernikov 			 * like failure in mask allocation code.
427ac35ff17SAlexander V. Chernikov 			 */
428ac35ff17SAlexander V. Chernikov 			return (EINVAL);
429ac35ff17SAlexander V. Chernikov 		}
430ac35ff17SAlexander V. Chernikov 
431ac35ff17SAlexander V. Chernikov 		if (tei->subtype == AF_INET) {
432ac35ff17SAlexander V. Chernikov 			/* IPv4. */
433e0a8b9eeSAlexander V. Chernikov 			value = ((struct radix_cidr_entry *)tb->ent_ptr)->value;
434e0a8b9eeSAlexander V. Chernikov 			((struct radix_cidr_entry *)rn)->value = value;
435ac35ff17SAlexander V. Chernikov 		} else {
436ac35ff17SAlexander V. Chernikov 			/* IPv6 */
437e0a8b9eeSAlexander V. Chernikov 			value = ((struct radix_cidr_xentry *)tb->ent_ptr)->value;
438e0a8b9eeSAlexander V. Chernikov 			((struct radix_cidr_xentry *)rn)->value = value;
439ac35ff17SAlexander V. Chernikov 		}
440ac35ff17SAlexander V. Chernikov 
441ac35ff17SAlexander V. Chernikov 		/* Indicate that update has happened instead of addition */
442ac35ff17SAlexander V. Chernikov 		tei->flags |= TEI_FLAGS_UPDATED;
443adea6201SAlexander V. Chernikov 		*pnum = 0;
444e0a8b9eeSAlexander V. Chernikov 
445e0a8b9eeSAlexander V. Chernikov 		return (0);
446ac35ff17SAlexander V. Chernikov 	}
447ac35ff17SAlexander V. Chernikov 
448ac35ff17SAlexander V. Chernikov 	tb->ent_ptr = NULL;
449adea6201SAlexander V. Chernikov 	*pnum = 1;
4509f7d47b0SAlexander V. Chernikov 
4519f7d47b0SAlexander V. Chernikov 	return (0);
4529f7d47b0SAlexander V. Chernikov }
4539f7d47b0SAlexander V. Chernikov 
4549f7d47b0SAlexander V. Chernikov static int
45568394ec8SAlexander V. Chernikov ta_prepare_del_cidr(struct ip_fw_chain *ch, struct tentry_info *tei,
45668394ec8SAlexander V. Chernikov     void *ta_buf)
4579f7d47b0SAlexander V. Chernikov {
4589f7d47b0SAlexander V. Chernikov 	struct ta_buf_cidr *tb;
459*2e324d29SAlexander V. Chernikov 	struct sockaddr *addr, *mask;
460*2e324d29SAlexander V. Chernikov 	int mlen, set_mask;
4619f7d47b0SAlexander V. Chernikov 
4629f7d47b0SAlexander V. Chernikov 	tb = (struct ta_buf_cidr *)ta_buf;
4639f7d47b0SAlexander V. Chernikov 	memset(tb, 0, sizeof(struct ta_buf_cidr));
4649f7d47b0SAlexander V. Chernikov 
4659f7d47b0SAlexander V. Chernikov 	mlen = tei->masklen;
466*2e324d29SAlexander V. Chernikov 	set_mask = 0;
4679f7d47b0SAlexander V. Chernikov 
468ac35ff17SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
469e0a8b9eeSAlexander V. Chernikov 		if (mlen > 32)
470e0a8b9eeSAlexander V. Chernikov 			return (EINVAL);
471*2e324d29SAlexander V. Chernikov 
472*2e324d29SAlexander V. Chernikov 		addr = (struct sockaddr *)&tb->addr.a4.sa;
473*2e324d29SAlexander V. Chernikov 		mask = (struct sockaddr *)&tb->addr.a4.ma;
4749f7d47b0SAlexander V. Chernikov #ifdef INET6
475ac35ff17SAlexander V. Chernikov 	} else if (tei->subtype == AF_INET6) {
4769f7d47b0SAlexander V. Chernikov 		if (mlen > 128)
4779f7d47b0SAlexander V. Chernikov 			return (EINVAL);
478*2e324d29SAlexander V. Chernikov 
479*2e324d29SAlexander V. Chernikov 		addr = (struct sockaddr *)&tb->addr.a6.sa;
480*2e324d29SAlexander V. Chernikov 		mask = (struct sockaddr *)&tb->addr.a6.ma;
4819f7d47b0SAlexander V. Chernikov #endif
4829f7d47b0SAlexander V. Chernikov 	} else
4839f7d47b0SAlexander V. Chernikov 		return (EINVAL);
4849f7d47b0SAlexander V. Chernikov 
485*2e324d29SAlexander V. Chernikov 	tei_to_sockaddr_ent(tei, addr, mask, &set_mask);
486*2e324d29SAlexander V. Chernikov 	tb->addr_ptr = addr;
487*2e324d29SAlexander V. Chernikov 	if (set_mask != 0)
488*2e324d29SAlexander V. Chernikov 		tb->mask_ptr = mask;
489*2e324d29SAlexander V. Chernikov 
4909f7d47b0SAlexander V. Chernikov 	return (0);
4919f7d47b0SAlexander V. Chernikov }
4929f7d47b0SAlexander V. Chernikov 
4939f7d47b0SAlexander V. Chernikov static int
494adea6201SAlexander V. Chernikov ta_del_cidr(void *ta_state, struct table_info *ti, struct tentry_info *tei,
495adea6201SAlexander V. Chernikov     void *ta_buf, uint64_t *pflags, uint32_t *pnum)
4969f7d47b0SAlexander V. Chernikov {
4979f7d47b0SAlexander V. Chernikov 	struct radix_node_head *rnh;
4989f7d47b0SAlexander V. Chernikov 	struct radix_node *rn;
4999f7d47b0SAlexander V. Chernikov 	struct ta_buf_cidr *tb;
5009f7d47b0SAlexander V. Chernikov 
5019f7d47b0SAlexander V. Chernikov 	tb = (struct ta_buf_cidr *)ta_buf;
5029f7d47b0SAlexander V. Chernikov 
503ac35ff17SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
5049f7d47b0SAlexander V. Chernikov 		rnh = ti->state;
5059f7d47b0SAlexander V. Chernikov 	else
5069f7d47b0SAlexander V. Chernikov 		rnh = ti->xstate;
5079f7d47b0SAlexander V. Chernikov 
5089f7d47b0SAlexander V. Chernikov 	rn = rnh->rnh_deladdr(tb->addr_ptr, tb->mask_ptr, rnh);
5099f7d47b0SAlexander V. Chernikov 
5109f7d47b0SAlexander V. Chernikov 	tb->ent_ptr = rn;
5119f7d47b0SAlexander V. Chernikov 
5129f7d47b0SAlexander V. Chernikov 	if (rn == NULL)
51381d3153dSAlexander V. Chernikov 		return (ENOENT);
5149f7d47b0SAlexander V. Chernikov 
515adea6201SAlexander V. Chernikov 	*pnum = 1;
516adea6201SAlexander V. Chernikov 
5179f7d47b0SAlexander V. Chernikov 	return (0);
5189f7d47b0SAlexander V. Chernikov }
5199f7d47b0SAlexander V. Chernikov 
5209f7d47b0SAlexander V. Chernikov static void
52168394ec8SAlexander V. Chernikov ta_flush_cidr_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
52268394ec8SAlexander V. Chernikov     void *ta_buf)
5239f7d47b0SAlexander V. Chernikov {
5249f7d47b0SAlexander V. Chernikov 	struct ta_buf_cidr *tb;
5259f7d47b0SAlexander V. Chernikov 
5269f7d47b0SAlexander V. Chernikov 	tb = (struct ta_buf_cidr *)ta_buf;
5279f7d47b0SAlexander V. Chernikov 
528ac35ff17SAlexander V. Chernikov 	if (tb->ent_ptr != NULL)
5299f7d47b0SAlexander V. Chernikov 		free(tb->ent_ptr, M_IPFW_TBL);
5309f7d47b0SAlexander V. Chernikov }
5319f7d47b0SAlexander V. Chernikov 
53274b941f0SAlexander V. Chernikov struct table_algo cidr_radix = {
533adea6201SAlexander V. Chernikov 	.name		= "cidr:radix",
5349d099b4fSAlexander V. Chernikov 	.type		= IPFW_TABLE_CIDR,
53557a1cf95SAlexander V. Chernikov 	.flags		= TA_FLAG_DEFAULT,
53657a1cf95SAlexander V. Chernikov 	.ta_buf_size	= sizeof(struct ta_buf_cidr),
5379f7d47b0SAlexander V. Chernikov 	.init		= ta_init_radix,
5389f7d47b0SAlexander V. Chernikov 	.destroy	= ta_destroy_radix,
5399f7d47b0SAlexander V. Chernikov 	.prepare_add	= ta_prepare_add_cidr,
5409f7d47b0SAlexander V. Chernikov 	.prepare_del	= ta_prepare_del_cidr,
5419f7d47b0SAlexander V. Chernikov 	.add		= ta_add_cidr,
5429f7d47b0SAlexander V. Chernikov 	.del		= ta_del_cidr,
5439f7d47b0SAlexander V. Chernikov 	.flush_entry	= ta_flush_cidr_entry,
5449f7d47b0SAlexander V. Chernikov 	.foreach	= ta_foreach_radix,
54581d3153dSAlexander V. Chernikov 	.dump_tentry	= ta_dump_radix_tentry,
54681d3153dSAlexander V. Chernikov 	.find_tentry	= ta_find_radix_tentry,
5479f7d47b0SAlexander V. Chernikov };
5489f7d47b0SAlexander V. Chernikov 
5499f7d47b0SAlexander V. Chernikov 
5509f7d47b0SAlexander V. Chernikov /*
55174b941f0SAlexander V. Chernikov  * cidr:hash cmds
55274b941f0SAlexander V. Chernikov  *
55374b941f0SAlexander V. Chernikov  *
55474b941f0SAlexander V. Chernikov  * ti->data:
55574b941f0SAlexander V. Chernikov  * [inv.mask4][inv.mask6][log2hsize4][log2hsize6]
55674b941f0SAlexander V. Chernikov  * [        8][        8[          8][         8]
55774b941f0SAlexander V. Chernikov  *
55874b941f0SAlexander V. Chernikov  * inv.mask4: 32 - mask
55974b941f0SAlexander V. Chernikov  * inv.mask6:
56074b941f0SAlexander V. Chernikov  * 1) _slow lookup: mask
56174b941f0SAlexander V. Chernikov  * 2) _aligned: (128 - mask) / 8
56274b941f0SAlexander V. Chernikov  * 3) _64: 8
563ce2817b5SAlexander V. Chernikov  *
564ce2817b5SAlexander V. Chernikov  *
565ce2817b5SAlexander V. Chernikov  * pflags:
566ce2817b5SAlexander V. Chernikov  * [v4=1/v6=0][hsize]
567ce2817b5SAlexander V. Chernikov  * [       32][   32]
56874b941f0SAlexander V. Chernikov  */
56974b941f0SAlexander V. Chernikov 
57074b941f0SAlexander V. Chernikov struct chashentry;
57174b941f0SAlexander V. Chernikov 
57274b941f0SAlexander V. Chernikov SLIST_HEAD(chashbhead, chashentry);
57374b941f0SAlexander V. Chernikov 
57474b941f0SAlexander V. Chernikov struct chash_cfg {
57574b941f0SAlexander V. Chernikov 	struct chashbhead *head4;
57674b941f0SAlexander V. Chernikov 	struct chashbhead *head6;
57774b941f0SAlexander V. Chernikov 	size_t	size4;
57874b941f0SAlexander V. Chernikov 	size_t	size6;
579ce2817b5SAlexander V. Chernikov 	size_t	items4;
580ce2817b5SAlexander V. Chernikov 	size_t	items6;
58174b941f0SAlexander V. Chernikov 	uint8_t	mask4;
58274b941f0SAlexander V. Chernikov 	uint8_t	mask6;
58374b941f0SAlexander V. Chernikov };
58474b941f0SAlexander V. Chernikov 
58574b941f0SAlexander V. Chernikov struct chashentry {
58674b941f0SAlexander V. Chernikov 	SLIST_ENTRY(chashentry)	next;
58774b941f0SAlexander V. Chernikov 	uint32_t	value;
58874b941f0SAlexander V. Chernikov 	uint32_t	type;
58974b941f0SAlexander V. Chernikov 	union {
59074b941f0SAlexander V. Chernikov 		uint32_t	a4;	/* Host format */
59174b941f0SAlexander V. Chernikov 		struct in6_addr	a6;	/* Network format */
59274b941f0SAlexander V. Chernikov 	} a;
59374b941f0SAlexander V. Chernikov };
59474b941f0SAlexander V. Chernikov 
59574b941f0SAlexander V. Chernikov static __inline uint32_t
59674b941f0SAlexander V. Chernikov hash_ip(uint32_t addr, int hsize)
59774b941f0SAlexander V. Chernikov {
59874b941f0SAlexander V. Chernikov 
59974b941f0SAlexander V. Chernikov 	return (addr % (hsize - 1));
60074b941f0SAlexander V. Chernikov }
60174b941f0SAlexander V. Chernikov 
60274b941f0SAlexander V. Chernikov static __inline uint32_t
60374b941f0SAlexander V. Chernikov hash_ip6(struct in6_addr *addr6, int hsize)
60474b941f0SAlexander V. Chernikov {
60574b941f0SAlexander V. Chernikov 	uint32_t i;
60674b941f0SAlexander V. Chernikov 
60774b941f0SAlexander V. Chernikov 	i = addr6->s6_addr32[0] ^ addr6->s6_addr32[1] ^
60874b941f0SAlexander V. Chernikov 	    addr6->s6_addr32[2] ^ addr6->s6_addr32[3];
60974b941f0SAlexander V. Chernikov 
61074b941f0SAlexander V. Chernikov 	return (i % (hsize - 1));
61174b941f0SAlexander V. Chernikov }
61274b941f0SAlexander V. Chernikov 
61374b941f0SAlexander V. Chernikov 
61474b941f0SAlexander V. Chernikov static __inline uint16_t
61574b941f0SAlexander V. Chernikov hash_ip64(struct in6_addr *addr6, int hsize)
61674b941f0SAlexander V. Chernikov {
61774b941f0SAlexander V. Chernikov 	uint32_t i;
61874b941f0SAlexander V. Chernikov 
61974b941f0SAlexander V. Chernikov 	i = addr6->s6_addr32[0] ^ addr6->s6_addr32[1];
62074b941f0SAlexander V. Chernikov 
62174b941f0SAlexander V. Chernikov 	return (i % (hsize - 1));
62274b941f0SAlexander V. Chernikov }
62374b941f0SAlexander V. Chernikov 
62474b941f0SAlexander V. Chernikov 
62574b941f0SAlexander V. Chernikov static __inline uint32_t
62674b941f0SAlexander V. Chernikov hash_ip6_slow(struct in6_addr *addr6, void *key, int mask, int hsize)
62774b941f0SAlexander V. Chernikov {
62874b941f0SAlexander V. Chernikov 	struct in6_addr mask6;
62974b941f0SAlexander V. Chernikov 
63074b941f0SAlexander V. Chernikov 	ipv6_writemask(&mask6, mask);
63174b941f0SAlexander V. Chernikov 	memcpy(addr6, key, sizeof(struct in6_addr));
63274b941f0SAlexander V. Chernikov 	APPLY_MASK(addr6, &mask6);
63374b941f0SAlexander V. Chernikov 	return (hash_ip6(addr6, hsize));
63474b941f0SAlexander V. Chernikov }
63574b941f0SAlexander V. Chernikov 
63674b941f0SAlexander V. Chernikov static __inline uint32_t
63774b941f0SAlexander V. Chernikov hash_ip6_al(struct in6_addr *addr6, void *key, int mask, int hsize)
63874b941f0SAlexander V. Chernikov {
63974b941f0SAlexander V. Chernikov 	uint64_t *paddr;
64074b941f0SAlexander V. Chernikov 
64174b941f0SAlexander V. Chernikov 	paddr = (uint64_t *)addr6;
64274b941f0SAlexander V. Chernikov 	*paddr = 0;
64374b941f0SAlexander V. Chernikov 	*(paddr + 1) = 0;
64474b941f0SAlexander V. Chernikov 	memcpy(addr6, key, mask);
64574b941f0SAlexander V. Chernikov 	return (hash_ip6(addr6, hsize));
64674b941f0SAlexander V. Chernikov }
64774b941f0SAlexander V. Chernikov 
64874b941f0SAlexander V. Chernikov static int
64974b941f0SAlexander V. Chernikov ta_lookup_chash_slow(struct table_info *ti, void *key, uint32_t keylen,
65074b941f0SAlexander V. Chernikov     uint32_t *val)
65174b941f0SAlexander V. Chernikov {
65274b941f0SAlexander V. Chernikov 	struct chashbhead *head;
65374b941f0SAlexander V. Chernikov 	struct chashentry *ent;
65474b941f0SAlexander V. Chernikov 	uint16_t hash, hsize;
65574b941f0SAlexander V. Chernikov 	uint8_t imask;
65674b941f0SAlexander V. Chernikov 
65774b941f0SAlexander V. Chernikov 	if (keylen == sizeof(in_addr_t)) {
65874b941f0SAlexander V. Chernikov 		head = (struct chashbhead *)ti->state;
65974b941f0SAlexander V. Chernikov 		imask = ti->data >> 24;
66074b941f0SAlexander V. Chernikov 		hsize = 1 << ((ti->data & 0xFFFF) >> 8);
66174b941f0SAlexander V. Chernikov 		uint32_t a;
66274b941f0SAlexander V. Chernikov 		a = ntohl(*((in_addr_t *)key));
66374b941f0SAlexander V. Chernikov 		a = a >> imask;
66474b941f0SAlexander V. Chernikov 		hash = hash_ip(a, hsize);
66574b941f0SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
66674b941f0SAlexander V. Chernikov 			if (ent->a.a4 == a) {
66774b941f0SAlexander V. Chernikov 				*val = ent->value;
66874b941f0SAlexander V. Chernikov 				return (1);
66974b941f0SAlexander V. Chernikov 			}
67074b941f0SAlexander V. Chernikov 		}
67174b941f0SAlexander V. Chernikov 	} else {
67274b941f0SAlexander V. Chernikov 		/* IPv6: worst scenario: non-round mask */
67374b941f0SAlexander V. Chernikov 		struct in6_addr addr6;
67474b941f0SAlexander V. Chernikov 		head = (struct chashbhead *)ti->xstate;
67574b941f0SAlexander V. Chernikov 		imask = (ti->data & 0xFF0000) >> 16;
67674b941f0SAlexander V. Chernikov 		hsize = 1 << (ti->data & 0xFF);
67774b941f0SAlexander V. Chernikov 		hash = hash_ip6_slow(&addr6, key, imask, hsize);
67874b941f0SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
67974b941f0SAlexander V. Chernikov 			if (memcmp(&ent->a.a6, &addr6, 16) == 0) {
68074b941f0SAlexander V. Chernikov 				*val = ent->value;
68174b941f0SAlexander V. Chernikov 				return (1);
68274b941f0SAlexander V. Chernikov 			}
68374b941f0SAlexander V. Chernikov 		}
68474b941f0SAlexander V. Chernikov 	}
68574b941f0SAlexander V. Chernikov 
68674b941f0SAlexander V. Chernikov 	return (0);
68774b941f0SAlexander V. Chernikov }
68874b941f0SAlexander V. Chernikov 
68974b941f0SAlexander V. Chernikov static int
69074b941f0SAlexander V. Chernikov ta_lookup_chash_aligned(struct table_info *ti, void *key, uint32_t keylen,
69174b941f0SAlexander V. Chernikov     uint32_t *val)
69274b941f0SAlexander V. Chernikov {
69374b941f0SAlexander V. Chernikov 	struct chashbhead *head;
69474b941f0SAlexander V. Chernikov 	struct chashentry *ent;
69574b941f0SAlexander V. Chernikov 	uint16_t hash, hsize;
69674b941f0SAlexander V. Chernikov 	uint8_t imask;
69774b941f0SAlexander V. Chernikov 
69874b941f0SAlexander V. Chernikov 	if (keylen == sizeof(in_addr_t)) {
69974b941f0SAlexander V. Chernikov 		head = (struct chashbhead *)ti->state;
70074b941f0SAlexander V. Chernikov 		imask = ti->data >> 24;
70174b941f0SAlexander V. Chernikov 		hsize = 1 << ((ti->data & 0xFFFF) >> 8);
70274b941f0SAlexander V. Chernikov 		uint32_t a;
70374b941f0SAlexander V. Chernikov 		a = ntohl(*((in_addr_t *)key));
70474b941f0SAlexander V. Chernikov 		a = a >> imask;
70574b941f0SAlexander V. Chernikov 		hash = hash_ip(a, hsize);
70674b941f0SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
70774b941f0SAlexander V. Chernikov 			if (ent->a.a4 == a) {
70874b941f0SAlexander V. Chernikov 				*val = ent->value;
70974b941f0SAlexander V. Chernikov 				return (1);
71074b941f0SAlexander V. Chernikov 			}
71174b941f0SAlexander V. Chernikov 		}
71274b941f0SAlexander V. Chernikov 	} else {
71374b941f0SAlexander V. Chernikov 		/* IPv6: aligned to 8bit mask */
71474b941f0SAlexander V. Chernikov 		struct in6_addr addr6;
71574b941f0SAlexander V. Chernikov 		uint64_t *paddr, *ptmp;
71674b941f0SAlexander V. Chernikov 		head = (struct chashbhead *)ti->xstate;
71774b941f0SAlexander V. Chernikov 		imask = (ti->data & 0xFF0000) >> 16;
71874b941f0SAlexander V. Chernikov 		hsize = 1 << (ti->data & 0xFF);
71974b941f0SAlexander V. Chernikov 
72074b941f0SAlexander V. Chernikov 		hash = hash_ip6_al(&addr6, key, imask, hsize);
72174b941f0SAlexander V. Chernikov 		paddr = (uint64_t *)&addr6;
72274b941f0SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
72374b941f0SAlexander V. Chernikov 			ptmp = (uint64_t *)&ent->a.a6;
72474b941f0SAlexander V. Chernikov 			if (paddr[0] == ptmp[0] && paddr[1] == ptmp[1]) {
72574b941f0SAlexander V. Chernikov 				*val = ent->value;
72674b941f0SAlexander V. Chernikov 				return (1);
72774b941f0SAlexander V. Chernikov 			}
72874b941f0SAlexander V. Chernikov 		}
72974b941f0SAlexander V. Chernikov 	}
73074b941f0SAlexander V. Chernikov 
73174b941f0SAlexander V. Chernikov 	return (0);
73274b941f0SAlexander V. Chernikov }
73374b941f0SAlexander V. Chernikov 
73474b941f0SAlexander V. Chernikov static int
73574b941f0SAlexander V. Chernikov ta_lookup_chash_64(struct table_info *ti, void *key, uint32_t keylen,
73674b941f0SAlexander V. Chernikov     uint32_t *val)
73774b941f0SAlexander V. Chernikov {
73874b941f0SAlexander V. Chernikov 	struct chashbhead *head;
73974b941f0SAlexander V. Chernikov 	struct chashentry *ent;
74074b941f0SAlexander V. Chernikov 	uint16_t hash, hsize;
74174b941f0SAlexander V. Chernikov 	uint8_t imask;
74274b941f0SAlexander V. Chernikov 
74374b941f0SAlexander V. Chernikov 	if (keylen == sizeof(in_addr_t)) {
74474b941f0SAlexander V. Chernikov 		head = (struct chashbhead *)ti->state;
74574b941f0SAlexander V. Chernikov 		imask = ti->data >> 24;
74674b941f0SAlexander V. Chernikov 		hsize = 1 << ((ti->data & 0xFFFF) >> 8);
74774b941f0SAlexander V. Chernikov 		uint32_t a;
74874b941f0SAlexander V. Chernikov 		a = ntohl(*((in_addr_t *)key));
74974b941f0SAlexander V. Chernikov 		a = a >> imask;
75074b941f0SAlexander V. Chernikov 		hash = hash_ip(a, hsize);
75174b941f0SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
75274b941f0SAlexander V. Chernikov 			if (ent->a.a4 == a) {
75374b941f0SAlexander V. Chernikov 				*val = ent->value;
75474b941f0SAlexander V. Chernikov 				return (1);
75574b941f0SAlexander V. Chernikov 			}
75674b941f0SAlexander V. Chernikov 		}
75774b941f0SAlexander V. Chernikov 	} else {
75874b941f0SAlexander V. Chernikov 		/* IPv6: /64 */
75974b941f0SAlexander V. Chernikov 		uint64_t a6, *paddr;
76074b941f0SAlexander V. Chernikov 		head = (struct chashbhead *)ti->xstate;
76174b941f0SAlexander V. Chernikov 		paddr = (uint64_t *)key;
76274b941f0SAlexander V. Chernikov 		hsize = 1 << (ti->data & 0xFF);
76374b941f0SAlexander V. Chernikov 		a6 = *paddr;
76474b941f0SAlexander V. Chernikov 		hash = hash_ip64((struct in6_addr *)key, hsize);
76574b941f0SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
76674b941f0SAlexander V. Chernikov 			paddr = (uint64_t *)&ent->a.a6;
76774b941f0SAlexander V. Chernikov 			if (a6 == *paddr) {
76874b941f0SAlexander V. Chernikov 				*val = ent->value;
76974b941f0SAlexander V. Chernikov 				return (1);
77074b941f0SAlexander V. Chernikov 			}
77174b941f0SAlexander V. Chernikov 		}
77274b941f0SAlexander V. Chernikov 	}
77374b941f0SAlexander V. Chernikov 
77474b941f0SAlexander V. Chernikov 	return (0);
77574b941f0SAlexander V. Chernikov }
77674b941f0SAlexander V. Chernikov 
77774b941f0SAlexander V. Chernikov static int
77874b941f0SAlexander V. Chernikov chash_parse_opts(struct chash_cfg *ccfg, char *data)
77974b941f0SAlexander V. Chernikov {
78074b941f0SAlexander V. Chernikov 	char *pdel, *pend, *s;
78174b941f0SAlexander V. Chernikov 	int mask4, mask6;
78274b941f0SAlexander V. Chernikov 
78374b941f0SAlexander V. Chernikov 	mask4 = ccfg->mask4;
78474b941f0SAlexander V. Chernikov 	mask6 = ccfg->mask6;
78574b941f0SAlexander V. Chernikov 
78674b941f0SAlexander V. Chernikov 	if (data == NULL)
78774b941f0SAlexander V. Chernikov 		return (0);
78874b941f0SAlexander V. Chernikov 	if ((pdel = strchr(data, ' ')) == NULL)
78974b941f0SAlexander V. Chernikov 		return (0);
79074b941f0SAlexander V. Chernikov 	while (*pdel == ' ')
79174b941f0SAlexander V. Chernikov 		pdel++;
79274b941f0SAlexander V. Chernikov 	if (strncmp(pdel, "masks=", 6) != 0)
79374b941f0SAlexander V. Chernikov 		return (EINVAL);
79474b941f0SAlexander V. Chernikov 	if ((s = strchr(pdel, ' ')) != NULL)
79574b941f0SAlexander V. Chernikov 		*s++ = '\0';
79674b941f0SAlexander V. Chernikov 
79774b941f0SAlexander V. Chernikov 	pdel += 6;
79874b941f0SAlexander V. Chernikov 	/* Need /XX[,/YY] */
79974b941f0SAlexander V. Chernikov 	if (*pdel++ != '/')
80074b941f0SAlexander V. Chernikov 		return (EINVAL);
80174b941f0SAlexander V. Chernikov 	mask4 = strtol(pdel, &pend, 10);
80274b941f0SAlexander V. Chernikov 	if (*pend == ',') {
80374b941f0SAlexander V. Chernikov 		/* ,/YY */
80474b941f0SAlexander V. Chernikov 		pdel = pend + 1;
80574b941f0SAlexander V. Chernikov 		if (*pdel++ != '/')
80674b941f0SAlexander V. Chernikov 			return (EINVAL);
80774b941f0SAlexander V. Chernikov 		mask6 = strtol(pdel, &pend, 10);
80874b941f0SAlexander V. Chernikov 		if (*pend != '\0')
80974b941f0SAlexander V. Chernikov 			return (EINVAL);
81074b941f0SAlexander V. Chernikov 	} else if (*pend != '\0')
81174b941f0SAlexander V. Chernikov 		return (EINVAL);
81274b941f0SAlexander V. Chernikov 
81374b941f0SAlexander V. Chernikov 	if (mask4 < 0 || mask4 > 32 || mask6 < 0 || mask6 > 128)
81474b941f0SAlexander V. Chernikov 		return (EINVAL);
81574b941f0SAlexander V. Chernikov 
81674b941f0SAlexander V. Chernikov 	ccfg->mask4 = mask4;
81774b941f0SAlexander V. Chernikov 	ccfg->mask6 = mask6;
81874b941f0SAlexander V. Chernikov 
81974b941f0SAlexander V. Chernikov 	return (0);
82074b941f0SAlexander V. Chernikov }
82174b941f0SAlexander V. Chernikov 
82274b941f0SAlexander V. Chernikov static void
82374b941f0SAlexander V. Chernikov ta_print_chash_config(void *ta_state, struct table_info *ti, char *buf,
82474b941f0SAlexander V. Chernikov     size_t bufsize)
82574b941f0SAlexander V. Chernikov {
82674b941f0SAlexander V. Chernikov 	struct chash_cfg *ccfg;
82774b941f0SAlexander V. Chernikov 
82874b941f0SAlexander V. Chernikov 	ccfg = (struct chash_cfg *)ta_state;
82974b941f0SAlexander V. Chernikov 
83074b941f0SAlexander V. Chernikov 	if (ccfg->mask4 != 32 || ccfg->mask6 != 128)
83174b941f0SAlexander V. Chernikov 		snprintf(buf, bufsize, "%s masks=/%d,/%d", "cidr:hash",
83274b941f0SAlexander V. Chernikov 		    ccfg->mask4, ccfg->mask6);
83374b941f0SAlexander V. Chernikov 	else
83474b941f0SAlexander V. Chernikov 		snprintf(buf, bufsize, "%s", "cidr:hash");
83574b941f0SAlexander V. Chernikov }
83674b941f0SAlexander V. Chernikov 
837914bffb6SAlexander V. Chernikov static int
838914bffb6SAlexander V. Chernikov log2(uint32_t v)
839914bffb6SAlexander V. Chernikov {
840914bffb6SAlexander V. Chernikov 	uint32_t r;
841914bffb6SAlexander V. Chernikov 
842914bffb6SAlexander V. Chernikov 	r = 0;
843914bffb6SAlexander V. Chernikov 	while (v >>= 1)
844914bffb6SAlexander V. Chernikov 		r++;
845914bffb6SAlexander V. Chernikov 
846914bffb6SAlexander V. Chernikov 	return (r);
847914bffb6SAlexander V. Chernikov }
84874b941f0SAlexander V. Chernikov 
84974b941f0SAlexander V. Chernikov /*
85074b941f0SAlexander V. Chernikov  * New table.
85174b941f0SAlexander V. Chernikov  * We assume 'data' to be either NULL or the following format:
85274b941f0SAlexander V. Chernikov  * 'cidr:hash [masks=/32[,/128]]'
85374b941f0SAlexander V. Chernikov  */
85474b941f0SAlexander V. Chernikov static int
85574b941f0SAlexander V. Chernikov ta_init_chash(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
856914bffb6SAlexander V. Chernikov     char *data, uint8_t tflags)
85774b941f0SAlexander V. Chernikov {
85874b941f0SAlexander V. Chernikov 	int error, i;
859914bffb6SAlexander V. Chernikov 	uint32_t hsize;
86074b941f0SAlexander V. Chernikov 	struct chash_cfg *ccfg;
86174b941f0SAlexander V. Chernikov 
86274b941f0SAlexander V. Chernikov 	ccfg = malloc(sizeof(struct chash_cfg), M_IPFW, M_WAITOK | M_ZERO);
86374b941f0SAlexander V. Chernikov 
86474b941f0SAlexander V. Chernikov 	ccfg->mask4 = 32;
86574b941f0SAlexander V. Chernikov 	ccfg->mask6 = 128;
86674b941f0SAlexander V. Chernikov 
86774b941f0SAlexander V. Chernikov 	if ((error = chash_parse_opts(ccfg, data)) != 0) {
86874b941f0SAlexander V. Chernikov 		free(ccfg, M_IPFW);
86974b941f0SAlexander V. Chernikov 		return (error);
87074b941f0SAlexander V. Chernikov 	}
87174b941f0SAlexander V. Chernikov 
872914bffb6SAlexander V. Chernikov 	ccfg->size4 = 128;
873914bffb6SAlexander V. Chernikov 	ccfg->size6 = 128;
87474b941f0SAlexander V. Chernikov 
87574b941f0SAlexander V. Chernikov 	ccfg->head4 = malloc(sizeof(struct chashbhead) * ccfg->size4, M_IPFW,
87674b941f0SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
87774b941f0SAlexander V. Chernikov 	ccfg->head6 = malloc(sizeof(struct chashbhead) * ccfg->size6, M_IPFW,
87874b941f0SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
87974b941f0SAlexander V. Chernikov 	for (i = 0; i < ccfg->size4; i++)
88074b941f0SAlexander V. Chernikov 		SLIST_INIT(&ccfg->head4[i]);
88174b941f0SAlexander V. Chernikov 	for (i = 0; i < ccfg->size6; i++)
88274b941f0SAlexander V. Chernikov 		SLIST_INIT(&ccfg->head6[i]);
88374b941f0SAlexander V. Chernikov 
88474b941f0SAlexander V. Chernikov 
88574b941f0SAlexander V. Chernikov 	*ta_state = ccfg;
88674b941f0SAlexander V. Chernikov 	ti->state = ccfg->head4;
88774b941f0SAlexander V. Chernikov 	ti->xstate = ccfg->head6;
88874b941f0SAlexander V. Chernikov 
88974b941f0SAlexander V. Chernikov 	/* Store data depending on v6 mask length */
890914bffb6SAlexander V. Chernikov 	hsize = log2(ccfg->size4) << 8 | log2(ccfg->size6);
89174b941f0SAlexander V. Chernikov 	if (ccfg->mask6 == 64) {
89274b941f0SAlexander V. Chernikov 		ti->data = (32 - ccfg->mask4) << 24 | (128 - ccfg->mask6) << 16|
893914bffb6SAlexander V. Chernikov 		    hsize;
89474b941f0SAlexander V. Chernikov 		ti->lookup = ta_lookup_chash_64;
89574b941f0SAlexander V. Chernikov 	} else if ((ccfg->mask6  % 8) == 0) {
89674b941f0SAlexander V. Chernikov 		ti->data = (32 - ccfg->mask4) << 24 |
897914bffb6SAlexander V. Chernikov 		    ccfg->mask6 << 13 | hsize;
89874b941f0SAlexander V. Chernikov 		ti->lookup = ta_lookup_chash_aligned;
89974b941f0SAlexander V. Chernikov 	} else {
90074b941f0SAlexander V. Chernikov 		/* don't do that! */
90174b941f0SAlexander V. Chernikov 		ti->data = (32 - ccfg->mask4) << 24 |
902914bffb6SAlexander V. Chernikov 		    ccfg->mask6 << 16 | hsize;
90374b941f0SAlexander V. Chernikov 		ti->lookup = ta_lookup_chash_slow;
90474b941f0SAlexander V. Chernikov 	}
90574b941f0SAlexander V. Chernikov 
90674b941f0SAlexander V. Chernikov 	return (0);
90774b941f0SAlexander V. Chernikov }
90874b941f0SAlexander V. Chernikov 
90974b941f0SAlexander V. Chernikov static void
91074b941f0SAlexander V. Chernikov ta_destroy_chash(void *ta_state, struct table_info *ti)
91174b941f0SAlexander V. Chernikov {
91274b941f0SAlexander V. Chernikov 	struct chash_cfg *ccfg;
91374b941f0SAlexander V. Chernikov 	struct chashentry *ent, *ent_next;
91474b941f0SAlexander V. Chernikov 	int i;
91574b941f0SAlexander V. Chernikov 
91674b941f0SAlexander V. Chernikov 	ccfg = (struct chash_cfg *)ta_state;
91774b941f0SAlexander V. Chernikov 
91874b941f0SAlexander V. Chernikov 	for (i = 0; i < ccfg->size4; i++)
91974b941f0SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &ccfg->head4[i], next, ent_next)
92074b941f0SAlexander V. Chernikov 			free(ent, M_IPFW_TBL);
92174b941f0SAlexander V. Chernikov 
92274b941f0SAlexander V. Chernikov 	for (i = 0; i < ccfg->size6; i++)
92374b941f0SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &ccfg->head6[i], next, ent_next)
92474b941f0SAlexander V. Chernikov 			free(ent, M_IPFW_TBL);
92574b941f0SAlexander V. Chernikov 
92674b941f0SAlexander V. Chernikov 	free(ccfg->head4, M_IPFW);
92774b941f0SAlexander V. Chernikov 	free(ccfg->head6, M_IPFW);
928ce2817b5SAlexander V. Chernikov 
929ce2817b5SAlexander V. Chernikov 	free(ccfg, M_IPFW);
93074b941f0SAlexander V. Chernikov }
93174b941f0SAlexander V. Chernikov 
93274b941f0SAlexander V. Chernikov static int
93374b941f0SAlexander V. Chernikov ta_dump_chash_tentry(void *ta_state, struct table_info *ti, void *e,
93474b941f0SAlexander V. Chernikov     ipfw_obj_tentry *tent)
93574b941f0SAlexander V. Chernikov {
93674b941f0SAlexander V. Chernikov 	struct chash_cfg *ccfg;
93774b941f0SAlexander V. Chernikov 	struct chashentry *ent;
93874b941f0SAlexander V. Chernikov 
93974b941f0SAlexander V. Chernikov 	ccfg = (struct chash_cfg *)ta_state;
94074b941f0SAlexander V. Chernikov 	ent = (struct chashentry *)e;
94174b941f0SAlexander V. Chernikov 
94274b941f0SAlexander V. Chernikov 	if (ent->type == AF_INET) {
94374b941f0SAlexander V. Chernikov 		tent->k.addr.s_addr = htonl(ent->a.a4 << (32 - ccfg->mask4));
94474b941f0SAlexander V. Chernikov 		tent->masklen = ccfg->mask4;
94574b941f0SAlexander V. Chernikov 		tent->subtype = AF_INET;
94674b941f0SAlexander V. Chernikov 		tent->value = ent->value;
94774b941f0SAlexander V. Chernikov #ifdef INET6
94874b941f0SAlexander V. Chernikov 	} else {
94974b941f0SAlexander V. Chernikov 		memcpy(&tent->k, &ent->a.a6, sizeof(struct in6_addr));
95074b941f0SAlexander V. Chernikov 		tent->masklen = ccfg->mask6;
95174b941f0SAlexander V. Chernikov 		tent->subtype = AF_INET6;
95274b941f0SAlexander V. Chernikov 		tent->value = ent->value;
95374b941f0SAlexander V. Chernikov #endif
95474b941f0SAlexander V. Chernikov 	}
95574b941f0SAlexander V. Chernikov 
95674b941f0SAlexander V. Chernikov 	return (0);
95774b941f0SAlexander V. Chernikov }
95874b941f0SAlexander V. Chernikov 
959ce2817b5SAlexander V. Chernikov static uint32_t
960ce2817b5SAlexander V. Chernikov hash_ent(struct chashentry *ent, int af, int mlen, uint32_t size)
961ce2817b5SAlexander V. Chernikov {
962ce2817b5SAlexander V. Chernikov 	uint32_t hash;
963ce2817b5SAlexander V. Chernikov 
964ce2817b5SAlexander V. Chernikov 	if (af == AF_INET) {
965ce2817b5SAlexander V. Chernikov 		hash = hash_ip(ent->a.a4, size);
966ce2817b5SAlexander V. Chernikov 	} else {
967ce2817b5SAlexander V. Chernikov 		if (mlen == 64)
968ce2817b5SAlexander V. Chernikov 			hash = hash_ip64(&ent->a.a6, size);
969ce2817b5SAlexander V. Chernikov 		else
970ce2817b5SAlexander V. Chernikov 			hash = hash_ip6(&ent->a.a6, size);
971ce2817b5SAlexander V. Chernikov 	}
972ce2817b5SAlexander V. Chernikov 
973ce2817b5SAlexander V. Chernikov 	return (hash);
974ce2817b5SAlexander V. Chernikov }
975ce2817b5SAlexander V. Chernikov 
976ce2817b5SAlexander V. Chernikov static int
977ce2817b5SAlexander V. Chernikov tei_to_chash_ent(struct tentry_info *tei, struct chashentry *ent)
978ce2817b5SAlexander V. Chernikov {
979ce2817b5SAlexander V. Chernikov 	struct in6_addr mask6;
980ce2817b5SAlexander V. Chernikov 	int mlen;
981ce2817b5SAlexander V. Chernikov 
982ce2817b5SAlexander V. Chernikov 
983ce2817b5SAlexander V. Chernikov 	mlen = tei->masklen;
984ce2817b5SAlexander V. Chernikov 
985ce2817b5SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
986ce2817b5SAlexander V. Chernikov #ifdef INET
987ce2817b5SAlexander V. Chernikov 		if (mlen > 32)
988ce2817b5SAlexander V. Chernikov 			return (EINVAL);
989ce2817b5SAlexander V. Chernikov 		ent->type = AF_INET;
990ce2817b5SAlexander V. Chernikov 
991ce2817b5SAlexander V. Chernikov 		/* Calculate masked address */
992ce2817b5SAlexander V. Chernikov 		ent->a.a4 = ntohl(*((in_addr_t *)tei->paddr)) >> (32 - mlen);
993ce2817b5SAlexander V. Chernikov #endif
994ce2817b5SAlexander V. Chernikov #ifdef INET6
995ce2817b5SAlexander V. Chernikov 	} else if (tei->subtype == AF_INET6) {
996ce2817b5SAlexander V. Chernikov 		/* IPv6 case */
997ce2817b5SAlexander V. Chernikov 		if (mlen > 128)
998ce2817b5SAlexander V. Chernikov 			return (EINVAL);
999ce2817b5SAlexander V. Chernikov 		ent->type = AF_INET6;
1000ce2817b5SAlexander V. Chernikov 
1001ce2817b5SAlexander V. Chernikov 		ipv6_writemask(&mask6, mlen);
1002ce2817b5SAlexander V. Chernikov 		memcpy(&ent->a.a6, tei->paddr, sizeof(struct in6_addr));
1003ce2817b5SAlexander V. Chernikov 		APPLY_MASK(&ent->a.a6, &mask6);
1004ce2817b5SAlexander V. Chernikov #endif
1005ce2817b5SAlexander V. Chernikov 	} else {
1006ce2817b5SAlexander V. Chernikov 		/* Unknown CIDR type */
1007ce2817b5SAlexander V. Chernikov 		return (EINVAL);
1008ce2817b5SAlexander V. Chernikov 	}
1009ce2817b5SAlexander V. Chernikov 	ent->value = tei->value;
1010ce2817b5SAlexander V. Chernikov 
1011ce2817b5SAlexander V. Chernikov 	return (0);
1012ce2817b5SAlexander V. Chernikov }
1013ce2817b5SAlexander V. Chernikov 
1014ce2817b5SAlexander V. Chernikov 
101574b941f0SAlexander V. Chernikov static int
1016914bffb6SAlexander V. Chernikov ta_find_chash_tentry(void *ta_state, struct table_info *ti,
1017914bffb6SAlexander V. Chernikov     ipfw_obj_tentry *tent)
101874b941f0SAlexander V. Chernikov {
1019ce2817b5SAlexander V. Chernikov 	struct chash_cfg *ccfg;
1020ce2817b5SAlexander V. Chernikov 	struct chashbhead *head;
1021ce2817b5SAlexander V. Chernikov 	struct chashentry ent, *tmp;
1022ce2817b5SAlexander V. Chernikov 	struct tentry_info tei;
1023ce2817b5SAlexander V. Chernikov 	int error;
1024ce2817b5SAlexander V. Chernikov 	uint32_t hash;
102574b941f0SAlexander V. Chernikov 
1026ce2817b5SAlexander V. Chernikov 	ccfg = (struct chash_cfg *)ta_state;
1027ce2817b5SAlexander V. Chernikov 
1028ce2817b5SAlexander V. Chernikov 	memset(&ent, 0, sizeof(ent));
1029ce2817b5SAlexander V. Chernikov 	memset(&tei, 0, sizeof(tei));
1030ce2817b5SAlexander V. Chernikov 
1031914bffb6SAlexander V. Chernikov 	if (tent->subtype == AF_INET) {
1032914bffb6SAlexander V. Chernikov 		tei.paddr = &tent->k.addr;
1033ce2817b5SAlexander V. Chernikov 		tei.masklen = ccfg->mask4;
1034ce2817b5SAlexander V. Chernikov 		tei.subtype = AF_INET;
103574b941f0SAlexander V. Chernikov 
1036ce2817b5SAlexander V. Chernikov 		if ((error = tei_to_chash_ent(&tei, &ent)) != 0)
1037ce2817b5SAlexander V. Chernikov 			return (error);
1038ce2817b5SAlexander V. Chernikov 
1039ce2817b5SAlexander V. Chernikov 		head = ccfg->head4;
1040ce2817b5SAlexander V. Chernikov 		hash = hash_ent(&ent, AF_INET, ccfg->mask4, ccfg->size4);
1041ce2817b5SAlexander V. Chernikov 		/* Check for existence */
1042ce2817b5SAlexander V. Chernikov 		SLIST_FOREACH(tmp, &head[hash], next) {
1043ce2817b5SAlexander V. Chernikov 			if (tmp->a.a4 != ent.a.a4)
1044ce2817b5SAlexander V. Chernikov 				continue;
1045ce2817b5SAlexander V. Chernikov 
1046ce2817b5SAlexander V. Chernikov 			ta_dump_chash_tentry(ta_state, ti, tmp, tent);
104774b941f0SAlexander V. Chernikov 			return (0);
104874b941f0SAlexander V. Chernikov 		}
1049ce2817b5SAlexander V. Chernikov 	} else {
1050914bffb6SAlexander V. Chernikov 		tei.paddr = &tent->k.addr6;
1051ce2817b5SAlexander V. Chernikov 		tei.masklen = ccfg->mask6;
1052ce2817b5SAlexander V. Chernikov 		tei.subtype = AF_INET6;
1053ce2817b5SAlexander V. Chernikov 
1054ce2817b5SAlexander V. Chernikov 		if ((error = tei_to_chash_ent(&tei, &ent)) != 0)
1055ce2817b5SAlexander V. Chernikov 			return (error);
1056ce2817b5SAlexander V. Chernikov 
1057ce2817b5SAlexander V. Chernikov 		head = ccfg->head6;
1058ce2817b5SAlexander V. Chernikov 		hash = hash_ent(&ent, AF_INET6, ccfg->mask6, ccfg->size6);
1059ce2817b5SAlexander V. Chernikov 		/* Check for existence */
1060ce2817b5SAlexander V. Chernikov 		SLIST_FOREACH(tmp, &head[hash], next) {
1061ce2817b5SAlexander V. Chernikov 			if (memcmp(&tmp->a.a6, &ent.a.a6, 16) != 0)
1062ce2817b5SAlexander V. Chernikov 				continue;
1063ce2817b5SAlexander V. Chernikov 			ta_dump_chash_tentry(ta_state, ti, tmp, tent);
1064ce2817b5SAlexander V. Chernikov 			return (0);
1065ce2817b5SAlexander V. Chernikov 		}
1066ce2817b5SAlexander V. Chernikov 	}
1067ce2817b5SAlexander V. Chernikov 
106874b941f0SAlexander V. Chernikov 	return (ENOENT);
106974b941f0SAlexander V. Chernikov }
107074b941f0SAlexander V. Chernikov 
107174b941f0SAlexander V. Chernikov static void
107274b941f0SAlexander V. Chernikov ta_foreach_chash(void *ta_state, struct table_info *ti, ta_foreach_f *f,
107374b941f0SAlexander V. Chernikov     void *arg)
107474b941f0SAlexander V. Chernikov {
107574b941f0SAlexander V. Chernikov 	struct chash_cfg *ccfg;
107674b941f0SAlexander V. Chernikov 	struct chashentry *ent, *ent_next;
107774b941f0SAlexander V. Chernikov 	int i;
107874b941f0SAlexander V. Chernikov 
107974b941f0SAlexander V. Chernikov 	ccfg = (struct chash_cfg *)ta_state;
108074b941f0SAlexander V. Chernikov 
108174b941f0SAlexander V. Chernikov 	for (i = 0; i < ccfg->size4; i++)
108274b941f0SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &ccfg->head4[i], next, ent_next)
108374b941f0SAlexander V. Chernikov 			f(ent, arg);
108474b941f0SAlexander V. Chernikov 
108574b941f0SAlexander V. Chernikov 	for (i = 0; i < ccfg->size6; i++)
108674b941f0SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &ccfg->head6[i], next, ent_next)
108774b941f0SAlexander V. Chernikov 			f(ent, arg);
108874b941f0SAlexander V. Chernikov }
108974b941f0SAlexander V. Chernikov 
109074b941f0SAlexander V. Chernikov 
109174b941f0SAlexander V. Chernikov struct ta_buf_chash
109274b941f0SAlexander V. Chernikov {
109374b941f0SAlexander V. Chernikov 	void *ent_ptr;
1094ce2817b5SAlexander V. Chernikov 	struct chashentry ent;
109574b941f0SAlexander V. Chernikov };
109674b941f0SAlexander V. Chernikov 
109774b941f0SAlexander V. Chernikov static int
109874b941f0SAlexander V. Chernikov ta_prepare_add_chash(struct ip_fw_chain *ch, struct tentry_info *tei,
109974b941f0SAlexander V. Chernikov     void *ta_buf)
110074b941f0SAlexander V. Chernikov {
110174b941f0SAlexander V. Chernikov 	struct ta_buf_chash *tb;
110274b941f0SAlexander V. Chernikov 	struct chashentry *ent;
1103ce2817b5SAlexander V. Chernikov 	int error;
110474b941f0SAlexander V. Chernikov 
110574b941f0SAlexander V. Chernikov 	tb = (struct ta_buf_chash *)ta_buf;
110674b941f0SAlexander V. Chernikov 	memset(tb, 0, sizeof(struct ta_buf_chash));
110774b941f0SAlexander V. Chernikov 
110874b941f0SAlexander V. Chernikov 	ent = malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO);
110974b941f0SAlexander V. Chernikov 
1110ce2817b5SAlexander V. Chernikov 	error = tei_to_chash_ent(tei, ent);
1111ce2817b5SAlexander V. Chernikov 	if (error != 0) {
1112ce2817b5SAlexander V. Chernikov 		free(ent, M_IPFW_TBL);
1113ce2817b5SAlexander V. Chernikov 		return (error);
111474b941f0SAlexander V. Chernikov 	}
1115ce2817b5SAlexander V. Chernikov 	tb->ent_ptr = ent;
111674b941f0SAlexander V. Chernikov 
111774b941f0SAlexander V. Chernikov 	return (0);
111874b941f0SAlexander V. Chernikov }
111974b941f0SAlexander V. Chernikov 
112074b941f0SAlexander V. Chernikov static int
112174b941f0SAlexander V. Chernikov ta_add_chash(void *ta_state, struct table_info *ti, struct tentry_info *tei,
112274b941f0SAlexander V. Chernikov     void *ta_buf, uint64_t *pflags, uint32_t *pnum)
112374b941f0SAlexander V. Chernikov {
112474b941f0SAlexander V. Chernikov 	struct chash_cfg *ccfg;
112574b941f0SAlexander V. Chernikov 	struct chashbhead *head;
112674b941f0SAlexander V. Chernikov 	struct chashentry *ent, *tmp;
112774b941f0SAlexander V. Chernikov 	struct ta_buf_chash *tb;
112874b941f0SAlexander V. Chernikov 	int exists;
112974b941f0SAlexander V. Chernikov 	uint32_t hash;
113074b941f0SAlexander V. Chernikov 
113174b941f0SAlexander V. Chernikov 	ccfg = (struct chash_cfg *)ta_state;
113274b941f0SAlexander V. Chernikov 	tb = (struct ta_buf_chash *)ta_buf;
113374b941f0SAlexander V. Chernikov 	ent = (struct chashentry *)tb->ent_ptr;
113474b941f0SAlexander V. Chernikov 	hash = 0;
113574b941f0SAlexander V. Chernikov 	exists = 0;
113674b941f0SAlexander V. Chernikov 
113774b941f0SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
113874b941f0SAlexander V. Chernikov 		if (tei->masklen != ccfg->mask4)
113974b941f0SAlexander V. Chernikov 			return (EINVAL);
114074b941f0SAlexander V. Chernikov 		head = ccfg->head4;
1141ce2817b5SAlexander V. Chernikov 		hash = hash_ent(ent, AF_INET, ccfg->mask4, ccfg->size4);
1142ce2817b5SAlexander V. Chernikov 
114374b941f0SAlexander V. Chernikov 		/* Check for existence */
114474b941f0SAlexander V. Chernikov 		SLIST_FOREACH(tmp, &head[hash], next) {
114574b941f0SAlexander V. Chernikov 			if (tmp->a.a4 == ent->a.a4) {
114674b941f0SAlexander V. Chernikov 				exists = 1;
114774b941f0SAlexander V. Chernikov 				break;
114874b941f0SAlexander V. Chernikov 			}
114974b941f0SAlexander V. Chernikov 		}
115074b941f0SAlexander V. Chernikov 	} else {
115174b941f0SAlexander V. Chernikov 		if (tei->masklen != ccfg->mask6)
115274b941f0SAlexander V. Chernikov 			return (EINVAL);
115374b941f0SAlexander V. Chernikov 		head = ccfg->head6;
1154ce2817b5SAlexander V. Chernikov 		hash = hash_ent(ent, AF_INET6, ccfg->mask6, ccfg->size6);
115574b941f0SAlexander V. Chernikov 		/* Check for existence */
115674b941f0SAlexander V. Chernikov 		SLIST_FOREACH(tmp, &head[hash], next) {
1157ce2817b5SAlexander V. Chernikov 			if (memcmp(&tmp->a.a6, &ent->a.a6, 16) == 0) {
115874b941f0SAlexander V. Chernikov 				exists = 1;
115974b941f0SAlexander V. Chernikov 				break;
116074b941f0SAlexander V. Chernikov 			}
116174b941f0SAlexander V. Chernikov 		}
116274b941f0SAlexander V. Chernikov 	}
116374b941f0SAlexander V. Chernikov 
116474b941f0SAlexander V. Chernikov 	if (exists == 1) {
116574b941f0SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
116674b941f0SAlexander V. Chernikov 			return (EEXIST);
116774b941f0SAlexander V. Chernikov 		/* Record already exists. Update value if we're asked to */
116874b941f0SAlexander V. Chernikov 		tmp->value = tei->value;
116974b941f0SAlexander V. Chernikov 		/* Indicate that update has happened instead of addition */
117074b941f0SAlexander V. Chernikov 		tei->flags |= TEI_FLAGS_UPDATED;
117174b941f0SAlexander V. Chernikov 		*pnum = 0;
117274b941f0SAlexander V. Chernikov 	} else {
117374b941f0SAlexander V. Chernikov 		SLIST_INSERT_HEAD(&head[hash], ent, next);
117474b941f0SAlexander V. Chernikov 		tb->ent_ptr = NULL;
117574b941f0SAlexander V. Chernikov 		*pnum = 1;
1176ce2817b5SAlexander V. Chernikov 
1177ce2817b5SAlexander V. Chernikov 		/* Update counters and check if we need to grow hash */
1178ce2817b5SAlexander V. Chernikov 		if (tei->subtype == AF_INET) {
1179ce2817b5SAlexander V. Chernikov 			ccfg->items4++;
1180ce2817b5SAlexander V. Chernikov 			if (ccfg->items4 > ccfg->size4 && ccfg->size4 < 65536)
1181ce2817b5SAlexander V. Chernikov 				*pflags = (ccfg->size4 * 2) | (1UL << 32);
1182ce2817b5SAlexander V. Chernikov 		} else {
1183ce2817b5SAlexander V. Chernikov 			ccfg->items6++;
1184ce2817b5SAlexander V. Chernikov 			if (ccfg->items6 > ccfg->size6 && ccfg->size6 < 65536)
1185ce2817b5SAlexander V. Chernikov 				*pflags = ccfg->size6 * 2;
1186ce2817b5SAlexander V. Chernikov 		}
118774b941f0SAlexander V. Chernikov 	}
118874b941f0SAlexander V. Chernikov 
118974b941f0SAlexander V. Chernikov 	return (0);
119074b941f0SAlexander V. Chernikov }
119174b941f0SAlexander V. Chernikov 
119274b941f0SAlexander V. Chernikov static int
119374b941f0SAlexander V. Chernikov ta_prepare_del_chash(struct ip_fw_chain *ch, struct tentry_info *tei,
119474b941f0SAlexander V. Chernikov     void *ta_buf)
119574b941f0SAlexander V. Chernikov {
119674b941f0SAlexander V. Chernikov 	struct ta_buf_chash *tb;
119774b941f0SAlexander V. Chernikov 
119874b941f0SAlexander V. Chernikov 	tb = (struct ta_buf_chash *)ta_buf;
119974b941f0SAlexander V. Chernikov 	memset(tb, 0, sizeof(struct ta_buf_chash));
120074b941f0SAlexander V. Chernikov 
1201ce2817b5SAlexander V. Chernikov 	return (tei_to_chash_ent(tei, &tb->ent));
120274b941f0SAlexander V. Chernikov }
120374b941f0SAlexander V. Chernikov 
120474b941f0SAlexander V. Chernikov static int
120574b941f0SAlexander V. Chernikov ta_del_chash(void *ta_state, struct table_info *ti, struct tentry_info *tei,
120674b941f0SAlexander V. Chernikov     void *ta_buf, uint64_t *pflags, uint32_t *pnum)
120774b941f0SAlexander V. Chernikov {
120874b941f0SAlexander V. Chernikov 	struct chash_cfg *ccfg;
120974b941f0SAlexander V. Chernikov 	struct chashbhead *head;
1210ce2817b5SAlexander V. Chernikov 	struct chashentry *ent, *tmp_next, *dent;
121174b941f0SAlexander V. Chernikov 	struct ta_buf_chash *tb;
121274b941f0SAlexander V. Chernikov 	uint32_t hash;
121374b941f0SAlexander V. Chernikov 
121474b941f0SAlexander V. Chernikov 	ccfg = (struct chash_cfg *)ta_state;
121574b941f0SAlexander V. Chernikov 	tb = (struct ta_buf_chash *)ta_buf;
1216ce2817b5SAlexander V. Chernikov 	dent = &tb->ent;
121774b941f0SAlexander V. Chernikov 
121874b941f0SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
121974b941f0SAlexander V. Chernikov 		if (tei->masklen != ccfg->mask4)
122074b941f0SAlexander V. Chernikov 			return (EINVAL);
122174b941f0SAlexander V. Chernikov 		head = ccfg->head4;
1222ce2817b5SAlexander V. Chernikov 		hash = hash_ent(dent, AF_INET, ccfg->mask4, ccfg->size4);
122374b941f0SAlexander V. Chernikov 
122474b941f0SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &head[hash], next, tmp_next) {
1225ce2817b5SAlexander V. Chernikov 			if (ent->a.a4 == dent->a.a4) {
122674b941f0SAlexander V. Chernikov 				SLIST_REMOVE(&head[hash], ent, chashentry,next);
122774b941f0SAlexander V. Chernikov 				*pnum = 1;
1228ce2817b5SAlexander V. Chernikov 				ccfg->items4--;
122974b941f0SAlexander V. Chernikov 				return (0);
123074b941f0SAlexander V. Chernikov 			}
123174b941f0SAlexander V. Chernikov 		}
123274b941f0SAlexander V. Chernikov 	} else {
123374b941f0SAlexander V. Chernikov 		if (tei->masklen != ccfg->mask6)
123474b941f0SAlexander V. Chernikov 			return (EINVAL);
123574b941f0SAlexander V. Chernikov 		head = ccfg->head6;
1236ce2817b5SAlexander V. Chernikov 		hash = hash_ent(dent, AF_INET6, ccfg->mask6, ccfg->size6);
123774b941f0SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &head[hash], next, tmp_next) {
1238ce2817b5SAlexander V. Chernikov 			if (memcmp(&ent->a.a6, &dent->a.a6, 16) == 0) {
123974b941f0SAlexander V. Chernikov 				SLIST_REMOVE(&head[hash], ent, chashentry,next);
1240ce2817b5SAlexander V. Chernikov 				ccfg->items6--;
124174b941f0SAlexander V. Chernikov 				*pnum = 1;
124274b941f0SAlexander V. Chernikov 				return (0);
124374b941f0SAlexander V. Chernikov 			}
124474b941f0SAlexander V. Chernikov 		}
124574b941f0SAlexander V. Chernikov 	}
124674b941f0SAlexander V. Chernikov 
124774b941f0SAlexander V. Chernikov 	return (ENOENT);
124874b941f0SAlexander V. Chernikov }
124974b941f0SAlexander V. Chernikov 
125074b941f0SAlexander V. Chernikov static void
125174b941f0SAlexander V. Chernikov ta_flush_chash_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
125274b941f0SAlexander V. Chernikov     void *ta_buf)
125374b941f0SAlexander V. Chernikov {
125474b941f0SAlexander V. Chernikov 	struct ta_buf_chash *tb;
125574b941f0SAlexander V. Chernikov 
125674b941f0SAlexander V. Chernikov 	tb = (struct ta_buf_chash *)ta_buf;
125774b941f0SAlexander V. Chernikov 
125874b941f0SAlexander V. Chernikov 	if (tb->ent_ptr != NULL)
125974b941f0SAlexander V. Chernikov 		free(tb->ent_ptr, M_IPFW_TBL);
126074b941f0SAlexander V. Chernikov }
126174b941f0SAlexander V. Chernikov 
1262ce2817b5SAlexander V. Chernikov /*
1263ce2817b5SAlexander V. Chernikov  * Hash growing callbacks.
1264ce2817b5SAlexander V. Chernikov  */
1265ce2817b5SAlexander V. Chernikov 
1266ce2817b5SAlexander V. Chernikov struct mod_item {
1267ce2817b5SAlexander V. Chernikov 	void	*main_ptr;
1268ce2817b5SAlexander V. Chernikov 	size_t	size;
1269ce2817b5SAlexander V. Chernikov };
1270ce2817b5SAlexander V. Chernikov 
1271ce2817b5SAlexander V. Chernikov /*
1272ce2817b5SAlexander V. Chernikov  * Allocate new, larger chash.
1273ce2817b5SAlexander V. Chernikov  */
1274ce2817b5SAlexander V. Chernikov static int
1275ce2817b5SAlexander V. Chernikov ta_prepare_mod_chash(void *ta_buf, uint64_t *pflags)
1276ce2817b5SAlexander V. Chernikov {
1277ce2817b5SAlexander V. Chernikov 	struct mod_item *mi;
1278ce2817b5SAlexander V. Chernikov 	struct chashbhead *head;
1279ce2817b5SAlexander V. Chernikov 	int i;
1280ce2817b5SAlexander V. Chernikov 
1281ce2817b5SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
1282ce2817b5SAlexander V. Chernikov 
1283ce2817b5SAlexander V. Chernikov 	memset(mi, 0, sizeof(struct mod_item));
1284ce2817b5SAlexander V. Chernikov 	mi->size = *pflags & 0xFFFFFFFF;
1285ce2817b5SAlexander V. Chernikov 	head = malloc(sizeof(struct chashbhead) * mi->size, M_IPFW,
1286ce2817b5SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
1287ce2817b5SAlexander V. Chernikov 	for (i = 0; i < mi->size; i++)
1288ce2817b5SAlexander V. Chernikov 		SLIST_INIT(&head[i]);
1289ce2817b5SAlexander V. Chernikov 
1290ce2817b5SAlexander V. Chernikov 	mi->main_ptr = head;
1291ce2817b5SAlexander V. Chernikov 
1292ce2817b5SAlexander V. Chernikov 	return (0);
1293ce2817b5SAlexander V. Chernikov }
1294ce2817b5SAlexander V. Chernikov 
1295ce2817b5SAlexander V. Chernikov /*
1296ce2817b5SAlexander V. Chernikov  * Copy data from old runtime array to new one.
1297ce2817b5SAlexander V. Chernikov  */
1298ce2817b5SAlexander V. Chernikov static int
1299ce2817b5SAlexander V. Chernikov ta_fill_mod_chash(void *ta_state, struct table_info *ti, void *ta_buf,
1300ce2817b5SAlexander V. Chernikov     uint64_t *pflags)
1301ce2817b5SAlexander V. Chernikov {
1302ce2817b5SAlexander V. Chernikov 
1303ce2817b5SAlexander V. Chernikov 	/* In is not possible to do rehash if we're not holidng WLOCK. */
1304ce2817b5SAlexander V. Chernikov 	return (0);
1305ce2817b5SAlexander V. Chernikov }
1306ce2817b5SAlexander V. Chernikov 
1307ce2817b5SAlexander V. Chernikov 
1308ce2817b5SAlexander V. Chernikov /*
1309ce2817b5SAlexander V. Chernikov  * Switch old & new arrays.
1310ce2817b5SAlexander V. Chernikov  */
1311ce2817b5SAlexander V. Chernikov static int
1312ce2817b5SAlexander V. Chernikov ta_modify_chash(void *ta_state, struct table_info *ti, void *ta_buf,
1313ce2817b5SAlexander V. Chernikov     uint64_t pflags)
1314ce2817b5SAlexander V. Chernikov {
1315ce2817b5SAlexander V. Chernikov 	struct mod_item *mi;
1316ce2817b5SAlexander V. Chernikov 	struct chash_cfg *ccfg;
1317ce2817b5SAlexander V. Chernikov 	struct chashbhead *old_head, *new_head;
1318ce2817b5SAlexander V. Chernikov 	struct chashentry *ent, *ent_next;
1319ce2817b5SAlexander V. Chernikov 	int af, i, mlen;
1320ce2817b5SAlexander V. Chernikov 	uint32_t nhash;
1321ce2817b5SAlexander V. Chernikov 	size_t old_size;
1322ce2817b5SAlexander V. Chernikov 
1323ce2817b5SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
1324ce2817b5SAlexander V. Chernikov 	ccfg = (struct chash_cfg *)ta_state;
1325ce2817b5SAlexander V. Chernikov 
1326ce2817b5SAlexander V. Chernikov 	/* Check which hash we need to grow and do we still need that */
1327ce2817b5SAlexander V. Chernikov 	if ((pflags >> 32) == 1) {
1328ce2817b5SAlexander V. Chernikov 		old_size = ccfg->size4;
1329ce2817b5SAlexander V. Chernikov 		old_head = ti->state;
1330ce2817b5SAlexander V. Chernikov 		mlen = ccfg->mask4;
1331ce2817b5SAlexander V. Chernikov 		af = AF_INET;
1332ce2817b5SAlexander V. Chernikov 	} else {
1333ce2817b5SAlexander V. Chernikov 		old_size = ccfg->size6;
1334ce2817b5SAlexander V. Chernikov 		old_head = ti->xstate;
1335ce2817b5SAlexander V. Chernikov 		mlen = ccfg->mask6;
1336ce2817b5SAlexander V. Chernikov 		af = AF_INET6;
1337ce2817b5SAlexander V. Chernikov 	}
1338ce2817b5SAlexander V. Chernikov 
1339ce2817b5SAlexander V. Chernikov 	if (old_size >= mi->size)
1340ce2817b5SAlexander V. Chernikov 		return (0);
1341ce2817b5SAlexander V. Chernikov 
1342ce2817b5SAlexander V. Chernikov 	new_head = (struct chashbhead *)mi->main_ptr;
1343ce2817b5SAlexander V. Chernikov 	for (i = 0; i < old_size; i++) {
1344ce2817b5SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &old_head[i], next, ent_next) {
1345ce2817b5SAlexander V. Chernikov 			nhash = hash_ent(ent, af, mlen, mi->size);
1346ce2817b5SAlexander V. Chernikov 			SLIST_INSERT_HEAD(&new_head[nhash], ent, next);
1347ce2817b5SAlexander V. Chernikov 		}
1348ce2817b5SAlexander V. Chernikov 	}
1349ce2817b5SAlexander V. Chernikov 
1350ce2817b5SAlexander V. Chernikov 	if (af == AF_INET) {
1351ce2817b5SAlexander V. Chernikov 		ti->state = new_head;
1352ce2817b5SAlexander V. Chernikov 		ccfg->head4 = new_head;
1353ce2817b5SAlexander V. Chernikov 		ccfg->size4 = mi->size;
1354ce2817b5SAlexander V. Chernikov 	} else {
1355ce2817b5SAlexander V. Chernikov 		ti->xstate = new_head;
1356ce2817b5SAlexander V. Chernikov 		ccfg->head6 = new_head;
1357ce2817b5SAlexander V. Chernikov 		ccfg->size6 = mi->size;
1358ce2817b5SAlexander V. Chernikov 	}
1359ce2817b5SAlexander V. Chernikov 
1360914bffb6SAlexander V. Chernikov 	ti->data = (ti->data & 0xFFFFFFFF00000000) | log2(ccfg->size4) << 8 |
1361914bffb6SAlexander V. Chernikov 	    log2(ccfg->size6);
1362914bffb6SAlexander V. Chernikov 
1363ce2817b5SAlexander V. Chernikov 	mi->main_ptr = old_head;
1364ce2817b5SAlexander V. Chernikov 
1365ce2817b5SAlexander V. Chernikov 	return (0);
1366ce2817b5SAlexander V. Chernikov }
1367ce2817b5SAlexander V. Chernikov 
1368ce2817b5SAlexander V. Chernikov /*
1369ce2817b5SAlexander V. Chernikov  * Free unneded array.
1370ce2817b5SAlexander V. Chernikov  */
1371ce2817b5SAlexander V. Chernikov static void
1372ce2817b5SAlexander V. Chernikov ta_flush_mod_chash(void *ta_buf)
1373ce2817b5SAlexander V. Chernikov {
1374ce2817b5SAlexander V. Chernikov 	struct mod_item *mi;
1375ce2817b5SAlexander V. Chernikov 
1376ce2817b5SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
1377ce2817b5SAlexander V. Chernikov 	if (mi->main_ptr != NULL)
1378ce2817b5SAlexander V. Chernikov 		free(mi->main_ptr, M_IPFW);
1379ce2817b5SAlexander V. Chernikov }
1380ce2817b5SAlexander V. Chernikov 
138174b941f0SAlexander V. Chernikov struct table_algo cidr_hash = {
138274b941f0SAlexander V. Chernikov 	.name		= "cidr:hash",
13839d099b4fSAlexander V. Chernikov 	.type		= IPFW_TABLE_CIDR,
138457a1cf95SAlexander V. Chernikov 	.ta_buf_size	= sizeof(struct ta_buf_chash),
138574b941f0SAlexander V. Chernikov 	.init		= ta_init_chash,
138674b941f0SAlexander V. Chernikov 	.destroy	= ta_destroy_chash,
138774b941f0SAlexander V. Chernikov 	.prepare_add	= ta_prepare_add_chash,
138874b941f0SAlexander V. Chernikov 	.prepare_del	= ta_prepare_del_chash,
138974b941f0SAlexander V. Chernikov 	.add		= ta_add_chash,
139074b941f0SAlexander V. Chernikov 	.del		= ta_del_chash,
139174b941f0SAlexander V. Chernikov 	.flush_entry	= ta_flush_chash_entry,
139274b941f0SAlexander V. Chernikov 	.foreach	= ta_foreach_chash,
139374b941f0SAlexander V. Chernikov 	.dump_tentry	= ta_dump_chash_tentry,
139474b941f0SAlexander V. Chernikov 	.find_tentry	= ta_find_chash_tentry,
139574b941f0SAlexander V. Chernikov 	.print_config	= ta_print_chash_config,
1396ce2817b5SAlexander V. Chernikov 	.prepare_mod	= ta_prepare_mod_chash,
1397ce2817b5SAlexander V. Chernikov 	.fill_mod	= ta_fill_mod_chash,
1398ce2817b5SAlexander V. Chernikov 	.modify		= ta_modify_chash,
1399ce2817b5SAlexander V. Chernikov 	.flush_mod	= ta_flush_mod_chash,
140074b941f0SAlexander V. Chernikov };
140174b941f0SAlexander V. Chernikov 
140274b941f0SAlexander V. Chernikov 
140374b941f0SAlexander V. Chernikov /*
140468394ec8SAlexander V. Chernikov  * Iface table cmds.
140568394ec8SAlexander V. Chernikov  *
140668394ec8SAlexander V. Chernikov  * Implementation:
140768394ec8SAlexander V. Chernikov  *
140868394ec8SAlexander V. Chernikov  * Runtime part:
140968394ec8SAlexander V. Chernikov  * - sorted array of "struct ifidx" pointed by ti->state.
1410b23d5de9SAlexander V. Chernikov  *   Array is allocated with rounding up to IFIDX_CHUNK. Only existing
141168394ec8SAlexander V. Chernikov  *   interfaces are stored in array, however its allocated size is
141268394ec8SAlexander V. Chernikov  *   sufficient to hold all table records if needed.
141368394ec8SAlexander V. Chernikov  * - current array size is stored in ti->data
141468394ec8SAlexander V. Chernikov  *
141568394ec8SAlexander V. Chernikov  * Table data:
141668394ec8SAlexander V. Chernikov  * - "struct iftable_cfg" is allocated to store table state (ta_state).
141768394ec8SAlexander V. Chernikov  * - All table records are stored inside namedobj instance.
14189f7d47b0SAlexander V. Chernikov  *
14199f7d47b0SAlexander V. Chernikov  */
14209f7d47b0SAlexander V. Chernikov 
142168394ec8SAlexander V. Chernikov struct ifidx {
142268394ec8SAlexander V. Chernikov 	uint16_t	kidx;
142368394ec8SAlexander V. Chernikov 	uint16_t	spare;
142468394ec8SAlexander V. Chernikov 	uint32_t	value;
142568394ec8SAlexander V. Chernikov };
142668394ec8SAlexander V. Chernikov 
142768394ec8SAlexander V. Chernikov struct iftable_cfg;
142868394ec8SAlexander V. Chernikov 
142968394ec8SAlexander V. Chernikov struct ifentry {
143068394ec8SAlexander V. Chernikov 	struct named_object	no;
143168394ec8SAlexander V. Chernikov 	struct ipfw_ifc		ic;
143268394ec8SAlexander V. Chernikov 	struct iftable_cfg	*icfg;
143368394ec8SAlexander V. Chernikov 	uint32_t		value;
143468394ec8SAlexander V. Chernikov 	int			linked;
143568394ec8SAlexander V. Chernikov };
143668394ec8SAlexander V. Chernikov 
143768394ec8SAlexander V. Chernikov struct iftable_cfg {
143868394ec8SAlexander V. Chernikov 	struct namedobj_instance	*ii;
143968394ec8SAlexander V. Chernikov 	struct ip_fw_chain	*ch;
144068394ec8SAlexander V. Chernikov 	struct table_info	*ti;
144168394ec8SAlexander V. Chernikov 	void	*main_ptr;
144268394ec8SAlexander V. Chernikov 	size_t	size;	/* Number of items allocated in array */
144368394ec8SAlexander V. Chernikov 	size_t	count;	/* Number of all items */
144468394ec8SAlexander V. Chernikov 	size_t	used;	/* Number of items _active_ now */
144568394ec8SAlexander V. Chernikov };
144668394ec8SAlexander V. Chernikov 
144768394ec8SAlexander V. Chernikov #define	IFIDX_CHUNK	16
144868394ec8SAlexander V. Chernikov 
144968394ec8SAlexander V. Chernikov int compare_ifidx(const void *k, const void *v);
145068394ec8SAlexander V. Chernikov static void if_notifier(struct ip_fw_chain *ch, void *cbdata, uint16_t ifindex);
145168394ec8SAlexander V. Chernikov 
145268394ec8SAlexander V. Chernikov int
145368394ec8SAlexander V. Chernikov compare_ifidx(const void *k, const void *v)
145468394ec8SAlexander V. Chernikov {
145568394ec8SAlexander V. Chernikov 	struct ifidx *ifidx;
145668394ec8SAlexander V. Chernikov 	uint16_t key;
145768394ec8SAlexander V. Chernikov 
145868394ec8SAlexander V. Chernikov 	key = *((uint16_t *)k);
145968394ec8SAlexander V. Chernikov 	ifidx = (struct ifidx *)v;
146068394ec8SAlexander V. Chernikov 
146168394ec8SAlexander V. Chernikov 	if (key < ifidx->kidx)
146268394ec8SAlexander V. Chernikov 		return (-1);
146368394ec8SAlexander V. Chernikov 	else if (key > ifidx->kidx)
146468394ec8SAlexander V. Chernikov 		return (1);
146568394ec8SAlexander V. Chernikov 
146668394ec8SAlexander V. Chernikov 	return (0);
146768394ec8SAlexander V. Chernikov }
146868394ec8SAlexander V. Chernikov 
146968394ec8SAlexander V. Chernikov /*
147068394ec8SAlexander V. Chernikov  * Adds item @item with key @key into ascending-sorted array @base.
147168394ec8SAlexander V. Chernikov  * Assumes @base has enough additional storage.
147268394ec8SAlexander V. Chernikov  *
147368394ec8SAlexander V. Chernikov  * Returns 1 on success, 0 on duplicate key.
147468394ec8SAlexander V. Chernikov  */
14759f7d47b0SAlexander V. Chernikov static int
147668394ec8SAlexander V. Chernikov badd(const void *key, void *item, void *base, size_t nmemb,
147768394ec8SAlexander V. Chernikov     size_t size, int (*compar) (const void *, const void *))
147868394ec8SAlexander V. Chernikov {
147968394ec8SAlexander V. Chernikov 	int min, max, mid, shift, res;
148068394ec8SAlexander V. Chernikov 	caddr_t paddr;
148168394ec8SAlexander V. Chernikov 
148268394ec8SAlexander V. Chernikov 	if (nmemb == 0) {
148368394ec8SAlexander V. Chernikov 		memcpy(base, item, size);
148468394ec8SAlexander V. Chernikov 		return (1);
148568394ec8SAlexander V. Chernikov 	}
148668394ec8SAlexander V. Chernikov 
148768394ec8SAlexander V. Chernikov 	/* Binary search */
148868394ec8SAlexander V. Chernikov 	min = 0;
148968394ec8SAlexander V. Chernikov 	max = nmemb - 1;
149068394ec8SAlexander V. Chernikov 	mid = 0;
149168394ec8SAlexander V. Chernikov 	while (min <= max) {
149268394ec8SAlexander V. Chernikov 		mid = (min + max) / 2;
149368394ec8SAlexander V. Chernikov 		res = compar(key, (const void *)((caddr_t)base + mid * size));
149468394ec8SAlexander V. Chernikov 		if (res == 0)
149568394ec8SAlexander V. Chernikov 			return (0);
149668394ec8SAlexander V. Chernikov 
149768394ec8SAlexander V. Chernikov 		if (res > 0)
149868394ec8SAlexander V. Chernikov 			 min = mid + 1;
149968394ec8SAlexander V. Chernikov 		else
150068394ec8SAlexander V. Chernikov 			 max = mid - 1;
150168394ec8SAlexander V. Chernikov 	}
150268394ec8SAlexander V. Chernikov 
150368394ec8SAlexander V. Chernikov 	/* Item not found. */
150468394ec8SAlexander V. Chernikov 	res = compar(key, (const void *)((caddr_t)base + mid * size));
150568394ec8SAlexander V. Chernikov 	if (res > 0)
150668394ec8SAlexander V. Chernikov 		shift = mid + 1;
150768394ec8SAlexander V. Chernikov 	else
150868394ec8SAlexander V. Chernikov 		shift = mid;
150968394ec8SAlexander V. Chernikov 
151068394ec8SAlexander V. Chernikov 	paddr = (caddr_t)base + shift * size;
151168394ec8SAlexander V. Chernikov 	if (nmemb > shift)
151268394ec8SAlexander V. Chernikov 		memmove(paddr + size, paddr, (nmemb - shift) * size);
151368394ec8SAlexander V. Chernikov 
151468394ec8SAlexander V. Chernikov 	memcpy(paddr, item, size);
151568394ec8SAlexander V. Chernikov 
151668394ec8SAlexander V. Chernikov 	return (1);
151768394ec8SAlexander V. Chernikov }
151868394ec8SAlexander V. Chernikov 
151968394ec8SAlexander V. Chernikov /*
152068394ec8SAlexander V. Chernikov  * Deletes item with key @key from ascending-sorted array @base.
152168394ec8SAlexander V. Chernikov  *
152268394ec8SAlexander V. Chernikov  * Returns 1 on success, 0 for non-existent key.
152368394ec8SAlexander V. Chernikov  */
152468394ec8SAlexander V. Chernikov static int
152568394ec8SAlexander V. Chernikov bdel(const void *key, void *base, size_t nmemb, size_t size,
152668394ec8SAlexander V. Chernikov     int (*compar) (const void *, const void *))
152768394ec8SAlexander V. Chernikov {
152868394ec8SAlexander V. Chernikov 	caddr_t item;
152968394ec8SAlexander V. Chernikov 	size_t sz;
153068394ec8SAlexander V. Chernikov 
153168394ec8SAlexander V. Chernikov 	item = (caddr_t)bsearch(key, base, nmemb, size, compar);
153268394ec8SAlexander V. Chernikov 
153368394ec8SAlexander V. Chernikov 	if (item == NULL)
153468394ec8SAlexander V. Chernikov 		return (0);
153568394ec8SAlexander V. Chernikov 
153668394ec8SAlexander V. Chernikov 	sz = (caddr_t)base + nmemb * size - item;
153768394ec8SAlexander V. Chernikov 
153868394ec8SAlexander V. Chernikov 	if (sz > 0)
153968394ec8SAlexander V. Chernikov 		memmove(item, item + size, sz);
154068394ec8SAlexander V. Chernikov 
154168394ec8SAlexander V. Chernikov 	return (1);
154268394ec8SAlexander V. Chernikov }
154368394ec8SAlexander V. Chernikov 
154468394ec8SAlexander V. Chernikov static struct ifidx *
154568394ec8SAlexander V. Chernikov ifidx_find(struct table_info *ti, void *key)
154668394ec8SAlexander V. Chernikov {
154768394ec8SAlexander V. Chernikov 	struct ifidx *ifi;
154868394ec8SAlexander V. Chernikov 
154968394ec8SAlexander V. Chernikov 	ifi = bsearch(key, ti->state, ti->data, sizeof(struct ifidx),
155068394ec8SAlexander V. Chernikov 	    compare_ifidx);
155168394ec8SAlexander V. Chernikov 
155268394ec8SAlexander V. Chernikov 	return (ifi);
155368394ec8SAlexander V. Chernikov }
155468394ec8SAlexander V. Chernikov 
155568394ec8SAlexander V. Chernikov static int
155668394ec8SAlexander V. Chernikov ta_lookup_ifidx(struct table_info *ti, void *key, uint32_t keylen,
15579f7d47b0SAlexander V. Chernikov     uint32_t *val)
15589f7d47b0SAlexander V. Chernikov {
155968394ec8SAlexander V. Chernikov 	struct ifidx *ifi;
15609f7d47b0SAlexander V. Chernikov 
156168394ec8SAlexander V. Chernikov 	ifi = ifidx_find(ti, key);
15629f7d47b0SAlexander V. Chernikov 
156368394ec8SAlexander V. Chernikov 	if (ifi != NULL) {
156468394ec8SAlexander V. Chernikov 		*val = ifi->value;
15659f7d47b0SAlexander V. Chernikov 		return (1);
15669f7d47b0SAlexander V. Chernikov 	}
15679f7d47b0SAlexander V. Chernikov 
15689f7d47b0SAlexander V. Chernikov 	return (0);
15699f7d47b0SAlexander V. Chernikov }
15709f7d47b0SAlexander V. Chernikov 
15719f7d47b0SAlexander V. Chernikov static int
157268394ec8SAlexander V. Chernikov ta_init_ifidx(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
1573914bffb6SAlexander V. Chernikov     char *data, uint8_t tflags)
15749f7d47b0SAlexander V. Chernikov {
157568394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
15769f7d47b0SAlexander V. Chernikov 
157768394ec8SAlexander V. Chernikov 	icfg = malloc(sizeof(struct iftable_cfg), M_IPFW, M_WAITOK | M_ZERO);
15789f7d47b0SAlexander V. Chernikov 
157968394ec8SAlexander V. Chernikov 	icfg->ii = ipfw_objhash_create(16);
158068394ec8SAlexander V. Chernikov 	icfg->main_ptr = malloc(sizeof(struct ifidx) * IFIDX_CHUNK,  M_IPFW,
158168394ec8SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
158268394ec8SAlexander V. Chernikov 	icfg->size = IFIDX_CHUNK;
158368394ec8SAlexander V. Chernikov 	icfg->ch = ch;
15849f7d47b0SAlexander V. Chernikov 
158568394ec8SAlexander V. Chernikov 	*ta_state = icfg;
158668394ec8SAlexander V. Chernikov 	ti->state = icfg->main_ptr;
158768394ec8SAlexander V. Chernikov 	ti->lookup = ta_lookup_ifidx;
15889f7d47b0SAlexander V. Chernikov 
15899f7d47b0SAlexander V. Chernikov 	return (0);
15909f7d47b0SAlexander V. Chernikov }
15919f7d47b0SAlexander V. Chernikov 
159268394ec8SAlexander V. Chernikov /*
159368394ec8SAlexander V. Chernikov  * Handle tableinfo @ti pointer change (on table array resize).
159468394ec8SAlexander V. Chernikov  */
159568394ec8SAlexander V. Chernikov static void
159668394ec8SAlexander V. Chernikov ta_change_ti_ifidx(void *ta_state, struct table_info *ti)
159768394ec8SAlexander V. Chernikov {
159868394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
159968394ec8SAlexander V. Chernikov 
160068394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
160168394ec8SAlexander V. Chernikov 	icfg->ti = ti;
160268394ec8SAlexander V. Chernikov }
16039f7d47b0SAlexander V. Chernikov 
16049f7d47b0SAlexander V. Chernikov static void
160568394ec8SAlexander V. Chernikov destroy_ifidx_locked(struct namedobj_instance *ii, struct named_object *no,
160668394ec8SAlexander V. Chernikov     void *arg)
16079f7d47b0SAlexander V. Chernikov {
160868394ec8SAlexander V. Chernikov 	struct ifentry *ife;
160968394ec8SAlexander V. Chernikov 	struct ip_fw_chain *ch;
16109f7d47b0SAlexander V. Chernikov 
161168394ec8SAlexander V. Chernikov 	ch = (struct ip_fw_chain *)arg;
161268394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)no;
161368394ec8SAlexander V. Chernikov 
161468394ec8SAlexander V. Chernikov 	ipfw_iface_del_notify(ch, &ife->ic);
161568394ec8SAlexander V. Chernikov 	free(ife, M_IPFW_TBL);
16169f7d47b0SAlexander V. Chernikov }
16179f7d47b0SAlexander V. Chernikov 
161868394ec8SAlexander V. Chernikov 
161968394ec8SAlexander V. Chernikov /*
162068394ec8SAlexander V. Chernikov  * Destroys table @ti
162168394ec8SAlexander V. Chernikov  */
162268394ec8SAlexander V. Chernikov static void
162368394ec8SAlexander V. Chernikov ta_destroy_ifidx(void *ta_state, struct table_info *ti)
16249f7d47b0SAlexander V. Chernikov {
162568394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
162668394ec8SAlexander V. Chernikov 	struct ip_fw_chain *ch;
162768394ec8SAlexander V. Chernikov 
162868394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
162968394ec8SAlexander V. Chernikov 	ch = icfg->ch;
163068394ec8SAlexander V. Chernikov 
163168394ec8SAlexander V. Chernikov 	if (icfg->main_ptr != NULL)
163268394ec8SAlexander V. Chernikov 		free(icfg->main_ptr, M_IPFW);
163368394ec8SAlexander V. Chernikov 
163468394ec8SAlexander V. Chernikov 	ipfw_objhash_foreach(icfg->ii, destroy_ifidx_locked, ch);
163568394ec8SAlexander V. Chernikov 
163668394ec8SAlexander V. Chernikov 	ipfw_objhash_destroy(icfg->ii);
163768394ec8SAlexander V. Chernikov 
163868394ec8SAlexander V. Chernikov 	free(icfg, M_IPFW);
163968394ec8SAlexander V. Chernikov }
164068394ec8SAlexander V. Chernikov 
164168394ec8SAlexander V. Chernikov struct ta_buf_ifidx
164268394ec8SAlexander V. Chernikov {
164368394ec8SAlexander V. Chernikov 	struct ifentry *ife;
164468394ec8SAlexander V. Chernikov 	uint32_t value;
16459f7d47b0SAlexander V. Chernikov };
16469f7d47b0SAlexander V. Chernikov 
164768394ec8SAlexander V. Chernikov /*
164868394ec8SAlexander V. Chernikov  * Prepare state to add to the table:
164968394ec8SAlexander V. Chernikov  * allocate ifentry and reference needed interface.
165068394ec8SAlexander V. Chernikov  */
16519f7d47b0SAlexander V. Chernikov static int
165268394ec8SAlexander V. Chernikov ta_prepare_add_ifidx(struct ip_fw_chain *ch, struct tentry_info *tei,
165368394ec8SAlexander V. Chernikov     void *ta_buf)
165468394ec8SAlexander V. Chernikov {
165568394ec8SAlexander V. Chernikov 	struct ta_buf_ifidx *tb;
165668394ec8SAlexander V. Chernikov 	char *ifname;
165768394ec8SAlexander V. Chernikov 	struct ifentry *ife;
165868394ec8SAlexander V. Chernikov 
165968394ec8SAlexander V. Chernikov 	tb = (struct ta_buf_ifidx *)ta_buf;
166074b941f0SAlexander V. Chernikov 	memset(tb, 0, sizeof(struct ta_buf_ifidx));
166168394ec8SAlexander V. Chernikov 
166268394ec8SAlexander V. Chernikov 	/* Check if string is terminated */
166368394ec8SAlexander V. Chernikov 	ifname = (char *)tei->paddr;
166468394ec8SAlexander V. Chernikov 	if (strnlen(ifname, IF_NAMESIZE) == IF_NAMESIZE)
166568394ec8SAlexander V. Chernikov 		return (EINVAL);
166668394ec8SAlexander V. Chernikov 
166768394ec8SAlexander V. Chernikov 	ife = malloc(sizeof(struct ifentry), M_IPFW_TBL, M_WAITOK | M_ZERO);
166868394ec8SAlexander V. Chernikov 	ife->value = tei->value;
166968394ec8SAlexander V. Chernikov 	ife->ic.cb = if_notifier;
167068394ec8SAlexander V. Chernikov 	ife->ic.cbdata = ife;
167168394ec8SAlexander V. Chernikov 
167268394ec8SAlexander V. Chernikov 	if (ipfw_iface_ref(ch, ifname, &ife->ic) != 0)
167368394ec8SAlexander V. Chernikov 		return (EINVAL);
167468394ec8SAlexander V. Chernikov 
167568394ec8SAlexander V. Chernikov 	/* Use ipfw_iface 'ifname' field as stable storage */
167668394ec8SAlexander V. Chernikov 	ife->no.name = ife->ic.iface->ifname;
167768394ec8SAlexander V. Chernikov 
167868394ec8SAlexander V. Chernikov 	tb->ife = ife;
167968394ec8SAlexander V. Chernikov 
168068394ec8SAlexander V. Chernikov 	return (0);
168168394ec8SAlexander V. Chernikov }
168268394ec8SAlexander V. Chernikov 
168368394ec8SAlexander V. Chernikov static int
1684adea6201SAlexander V. Chernikov ta_add_ifidx(void *ta_state, struct table_info *ti, struct tentry_info *tei,
1685adea6201SAlexander V. Chernikov     void *ta_buf, uint64_t *pflags, uint32_t *pnum)
168668394ec8SAlexander V. Chernikov {
168768394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
168868394ec8SAlexander V. Chernikov 	struct ifentry *ife, *tmp;
168968394ec8SAlexander V. Chernikov 	struct ta_buf_ifidx *tb;
169068394ec8SAlexander V. Chernikov 	struct ipfw_iface *iif;
169168394ec8SAlexander V. Chernikov 	struct ifidx *ifi;
169268394ec8SAlexander V. Chernikov 	char *ifname;
169368394ec8SAlexander V. Chernikov 
169468394ec8SAlexander V. Chernikov 	tb = (struct ta_buf_ifidx *)ta_buf;
169568394ec8SAlexander V. Chernikov 	ifname = (char *)tei->paddr;
169668394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
169768394ec8SAlexander V. Chernikov 	ife = tb->ife;
169868394ec8SAlexander V. Chernikov 
169968394ec8SAlexander V. Chernikov 	ife->icfg = icfg;
170068394ec8SAlexander V. Chernikov 
170168394ec8SAlexander V. Chernikov 	tmp = (struct ifentry *)ipfw_objhash_lookup_name(icfg->ii, 0, ifname);
170268394ec8SAlexander V. Chernikov 
170368394ec8SAlexander V. Chernikov 	if (tmp != NULL) {
170468394ec8SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
170568394ec8SAlexander V. Chernikov 			return (EEXIST);
170668394ec8SAlexander V. Chernikov 
170768394ec8SAlexander V. Chernikov 		/* We need to update value */
170868394ec8SAlexander V. Chernikov 		iif = tmp->ic.iface;
170968394ec8SAlexander V. Chernikov 		tmp->value = ife->value;
171068394ec8SAlexander V. Chernikov 
171168394ec8SAlexander V. Chernikov 		if (iif->resolved != 0) {
171268394ec8SAlexander V. Chernikov 			/* We need to update runtime value, too */
171368394ec8SAlexander V. Chernikov 			ifi = ifidx_find(ti, &iif->ifindex);
171468394ec8SAlexander V. Chernikov 			ifi->value = ife->value;
171568394ec8SAlexander V. Chernikov 		}
171668394ec8SAlexander V. Chernikov 
171768394ec8SAlexander V. Chernikov 		/* Indicate that update has happened instead of addition */
171868394ec8SAlexander V. Chernikov 		tei->flags |= TEI_FLAGS_UPDATED;
1719adea6201SAlexander V. Chernikov 		*pnum = 0;
172068394ec8SAlexander V. Chernikov 		return (0);
172168394ec8SAlexander V. Chernikov 	}
172268394ec8SAlexander V. Chernikov 
172368394ec8SAlexander V. Chernikov 	/* Link to internal list */
172468394ec8SAlexander V. Chernikov 	ipfw_objhash_add(icfg->ii, &ife->no);
172568394ec8SAlexander V. Chernikov 
172668394ec8SAlexander V. Chernikov 	/* Link notifier (possible running its callback) */
172768394ec8SAlexander V. Chernikov 	ipfw_iface_add_notify(icfg->ch, &ife->ic);
172868394ec8SAlexander V. Chernikov 	icfg->count++;
172968394ec8SAlexander V. Chernikov 
173068394ec8SAlexander V. Chernikov 	if (icfg->count + 1 == icfg->size) {
173168394ec8SAlexander V. Chernikov 		/* Notify core we need to grow */
173268394ec8SAlexander V. Chernikov 		*pflags = icfg->size + IFIDX_CHUNK;
173368394ec8SAlexander V. Chernikov 	}
173468394ec8SAlexander V. Chernikov 
173568394ec8SAlexander V. Chernikov 	tb->ife = NULL;
1736adea6201SAlexander V. Chernikov 	*pnum = 1;
173768394ec8SAlexander V. Chernikov 
173868394ec8SAlexander V. Chernikov 	return (0);
173968394ec8SAlexander V. Chernikov }
174068394ec8SAlexander V. Chernikov 
174168394ec8SAlexander V. Chernikov /*
174268394ec8SAlexander V. Chernikov  * Prepare to delete key from table.
174368394ec8SAlexander V. Chernikov  * Do basic interface name checks.
174468394ec8SAlexander V. Chernikov  */
174568394ec8SAlexander V. Chernikov static int
174668394ec8SAlexander V. Chernikov ta_prepare_del_ifidx(struct ip_fw_chain *ch, struct tentry_info *tei,
174768394ec8SAlexander V. Chernikov     void *ta_buf)
17489f7d47b0SAlexander V. Chernikov {
174974b941f0SAlexander V. Chernikov 	struct ta_buf_ifidx *tb;
1750e0a8b9eeSAlexander V. Chernikov 	char *ifname;
17519f7d47b0SAlexander V. Chernikov 
175274b941f0SAlexander V. Chernikov 	tb = (struct ta_buf_ifidx *)ta_buf;
175374b941f0SAlexander V. Chernikov 	memset(tb, 0, sizeof(struct ta_buf_ifidx));
17549f7d47b0SAlexander V. Chernikov 
17559f7d47b0SAlexander V. Chernikov 	/* Check if string is terminated */
1756e0a8b9eeSAlexander V. Chernikov 	ifname = (char *)tei->paddr;
1757e0a8b9eeSAlexander V. Chernikov 	if (strnlen(ifname, IF_NAMESIZE) == IF_NAMESIZE)
17589f7d47b0SAlexander V. Chernikov 		return (EINVAL);
17599f7d47b0SAlexander V. Chernikov 
17609f7d47b0SAlexander V. Chernikov 	return (0);
17619f7d47b0SAlexander V. Chernikov }
17629f7d47b0SAlexander V. Chernikov 
176368394ec8SAlexander V. Chernikov /*
176468394ec8SAlexander V. Chernikov  * Remove key from both configuration list and
176568394ec8SAlexander V. Chernikov  * runtime array. Removed interface notification.
176668394ec8SAlexander V. Chernikov  */
17679f7d47b0SAlexander V. Chernikov static int
1768adea6201SAlexander V. Chernikov ta_del_ifidx(void *ta_state, struct table_info *ti, struct tentry_info *tei,
1769adea6201SAlexander V. Chernikov     void *ta_buf, uint64_t *pflags, uint32_t *pnum)
17709f7d47b0SAlexander V. Chernikov {
177168394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
177268394ec8SAlexander V. Chernikov 	struct ifentry *ife;
177368394ec8SAlexander V. Chernikov 	struct ta_buf_ifidx *tb;
177468394ec8SAlexander V. Chernikov 	char *ifname;
177568394ec8SAlexander V. Chernikov 	uint16_t ifindex;
177668394ec8SAlexander V. Chernikov 	int res;
17779f7d47b0SAlexander V. Chernikov 
177868394ec8SAlexander V. Chernikov 	tb = (struct ta_buf_ifidx *)ta_buf;
177968394ec8SAlexander V. Chernikov 	ifname = (char *)tei->paddr;
178068394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
178168394ec8SAlexander V. Chernikov 	ife = tb->ife;
17829f7d47b0SAlexander V. Chernikov 
178368394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)ipfw_objhash_lookup_name(icfg->ii, 0, ifname);
17849f7d47b0SAlexander V. Chernikov 
178568394ec8SAlexander V. Chernikov 	if (ife == NULL)
178681d3153dSAlexander V. Chernikov 		return (ENOENT);
17879f7d47b0SAlexander V. Chernikov 
178868394ec8SAlexander V. Chernikov 	if (ife->linked != 0) {
178968394ec8SAlexander V. Chernikov 		/* We have to remove item from runtime */
179068394ec8SAlexander V. Chernikov 		ifindex = ife->ic.iface->ifindex;
179168394ec8SAlexander V. Chernikov 
179268394ec8SAlexander V. Chernikov 		res = bdel(&ifindex, icfg->main_ptr, icfg->used,
179368394ec8SAlexander V. Chernikov 		    sizeof(struct ifidx), compare_ifidx);
179468394ec8SAlexander V. Chernikov 
179568394ec8SAlexander V. Chernikov 		KASSERT(res == 1, ("index %d does not exist", ifindex));
179668394ec8SAlexander V. Chernikov 		icfg->used--;
179768394ec8SAlexander V. Chernikov 		ti->data = icfg->used;
179868394ec8SAlexander V. Chernikov 		ife->linked = 0;
179968394ec8SAlexander V. Chernikov 	}
180068394ec8SAlexander V. Chernikov 
180168394ec8SAlexander V. Chernikov 	/* Unlink from local list */
180268394ec8SAlexander V. Chernikov 	ipfw_objhash_del(icfg->ii, &ife->no);
180368394ec8SAlexander V. Chernikov 	/* Unlink notifier */
180468394ec8SAlexander V. Chernikov 	ipfw_iface_del_notify(icfg->ch, &ife->ic);
180568394ec8SAlexander V. Chernikov 
180668394ec8SAlexander V. Chernikov 	icfg->count--;
180768394ec8SAlexander V. Chernikov 
180868394ec8SAlexander V. Chernikov 	tb->ife = ife;
1809adea6201SAlexander V. Chernikov 	*pnum = 1;
181068394ec8SAlexander V. Chernikov 
18119f7d47b0SAlexander V. Chernikov 	return (0);
18129f7d47b0SAlexander V. Chernikov }
18139f7d47b0SAlexander V. Chernikov 
181468394ec8SAlexander V. Chernikov /*
181568394ec8SAlexander V. Chernikov  * Flush deleted entry.
181668394ec8SAlexander V. Chernikov  * Drops interface reference and frees entry.
181768394ec8SAlexander V. Chernikov  */
18189f7d47b0SAlexander V. Chernikov static void
181968394ec8SAlexander V. Chernikov ta_flush_ifidx_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
182068394ec8SAlexander V. Chernikov     void *ta_buf)
18219f7d47b0SAlexander V. Chernikov {
182268394ec8SAlexander V. Chernikov 	struct ta_buf_ifidx *tb;
18239f7d47b0SAlexander V. Chernikov 
182468394ec8SAlexander V. Chernikov 	tb = (struct ta_buf_ifidx *)ta_buf;
18259f7d47b0SAlexander V. Chernikov 
182668394ec8SAlexander V. Chernikov 	if (tb->ife != NULL) {
182768394ec8SAlexander V. Chernikov 		/* Unlink first */
182868394ec8SAlexander V. Chernikov 		ipfw_iface_unref(ch, &tb->ife->ic);
182968394ec8SAlexander V. Chernikov 		free(tb->ife, M_IPFW_TBL);
183068394ec8SAlexander V. Chernikov 	}
183168394ec8SAlexander V. Chernikov }
183268394ec8SAlexander V. Chernikov 
183368394ec8SAlexander V. Chernikov 
183468394ec8SAlexander V. Chernikov /*
183568394ec8SAlexander V. Chernikov  * Handle interface announce/withdrawal for particular table.
183668394ec8SAlexander V. Chernikov  * Every real runtime array modification happens here.
183768394ec8SAlexander V. Chernikov  */
183868394ec8SAlexander V. Chernikov static void
183968394ec8SAlexander V. Chernikov if_notifier(struct ip_fw_chain *ch, void *cbdata, uint16_t ifindex)
184068394ec8SAlexander V. Chernikov {
184168394ec8SAlexander V. Chernikov 	struct ifentry *ife;
184268394ec8SAlexander V. Chernikov 	struct ifidx ifi;
184368394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
184468394ec8SAlexander V. Chernikov 	struct table_info *ti;
184568394ec8SAlexander V. Chernikov 	int res;
184668394ec8SAlexander V. Chernikov 
184768394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)cbdata;
184868394ec8SAlexander V. Chernikov 	icfg = ife->icfg;
184968394ec8SAlexander V. Chernikov 	ti = icfg->ti;
185068394ec8SAlexander V. Chernikov 
185168394ec8SAlexander V. Chernikov 	KASSERT(ti != NULL, ("ti=NULL, check change_ti handler"));
185268394ec8SAlexander V. Chernikov 
185368394ec8SAlexander V. Chernikov 	if (ife->linked == 0 && ifindex != 0) {
185468394ec8SAlexander V. Chernikov 		/* Interface announce */
185568394ec8SAlexander V. Chernikov 		ifi.kidx = ifindex;
185668394ec8SAlexander V. Chernikov 		ifi.spare = 0;
185768394ec8SAlexander V. Chernikov 		ifi.value = ife->value;
185868394ec8SAlexander V. Chernikov 		res = badd(&ifindex, &ifi, icfg->main_ptr, icfg->used,
185968394ec8SAlexander V. Chernikov 		    sizeof(struct ifidx), compare_ifidx);
186068394ec8SAlexander V. Chernikov 		KASSERT(res == 1, ("index %d already exists", ifindex));
186168394ec8SAlexander V. Chernikov 		icfg->used++;
186268394ec8SAlexander V. Chernikov 		ti->data = icfg->used;
186368394ec8SAlexander V. Chernikov 		ife->linked = 1;
186468394ec8SAlexander V. Chernikov 	} else if (ife->linked != 0 && ifindex == 0) {
186568394ec8SAlexander V. Chernikov 		/* Interface withdrawal */
186668394ec8SAlexander V. Chernikov 		ifindex = ife->ic.iface->ifindex;
186768394ec8SAlexander V. Chernikov 
186868394ec8SAlexander V. Chernikov 		res = bdel(&ifindex, icfg->main_ptr, icfg->used,
186968394ec8SAlexander V. Chernikov 		    sizeof(struct ifidx), compare_ifidx);
187068394ec8SAlexander V. Chernikov 
187168394ec8SAlexander V. Chernikov 		KASSERT(res == 1, ("index %d does not exist", ifindex));
187268394ec8SAlexander V. Chernikov 		icfg->used--;
187368394ec8SAlexander V. Chernikov 		ti->data = icfg->used;
187468394ec8SAlexander V. Chernikov 		ife->linked = 0;
187568394ec8SAlexander V. Chernikov 	}
187668394ec8SAlexander V. Chernikov }
187768394ec8SAlexander V. Chernikov 
187868394ec8SAlexander V. Chernikov 
187968394ec8SAlexander V. Chernikov /*
188068394ec8SAlexander V. Chernikov  * Table growing callbacks.
188168394ec8SAlexander V. Chernikov  */
188268394ec8SAlexander V. Chernikov 
188368394ec8SAlexander V. Chernikov struct mod_ifidx {
188468394ec8SAlexander V. Chernikov 	void	*main_ptr;
188568394ec8SAlexander V. Chernikov 	size_t	size;
188668394ec8SAlexander V. Chernikov };
188768394ec8SAlexander V. Chernikov 
188868394ec8SAlexander V. Chernikov /*
188968394ec8SAlexander V. Chernikov  * Allocate ned, larger runtime ifidx array.
189068394ec8SAlexander V. Chernikov  */
189168394ec8SAlexander V. Chernikov static int
189268394ec8SAlexander V. Chernikov ta_prepare_mod_ifidx(void *ta_buf, uint64_t *pflags)
189368394ec8SAlexander V. Chernikov {
189468394ec8SAlexander V. Chernikov 	struct mod_ifidx *mi;
189568394ec8SAlexander V. Chernikov 
189668394ec8SAlexander V. Chernikov 	mi = (struct mod_ifidx *)ta_buf;
189768394ec8SAlexander V. Chernikov 
189868394ec8SAlexander V. Chernikov 	memset(mi, 0, sizeof(struct mod_ifidx));
189968394ec8SAlexander V. Chernikov 	mi->size = *pflags;
190068394ec8SAlexander V. Chernikov 	mi->main_ptr = malloc(sizeof(struct ifidx) * mi->size, M_IPFW,
190168394ec8SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
190268394ec8SAlexander V. Chernikov 
190368394ec8SAlexander V. Chernikov 	return (0);
190468394ec8SAlexander V. Chernikov }
190568394ec8SAlexander V. Chernikov 
190668394ec8SAlexander V. Chernikov /*
190768394ec8SAlexander V. Chernikov  * Copy data from old runtime array to new one.
190868394ec8SAlexander V. Chernikov  */
190968394ec8SAlexander V. Chernikov static int
191068394ec8SAlexander V. Chernikov ta_fill_mod_ifidx(void *ta_state, struct table_info *ti, void *ta_buf,
191168394ec8SAlexander V. Chernikov     uint64_t *pflags)
191268394ec8SAlexander V. Chernikov {
191368394ec8SAlexander V. Chernikov 	struct mod_ifidx *mi;
191468394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
191568394ec8SAlexander V. Chernikov 
191668394ec8SAlexander V. Chernikov 	mi = (struct mod_ifidx *)ta_buf;
191768394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
191868394ec8SAlexander V. Chernikov 
191968394ec8SAlexander V. Chernikov 	/* Check if we still need to grow array */
192068394ec8SAlexander V. Chernikov 	if (icfg->size >= mi->size) {
192168394ec8SAlexander V. Chernikov 		*pflags = 0;
192268394ec8SAlexander V. Chernikov 		return (0);
192368394ec8SAlexander V. Chernikov 	}
192468394ec8SAlexander V. Chernikov 
192568394ec8SAlexander V. Chernikov 	memcpy(mi->main_ptr, icfg->main_ptr, icfg->used * sizeof(struct ifidx));
192668394ec8SAlexander V. Chernikov 
192768394ec8SAlexander V. Chernikov 	return (0);
192868394ec8SAlexander V. Chernikov }
192968394ec8SAlexander V. Chernikov 
193068394ec8SAlexander V. Chernikov /*
193168394ec8SAlexander V. Chernikov  * Switch old & new arrays.
193268394ec8SAlexander V. Chernikov  */
193368394ec8SAlexander V. Chernikov static int
193468394ec8SAlexander V. Chernikov ta_modify_ifidx(void *ta_state, struct table_info *ti, void *ta_buf,
193568394ec8SAlexander V. Chernikov     uint64_t pflags)
193668394ec8SAlexander V. Chernikov {
193768394ec8SAlexander V. Chernikov 	struct mod_ifidx *mi;
193868394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
193968394ec8SAlexander V. Chernikov 	void *old_ptr;
194068394ec8SAlexander V. Chernikov 
194168394ec8SAlexander V. Chernikov 	mi = (struct mod_ifidx *)ta_buf;
194268394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
194368394ec8SAlexander V. Chernikov 
194468394ec8SAlexander V. Chernikov 	old_ptr = icfg->main_ptr;
194568394ec8SAlexander V. Chernikov 	icfg->main_ptr = mi->main_ptr;
194668394ec8SAlexander V. Chernikov 	icfg->size = mi->size;
194768394ec8SAlexander V. Chernikov 	ti->state = icfg->main_ptr;
194868394ec8SAlexander V. Chernikov 
194968394ec8SAlexander V. Chernikov 	mi->main_ptr = old_ptr;
195068394ec8SAlexander V. Chernikov 
195168394ec8SAlexander V. Chernikov 	return (0);
195268394ec8SAlexander V. Chernikov }
195368394ec8SAlexander V. Chernikov 
195468394ec8SAlexander V. Chernikov /*
195568394ec8SAlexander V. Chernikov  * Free unneded array.
195668394ec8SAlexander V. Chernikov  */
195768394ec8SAlexander V. Chernikov static void
195868394ec8SAlexander V. Chernikov ta_flush_mod_ifidx(void *ta_buf)
195968394ec8SAlexander V. Chernikov {
196068394ec8SAlexander V. Chernikov 	struct mod_ifidx *mi;
196168394ec8SAlexander V. Chernikov 
196268394ec8SAlexander V. Chernikov 	mi = (struct mod_ifidx *)ta_buf;
196368394ec8SAlexander V. Chernikov 	if (mi->main_ptr != NULL)
196468394ec8SAlexander V. Chernikov 		free(mi->main_ptr, M_IPFW);
19659f7d47b0SAlexander V. Chernikov }
19669f7d47b0SAlexander V. Chernikov 
19679f7d47b0SAlexander V. Chernikov static int
196868394ec8SAlexander V. Chernikov ta_dump_ifidx_tentry(void *ta_state, struct table_info *ti, void *e,
196981d3153dSAlexander V. Chernikov     ipfw_obj_tentry *tent)
19709f7d47b0SAlexander V. Chernikov {
197168394ec8SAlexander V. Chernikov 	struct ifentry *ife;
19729f7d47b0SAlexander V. Chernikov 
197368394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)e;
197468394ec8SAlexander V. Chernikov 
197581d3153dSAlexander V. Chernikov 	tent->masklen = 8 * IF_NAMESIZE;
197668394ec8SAlexander V. Chernikov 	memcpy(&tent->k, ife->no.name, IF_NAMESIZE);
197768394ec8SAlexander V. Chernikov 	tent->value = ife->value;
19789f7d47b0SAlexander V. Chernikov 
19799f7d47b0SAlexander V. Chernikov 	return (0);
19809f7d47b0SAlexander V. Chernikov }
19819f7d47b0SAlexander V. Chernikov 
198281d3153dSAlexander V. Chernikov static int
1983914bffb6SAlexander V. Chernikov ta_find_ifidx_tentry(void *ta_state, struct table_info *ti,
1984914bffb6SAlexander V. Chernikov     ipfw_obj_tentry *tent)
198581d3153dSAlexander V. Chernikov {
198668394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
198768394ec8SAlexander V. Chernikov 	struct ifentry *ife;
198868394ec8SAlexander V. Chernikov 	char *ifname;
198981d3153dSAlexander V. Chernikov 
199068394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
1991914bffb6SAlexander V. Chernikov 	ifname = tent->k.iface;
199281d3153dSAlexander V. Chernikov 
199368394ec8SAlexander V. Chernikov 	if (strnlen(ifname, IF_NAMESIZE) == IF_NAMESIZE)
199468394ec8SAlexander V. Chernikov 		return (EINVAL);
199581d3153dSAlexander V. Chernikov 
199668394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)ipfw_objhash_lookup_name(icfg->ii, 0, ifname);
199768394ec8SAlexander V. Chernikov 
199868394ec8SAlexander V. Chernikov 	if (ife != NULL) {
199968394ec8SAlexander V. Chernikov 		ta_dump_ifidx_tentry(ta_state, ti, ife, tent);
200081d3153dSAlexander V. Chernikov 		return (0);
200181d3153dSAlexander V. Chernikov 	}
200281d3153dSAlexander V. Chernikov 
200381d3153dSAlexander V. Chernikov 	return (ENOENT);
200481d3153dSAlexander V. Chernikov }
200581d3153dSAlexander V. Chernikov 
200668394ec8SAlexander V. Chernikov struct wa_ifidx {
200768394ec8SAlexander V. Chernikov 	ta_foreach_f	*f;
200868394ec8SAlexander V. Chernikov 	void		*arg;
200968394ec8SAlexander V. Chernikov };
201068394ec8SAlexander V. Chernikov 
20119f7d47b0SAlexander V. Chernikov static void
201268394ec8SAlexander V. Chernikov foreach_ifidx(struct namedobj_instance *ii, struct named_object *no,
20139f7d47b0SAlexander V. Chernikov     void *arg)
20149f7d47b0SAlexander V. Chernikov {
201568394ec8SAlexander V. Chernikov 	struct ifentry *ife;
201668394ec8SAlexander V. Chernikov 	struct wa_ifidx *wa;
20179f7d47b0SAlexander V. Chernikov 
201868394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)no;
201968394ec8SAlexander V. Chernikov 	wa = (struct wa_ifidx *)arg;
202068394ec8SAlexander V. Chernikov 
202168394ec8SAlexander V. Chernikov 	wa->f(ife, wa->arg);
20229f7d47b0SAlexander V. Chernikov }
20239f7d47b0SAlexander V. Chernikov 
202468394ec8SAlexander V. Chernikov static void
202568394ec8SAlexander V. Chernikov ta_foreach_ifidx(void *ta_state, struct table_info *ti, ta_foreach_f *f,
202668394ec8SAlexander V. Chernikov     void *arg)
202768394ec8SAlexander V. Chernikov {
202868394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
202968394ec8SAlexander V. Chernikov 	struct wa_ifidx wa;
203068394ec8SAlexander V. Chernikov 
203168394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
203268394ec8SAlexander V. Chernikov 
203368394ec8SAlexander V. Chernikov 	wa.f = f;
203468394ec8SAlexander V. Chernikov 	wa.arg = arg;
203568394ec8SAlexander V. Chernikov 
203668394ec8SAlexander V. Chernikov 	ipfw_objhash_foreach(icfg->ii, foreach_ifidx, &wa);
203768394ec8SAlexander V. Chernikov }
203868394ec8SAlexander V. Chernikov 
203974b941f0SAlexander V. Chernikov struct table_algo iface_idx = {
2040adea6201SAlexander V. Chernikov 	.name		= "iface:array",
20419d099b4fSAlexander V. Chernikov 	.type		= IPFW_TABLE_INTERFACE,
204257a1cf95SAlexander V. Chernikov 	.flags		= TA_FLAG_DEFAULT,
204357a1cf95SAlexander V. Chernikov 	.ta_buf_size	= sizeof(struct ta_buf_ifidx),
204468394ec8SAlexander V. Chernikov 	.init		= ta_init_ifidx,
204568394ec8SAlexander V. Chernikov 	.destroy	= ta_destroy_ifidx,
204668394ec8SAlexander V. Chernikov 	.prepare_add	= ta_prepare_add_ifidx,
204768394ec8SAlexander V. Chernikov 	.prepare_del	= ta_prepare_del_ifidx,
204868394ec8SAlexander V. Chernikov 	.add		= ta_add_ifidx,
204968394ec8SAlexander V. Chernikov 	.del		= ta_del_ifidx,
205068394ec8SAlexander V. Chernikov 	.flush_entry	= ta_flush_ifidx_entry,
205168394ec8SAlexander V. Chernikov 	.foreach	= ta_foreach_ifidx,
205268394ec8SAlexander V. Chernikov 	.dump_tentry	= ta_dump_ifidx_tentry,
205368394ec8SAlexander V. Chernikov 	.find_tentry	= ta_find_ifidx_tentry,
205468394ec8SAlexander V. Chernikov 	.prepare_mod	= ta_prepare_mod_ifidx,
205568394ec8SAlexander V. Chernikov 	.fill_mod	= ta_fill_mod_ifidx,
205668394ec8SAlexander V. Chernikov 	.modify		= ta_modify_ifidx,
205768394ec8SAlexander V. Chernikov 	.flush_mod	= ta_flush_mod_ifidx,
205868394ec8SAlexander V. Chernikov 	.change_ti	= ta_change_ti_ifidx,
20599f7d47b0SAlexander V. Chernikov };
20609f7d47b0SAlexander V. Chernikov 
2061b23d5de9SAlexander V. Chernikov /*
2062b23d5de9SAlexander V. Chernikov  * Number array cmds.
2063b23d5de9SAlexander V. Chernikov  *
2064b23d5de9SAlexander V. Chernikov  * Implementation:
2065b23d5de9SAlexander V. Chernikov  *
2066b23d5de9SAlexander V. Chernikov  * Runtime part:
2067b23d5de9SAlexander V. Chernikov  * - sorted array of "struct numarray" pointed by ti->state.
2068b23d5de9SAlexander V. Chernikov  *   Array is allocated with rounding up to NUMARRAY_CHUNK.
2069b23d5de9SAlexander V. Chernikov  * - current array size is stored in ti->data
2070b23d5de9SAlexander V. Chernikov  *
2071b23d5de9SAlexander V. Chernikov  */
2072b23d5de9SAlexander V. Chernikov 
2073b23d5de9SAlexander V. Chernikov struct numarray {
2074b23d5de9SAlexander V. Chernikov 	uint32_t	number;
2075b23d5de9SAlexander V. Chernikov 	uint32_t	value;
2076b23d5de9SAlexander V. Chernikov };
2077b23d5de9SAlexander V. Chernikov 
2078b23d5de9SAlexander V. Chernikov struct numarray_cfg {
2079b23d5de9SAlexander V. Chernikov 	void	*main_ptr;
2080b23d5de9SAlexander V. Chernikov 	size_t	size;	/* Number of items allocated in array */
2081b23d5de9SAlexander V. Chernikov 	size_t	used;	/* Number of items _active_ now */
2082b23d5de9SAlexander V. Chernikov };
2083b23d5de9SAlexander V. Chernikov 
2084b23d5de9SAlexander V. Chernikov #define	NUMARRAY_CHUNK	16
2085b23d5de9SAlexander V. Chernikov 
2086b23d5de9SAlexander V. Chernikov int compare_numarray(const void *k, const void *v);
2087b23d5de9SAlexander V. Chernikov 
2088b23d5de9SAlexander V. Chernikov int
2089b23d5de9SAlexander V. Chernikov compare_numarray(const void *k, const void *v)
2090b23d5de9SAlexander V. Chernikov {
2091b23d5de9SAlexander V. Chernikov 	struct numarray *na;
2092b23d5de9SAlexander V. Chernikov 	uint32_t key;
2093b23d5de9SAlexander V. Chernikov 
2094b23d5de9SAlexander V. Chernikov 	key = *((uint32_t *)k);
2095b23d5de9SAlexander V. Chernikov 	na = (struct numarray *)v;
2096b23d5de9SAlexander V. Chernikov 
2097b23d5de9SAlexander V. Chernikov 	if (key < na->number)
2098b23d5de9SAlexander V. Chernikov 		return (-1);
2099b23d5de9SAlexander V. Chernikov 	else if (key > na->number)
2100b23d5de9SAlexander V. Chernikov 		return (1);
2101b23d5de9SAlexander V. Chernikov 
2102b23d5de9SAlexander V. Chernikov 	return (0);
2103b23d5de9SAlexander V. Chernikov }
2104b23d5de9SAlexander V. Chernikov 
2105b23d5de9SAlexander V. Chernikov static struct numarray *
2106b23d5de9SAlexander V. Chernikov numarray_find(struct table_info *ti, void *key)
2107b23d5de9SAlexander V. Chernikov {
2108b23d5de9SAlexander V. Chernikov 	struct numarray *ri;
2109b23d5de9SAlexander V. Chernikov 
2110b23d5de9SAlexander V. Chernikov 	ri = bsearch(key, ti->state, ti->data, sizeof(struct numarray),
2111b23d5de9SAlexander V. Chernikov 	    compare_ifidx);
2112b23d5de9SAlexander V. Chernikov 
2113b23d5de9SAlexander V. Chernikov 	return (ri);
2114b23d5de9SAlexander V. Chernikov }
2115b23d5de9SAlexander V. Chernikov 
2116b23d5de9SAlexander V. Chernikov static int
2117b23d5de9SAlexander V. Chernikov ta_lookup_numarray(struct table_info *ti, void *key, uint32_t keylen,
2118b23d5de9SAlexander V. Chernikov     uint32_t *val)
2119b23d5de9SAlexander V. Chernikov {
2120b23d5de9SAlexander V. Chernikov 	struct numarray *ri;
2121b23d5de9SAlexander V. Chernikov 
2122b23d5de9SAlexander V. Chernikov 	ri = numarray_find(ti, key);
2123b23d5de9SAlexander V. Chernikov 
2124b23d5de9SAlexander V. Chernikov 	if (ri != NULL) {
2125b23d5de9SAlexander V. Chernikov 		*val = ri->value;
2126b23d5de9SAlexander V. Chernikov 		return (1);
2127b23d5de9SAlexander V. Chernikov 	}
2128b23d5de9SAlexander V. Chernikov 
2129b23d5de9SAlexander V. Chernikov 	return (0);
2130b23d5de9SAlexander V. Chernikov }
2131b23d5de9SAlexander V. Chernikov 
2132b23d5de9SAlexander V. Chernikov static int
2133b23d5de9SAlexander V. Chernikov ta_init_numarray(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
2134914bffb6SAlexander V. Chernikov     char *data, uint8_t tflags)
2135b23d5de9SAlexander V. Chernikov {
2136b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2137b23d5de9SAlexander V. Chernikov 
2138b23d5de9SAlexander V. Chernikov 	cfg = malloc(sizeof(*cfg), M_IPFW, M_WAITOK | M_ZERO);
2139b23d5de9SAlexander V. Chernikov 
2140b23d5de9SAlexander V. Chernikov 	cfg->size = NUMARRAY_CHUNK;
2141b23d5de9SAlexander V. Chernikov 	cfg->main_ptr = malloc(sizeof(struct numarray) * cfg->size, M_IPFW,
2142b23d5de9SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
2143b23d5de9SAlexander V. Chernikov 
2144b23d5de9SAlexander V. Chernikov 	*ta_state = cfg;
2145b23d5de9SAlexander V. Chernikov 	ti->state = cfg->main_ptr;
2146b23d5de9SAlexander V. Chernikov 	ti->lookup = ta_lookup_numarray;
2147b23d5de9SAlexander V. Chernikov 
2148b23d5de9SAlexander V. Chernikov 	return (0);
2149b23d5de9SAlexander V. Chernikov }
2150b23d5de9SAlexander V. Chernikov 
2151b23d5de9SAlexander V. Chernikov /*
2152b23d5de9SAlexander V. Chernikov  * Destroys table @ti
2153b23d5de9SAlexander V. Chernikov  */
2154b23d5de9SAlexander V. Chernikov static void
2155b23d5de9SAlexander V. Chernikov ta_destroy_numarray(void *ta_state, struct table_info *ti)
2156b23d5de9SAlexander V. Chernikov {
2157b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2158b23d5de9SAlexander V. Chernikov 
2159b23d5de9SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2160b23d5de9SAlexander V. Chernikov 
2161b23d5de9SAlexander V. Chernikov 	if (cfg->main_ptr != NULL)
2162b23d5de9SAlexander V. Chernikov 		free(cfg->main_ptr, M_IPFW);
2163b23d5de9SAlexander V. Chernikov 
2164b23d5de9SAlexander V. Chernikov 	free(cfg, M_IPFW);
2165b23d5de9SAlexander V. Chernikov }
2166b23d5de9SAlexander V. Chernikov 
2167b23d5de9SAlexander V. Chernikov struct ta_buf_numarray
2168b23d5de9SAlexander V. Chernikov {
2169b23d5de9SAlexander V. Chernikov 	struct numarray na;
2170b23d5de9SAlexander V. Chernikov };
2171b23d5de9SAlexander V. Chernikov 
2172b23d5de9SAlexander V. Chernikov /*
2173b23d5de9SAlexander V. Chernikov  * Prepare for addition/deletion to an array.
2174b23d5de9SAlexander V. Chernikov  */
2175b23d5de9SAlexander V. Chernikov static int
2176b23d5de9SAlexander V. Chernikov ta_prepare_add_numarray(struct ip_fw_chain *ch, struct tentry_info *tei,
2177b23d5de9SAlexander V. Chernikov     void *ta_buf)
2178b23d5de9SAlexander V. Chernikov {
2179b23d5de9SAlexander V. Chernikov 	struct ta_buf_numarray *tb;
2180b23d5de9SAlexander V. Chernikov 
2181b23d5de9SAlexander V. Chernikov 	tb = (struct ta_buf_numarray *)ta_buf;
2182b23d5de9SAlexander V. Chernikov 	memset(tb, 0, sizeof(*tb));
2183b23d5de9SAlexander V. Chernikov 
2184b23d5de9SAlexander V. Chernikov 	tb->na.number = *((uint32_t *)tei->paddr);
2185b23d5de9SAlexander V. Chernikov 	tb->na.value = tei->value;
2186b23d5de9SAlexander V. Chernikov 
2187b23d5de9SAlexander V. Chernikov 	return (0);
2188b23d5de9SAlexander V. Chernikov }
2189b23d5de9SAlexander V. Chernikov 
2190b23d5de9SAlexander V. Chernikov static int
2191b23d5de9SAlexander V. Chernikov ta_add_numarray(void *ta_state, struct table_info *ti, struct tentry_info *tei,
2192b23d5de9SAlexander V. Chernikov     void *ta_buf, uint64_t *pflags, uint32_t *pnum)
2193b23d5de9SAlexander V. Chernikov {
2194b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2195b23d5de9SAlexander V. Chernikov 	struct ta_buf_numarray *tb;
2196b23d5de9SAlexander V. Chernikov 	struct numarray *ri;
2197b23d5de9SAlexander V. Chernikov 	int res;
2198b23d5de9SAlexander V. Chernikov 
2199b23d5de9SAlexander V. Chernikov 	tb = (struct ta_buf_numarray*)ta_buf;
2200b23d5de9SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2201b23d5de9SAlexander V. Chernikov 
2202b23d5de9SAlexander V. Chernikov 	ri = numarray_find(ti, &tb->na.number);
2203b23d5de9SAlexander V. Chernikov 
2204b23d5de9SAlexander V. Chernikov 	if (ri != NULL) {
2205b23d5de9SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
2206b23d5de9SAlexander V. Chernikov 			return (EEXIST);
2207b23d5de9SAlexander V. Chernikov 
2208b23d5de9SAlexander V. Chernikov 		/* We need to update value */
2209b23d5de9SAlexander V. Chernikov 		ri->value = tb->na.value;
2210b23d5de9SAlexander V. Chernikov 		/* Indicate that update has happened instead of addition */
2211b23d5de9SAlexander V. Chernikov 		tei->flags |= TEI_FLAGS_UPDATED;
2212b23d5de9SAlexander V. Chernikov 		*pnum = 0;
2213b23d5de9SAlexander V. Chernikov 		return (0);
2214b23d5de9SAlexander V. Chernikov 	}
2215b23d5de9SAlexander V. Chernikov 
2216b23d5de9SAlexander V. Chernikov 	res = badd(&tb->na.number, &tb->na, cfg->main_ptr, cfg->used,
2217b23d5de9SAlexander V. Chernikov 	    sizeof(struct numarray), compare_numarray);
2218b23d5de9SAlexander V. Chernikov 
2219b23d5de9SAlexander V. Chernikov 	KASSERT(res == 1, ("number %d already exists", tb->na.number));
2220b23d5de9SAlexander V. Chernikov 	cfg->used++;
2221b23d5de9SAlexander V. Chernikov 	ti->data = cfg->used;
2222b23d5de9SAlexander V. Chernikov 
2223b23d5de9SAlexander V. Chernikov 	if (cfg->used + 1 == cfg->size) {
2224b23d5de9SAlexander V. Chernikov 		/* Notify core we need to grow */
2225b23d5de9SAlexander V. Chernikov 		*pflags = cfg->size + NUMARRAY_CHUNK;
2226b23d5de9SAlexander V. Chernikov 	}
2227b23d5de9SAlexander V. Chernikov 	*pnum = 1;
2228b23d5de9SAlexander V. Chernikov 
2229b23d5de9SAlexander V. Chernikov 	return (0);
2230b23d5de9SAlexander V. Chernikov }
2231b23d5de9SAlexander V. Chernikov 
2232b23d5de9SAlexander V. Chernikov /*
2233b23d5de9SAlexander V. Chernikov  * Remove key from both configuration list and
2234b23d5de9SAlexander V. Chernikov  * runtime array. Removed interface notification.
2235b23d5de9SAlexander V. Chernikov  */
2236b23d5de9SAlexander V. Chernikov static int
2237b23d5de9SAlexander V. Chernikov ta_del_numarray(void *ta_state, struct table_info *ti, struct tentry_info *tei,
2238b23d5de9SAlexander V. Chernikov     void *ta_buf, uint64_t *pflags, uint32_t *pnum)
2239b23d5de9SAlexander V. Chernikov {
2240b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2241b23d5de9SAlexander V. Chernikov 	struct ta_buf_numarray *tb;
2242b23d5de9SAlexander V. Chernikov 	struct numarray *ri;
2243b23d5de9SAlexander V. Chernikov 	int res;
2244b23d5de9SAlexander V. Chernikov 
2245b23d5de9SAlexander V. Chernikov 	tb = (struct ta_buf_numarray *)ta_buf;
2246b23d5de9SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2247b23d5de9SAlexander V. Chernikov 
2248b23d5de9SAlexander V. Chernikov 	ri = numarray_find(ti, &tb->na.number);
2249b23d5de9SAlexander V. Chernikov 	if (ri == NULL)
2250b23d5de9SAlexander V. Chernikov 		return (ENOENT);
2251b23d5de9SAlexander V. Chernikov 
2252b23d5de9SAlexander V. Chernikov 	res = bdel(&tb->na.number, cfg->main_ptr, cfg->used,
2253b23d5de9SAlexander V. Chernikov 	    sizeof(struct numarray), compare_numarray);
2254b23d5de9SAlexander V. Chernikov 
2255b23d5de9SAlexander V. Chernikov 	KASSERT(res == 1, ("number %u does not exist", tb->na.number));
2256b23d5de9SAlexander V. Chernikov 	cfg->used--;
2257b23d5de9SAlexander V. Chernikov 	ti->data = cfg->used;
2258b23d5de9SAlexander V. Chernikov 
2259b23d5de9SAlexander V. Chernikov 	*pnum = 1;
2260b23d5de9SAlexander V. Chernikov 
2261b23d5de9SAlexander V. Chernikov 	return (0);
2262b23d5de9SAlexander V. Chernikov }
2263b23d5de9SAlexander V. Chernikov 
2264b23d5de9SAlexander V. Chernikov static void
2265b23d5de9SAlexander V. Chernikov ta_flush_numarray_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
2266b23d5de9SAlexander V. Chernikov     void *ta_buf)
2267b23d5de9SAlexander V. Chernikov {
2268b23d5de9SAlexander V. Chernikov 
2269b23d5de9SAlexander V. Chernikov 	/* Do nothing */
2270b23d5de9SAlexander V. Chernikov }
2271b23d5de9SAlexander V. Chernikov 
2272b23d5de9SAlexander V. Chernikov 
2273b23d5de9SAlexander V. Chernikov /*
2274b23d5de9SAlexander V. Chernikov  * Table growing callbacks.
2275b23d5de9SAlexander V. Chernikov  */
2276b23d5de9SAlexander V. Chernikov 
2277b23d5de9SAlexander V. Chernikov /*
2278b23d5de9SAlexander V. Chernikov  * Allocate ned, larger runtime numarray array.
2279b23d5de9SAlexander V. Chernikov  */
2280b23d5de9SAlexander V. Chernikov static int
2281b23d5de9SAlexander V. Chernikov ta_prepare_mod_numarray(void *ta_buf, uint64_t *pflags)
2282b23d5de9SAlexander V. Chernikov {
2283b23d5de9SAlexander V. Chernikov 	struct mod_item *mi;
2284b23d5de9SAlexander V. Chernikov 
2285b23d5de9SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
2286b23d5de9SAlexander V. Chernikov 
2287b23d5de9SAlexander V. Chernikov 	memset(mi, 0, sizeof(struct mod_item));
2288b23d5de9SAlexander V. Chernikov 	mi->size = *pflags;
2289b23d5de9SAlexander V. Chernikov 	mi->main_ptr = malloc(sizeof(struct numarray) * mi->size, M_IPFW,
2290b23d5de9SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
2291b23d5de9SAlexander V. Chernikov 
2292b23d5de9SAlexander V. Chernikov 	return (0);
2293b23d5de9SAlexander V. Chernikov }
2294b23d5de9SAlexander V. Chernikov 
2295b23d5de9SAlexander V. Chernikov /*
2296b23d5de9SAlexander V. Chernikov  * Copy data from old runtime array to new one.
2297b23d5de9SAlexander V. Chernikov  */
2298b23d5de9SAlexander V. Chernikov static int
2299b23d5de9SAlexander V. Chernikov ta_fill_mod_numarray(void *ta_state, struct table_info *ti, void *ta_buf,
2300b23d5de9SAlexander V. Chernikov     uint64_t *pflags)
2301b23d5de9SAlexander V. Chernikov {
2302b23d5de9SAlexander V. Chernikov 	struct mod_item *mi;
2303b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2304b23d5de9SAlexander V. Chernikov 
2305b23d5de9SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
2306b23d5de9SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2307b23d5de9SAlexander V. Chernikov 
2308b23d5de9SAlexander V. Chernikov 	/* Check if we still need to grow array */
2309b23d5de9SAlexander V. Chernikov 	if (cfg->size >= mi->size) {
2310b23d5de9SAlexander V. Chernikov 		*pflags = 0;
2311b23d5de9SAlexander V. Chernikov 		return (0);
2312b23d5de9SAlexander V. Chernikov 	}
2313b23d5de9SAlexander V. Chernikov 
2314b23d5de9SAlexander V. Chernikov 	memcpy(mi->main_ptr, cfg->main_ptr, cfg->used * sizeof(struct numarray));
2315b23d5de9SAlexander V. Chernikov 
2316b23d5de9SAlexander V. Chernikov 	return (0);
2317b23d5de9SAlexander V. Chernikov }
2318b23d5de9SAlexander V. Chernikov 
2319b23d5de9SAlexander V. Chernikov /*
2320b23d5de9SAlexander V. Chernikov  * Switch old & new arrays.
2321b23d5de9SAlexander V. Chernikov  */
2322b23d5de9SAlexander V. Chernikov static int
2323b23d5de9SAlexander V. Chernikov ta_modify_numarray(void *ta_state, struct table_info *ti, void *ta_buf,
2324b23d5de9SAlexander V. Chernikov     uint64_t pflags)
2325b23d5de9SAlexander V. Chernikov {
2326b23d5de9SAlexander V. Chernikov 	struct mod_item *mi;
2327b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2328b23d5de9SAlexander V. Chernikov 	void *old_ptr;
2329b23d5de9SAlexander V. Chernikov 
2330b23d5de9SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
2331b23d5de9SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2332b23d5de9SAlexander V. Chernikov 
2333b23d5de9SAlexander V. Chernikov 	old_ptr = cfg->main_ptr;
2334b23d5de9SAlexander V. Chernikov 	cfg->main_ptr = mi->main_ptr;
2335b23d5de9SAlexander V. Chernikov 	cfg->size = mi->size;
2336b23d5de9SAlexander V. Chernikov 	ti->state = cfg->main_ptr;
2337b23d5de9SAlexander V. Chernikov 
2338b23d5de9SAlexander V. Chernikov 	mi->main_ptr = old_ptr;
2339b23d5de9SAlexander V. Chernikov 
2340b23d5de9SAlexander V. Chernikov 	return (0);
2341b23d5de9SAlexander V. Chernikov }
2342b23d5de9SAlexander V. Chernikov 
2343b23d5de9SAlexander V. Chernikov /*
2344b23d5de9SAlexander V. Chernikov  * Free unneded array.
2345b23d5de9SAlexander V. Chernikov  */
2346b23d5de9SAlexander V. Chernikov static void
2347b23d5de9SAlexander V. Chernikov ta_flush_mod_numarray(void *ta_buf)
2348b23d5de9SAlexander V. Chernikov {
2349b23d5de9SAlexander V. Chernikov 	struct mod_item *mi;
2350b23d5de9SAlexander V. Chernikov 
2351b23d5de9SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
2352b23d5de9SAlexander V. Chernikov 	if (mi->main_ptr != NULL)
2353b23d5de9SAlexander V. Chernikov 		free(mi->main_ptr, M_IPFW);
2354b23d5de9SAlexander V. Chernikov }
2355b23d5de9SAlexander V. Chernikov 
2356b23d5de9SAlexander V. Chernikov static int
2357b23d5de9SAlexander V. Chernikov ta_dump_numarray_tentry(void *ta_state, struct table_info *ti, void *e,
2358b23d5de9SAlexander V. Chernikov     ipfw_obj_tentry *tent)
2359b23d5de9SAlexander V. Chernikov {
2360b23d5de9SAlexander V. Chernikov 	struct numarray *na;
2361b23d5de9SAlexander V. Chernikov 
2362b23d5de9SAlexander V. Chernikov 	na = (struct numarray *)e;
2363b23d5de9SAlexander V. Chernikov 
2364b23d5de9SAlexander V. Chernikov 	tent->k.key = na->number;
2365b23d5de9SAlexander V. Chernikov 	tent->value = na->value;
2366b23d5de9SAlexander V. Chernikov 
2367b23d5de9SAlexander V. Chernikov 	return (0);
2368b23d5de9SAlexander V. Chernikov }
2369b23d5de9SAlexander V. Chernikov 
2370b23d5de9SAlexander V. Chernikov static int
2371914bffb6SAlexander V. Chernikov ta_find_numarray_tentry(void *ta_state, struct table_info *ti,
2372914bffb6SAlexander V. Chernikov     ipfw_obj_tentry *tent)
2373b23d5de9SAlexander V. Chernikov {
2374b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2375b23d5de9SAlexander V. Chernikov 	struct numarray *ri;
2376b23d5de9SAlexander V. Chernikov 
2377b23d5de9SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2378b23d5de9SAlexander V. Chernikov 
2379914bffb6SAlexander V. Chernikov 	ri = numarray_find(ti, &tent->k.key);
2380b23d5de9SAlexander V. Chernikov 
2381b23d5de9SAlexander V. Chernikov 	if (ri != NULL) {
2382b23d5de9SAlexander V. Chernikov 		ta_dump_numarray_tentry(ta_state, ti, ri, tent);
2383b23d5de9SAlexander V. Chernikov 		return (0);
2384b23d5de9SAlexander V. Chernikov 	}
2385b23d5de9SAlexander V. Chernikov 
2386b23d5de9SAlexander V. Chernikov 	return (ENOENT);
2387b23d5de9SAlexander V. Chernikov }
2388b23d5de9SAlexander V. Chernikov 
2389b23d5de9SAlexander V. Chernikov static void
2390b23d5de9SAlexander V. Chernikov ta_foreach_numarray(void *ta_state, struct table_info *ti, ta_foreach_f *f,
2391b23d5de9SAlexander V. Chernikov     void *arg)
2392b23d5de9SAlexander V. Chernikov {
2393b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2394b23d5de9SAlexander V. Chernikov 	struct numarray *array;
2395b23d5de9SAlexander V. Chernikov 	int i;
2396b23d5de9SAlexander V. Chernikov 
2397b23d5de9SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2398b23d5de9SAlexander V. Chernikov 	array = cfg->main_ptr;
2399b23d5de9SAlexander V. Chernikov 
2400b23d5de9SAlexander V. Chernikov 	for (i = 0; i < cfg->used; i++)
2401b23d5de9SAlexander V. Chernikov 		f(&array[i], arg);
2402b23d5de9SAlexander V. Chernikov }
2403b23d5de9SAlexander V. Chernikov 
2404b23d5de9SAlexander V. Chernikov struct table_algo number_array = {
2405b23d5de9SAlexander V. Chernikov 	.name		= "number:array",
2406b23d5de9SAlexander V. Chernikov 	.type		= IPFW_TABLE_NUMBER,
240757a1cf95SAlexander V. Chernikov 	.ta_buf_size	= sizeof(struct ta_buf_numarray),
2408b23d5de9SAlexander V. Chernikov 	.init		= ta_init_numarray,
2409b23d5de9SAlexander V. Chernikov 	.destroy	= ta_destroy_numarray,
2410b23d5de9SAlexander V. Chernikov 	.prepare_add	= ta_prepare_add_numarray,
2411b23d5de9SAlexander V. Chernikov 	.prepare_del	= ta_prepare_add_numarray,
2412b23d5de9SAlexander V. Chernikov 	.add		= ta_add_numarray,
2413b23d5de9SAlexander V. Chernikov 	.del		= ta_del_numarray,
2414b23d5de9SAlexander V. Chernikov 	.flush_entry	= ta_flush_numarray_entry,
2415b23d5de9SAlexander V. Chernikov 	.foreach	= ta_foreach_numarray,
2416b23d5de9SAlexander V. Chernikov 	.dump_tentry	= ta_dump_numarray_tentry,
2417b23d5de9SAlexander V. Chernikov 	.find_tentry	= ta_find_numarray_tentry,
2418b23d5de9SAlexander V. Chernikov 	.prepare_mod	= ta_prepare_mod_numarray,
2419b23d5de9SAlexander V. Chernikov 	.fill_mod	= ta_fill_mod_numarray,
2420b23d5de9SAlexander V. Chernikov 	.modify		= ta_modify_numarray,
2421b23d5de9SAlexander V. Chernikov 	.flush_mod	= ta_flush_mod_numarray,
2422b23d5de9SAlexander V. Chernikov };
2423b23d5de9SAlexander V. Chernikov 
2424914bffb6SAlexander V. Chernikov /*
2425914bffb6SAlexander V. Chernikov  * flow:hash cmds
2426914bffb6SAlexander V. Chernikov  *
2427914bffb6SAlexander V. Chernikov  *
2428914bffb6SAlexander V. Chernikov  * ti->data:
2429914bffb6SAlexander V. Chernikov  * [inv.mask4][inv.mask6][log2hsize4][log2hsize6]
2430914bffb6SAlexander V. Chernikov  * [        8][        8[          8][         8]
2431914bffb6SAlexander V. Chernikov  *
2432914bffb6SAlexander V. Chernikov  * inv.mask4: 32 - mask
2433914bffb6SAlexander V. Chernikov  * inv.mask6:
2434914bffb6SAlexander V. Chernikov  * 1) _slow lookup: mask
2435914bffb6SAlexander V. Chernikov  * 2) _aligned: (128 - mask) / 8
2436914bffb6SAlexander V. Chernikov  * 3) _64: 8
2437914bffb6SAlexander V. Chernikov  *
2438914bffb6SAlexander V. Chernikov  *
2439914bffb6SAlexander V. Chernikov  * pflags:
2440914bffb6SAlexander V. Chernikov  * [v4=1/v6=0][hsize]
2441914bffb6SAlexander V. Chernikov  * [       32][   32]
2442914bffb6SAlexander V. Chernikov  */
2443914bffb6SAlexander V. Chernikov 
2444914bffb6SAlexander V. Chernikov struct fhashentry;
2445914bffb6SAlexander V. Chernikov 
2446914bffb6SAlexander V. Chernikov SLIST_HEAD(fhashbhead, fhashentry);
2447914bffb6SAlexander V. Chernikov 
2448914bffb6SAlexander V. Chernikov /*
2449914bffb6SAlexander V. Chernikov struct tflow_entry {
2450914bffb6SAlexander V. Chernikov 	uint8_t		af;
2451914bffb6SAlexander V. Chernikov 	uint8_t		proto;
2452914bffb6SAlexander V. Chernikov 	uint16_t	spare;
2453914bffb6SAlexander V. Chernikov 	uint16_t	dport;
2454914bffb6SAlexander V. Chernikov 	uint16_t	sport;
2455914bffb6SAlexander V. Chernikov 	union {
2456914bffb6SAlexander V. Chernikov 		struct {
2457914bffb6SAlexander V. Chernikov 			struct in_addr	sip;
2458914bffb6SAlexander V. Chernikov 			struct in_addr	dip;
2459914bffb6SAlexander V. Chernikov 		} v4;
2460914bffb6SAlexander V. Chernikov 		struct {
2461914bffb6SAlexander V. Chernikov 			struct in6_addr	sip6;
2462914bffb6SAlexander V. Chernikov 			struct in6_addr	dip6;
2463914bffb6SAlexander V. Chernikov 		} v6;
2464914bffb6SAlexander V. Chernikov 	} a;
2465914bffb6SAlexander V. Chernikov };
2466914bffb6SAlexander V. Chernikov */
2467914bffb6SAlexander V. Chernikov 
2468914bffb6SAlexander V. Chernikov struct fhashentry {
2469914bffb6SAlexander V. Chernikov 	SLIST_ENTRY(fhashentry)	next;
2470914bffb6SAlexander V. Chernikov 	uint8_t		af;
2471914bffb6SAlexander V. Chernikov 	uint8_t		proto;
2472914bffb6SAlexander V. Chernikov 	uint16_t	spare0;
2473914bffb6SAlexander V. Chernikov 	uint16_t	dport;
2474914bffb6SAlexander V. Chernikov 	uint16_t	sport;
2475914bffb6SAlexander V. Chernikov 	uint32_t	value;
2476914bffb6SAlexander V. Chernikov 	uint32_t	spare1;
2477914bffb6SAlexander V. Chernikov };
2478914bffb6SAlexander V. Chernikov 
2479914bffb6SAlexander V. Chernikov struct fhashentry4 {
2480914bffb6SAlexander V. Chernikov 	struct fhashentry	e;
2481914bffb6SAlexander V. Chernikov 	struct in_addr		dip;
2482914bffb6SAlexander V. Chernikov 	struct in_addr		sip;
2483914bffb6SAlexander V. Chernikov };
2484914bffb6SAlexander V. Chernikov 
2485914bffb6SAlexander V. Chernikov struct fhashentry6 {
2486914bffb6SAlexander V. Chernikov 	struct fhashentry	e;
2487914bffb6SAlexander V. Chernikov 	struct in6_addr		dip6;
2488914bffb6SAlexander V. Chernikov 	struct in6_addr		sip6;
2489914bffb6SAlexander V. Chernikov };
2490914bffb6SAlexander V. Chernikov 
2491914bffb6SAlexander V. Chernikov struct fhash_cfg {
2492914bffb6SAlexander V. Chernikov 	struct fhashbhead	*head;
2493914bffb6SAlexander V. Chernikov 	size_t			size;
2494914bffb6SAlexander V. Chernikov 	size_t			items;
2495914bffb6SAlexander V. Chernikov 	struct fhashentry4	fe4;
2496914bffb6SAlexander V. Chernikov 	struct fhashentry6	fe6;
2497914bffb6SAlexander V. Chernikov };
2498914bffb6SAlexander V. Chernikov 
2499914bffb6SAlexander V. Chernikov static __inline int
2500914bffb6SAlexander V. Chernikov cmp_flow_ent(struct fhashentry *a, struct fhashentry *b, size_t sz)
2501914bffb6SAlexander V. Chernikov {
2502914bffb6SAlexander V. Chernikov 	uint64_t *ka, *kb;
2503914bffb6SAlexander V. Chernikov 
2504914bffb6SAlexander V. Chernikov 	ka = (uint64_t *)(&a->next + 1);
2505914bffb6SAlexander V. Chernikov 	kb = (uint64_t *)(&b->next + 1);
2506914bffb6SAlexander V. Chernikov 
2507914bffb6SAlexander V. Chernikov 	if (*ka == *kb && (memcmp(a + 1, b + 1, sz) == 0))
2508914bffb6SAlexander V. Chernikov 		return (1);
2509914bffb6SAlexander V. Chernikov 
2510914bffb6SAlexander V. Chernikov 	return (0);
2511914bffb6SAlexander V. Chernikov }
2512914bffb6SAlexander V. Chernikov 
2513914bffb6SAlexander V. Chernikov static __inline uint32_t
2514914bffb6SAlexander V. Chernikov hash_flow4(struct fhashentry4 *f, int hsize)
2515914bffb6SAlexander V. Chernikov {
2516914bffb6SAlexander V. Chernikov 	uint32_t i;
2517914bffb6SAlexander V. Chernikov 
2518914bffb6SAlexander V. Chernikov 	i = (f->dip.s_addr) ^ (f->sip.s_addr) ^ (f->e.dport) ^ (f->e.sport);
2519914bffb6SAlexander V. Chernikov 
2520914bffb6SAlexander V. Chernikov 	return (i % (hsize - 1));
2521914bffb6SAlexander V. Chernikov }
2522914bffb6SAlexander V. Chernikov 
2523914bffb6SAlexander V. Chernikov static __inline uint32_t
2524914bffb6SAlexander V. Chernikov hash_flow6(struct fhashentry6 *f, int hsize)
2525914bffb6SAlexander V. Chernikov {
2526914bffb6SAlexander V. Chernikov 	uint32_t i;
2527914bffb6SAlexander V. Chernikov 
2528914bffb6SAlexander V. Chernikov 	i = (f->dip6.__u6_addr.__u6_addr32[2]) ^
2529914bffb6SAlexander V. Chernikov 	    (f->dip6.__u6_addr.__u6_addr32[3]) ^
2530914bffb6SAlexander V. Chernikov 	    (f->sip6.__u6_addr.__u6_addr32[2]) ^
2531914bffb6SAlexander V. Chernikov 	    (f->sip6.__u6_addr.__u6_addr32[3]) ^
2532914bffb6SAlexander V. Chernikov 	    (f->e.dport) ^ (f->e.sport);
2533914bffb6SAlexander V. Chernikov 
2534914bffb6SAlexander V. Chernikov 	return (i % (hsize - 1));
2535914bffb6SAlexander V. Chernikov }
2536914bffb6SAlexander V. Chernikov 
2537914bffb6SAlexander V. Chernikov static uint32_t
2538914bffb6SAlexander V. Chernikov hash_flow_ent(struct fhashentry *ent, uint32_t size)
2539914bffb6SAlexander V. Chernikov {
2540914bffb6SAlexander V. Chernikov 	uint32_t hash;
2541914bffb6SAlexander V. Chernikov 
2542914bffb6SAlexander V. Chernikov 	if (ent->af == AF_INET) {
2543914bffb6SAlexander V. Chernikov 		hash = hash_flow4((struct fhashentry4 *)ent, size);
2544914bffb6SAlexander V. Chernikov 	} else {
2545914bffb6SAlexander V. Chernikov 		hash = hash_flow6((struct fhashentry6 *)ent, size);
2546914bffb6SAlexander V. Chernikov 	}
2547914bffb6SAlexander V. Chernikov 
2548914bffb6SAlexander V. Chernikov 	return (hash);
2549914bffb6SAlexander V. Chernikov }
2550914bffb6SAlexander V. Chernikov 
2551914bffb6SAlexander V. Chernikov static int
2552914bffb6SAlexander V. Chernikov ta_lookup_fhash(struct table_info *ti, void *key, uint32_t keylen,
2553914bffb6SAlexander V. Chernikov     uint32_t *val)
2554914bffb6SAlexander V. Chernikov {
2555914bffb6SAlexander V. Chernikov 	struct fhashbhead *head;
2556914bffb6SAlexander V. Chernikov 	struct fhashentry *ent;
2557914bffb6SAlexander V. Chernikov 	struct fhashentry4 *m4;
2558914bffb6SAlexander V. Chernikov 	struct ipfw_flow_id *id;
2559914bffb6SAlexander V. Chernikov 	uint16_t hash, hsize;
2560914bffb6SAlexander V. Chernikov 
2561914bffb6SAlexander V. Chernikov 	id = (struct ipfw_flow_id *)key;
2562914bffb6SAlexander V. Chernikov 	head = (struct fhashbhead *)ti->state;
2563914bffb6SAlexander V. Chernikov 	hsize = ti->data;
2564914bffb6SAlexander V. Chernikov 	m4 = (struct fhashentry4 *)ti->xstate;
2565914bffb6SAlexander V. Chernikov 
2566914bffb6SAlexander V. Chernikov 	if (id->addr_type == 4) {
2567914bffb6SAlexander V. Chernikov 		struct fhashentry4 f;
2568914bffb6SAlexander V. Chernikov 
2569914bffb6SAlexander V. Chernikov 		/* Copy hash mask */
2570914bffb6SAlexander V. Chernikov 		f = *m4;
2571914bffb6SAlexander V. Chernikov 
2572914bffb6SAlexander V. Chernikov 		f.dip.s_addr &= id->dst_ip;
2573914bffb6SAlexander V. Chernikov 		f.sip.s_addr &= id->src_ip;
2574914bffb6SAlexander V. Chernikov 		f.e.dport &= id->dst_port;
2575914bffb6SAlexander V. Chernikov 		f.e.sport &= id->src_port;
2576914bffb6SAlexander V. Chernikov 		f.e.proto &= id->proto;
2577914bffb6SAlexander V. Chernikov 		hash = hash_flow4(&f, hsize);
2578914bffb6SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
2579914bffb6SAlexander V. Chernikov 			if (cmp_flow_ent(ent, &f.e, 2 * 4) != 0) {
2580914bffb6SAlexander V. Chernikov 				*val = ent->value;
2581914bffb6SAlexander V. Chernikov 				return (1);
2582914bffb6SAlexander V. Chernikov 			}
2583914bffb6SAlexander V. Chernikov 		}
2584914bffb6SAlexander V. Chernikov 	} else if (id->addr_type == 6) {
2585914bffb6SAlexander V. Chernikov 		struct fhashentry6 f;
2586914bffb6SAlexander V. Chernikov 		uint64_t *fp, *idp;
2587914bffb6SAlexander V. Chernikov 
2588914bffb6SAlexander V. Chernikov 		/* Copy hash mask */
2589914bffb6SAlexander V. Chernikov 		f = *((struct fhashentry6 *)(m4 + 1));
2590914bffb6SAlexander V. Chernikov 
2591914bffb6SAlexander V. Chernikov 		/* Handle lack of __u6_addr.__u6_addr64 */
2592914bffb6SAlexander V. Chernikov 		fp = (uint64_t *)&f.dip6;
2593914bffb6SAlexander V. Chernikov 		idp = (uint64_t *)&id->dst_ip6;
2594914bffb6SAlexander V. Chernikov 		/* src IPv6 is stored after dst IPv6 */
2595914bffb6SAlexander V. Chernikov 		*fp++ &= *idp++;
2596914bffb6SAlexander V. Chernikov 		*fp++ &= *idp++;
2597914bffb6SAlexander V. Chernikov 		*fp++ &= *idp++;
2598914bffb6SAlexander V. Chernikov 		*fp &= *idp;
2599914bffb6SAlexander V. Chernikov 		f.e.dport &= id->dst_port;
2600914bffb6SAlexander V. Chernikov 		f.e.sport &= id->src_port;
2601914bffb6SAlexander V. Chernikov 		f.e.proto &= id->proto;
2602914bffb6SAlexander V. Chernikov 		hash = hash_flow6(&f, hsize);
2603914bffb6SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
2604914bffb6SAlexander V. Chernikov 			if (cmp_flow_ent(ent, &f.e, 2 * 16) != 0) {
2605914bffb6SAlexander V. Chernikov 				*val = ent->value;
2606914bffb6SAlexander V. Chernikov 				return (1);
2607914bffb6SAlexander V. Chernikov 			}
2608914bffb6SAlexander V. Chernikov 		}
2609914bffb6SAlexander V. Chernikov 	}
2610914bffb6SAlexander V. Chernikov 
2611914bffb6SAlexander V. Chernikov 	return (0);
2612914bffb6SAlexander V. Chernikov }
2613914bffb6SAlexander V. Chernikov 
2614914bffb6SAlexander V. Chernikov /*
2615914bffb6SAlexander V. Chernikov  * New table.
2616914bffb6SAlexander V. Chernikov  */
2617914bffb6SAlexander V. Chernikov static int
2618914bffb6SAlexander V. Chernikov ta_init_fhash(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
2619914bffb6SAlexander V. Chernikov     char *data, uint8_t tflags)
2620914bffb6SAlexander V. Chernikov {
2621914bffb6SAlexander V. Chernikov 	int i;
2622914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
2623914bffb6SAlexander V. Chernikov 	struct fhashentry4 *fe4;
2624914bffb6SAlexander V. Chernikov 	struct fhashentry6 *fe6;
2625914bffb6SAlexander V. Chernikov 
2626914bffb6SAlexander V. Chernikov 	cfg = malloc(sizeof(struct fhash_cfg), M_IPFW, M_WAITOK | M_ZERO);
2627914bffb6SAlexander V. Chernikov 
2628914bffb6SAlexander V. Chernikov 	cfg->size = 512;
2629914bffb6SAlexander V. Chernikov 
2630914bffb6SAlexander V. Chernikov 	cfg->head = malloc(sizeof(struct fhashbhead) * cfg->size, M_IPFW,
2631914bffb6SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
2632914bffb6SAlexander V. Chernikov 	for (i = 0; i < cfg->size; i++)
2633914bffb6SAlexander V. Chernikov 		SLIST_INIT(&cfg->head[i]);
2634914bffb6SAlexander V. Chernikov 
2635914bffb6SAlexander V. Chernikov 	/* Fill in fe masks based on @tflags */
2636914bffb6SAlexander V. Chernikov 	fe4 = &cfg->fe4;
2637914bffb6SAlexander V. Chernikov 	fe6 = &cfg->fe6;
2638914bffb6SAlexander V. Chernikov 	if (tflags & IPFW_TFFLAG_SRCIP) {
2639914bffb6SAlexander V. Chernikov 		memset(&fe4->sip, 0xFF, sizeof(fe4->sip));
2640914bffb6SAlexander V. Chernikov 		memset(&fe6->sip6, 0xFF, sizeof(fe6->sip6));
2641914bffb6SAlexander V. Chernikov 	}
2642914bffb6SAlexander V. Chernikov 	if (tflags & IPFW_TFFLAG_DSTIP) {
2643914bffb6SAlexander V. Chernikov 		memset(&fe4->dip, 0xFF, sizeof(fe4->dip));
2644914bffb6SAlexander V. Chernikov 		memset(&fe6->dip6, 0xFF, sizeof(fe6->dip6));
2645914bffb6SAlexander V. Chernikov 	}
2646914bffb6SAlexander V. Chernikov 	if (tflags & IPFW_TFFLAG_SRCPORT) {
2647914bffb6SAlexander V. Chernikov 		memset(&fe4->e.sport, 0xFF, sizeof(fe4->e.sport));
2648914bffb6SAlexander V. Chernikov 		memset(&fe6->e.sport, 0xFF, sizeof(fe6->e.sport));
2649914bffb6SAlexander V. Chernikov 	}
2650914bffb6SAlexander V. Chernikov 	if (tflags & IPFW_TFFLAG_DSTPORT) {
2651914bffb6SAlexander V. Chernikov 		memset(&fe4->e.dport, 0xFF, sizeof(fe4->e.dport));
2652914bffb6SAlexander V. Chernikov 		memset(&fe6->e.dport, 0xFF, sizeof(fe6->e.dport));
2653914bffb6SAlexander V. Chernikov 	}
2654914bffb6SAlexander V. Chernikov 	if (tflags & IPFW_TFFLAG_PROTO) {
2655914bffb6SAlexander V. Chernikov 		memset(&fe4->e.proto, 0xFF, sizeof(fe4->e.proto));
2656914bffb6SAlexander V. Chernikov 		memset(&fe6->e.proto, 0xFF, sizeof(fe6->e.proto));
2657914bffb6SAlexander V. Chernikov 	}
2658914bffb6SAlexander V. Chernikov 
2659914bffb6SAlexander V. Chernikov 	fe4->e.af = AF_INET;
2660914bffb6SAlexander V. Chernikov 	fe6->e.af = AF_INET6;
2661914bffb6SAlexander V. Chernikov 
2662914bffb6SAlexander V. Chernikov 	*ta_state = cfg;
2663914bffb6SAlexander V. Chernikov 	ti->state = cfg->head;
2664914bffb6SAlexander V. Chernikov 	ti->xstate = &cfg->fe4;
2665914bffb6SAlexander V. Chernikov 	ti->data = cfg->size;
2666914bffb6SAlexander V. Chernikov 	ti->lookup = ta_lookup_fhash;
2667914bffb6SAlexander V. Chernikov 
2668914bffb6SAlexander V. Chernikov 	return (0);
2669914bffb6SAlexander V. Chernikov }
2670914bffb6SAlexander V. Chernikov 
2671914bffb6SAlexander V. Chernikov static void
2672914bffb6SAlexander V. Chernikov ta_destroy_fhash(void *ta_state, struct table_info *ti)
2673914bffb6SAlexander V. Chernikov {
2674914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
2675914bffb6SAlexander V. Chernikov 	struct fhashentry *ent, *ent_next;
2676914bffb6SAlexander V. Chernikov 	int i;
2677914bffb6SAlexander V. Chernikov 
2678914bffb6SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
2679914bffb6SAlexander V. Chernikov 
2680914bffb6SAlexander V. Chernikov 	for (i = 0; i < cfg->size; i++)
2681914bffb6SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &cfg->head[i], next, ent_next)
2682914bffb6SAlexander V. Chernikov 			free(ent, M_IPFW_TBL);
2683914bffb6SAlexander V. Chernikov 
2684914bffb6SAlexander V. Chernikov 	free(cfg->head, M_IPFW);
2685914bffb6SAlexander V. Chernikov 	free(cfg, M_IPFW);
2686914bffb6SAlexander V. Chernikov }
2687914bffb6SAlexander V. Chernikov 
2688914bffb6SAlexander V. Chernikov static int
2689914bffb6SAlexander V. Chernikov ta_dump_fhash_tentry(void *ta_state, struct table_info *ti, void *e,
2690914bffb6SAlexander V. Chernikov     ipfw_obj_tentry *tent)
2691914bffb6SAlexander V. Chernikov {
2692914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
2693914bffb6SAlexander V. Chernikov 	struct fhashentry *ent;
2694914bffb6SAlexander V. Chernikov 	struct fhashentry4 *fe4;
2695914bffb6SAlexander V. Chernikov 	struct fhashentry6 *fe6;
2696914bffb6SAlexander V. Chernikov 	struct tflow_entry *tfe;
2697914bffb6SAlexander V. Chernikov 
2698914bffb6SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
2699914bffb6SAlexander V. Chernikov 	ent = (struct fhashentry *)e;
2700914bffb6SAlexander V. Chernikov 	tfe = &tent->k.flow;
2701914bffb6SAlexander V. Chernikov 
2702914bffb6SAlexander V. Chernikov 	tfe->af = ent->af;
2703914bffb6SAlexander V. Chernikov 	tfe->proto = ent->proto;
2704914bffb6SAlexander V. Chernikov 	tfe->dport = htons(ent->dport);
2705914bffb6SAlexander V. Chernikov 	tfe->sport = htons(ent->sport);
2706914bffb6SAlexander V. Chernikov 	tent->value = ent->value;
2707914bffb6SAlexander V. Chernikov 	tent->subtype = ent->af;
2708914bffb6SAlexander V. Chernikov 
2709914bffb6SAlexander V. Chernikov 	if (ent->af == AF_INET) {
2710914bffb6SAlexander V. Chernikov 		fe4 = (struct fhashentry4 *)ent;
2711914bffb6SAlexander V. Chernikov 		tfe->a.a4.sip.s_addr = htonl(fe4->sip.s_addr);
2712914bffb6SAlexander V. Chernikov 		tfe->a.a4.dip.s_addr = htonl(fe4->dip.s_addr);
2713914bffb6SAlexander V. Chernikov 		tent->masklen = 32;
2714914bffb6SAlexander V. Chernikov #ifdef INET6
2715914bffb6SAlexander V. Chernikov 	} else {
2716914bffb6SAlexander V. Chernikov 		fe6 = (struct fhashentry6 *)ent;
2717914bffb6SAlexander V. Chernikov 		tfe->a.a6.sip6 = fe6->sip6;
2718914bffb6SAlexander V. Chernikov 		tfe->a.a6.dip6 = fe6->dip6;
2719914bffb6SAlexander V. Chernikov 		tent->masklen = 128;
2720914bffb6SAlexander V. Chernikov #endif
2721914bffb6SAlexander V. Chernikov 	}
2722914bffb6SAlexander V. Chernikov 
2723914bffb6SAlexander V. Chernikov 	return (0);
2724914bffb6SAlexander V. Chernikov }
2725914bffb6SAlexander V. Chernikov 
2726914bffb6SAlexander V. Chernikov static int
2727914bffb6SAlexander V. Chernikov tei_to_fhash_ent(struct tentry_info *tei, struct fhashentry *ent)
2728914bffb6SAlexander V. Chernikov {
2729914bffb6SAlexander V. Chernikov 	struct fhashentry4 *fe4;
2730914bffb6SAlexander V. Chernikov 	struct fhashentry6 *fe6;
2731914bffb6SAlexander V. Chernikov 	struct tflow_entry *tfe;
2732914bffb6SAlexander V. Chernikov 
2733914bffb6SAlexander V. Chernikov 	tfe = (struct tflow_entry *)tei->paddr;
2734914bffb6SAlexander V. Chernikov 
2735914bffb6SAlexander V. Chernikov 	ent->af = tei->subtype;
2736914bffb6SAlexander V. Chernikov 	ent->proto = tfe->proto;
2737914bffb6SAlexander V. Chernikov 	ent->value = tei->value;
2738914bffb6SAlexander V. Chernikov 	ent->dport = ntohs(tfe->dport);
2739914bffb6SAlexander V. Chernikov 	ent->sport = ntohs(tfe->sport);
2740914bffb6SAlexander V. Chernikov 
2741914bffb6SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
2742914bffb6SAlexander V. Chernikov #ifdef INET
2743914bffb6SAlexander V. Chernikov 		fe4 = (struct fhashentry4 *)ent;
2744914bffb6SAlexander V. Chernikov 		fe4->sip.s_addr = ntohl(tfe->a.a4.sip.s_addr);
2745914bffb6SAlexander V. Chernikov 		fe4->dip.s_addr = ntohl(tfe->a.a4.dip.s_addr);
2746914bffb6SAlexander V. Chernikov #endif
2747914bffb6SAlexander V. Chernikov #ifdef INET6
2748914bffb6SAlexander V. Chernikov 	} else if (tei->subtype == AF_INET6) {
2749914bffb6SAlexander V. Chernikov 		fe6 = (struct fhashentry6 *)ent;
2750914bffb6SAlexander V. Chernikov 		fe6->sip6 = tfe->a.a6.sip6;
2751914bffb6SAlexander V. Chernikov 		fe6->dip6 = tfe->a.a6.dip6;
2752914bffb6SAlexander V. Chernikov #endif
2753914bffb6SAlexander V. Chernikov 	} else {
2754914bffb6SAlexander V. Chernikov 		/* Unknown CIDR type */
2755914bffb6SAlexander V. Chernikov 		return (EINVAL);
2756914bffb6SAlexander V. Chernikov 	}
2757914bffb6SAlexander V. Chernikov 
2758914bffb6SAlexander V. Chernikov 	return (0);
2759914bffb6SAlexander V. Chernikov }
2760914bffb6SAlexander V. Chernikov 
2761914bffb6SAlexander V. Chernikov 
2762914bffb6SAlexander V. Chernikov static int
2763914bffb6SAlexander V. Chernikov ta_find_fhash_tentry(void *ta_state, struct table_info *ti,
2764914bffb6SAlexander V. Chernikov     ipfw_obj_tentry *tent)
2765914bffb6SAlexander V. Chernikov {
2766914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
2767914bffb6SAlexander V. Chernikov 	struct fhashbhead *head;
2768914bffb6SAlexander V. Chernikov 	struct fhashentry *ent, *tmp;
2769914bffb6SAlexander V. Chernikov 	struct fhashentry6 fe6;
2770914bffb6SAlexander V. Chernikov 	struct tentry_info tei;
2771914bffb6SAlexander V. Chernikov 	int error;
2772914bffb6SAlexander V. Chernikov 	uint32_t hash;
2773914bffb6SAlexander V. Chernikov 	size_t sz;
2774914bffb6SAlexander V. Chernikov 
2775914bffb6SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
2776914bffb6SAlexander V. Chernikov 
2777914bffb6SAlexander V. Chernikov 	ent = &fe6.e;
2778914bffb6SAlexander V. Chernikov 
2779914bffb6SAlexander V. Chernikov 	memset(&fe6, 0, sizeof(fe6));
2780914bffb6SAlexander V. Chernikov 	memset(&tei, 0, sizeof(tei));
2781914bffb6SAlexander V. Chernikov 
2782914bffb6SAlexander V. Chernikov 	tei.paddr = &tent->k.flow;
2783914bffb6SAlexander V. Chernikov 	tei.subtype = tent->subtype;
2784914bffb6SAlexander V. Chernikov 
2785914bffb6SAlexander V. Chernikov 	if ((error = tei_to_fhash_ent(&tei, ent)) != 0)
2786914bffb6SAlexander V. Chernikov 		return (error);
2787914bffb6SAlexander V. Chernikov 
2788914bffb6SAlexander V. Chernikov 	head = cfg->head;
2789914bffb6SAlexander V. Chernikov 	hash = hash_flow_ent(ent, cfg->size);
2790914bffb6SAlexander V. Chernikov 
2791914bffb6SAlexander V. Chernikov 	if (tei.subtype == AF_INET)
2792914bffb6SAlexander V. Chernikov 		sz = 2 * sizeof(struct in_addr);
2793914bffb6SAlexander V. Chernikov 	else
2794914bffb6SAlexander V. Chernikov 		sz = 2 * sizeof(struct in6_addr);
2795914bffb6SAlexander V. Chernikov 
2796914bffb6SAlexander V. Chernikov 	/* Check for existence */
2797914bffb6SAlexander V. Chernikov 	SLIST_FOREACH(tmp, &head[hash], next) {
2798914bffb6SAlexander V. Chernikov 		if (cmp_flow_ent(tmp, ent, sz) != 0) {
2799914bffb6SAlexander V. Chernikov 			ta_dump_fhash_tentry(ta_state, ti, tmp, tent);
2800914bffb6SAlexander V. Chernikov 			return (0);
2801914bffb6SAlexander V. Chernikov 		}
2802914bffb6SAlexander V. Chernikov 	}
2803914bffb6SAlexander V. Chernikov 
2804914bffb6SAlexander V. Chernikov 	return (ENOENT);
2805914bffb6SAlexander V. Chernikov }
2806914bffb6SAlexander V. Chernikov 
2807914bffb6SAlexander V. Chernikov static void
2808914bffb6SAlexander V. Chernikov ta_foreach_fhash(void *ta_state, struct table_info *ti, ta_foreach_f *f,
2809914bffb6SAlexander V. Chernikov     void *arg)
2810914bffb6SAlexander V. Chernikov {
2811914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
2812914bffb6SAlexander V. Chernikov 	struct fhashentry *ent, *ent_next;
2813914bffb6SAlexander V. Chernikov 	int i;
2814914bffb6SAlexander V. Chernikov 
2815914bffb6SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
2816914bffb6SAlexander V. Chernikov 
2817914bffb6SAlexander V. Chernikov 	for (i = 0; i < cfg->size; i++)
2818914bffb6SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &cfg->head[i], next, ent_next)
2819914bffb6SAlexander V. Chernikov 			f(ent, arg);
2820914bffb6SAlexander V. Chernikov }
2821914bffb6SAlexander V. Chernikov 
2822914bffb6SAlexander V. Chernikov 
2823914bffb6SAlexander V. Chernikov struct ta_buf_fhash
2824914bffb6SAlexander V. Chernikov {
2825914bffb6SAlexander V. Chernikov 	void *ent_ptr;
2826914bffb6SAlexander V. Chernikov 	struct fhashentry6 fe6;
2827914bffb6SAlexander V. Chernikov };
2828914bffb6SAlexander V. Chernikov 
2829914bffb6SAlexander V. Chernikov static int
2830914bffb6SAlexander V. Chernikov ta_prepare_add_fhash(struct ip_fw_chain *ch, struct tentry_info *tei,
2831914bffb6SAlexander V. Chernikov     void *ta_buf)
2832914bffb6SAlexander V. Chernikov {
2833914bffb6SAlexander V. Chernikov 	struct ta_buf_fhash *tb;
2834914bffb6SAlexander V. Chernikov 	struct fhashentry *ent;
2835914bffb6SAlexander V. Chernikov 	size_t sz;
2836914bffb6SAlexander V. Chernikov 	int error;
2837914bffb6SAlexander V. Chernikov 
2838914bffb6SAlexander V. Chernikov 	tb = (struct ta_buf_fhash *)ta_buf;
2839914bffb6SAlexander V. Chernikov 	memset(tb, 0, sizeof(struct ta_buf_fhash));
2840914bffb6SAlexander V. Chernikov 
2841914bffb6SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
2842914bffb6SAlexander V. Chernikov 		sz = sizeof(struct fhashentry4);
2843914bffb6SAlexander V. Chernikov 	else if (tei->subtype == AF_INET6)
2844914bffb6SAlexander V. Chernikov 		sz = sizeof(struct fhashentry6);
2845914bffb6SAlexander V. Chernikov 	else
2846914bffb6SAlexander V. Chernikov 		return (EINVAL);
2847914bffb6SAlexander V. Chernikov 
2848914bffb6SAlexander V. Chernikov 	ent = malloc(sz, M_IPFW_TBL, M_WAITOK | M_ZERO);
2849914bffb6SAlexander V. Chernikov 
2850914bffb6SAlexander V. Chernikov 	error = tei_to_fhash_ent(tei, ent);
2851914bffb6SAlexander V. Chernikov 	if (error != 0) {
2852914bffb6SAlexander V. Chernikov 		free(ent, M_IPFW_TBL);
2853914bffb6SAlexander V. Chernikov 		return (error);
2854914bffb6SAlexander V. Chernikov 	}
2855914bffb6SAlexander V. Chernikov 	tb->ent_ptr = ent;
2856914bffb6SAlexander V. Chernikov 
2857914bffb6SAlexander V. Chernikov 	return (0);
2858914bffb6SAlexander V. Chernikov }
2859914bffb6SAlexander V. Chernikov 
2860914bffb6SAlexander V. Chernikov static int
2861914bffb6SAlexander V. Chernikov ta_add_fhash(void *ta_state, struct table_info *ti, struct tentry_info *tei,
2862914bffb6SAlexander V. Chernikov     void *ta_buf, uint64_t *pflags, uint32_t *pnum)
2863914bffb6SAlexander V. Chernikov {
2864914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
2865914bffb6SAlexander V. Chernikov 	struct fhashbhead *head;
2866914bffb6SAlexander V. Chernikov 	struct fhashentry *ent, *tmp;
2867914bffb6SAlexander V. Chernikov 	struct ta_buf_fhash *tb;
2868914bffb6SAlexander V. Chernikov 	int exists;
2869914bffb6SAlexander V. Chernikov 	uint32_t hash;
2870914bffb6SAlexander V. Chernikov 	size_t sz;
2871914bffb6SAlexander V. Chernikov 
2872914bffb6SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
2873914bffb6SAlexander V. Chernikov 	tb = (struct ta_buf_fhash *)ta_buf;
2874914bffb6SAlexander V. Chernikov 	ent = (struct fhashentry *)tb->ent_ptr;
2875914bffb6SAlexander V. Chernikov 	exists = 0;
2876914bffb6SAlexander V. Chernikov 
2877914bffb6SAlexander V. Chernikov 	head = cfg->head;
2878914bffb6SAlexander V. Chernikov 	hash = hash_flow_ent(ent, cfg->size);
2879914bffb6SAlexander V. Chernikov 
2880914bffb6SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
2881914bffb6SAlexander V. Chernikov 		sz = 2 * sizeof(struct in_addr);
2882914bffb6SAlexander V. Chernikov 	else
2883914bffb6SAlexander V. Chernikov 		sz = 2 * sizeof(struct in6_addr);
2884914bffb6SAlexander V. Chernikov 
2885914bffb6SAlexander V. Chernikov 	/* Check for existence */
2886914bffb6SAlexander V. Chernikov 	SLIST_FOREACH(tmp, &head[hash], next) {
2887914bffb6SAlexander V. Chernikov 		if (cmp_flow_ent(tmp, ent, sz) != 0) {
2888914bffb6SAlexander V. Chernikov 			exists = 1;
2889914bffb6SAlexander V. Chernikov 			break;
2890914bffb6SAlexander V. Chernikov 		}
2891914bffb6SAlexander V. Chernikov 	}
2892914bffb6SAlexander V. Chernikov 
2893914bffb6SAlexander V. Chernikov 	if (exists == 1) {
2894914bffb6SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
2895914bffb6SAlexander V. Chernikov 			return (EEXIST);
2896914bffb6SAlexander V. Chernikov 		/* Record already exists. Update value if we're asked to */
2897914bffb6SAlexander V. Chernikov 		tmp->value = tei->value;
2898914bffb6SAlexander V. Chernikov 		/* Indicate that update has happened instead of addition */
2899914bffb6SAlexander V. Chernikov 		tei->flags |= TEI_FLAGS_UPDATED;
2900914bffb6SAlexander V. Chernikov 		*pnum = 0;
2901914bffb6SAlexander V. Chernikov 	} else {
2902914bffb6SAlexander V. Chernikov 		SLIST_INSERT_HEAD(&head[hash], ent, next);
2903914bffb6SAlexander V. Chernikov 		tb->ent_ptr = NULL;
2904914bffb6SAlexander V. Chernikov 		*pnum = 1;
2905914bffb6SAlexander V. Chernikov 
2906914bffb6SAlexander V. Chernikov 		/* Update counters and check if we need to grow hash */
2907914bffb6SAlexander V. Chernikov 		cfg->items++;
2908914bffb6SAlexander V. Chernikov 		if (cfg->items > cfg->size && cfg->size < 65536)
2909914bffb6SAlexander V. Chernikov 			*pflags = cfg->size * 2;
2910914bffb6SAlexander V. Chernikov 	}
2911914bffb6SAlexander V. Chernikov 
2912914bffb6SAlexander V. Chernikov 	return (0);
2913914bffb6SAlexander V. Chernikov }
2914914bffb6SAlexander V. Chernikov 
2915914bffb6SAlexander V. Chernikov static int
2916914bffb6SAlexander V. Chernikov ta_prepare_del_fhash(struct ip_fw_chain *ch, struct tentry_info *tei,
2917914bffb6SAlexander V. Chernikov     void *ta_buf)
2918914bffb6SAlexander V. Chernikov {
2919914bffb6SAlexander V. Chernikov 	struct ta_buf_fhash *tb;
2920914bffb6SAlexander V. Chernikov 
2921914bffb6SAlexander V. Chernikov 	tb = (struct ta_buf_fhash *)ta_buf;
2922914bffb6SAlexander V. Chernikov 	memset(tb, 0, sizeof(struct ta_buf_fhash));
2923914bffb6SAlexander V. Chernikov 
2924914bffb6SAlexander V. Chernikov 	return (tei_to_fhash_ent(tei, &tb->fe6.e));
2925914bffb6SAlexander V. Chernikov }
2926914bffb6SAlexander V. Chernikov 
2927914bffb6SAlexander V. Chernikov static int
2928914bffb6SAlexander V. Chernikov ta_del_fhash(void *ta_state, struct table_info *ti, struct tentry_info *tei,
2929914bffb6SAlexander V. Chernikov     void *ta_buf, uint64_t *pflags, uint32_t *pnum)
2930914bffb6SAlexander V. Chernikov {
2931914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
2932914bffb6SAlexander V. Chernikov 	struct fhashbhead *head;
2933914bffb6SAlexander V. Chernikov 	struct fhashentry *ent, *tmp;
2934914bffb6SAlexander V. Chernikov 	struct ta_buf_fhash *tb;
2935914bffb6SAlexander V. Chernikov 	uint32_t hash;
2936914bffb6SAlexander V. Chernikov 	size_t sz;
2937914bffb6SAlexander V. Chernikov 
2938914bffb6SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
2939914bffb6SAlexander V. Chernikov 	tb = (struct ta_buf_fhash *)ta_buf;
2940914bffb6SAlexander V. Chernikov 	ent = &tb->fe6.e;
2941914bffb6SAlexander V. Chernikov 
2942914bffb6SAlexander V. Chernikov 	head = cfg->head;
2943914bffb6SAlexander V. Chernikov 	hash = hash_flow_ent(ent, cfg->size);
2944914bffb6SAlexander V. Chernikov 
2945914bffb6SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
2946914bffb6SAlexander V. Chernikov 		sz = 2 * sizeof(struct in_addr);
2947914bffb6SAlexander V. Chernikov 	else
2948914bffb6SAlexander V. Chernikov 		sz = 2 * sizeof(struct in6_addr);
2949914bffb6SAlexander V. Chernikov 
2950914bffb6SAlexander V. Chernikov 	/* Check for existence */
2951914bffb6SAlexander V. Chernikov 	SLIST_FOREACH(tmp, &head[hash], next) {
2952914bffb6SAlexander V. Chernikov 		if (cmp_flow_ent(tmp, ent, sz) != 0) {
2953914bffb6SAlexander V. Chernikov 			SLIST_REMOVE(&head[hash], tmp, fhashentry, next);
2954914bffb6SAlexander V. Chernikov 			*pnum = 1;
2955914bffb6SAlexander V. Chernikov 			cfg->items--;
2956914bffb6SAlexander V. Chernikov 			return (0);
2957914bffb6SAlexander V. Chernikov 		}
2958914bffb6SAlexander V. Chernikov 	}
2959914bffb6SAlexander V. Chernikov 
2960914bffb6SAlexander V. Chernikov 	return (ENOENT);
2961914bffb6SAlexander V. Chernikov }
2962914bffb6SAlexander V. Chernikov 
2963914bffb6SAlexander V. Chernikov static void
2964914bffb6SAlexander V. Chernikov ta_flush_fhash_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
2965914bffb6SAlexander V. Chernikov     void *ta_buf)
2966914bffb6SAlexander V. Chernikov {
2967914bffb6SAlexander V. Chernikov 	struct ta_buf_fhash *tb;
2968914bffb6SAlexander V. Chernikov 
2969914bffb6SAlexander V. Chernikov 	tb = (struct ta_buf_fhash *)ta_buf;
2970914bffb6SAlexander V. Chernikov 
2971914bffb6SAlexander V. Chernikov 	if (tb->ent_ptr != NULL)
2972914bffb6SAlexander V. Chernikov 		free(tb->ent_ptr, M_IPFW_TBL);
2973914bffb6SAlexander V. Chernikov }
2974914bffb6SAlexander V. Chernikov 
2975914bffb6SAlexander V. Chernikov /*
2976914bffb6SAlexander V. Chernikov  * Hash growing callbacks.
2977914bffb6SAlexander V. Chernikov  */
2978914bffb6SAlexander V. Chernikov 
2979914bffb6SAlexander V. Chernikov /*
2980914bffb6SAlexander V. Chernikov  * Allocate new, larger fhash.
2981914bffb6SAlexander V. Chernikov  */
2982914bffb6SAlexander V. Chernikov static int
2983914bffb6SAlexander V. Chernikov ta_prepare_mod_fhash(void *ta_buf, uint64_t *pflags)
2984914bffb6SAlexander V. Chernikov {
2985914bffb6SAlexander V. Chernikov 	struct mod_item *mi;
2986914bffb6SAlexander V. Chernikov 	struct fhashbhead *head;
2987914bffb6SAlexander V. Chernikov 	int i;
2988914bffb6SAlexander V. Chernikov 
2989914bffb6SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
2990914bffb6SAlexander V. Chernikov 
2991914bffb6SAlexander V. Chernikov 	memset(mi, 0, sizeof(struct mod_item));
2992914bffb6SAlexander V. Chernikov 	mi->size = *pflags;
2993914bffb6SAlexander V. Chernikov 	head = malloc(sizeof(struct fhashbhead) * mi->size, M_IPFW,
2994914bffb6SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
2995914bffb6SAlexander V. Chernikov 	for (i = 0; i < mi->size; i++)
2996914bffb6SAlexander V. Chernikov 		SLIST_INIT(&head[i]);
2997914bffb6SAlexander V. Chernikov 
2998914bffb6SAlexander V. Chernikov 	mi->main_ptr = head;
2999914bffb6SAlexander V. Chernikov 
3000914bffb6SAlexander V. Chernikov 	return (0);
3001914bffb6SAlexander V. Chernikov }
3002914bffb6SAlexander V. Chernikov 
3003914bffb6SAlexander V. Chernikov /*
3004914bffb6SAlexander V. Chernikov  * Copy data from old runtime array to new one.
3005914bffb6SAlexander V. Chernikov  */
3006914bffb6SAlexander V. Chernikov static int
3007914bffb6SAlexander V. Chernikov ta_fill_mod_fhash(void *ta_state, struct table_info *ti, void *ta_buf,
3008914bffb6SAlexander V. Chernikov     uint64_t *pflags)
3009914bffb6SAlexander V. Chernikov {
3010914bffb6SAlexander V. Chernikov 
3011914bffb6SAlexander V. Chernikov 	/* In is not possible to do rehash if we're not holidng WLOCK. */
3012914bffb6SAlexander V. Chernikov 	return (0);
3013914bffb6SAlexander V. Chernikov }
3014914bffb6SAlexander V. Chernikov 
3015914bffb6SAlexander V. Chernikov 
3016914bffb6SAlexander V. Chernikov /*
3017914bffb6SAlexander V. Chernikov  * Switch old & new arrays.
3018914bffb6SAlexander V. Chernikov  */
3019914bffb6SAlexander V. Chernikov static int
3020914bffb6SAlexander V. Chernikov ta_modify_fhash(void *ta_state, struct table_info *ti, void *ta_buf,
3021914bffb6SAlexander V. Chernikov     uint64_t pflags)
3022914bffb6SAlexander V. Chernikov {
3023914bffb6SAlexander V. Chernikov 	struct mod_item *mi;
3024914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
3025914bffb6SAlexander V. Chernikov 	struct fhashbhead *old_head, *new_head;
3026914bffb6SAlexander V. Chernikov 	struct fhashentry *ent, *ent_next;
3027914bffb6SAlexander V. Chernikov 	int i;
3028914bffb6SAlexander V. Chernikov 	uint32_t nhash;
3029914bffb6SAlexander V. Chernikov 	size_t old_size;
3030914bffb6SAlexander V. Chernikov 
3031914bffb6SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
3032914bffb6SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
3033914bffb6SAlexander V. Chernikov 
3034914bffb6SAlexander V. Chernikov 	/* Check which hash we need to grow and do we still need that */
3035914bffb6SAlexander V. Chernikov 	old_size = cfg->size;
3036914bffb6SAlexander V. Chernikov 	old_head = ti->state;
3037914bffb6SAlexander V. Chernikov 
3038914bffb6SAlexander V. Chernikov 	if (old_size >= mi->size)
3039914bffb6SAlexander V. Chernikov 		return (0);
3040914bffb6SAlexander V. Chernikov 
3041914bffb6SAlexander V. Chernikov 	new_head = (struct fhashbhead *)mi->main_ptr;
3042914bffb6SAlexander V. Chernikov 	for (i = 0; i < old_size; i++) {
3043914bffb6SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &old_head[i], next, ent_next) {
3044914bffb6SAlexander V. Chernikov 			nhash = hash_flow_ent(ent, mi->size);
3045914bffb6SAlexander V. Chernikov 			SLIST_INSERT_HEAD(&new_head[nhash], ent, next);
3046914bffb6SAlexander V. Chernikov 		}
3047914bffb6SAlexander V. Chernikov 	}
3048914bffb6SAlexander V. Chernikov 
3049914bffb6SAlexander V. Chernikov 	ti->state = new_head;
3050914bffb6SAlexander V. Chernikov 	ti->data = mi->size;
3051914bffb6SAlexander V. Chernikov 	cfg->head = new_head;
3052914bffb6SAlexander V. Chernikov 	cfg->size = mi->size;
3053914bffb6SAlexander V. Chernikov 
3054914bffb6SAlexander V. Chernikov 	mi->main_ptr = old_head;
3055914bffb6SAlexander V. Chernikov 
3056914bffb6SAlexander V. Chernikov 	return (0);
3057914bffb6SAlexander V. Chernikov }
3058914bffb6SAlexander V. Chernikov 
3059914bffb6SAlexander V. Chernikov /*
3060914bffb6SAlexander V. Chernikov  * Free unneded array.
3061914bffb6SAlexander V. Chernikov  */
3062914bffb6SAlexander V. Chernikov static void
3063914bffb6SAlexander V. Chernikov ta_flush_mod_fhash(void *ta_buf)
3064914bffb6SAlexander V. Chernikov {
3065914bffb6SAlexander V. Chernikov 	struct mod_item *mi;
3066914bffb6SAlexander V. Chernikov 
3067914bffb6SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
3068914bffb6SAlexander V. Chernikov 	if (mi->main_ptr != NULL)
3069914bffb6SAlexander V. Chernikov 		free(mi->main_ptr, M_IPFW);
3070914bffb6SAlexander V. Chernikov }
3071914bffb6SAlexander V. Chernikov 
3072914bffb6SAlexander V. Chernikov struct table_algo flow_hash = {
3073914bffb6SAlexander V. Chernikov 	.name		= "flow:hash",
3074914bffb6SAlexander V. Chernikov 	.type		= IPFW_TABLE_FLOW,
307557a1cf95SAlexander V. Chernikov 	.flags		= TA_FLAG_DEFAULT,
307657a1cf95SAlexander V. Chernikov 	.ta_buf_size	= sizeof(struct ta_buf_fhash),
3077914bffb6SAlexander V. Chernikov 	.init		= ta_init_fhash,
3078914bffb6SAlexander V. Chernikov 	.destroy	= ta_destroy_fhash,
3079914bffb6SAlexander V. Chernikov 	.prepare_add	= ta_prepare_add_fhash,
3080914bffb6SAlexander V. Chernikov 	.prepare_del	= ta_prepare_del_fhash,
3081914bffb6SAlexander V. Chernikov 	.add		= ta_add_fhash,
3082914bffb6SAlexander V. Chernikov 	.del		= ta_del_fhash,
3083914bffb6SAlexander V. Chernikov 	.flush_entry	= ta_flush_fhash_entry,
3084914bffb6SAlexander V. Chernikov 	.foreach	= ta_foreach_fhash,
3085914bffb6SAlexander V. Chernikov 	.dump_tentry	= ta_dump_fhash_tentry,
3086914bffb6SAlexander V. Chernikov 	.find_tentry	= ta_find_fhash_tentry,
3087914bffb6SAlexander V. Chernikov 	.prepare_mod	= ta_prepare_mod_fhash,
3088914bffb6SAlexander V. Chernikov 	.fill_mod	= ta_fill_mod_fhash,
3089914bffb6SAlexander V. Chernikov 	.modify		= ta_modify_fhash,
3090914bffb6SAlexander V. Chernikov 	.flush_mod	= ta_flush_mod_fhash,
3091914bffb6SAlexander V. Chernikov };
30929f7d47b0SAlexander V. Chernikov void
30930b565ac0SAlexander V. Chernikov ipfw_table_algo_init(struct ip_fw_chain *ch)
30949f7d47b0SAlexander V. Chernikov {
30950b565ac0SAlexander V. Chernikov 	size_t sz;
30960b565ac0SAlexander V. Chernikov 
30979f7d47b0SAlexander V. Chernikov 	/*
30989f7d47b0SAlexander V. Chernikov 	 * Register all algorithms presented here.
30999f7d47b0SAlexander V. Chernikov 	 */
31000b565ac0SAlexander V. Chernikov 	sz = sizeof(struct table_algo);
31010b565ac0SAlexander V. Chernikov 	ipfw_add_table_algo(ch, &cidr_radix, sz, &cidr_radix.idx);
31020b565ac0SAlexander V. Chernikov 	ipfw_add_table_algo(ch, &cidr_hash, sz, &cidr_hash.idx);
31030b565ac0SAlexander V. Chernikov 	ipfw_add_table_algo(ch, &iface_idx, sz, &iface_idx.idx);
3104b23d5de9SAlexander V. Chernikov 	ipfw_add_table_algo(ch, &number_array, sz, &number_array.idx);
3105914bffb6SAlexander V. Chernikov 	ipfw_add_table_algo(ch, &flow_hash, sz, &flow_hash.idx);
31069f7d47b0SAlexander V. Chernikov }
31079f7d47b0SAlexander V. Chernikov 
31089f7d47b0SAlexander V. Chernikov void
31090b565ac0SAlexander V. Chernikov ipfw_table_algo_destroy(struct ip_fw_chain *ch)
31109f7d47b0SAlexander V. Chernikov {
31110b565ac0SAlexander V. Chernikov 
31120b565ac0SAlexander V. Chernikov 	ipfw_del_table_algo(ch, cidr_radix.idx);
31130b565ac0SAlexander V. Chernikov 	ipfw_del_table_algo(ch, cidr_hash.idx);
31140b565ac0SAlexander V. Chernikov 	ipfw_del_table_algo(ch, iface_idx.idx);
3115b23d5de9SAlexander V. Chernikov 	ipfw_del_table_algo(ch, number_array.idx);
3116914bffb6SAlexander V. Chernikov 	ipfw_del_table_algo(ch, flow_hash.idx);
31179f7d47b0SAlexander V. Chernikov }
31189f7d47b0SAlexander V. Chernikov 
31199f7d47b0SAlexander V. Chernikov 
3120