xref: /freebsd/sys/netpfil/ipfw/ip_fw_table_algo.c (revision 68394ec88e70e77244f752290e856ea2b1f8348d)
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 
63*68394ec8SAlexander V. Chernikov static int badd(const void *key, void *item, void *base, size_t nmemb,
64*68394ec8SAlexander V. Chernikov     size_t size, int (*compar) (const void *, const void *));
65*68394ec8SAlexander V. Chernikov static int bdel(const void *key, void *base, size_t nmemb, size_t size,
66*68394ec8SAlexander V. Chernikov     int (*compar) (const void *, const void *));
67*68394ec8SAlexander V. Chernikov 
68*68394ec8SAlexander V. Chernikov 
69*68394ec8SAlexander V. Chernikov /*
70*68394ec8SAlexander V. Chernikov  * CIDR implementation using radix
71*68394ec8SAlexander V. Chernikov  *
72*68394ec8SAlexander V. Chernikov  */
73*68394ec8SAlexander 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
151*68394ec8SAlexander V. Chernikov ta_init_radix(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
152*68394ec8SAlexander V. Chernikov     char *data)
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
22481d3153dSAlexander V. Chernikov ta_find_radix_tentry(void *ta_state, struct table_info *ti, void *key,
22581d3153dSAlexander V. Chernikov     uint32_t keylen, 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;
23181d3153dSAlexander V. Chernikov 	if (keylen == sizeof(in_addr_t)) {
23281d3153dSAlexander V. Chernikov 		struct sockaddr_in sa;
23381d3153dSAlexander V. Chernikov 		KEY_LEN(sa) = KEY_LEN_INET;
23481d3153dSAlexander V. Chernikov 		sa.sin_addr.s_addr = *((in_addr_t *)key);
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;
24081d3153dSAlexander V. Chernikov 		memcpy(&sa6.sin6_addr, key, 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 
2969f7d47b0SAlexander V. Chernikov 
2979f7d47b0SAlexander V. Chernikov static int
298*68394ec8SAlexander V. Chernikov ta_prepare_add_cidr(struct ip_fw_chain *ch, struct tentry_info *tei,
299*68394ec8SAlexander V. Chernikov     void *ta_buf)
3009f7d47b0SAlexander V. Chernikov {
3019f7d47b0SAlexander V. Chernikov 	struct ta_buf_cidr *tb;
302e0a8b9eeSAlexander V. Chernikov 	struct radix_cidr_entry *ent;
303e0a8b9eeSAlexander V. Chernikov 	struct radix_cidr_xentry *xent;
3049f7d47b0SAlexander V. Chernikov 	in_addr_t addr;
305e0a8b9eeSAlexander V. Chernikov 	struct sockaddr_in *mask;
306e0a8b9eeSAlexander V. Chernikov 	struct sa_in6 *mask6;
3079f7d47b0SAlexander V. Chernikov 	int mlen;
3089f7d47b0SAlexander V. Chernikov 
3099f7d47b0SAlexander V. Chernikov 	tb = (struct ta_buf_cidr *)ta_buf;
3109f7d47b0SAlexander V. Chernikov 	memset(tb, 0, sizeof(struct ta_buf_cidr));
3119f7d47b0SAlexander V. Chernikov 
3129f7d47b0SAlexander V. Chernikov 	mlen = tei->masklen;
3139f7d47b0SAlexander V. Chernikov 
314ac35ff17SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
3159f7d47b0SAlexander V. Chernikov #ifdef INET
3169f7d47b0SAlexander V. Chernikov 		if (mlen > 32)
3179f7d47b0SAlexander V. Chernikov 			return (EINVAL);
3189f7d47b0SAlexander V. Chernikov 		ent = malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO);
3199f7d47b0SAlexander V. Chernikov 		ent->value = tei->value;
320e0a8b9eeSAlexander V. Chernikov 		mask = &tb->addr.a4.ma;
3219f7d47b0SAlexander V. Chernikov 		/* Set 'total' structure length */
3229f7d47b0SAlexander V. Chernikov 		KEY_LEN(ent->addr) = KEY_LEN_INET;
323e0a8b9eeSAlexander V. Chernikov 		KEY_LEN(*mask) = KEY_LEN_INET;
3249f7d47b0SAlexander V. Chernikov 		ent->addr.sin_family = AF_INET;
325e0a8b9eeSAlexander V. Chernikov 		mask->sin_addr.s_addr =
3269f7d47b0SAlexander V. Chernikov 		    htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);
3279f7d47b0SAlexander V. Chernikov 		addr = *((in_addr_t *)tei->paddr);
328e0a8b9eeSAlexander V. Chernikov 		ent->addr.sin_addr.s_addr = addr & mask->sin_addr.s_addr;
329e0a8b9eeSAlexander V. Chernikov 		ent->masklen = mlen;
3309f7d47b0SAlexander V. Chernikov 		/* Set pointers */
3319f7d47b0SAlexander V. Chernikov 		tb->ent_ptr = ent;
3329f7d47b0SAlexander V. Chernikov 		tb->addr_ptr = (struct sockaddr *)&ent->addr;
333e0a8b9eeSAlexander V. Chernikov 		if (mlen != 32)
334e0a8b9eeSAlexander V. Chernikov 			tb->mask_ptr = (struct sockaddr *)mask;
3359f7d47b0SAlexander V. Chernikov #endif
3369f7d47b0SAlexander V. Chernikov #ifdef INET6
337ac35ff17SAlexander V. Chernikov 	} else if (tei->subtype == AF_INET6) {
3389f7d47b0SAlexander V. Chernikov 		/* IPv6 case */
3399f7d47b0SAlexander V. Chernikov 		if (mlen > 128)
3409f7d47b0SAlexander V. Chernikov 			return (EINVAL);
3419f7d47b0SAlexander V. Chernikov 		xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO);
3429f7d47b0SAlexander V. Chernikov 		xent->value = tei->value;
343e0a8b9eeSAlexander V. Chernikov 		mask6 = &tb->addr.a6.ma;
3449f7d47b0SAlexander V. Chernikov 		/* Set 'total' structure length */
345e0a8b9eeSAlexander V. Chernikov 		KEY_LEN(xent->addr6) = KEY_LEN_INET6;
346e0a8b9eeSAlexander V. Chernikov 		KEY_LEN(*mask6) = KEY_LEN_INET6;
347e0a8b9eeSAlexander V. Chernikov 		xent->addr6.sin6_family = AF_INET6;
348e0a8b9eeSAlexander V. Chernikov 		ipv6_writemask(&mask6->sin6_addr, mlen);
349e0a8b9eeSAlexander V. Chernikov 		memcpy(&xent->addr6.sin6_addr, tei->paddr,
3509f7d47b0SAlexander V. Chernikov 		    sizeof(struct in6_addr));
351e0a8b9eeSAlexander V. Chernikov 		APPLY_MASK(&xent->addr6.sin6_addr, &mask6->sin6_addr);
352e0a8b9eeSAlexander V. Chernikov 		xent->masklen = mlen;
3539f7d47b0SAlexander V. Chernikov 		/* Set pointers */
3549f7d47b0SAlexander V. Chernikov 		tb->ent_ptr = xent;
355e0a8b9eeSAlexander V. Chernikov 		tb->addr_ptr = (struct sockaddr *)&xent->addr6;
356e0a8b9eeSAlexander V. Chernikov 		if (mlen != 128)
357e0a8b9eeSAlexander V. Chernikov 			tb->mask_ptr = (struct sockaddr *)mask6;
3589f7d47b0SAlexander V. Chernikov #endif
3599f7d47b0SAlexander V. Chernikov 	} else {
3609f7d47b0SAlexander V. Chernikov 		/* Unknown CIDR type */
3619f7d47b0SAlexander V. Chernikov 		return (EINVAL);
3629f7d47b0SAlexander V. Chernikov 	}
3639f7d47b0SAlexander V. Chernikov 
3649f7d47b0SAlexander V. Chernikov 	return (0);
3659f7d47b0SAlexander V. Chernikov }
3669f7d47b0SAlexander V. Chernikov 
3679f7d47b0SAlexander V. Chernikov static int
3689f7d47b0SAlexander V. Chernikov ta_add_cidr(void *ta_state, struct table_info *ti,
369db785d31SAlexander V. Chernikov     struct tentry_info *tei, void *ta_buf, uint64_t *pflags)
3709f7d47b0SAlexander V. Chernikov {
3719f7d47b0SAlexander V. Chernikov 	struct radix_node_head *rnh;
3729f7d47b0SAlexander V. Chernikov 	struct radix_node *rn;
3739f7d47b0SAlexander V. Chernikov 	struct ta_buf_cidr *tb;
374ac35ff17SAlexander V. Chernikov 	uint32_t value;
3759f7d47b0SAlexander V. Chernikov 
3769f7d47b0SAlexander V. Chernikov 	tb = (struct ta_buf_cidr *)ta_buf;
3779f7d47b0SAlexander V. Chernikov 
378ac35ff17SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
3799f7d47b0SAlexander V. Chernikov 		rnh = ti->state;
3809f7d47b0SAlexander V. Chernikov 	else
3819f7d47b0SAlexander V. Chernikov 		rnh = ti->xstate;
3829f7d47b0SAlexander V. Chernikov 
3839f7d47b0SAlexander V. Chernikov 	rn = rnh->rnh_addaddr(tb->addr_ptr, tb->mask_ptr, rnh, tb->ent_ptr);
3849f7d47b0SAlexander V. Chernikov 
385ac35ff17SAlexander V. Chernikov 	if (rn == NULL) {
386ac35ff17SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
3879f7d47b0SAlexander V. Chernikov 			return (EEXIST);
388ac35ff17SAlexander V. Chernikov 		/* Record already exists. Update value if we're asked to */
389ac35ff17SAlexander V. Chernikov 		rn = rnh->rnh_lookup(tb->addr_ptr, tb->mask_ptr, rnh);
390ac35ff17SAlexander V. Chernikov 		if (rn == NULL) {
391ac35ff17SAlexander V. Chernikov 
392ac35ff17SAlexander V. Chernikov 			/*
393ac35ff17SAlexander V. Chernikov 			 * Radix may have failed addition for other reasons
394ac35ff17SAlexander V. Chernikov 			 * like failure in mask allocation code.
395ac35ff17SAlexander V. Chernikov 			 */
396ac35ff17SAlexander V. Chernikov 			return (EINVAL);
397ac35ff17SAlexander V. Chernikov 		}
398ac35ff17SAlexander V. Chernikov 
399ac35ff17SAlexander V. Chernikov 		if (tei->subtype == AF_INET) {
400ac35ff17SAlexander V. Chernikov 			/* IPv4. */
401e0a8b9eeSAlexander V. Chernikov 			value = ((struct radix_cidr_entry *)tb->ent_ptr)->value;
402e0a8b9eeSAlexander V. Chernikov 			((struct radix_cidr_entry *)rn)->value = value;
403ac35ff17SAlexander V. Chernikov 		} else {
404ac35ff17SAlexander V. Chernikov 			/* IPv6 */
405e0a8b9eeSAlexander V. Chernikov 			value = ((struct radix_cidr_xentry *)tb->ent_ptr)->value;
406e0a8b9eeSAlexander V. Chernikov 			((struct radix_cidr_xentry *)rn)->value = value;
407ac35ff17SAlexander V. Chernikov 		}
408ac35ff17SAlexander V. Chernikov 
409ac35ff17SAlexander V. Chernikov 		/* Indicate that update has happened instead of addition */
410ac35ff17SAlexander V. Chernikov 		tei->flags |= TEI_FLAGS_UPDATED;
411e0a8b9eeSAlexander V. Chernikov 
412e0a8b9eeSAlexander V. Chernikov 		return (0);
413ac35ff17SAlexander V. Chernikov 	}
414ac35ff17SAlexander V. Chernikov 
415ac35ff17SAlexander V. Chernikov 	tb->ent_ptr = NULL;
4169f7d47b0SAlexander V. Chernikov 
4179f7d47b0SAlexander V. Chernikov 	return (0);
4189f7d47b0SAlexander V. Chernikov }
4199f7d47b0SAlexander V. Chernikov 
4209f7d47b0SAlexander V. Chernikov static int
421*68394ec8SAlexander V. Chernikov ta_prepare_del_cidr(struct ip_fw_chain *ch, struct tentry_info *tei,
422*68394ec8SAlexander V. Chernikov     void *ta_buf)
4239f7d47b0SAlexander V. Chernikov {
4249f7d47b0SAlexander V. Chernikov 	struct ta_buf_cidr *tb;
425e0a8b9eeSAlexander V. Chernikov 	struct sockaddr_in sa, mask;
426e0a8b9eeSAlexander V. Chernikov 	struct sa_in6 sa6, mask6;
4279f7d47b0SAlexander V. Chernikov 	in_addr_t addr;
4289f7d47b0SAlexander V. Chernikov 	int mlen;
4299f7d47b0SAlexander V. Chernikov 
4309f7d47b0SAlexander V. Chernikov 	tb = (struct ta_buf_cidr *)ta_buf;
4319f7d47b0SAlexander V. Chernikov 	memset(tb, 0, sizeof(struct ta_buf_cidr));
4329f7d47b0SAlexander V. Chernikov 
4339f7d47b0SAlexander V. Chernikov 	mlen = tei->masklen;
4349f7d47b0SAlexander V. Chernikov 
435ac35ff17SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
436e0a8b9eeSAlexander V. Chernikov 		if (mlen > 32)
437e0a8b9eeSAlexander V. Chernikov 			return (EINVAL);
438e0a8b9eeSAlexander V. Chernikov 		memset(&sa, 0, sizeof(struct sockaddr_in));
439e0a8b9eeSAlexander V. Chernikov 		memset(&mask, 0, sizeof(struct sockaddr_in));
4409f7d47b0SAlexander V. Chernikov 		/* Set 'total' structure length */
4419f7d47b0SAlexander V. Chernikov 		KEY_LEN(sa) = KEY_LEN_INET;
4429f7d47b0SAlexander V. Chernikov 		KEY_LEN(mask) = KEY_LEN_INET;
4439f7d47b0SAlexander V. Chernikov 		mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);
4449f7d47b0SAlexander V. Chernikov 		addr = *((in_addr_t *)tei->paddr);
4459f7d47b0SAlexander V. Chernikov 		sa.sin_addr.s_addr = addr & mask.sin_addr.s_addr;
4469f7d47b0SAlexander V. Chernikov 		tb->addr.a4.sa = sa;
4479f7d47b0SAlexander V. Chernikov 		tb->addr.a4.ma = mask;
4489f7d47b0SAlexander V. Chernikov 		tb->addr_ptr = (struct sockaddr *)&tb->addr.a4.sa;
449e0a8b9eeSAlexander V. Chernikov 		if (mlen != 32)
4509f7d47b0SAlexander V. Chernikov 			tb->mask_ptr = (struct sockaddr *)&tb->addr.a4.ma;
4519f7d47b0SAlexander V. Chernikov #ifdef INET6
452ac35ff17SAlexander V. Chernikov 	} else if (tei->subtype == AF_INET6) {
4539f7d47b0SAlexander V. Chernikov 		if (mlen > 128)
4549f7d47b0SAlexander V. Chernikov 			return (EINVAL);
455e0a8b9eeSAlexander V. Chernikov 		memset(&sa6, 0, sizeof(struct sa_in6));
456e0a8b9eeSAlexander V. Chernikov 		memset(&mask6, 0, sizeof(struct sa_in6));
4579f7d47b0SAlexander V. Chernikov 		/* Set 'total' structure length */
4589f7d47b0SAlexander V. Chernikov 		KEY_LEN(sa6) = KEY_LEN_INET6;
4599f7d47b0SAlexander V. Chernikov 		KEY_LEN(mask6) = KEY_LEN_INET6;
4609f7d47b0SAlexander V. Chernikov 		ipv6_writemask(&mask6.sin6_addr, mlen);
4619f7d47b0SAlexander V. Chernikov 		memcpy(&sa6.sin6_addr, tei->paddr,
4629f7d47b0SAlexander V. Chernikov 		    sizeof(struct in6_addr));
4639f7d47b0SAlexander V. Chernikov 		APPLY_MASK(&sa6.sin6_addr, &mask6.sin6_addr);
4649f7d47b0SAlexander V. Chernikov 		tb->addr.a6.sa = sa6;
4659f7d47b0SAlexander V. Chernikov 		tb->addr.a6.ma = mask6;
4669f7d47b0SAlexander V. Chernikov 		tb->addr_ptr = (struct sockaddr *)&tb->addr.a6.sa;
467e0a8b9eeSAlexander V. Chernikov 		if (mlen != 128)
4689f7d47b0SAlexander V. Chernikov 			tb->mask_ptr = (struct sockaddr *)&tb->addr.a6.ma;
4699f7d47b0SAlexander V. Chernikov #endif
4709f7d47b0SAlexander V. Chernikov 	} else
4719f7d47b0SAlexander V. Chernikov 		return (EINVAL);
4729f7d47b0SAlexander V. Chernikov 
4739f7d47b0SAlexander V. Chernikov 	return (0);
4749f7d47b0SAlexander V. Chernikov }
4759f7d47b0SAlexander V. Chernikov 
4769f7d47b0SAlexander V. Chernikov static int
4779f7d47b0SAlexander V. Chernikov ta_del_cidr(void *ta_state, struct table_info *ti,
478db785d31SAlexander V. Chernikov     struct tentry_info *tei, void *ta_buf, uint64_t *pflags)
4799f7d47b0SAlexander V. Chernikov {
4809f7d47b0SAlexander V. Chernikov 	struct radix_node_head *rnh;
4819f7d47b0SAlexander V. Chernikov 	struct radix_node *rn;
4829f7d47b0SAlexander V. Chernikov 	struct ta_buf_cidr *tb;
4839f7d47b0SAlexander V. Chernikov 
4849f7d47b0SAlexander V. Chernikov 	tb = (struct ta_buf_cidr *)ta_buf;
4859f7d47b0SAlexander V. Chernikov 
486ac35ff17SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
4879f7d47b0SAlexander V. Chernikov 		rnh = ti->state;
4889f7d47b0SAlexander V. Chernikov 	else
4899f7d47b0SAlexander V. Chernikov 		rnh = ti->xstate;
4909f7d47b0SAlexander V. Chernikov 
4919f7d47b0SAlexander V. Chernikov 	rn = rnh->rnh_deladdr(tb->addr_ptr, tb->mask_ptr, rnh);
4929f7d47b0SAlexander V. Chernikov 
4939f7d47b0SAlexander V. Chernikov 	tb->ent_ptr = rn;
4949f7d47b0SAlexander V. Chernikov 
4959f7d47b0SAlexander V. Chernikov 	if (rn == NULL)
49681d3153dSAlexander V. Chernikov 		return (ENOENT);
4979f7d47b0SAlexander V. Chernikov 
4989f7d47b0SAlexander V. Chernikov 	return (0);
4999f7d47b0SAlexander V. Chernikov }
5009f7d47b0SAlexander V. Chernikov 
5019f7d47b0SAlexander V. Chernikov static void
502*68394ec8SAlexander V. Chernikov ta_flush_cidr_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
503*68394ec8SAlexander V. Chernikov     void *ta_buf)
5049f7d47b0SAlexander V. Chernikov {
5059f7d47b0SAlexander V. Chernikov 	struct ta_buf_cidr *tb;
5069f7d47b0SAlexander V. Chernikov 
5079f7d47b0SAlexander V. Chernikov 	tb = (struct ta_buf_cidr *)ta_buf;
5089f7d47b0SAlexander V. Chernikov 
509ac35ff17SAlexander V. Chernikov 	if (tb->ent_ptr != NULL)
5109f7d47b0SAlexander V. Chernikov 		free(tb->ent_ptr, M_IPFW_TBL);
5119f7d47b0SAlexander V. Chernikov }
5129f7d47b0SAlexander V. Chernikov 
5139f7d47b0SAlexander V. Chernikov struct table_algo radix_cidr = {
514ac35ff17SAlexander V. Chernikov 	.name		= "radix_cidr",
5159f7d47b0SAlexander V. Chernikov 	.lookup		= ta_lookup_radix,
5169f7d47b0SAlexander V. Chernikov 	.init		= ta_init_radix,
5179f7d47b0SAlexander V. Chernikov 	.destroy	= ta_destroy_radix,
5189f7d47b0SAlexander V. Chernikov 	.prepare_add	= ta_prepare_add_cidr,
5199f7d47b0SAlexander V. Chernikov 	.prepare_del	= ta_prepare_del_cidr,
5209f7d47b0SAlexander V. Chernikov 	.add		= ta_add_cidr,
5219f7d47b0SAlexander V. Chernikov 	.del		= ta_del_cidr,
5229f7d47b0SAlexander V. Chernikov 	.flush_entry	= ta_flush_cidr_entry,
5239f7d47b0SAlexander V. Chernikov 	.foreach	= ta_foreach_radix,
52481d3153dSAlexander V. Chernikov 	.dump_tentry	= ta_dump_radix_tentry,
52581d3153dSAlexander V. Chernikov 	.find_tentry	= ta_find_radix_tentry,
5269f7d47b0SAlexander V. Chernikov };
5279f7d47b0SAlexander V. Chernikov 
5289f7d47b0SAlexander V. Chernikov 
5299f7d47b0SAlexander V. Chernikov /*
530*68394ec8SAlexander V. Chernikov  * Iface table cmds.
531*68394ec8SAlexander V. Chernikov  *
532*68394ec8SAlexander V. Chernikov  * Implementation:
533*68394ec8SAlexander V. Chernikov  *
534*68394ec8SAlexander V. Chernikov  * Runtime part:
535*68394ec8SAlexander V. Chernikov  * - sorted array of "struct ifidx" pointed by ti->state.
536*68394ec8SAlexander V. Chernikov  *   Array is allocated with routing up to IFIDX_CHUNK. Only existing
537*68394ec8SAlexander V. Chernikov  *   interfaces are stored in array, however its allocated size is
538*68394ec8SAlexander V. Chernikov  *   sufficient to hold all table records if needed.
539*68394ec8SAlexander V. Chernikov  * - current array size is stored in ti->data
540*68394ec8SAlexander V. Chernikov  *
541*68394ec8SAlexander V. Chernikov  * Table data:
542*68394ec8SAlexander V. Chernikov  * - "struct iftable_cfg" is allocated to store table state (ta_state).
543*68394ec8SAlexander V. Chernikov  * - All table records are stored inside namedobj instance.
5449f7d47b0SAlexander V. Chernikov  *
5459f7d47b0SAlexander V. Chernikov  */
5469f7d47b0SAlexander V. Chernikov 
547*68394ec8SAlexander V. Chernikov struct ifidx {
548*68394ec8SAlexander V. Chernikov 	uint16_t	kidx;
549*68394ec8SAlexander V. Chernikov 	uint16_t	spare;
550*68394ec8SAlexander V. Chernikov 	uint32_t	value;
551*68394ec8SAlexander V. Chernikov };
552*68394ec8SAlexander V. Chernikov 
553*68394ec8SAlexander V. Chernikov struct iftable_cfg;
554*68394ec8SAlexander V. Chernikov 
555*68394ec8SAlexander V. Chernikov struct ifentry {
556*68394ec8SAlexander V. Chernikov 	struct named_object	no;
557*68394ec8SAlexander V. Chernikov 	struct ipfw_ifc		ic;
558*68394ec8SAlexander V. Chernikov 	struct iftable_cfg	*icfg;
559*68394ec8SAlexander V. Chernikov 	TAILQ_ENTRY(ifentry)	next;
560*68394ec8SAlexander V. Chernikov 	uint32_t		value;
561*68394ec8SAlexander V. Chernikov 	int			linked;
562*68394ec8SAlexander V. Chernikov };
563*68394ec8SAlexander V. Chernikov 
564*68394ec8SAlexander V. Chernikov struct iftable_cfg {
565*68394ec8SAlexander V. Chernikov 	struct namedobj_instance	*ii;
566*68394ec8SAlexander V. Chernikov 	struct ip_fw_chain	*ch;
567*68394ec8SAlexander V. Chernikov 	struct table_info	*ti;
568*68394ec8SAlexander V. Chernikov 	void	*main_ptr;
569*68394ec8SAlexander V. Chernikov 	size_t	size;	/* Number of items allocated in array */
570*68394ec8SAlexander V. Chernikov 	size_t	count;	/* Number of all items */
571*68394ec8SAlexander V. Chernikov 	size_t	used;	/* Number of items _active_ now */
572*68394ec8SAlexander V. Chernikov };
573*68394ec8SAlexander V. Chernikov 
574*68394ec8SAlexander V. Chernikov #define	IFIDX_CHUNK	16
575*68394ec8SAlexander V. Chernikov 
576*68394ec8SAlexander V. Chernikov int compare_ifidx(const void *k, const void *v);
577*68394ec8SAlexander V. Chernikov static void if_notifier(struct ip_fw_chain *ch, void *cbdata, uint16_t ifindex);
578*68394ec8SAlexander V. Chernikov 
579*68394ec8SAlexander V. Chernikov int
580*68394ec8SAlexander V. Chernikov compare_ifidx(const void *k, const void *v)
581*68394ec8SAlexander V. Chernikov {
582*68394ec8SAlexander V. Chernikov 	struct ifidx *ifidx;
583*68394ec8SAlexander V. Chernikov 	uint16_t key;
584*68394ec8SAlexander V. Chernikov 
585*68394ec8SAlexander V. Chernikov 	key = *((uint16_t *)k);
586*68394ec8SAlexander V. Chernikov 	ifidx = (struct ifidx *)v;
587*68394ec8SAlexander V. Chernikov 
588*68394ec8SAlexander V. Chernikov 	if (key < ifidx->kidx)
589*68394ec8SAlexander V. Chernikov 		return (-1);
590*68394ec8SAlexander V. Chernikov 	else if (key > ifidx->kidx)
591*68394ec8SAlexander V. Chernikov 		return (1);
592*68394ec8SAlexander V. Chernikov 
593*68394ec8SAlexander V. Chernikov 	return (0);
594*68394ec8SAlexander V. Chernikov }
595*68394ec8SAlexander V. Chernikov 
596*68394ec8SAlexander V. Chernikov /*
597*68394ec8SAlexander V. Chernikov  * Adds item @item with key @key into ascending-sorted array @base.
598*68394ec8SAlexander V. Chernikov  * Assumes @base has enough additional storage.
599*68394ec8SAlexander V. Chernikov  *
600*68394ec8SAlexander V. Chernikov  * Returns 1 on success, 0 on duplicate key.
601*68394ec8SAlexander V. Chernikov  */
6029f7d47b0SAlexander V. Chernikov static int
603*68394ec8SAlexander V. Chernikov badd(const void *key, void *item, void *base, size_t nmemb,
604*68394ec8SAlexander V. Chernikov     size_t size, int (*compar) (const void *, const void *))
605*68394ec8SAlexander V. Chernikov {
606*68394ec8SAlexander V. Chernikov 	int min, max, mid, shift, res;
607*68394ec8SAlexander V. Chernikov 	caddr_t paddr;
608*68394ec8SAlexander V. Chernikov 
609*68394ec8SAlexander V. Chernikov 	if (nmemb == 0) {
610*68394ec8SAlexander V. Chernikov 		memcpy(base, item, size);
611*68394ec8SAlexander V. Chernikov 		return (1);
612*68394ec8SAlexander V. Chernikov 	}
613*68394ec8SAlexander V. Chernikov 
614*68394ec8SAlexander V. Chernikov 	/* Binary search */
615*68394ec8SAlexander V. Chernikov 	min = 0;
616*68394ec8SAlexander V. Chernikov 	max = nmemb - 1;
617*68394ec8SAlexander V. Chernikov 	mid = 0;
618*68394ec8SAlexander V. Chernikov 	while (min <= max) {
619*68394ec8SAlexander V. Chernikov 		mid = (min + max) / 2;
620*68394ec8SAlexander V. Chernikov 		res = compar(key, (const void *)((caddr_t)base + mid * size));
621*68394ec8SAlexander V. Chernikov 		if (res == 0)
622*68394ec8SAlexander V. Chernikov 			return (0);
623*68394ec8SAlexander V. Chernikov 
624*68394ec8SAlexander V. Chernikov 		if (res > 0)
625*68394ec8SAlexander V. Chernikov 			 min = mid + 1;
626*68394ec8SAlexander V. Chernikov 		else
627*68394ec8SAlexander V. Chernikov 			 max = mid - 1;
628*68394ec8SAlexander V. Chernikov 	}
629*68394ec8SAlexander V. Chernikov 
630*68394ec8SAlexander V. Chernikov 	/* Item not found. */
631*68394ec8SAlexander V. Chernikov 	res = compar(key, (const void *)((caddr_t)base + mid * size));
632*68394ec8SAlexander V. Chernikov 	if (res > 0)
633*68394ec8SAlexander V. Chernikov 		shift = mid + 1;
634*68394ec8SAlexander V. Chernikov 	else
635*68394ec8SAlexander V. Chernikov 		shift = mid;
636*68394ec8SAlexander V. Chernikov 
637*68394ec8SAlexander V. Chernikov 	paddr = (caddr_t)base + shift * size;
638*68394ec8SAlexander V. Chernikov 	if (nmemb > shift)
639*68394ec8SAlexander V. Chernikov 		memmove(paddr + size, paddr, (nmemb - shift) * size);
640*68394ec8SAlexander V. Chernikov 
641*68394ec8SAlexander V. Chernikov 	memcpy(paddr, item, size);
642*68394ec8SAlexander V. Chernikov 
643*68394ec8SAlexander V. Chernikov 	return (1);
644*68394ec8SAlexander V. Chernikov }
645*68394ec8SAlexander V. Chernikov 
646*68394ec8SAlexander V. Chernikov /*
647*68394ec8SAlexander V. Chernikov  * Deletes item with key @key from ascending-sorted array @base.
648*68394ec8SAlexander V. Chernikov  *
649*68394ec8SAlexander V. Chernikov  * Returns 1 on success, 0 for non-existent key.
650*68394ec8SAlexander V. Chernikov  */
651*68394ec8SAlexander V. Chernikov static int
652*68394ec8SAlexander V. Chernikov bdel(const void *key, void *base, size_t nmemb, size_t size,
653*68394ec8SAlexander V. Chernikov     int (*compar) (const void *, const void *))
654*68394ec8SAlexander V. Chernikov {
655*68394ec8SAlexander V. Chernikov 	caddr_t item;
656*68394ec8SAlexander V. Chernikov 	size_t sz;
657*68394ec8SAlexander V. Chernikov 
658*68394ec8SAlexander V. Chernikov 	item = (caddr_t)bsearch(key, base, nmemb, size, compar);
659*68394ec8SAlexander V. Chernikov 
660*68394ec8SAlexander V. Chernikov 	if (item == NULL)
661*68394ec8SAlexander V. Chernikov 		return (0);
662*68394ec8SAlexander V. Chernikov 
663*68394ec8SAlexander V. Chernikov 	sz = (caddr_t)base + nmemb * size - item;
664*68394ec8SAlexander V. Chernikov 
665*68394ec8SAlexander V. Chernikov 	if (sz > 0)
666*68394ec8SAlexander V. Chernikov 		memmove(item, item + size, sz);
667*68394ec8SAlexander V. Chernikov 
668*68394ec8SAlexander V. Chernikov 	return (1);
669*68394ec8SAlexander V. Chernikov }
670*68394ec8SAlexander V. Chernikov 
671*68394ec8SAlexander V. Chernikov static struct ifidx *
672*68394ec8SAlexander V. Chernikov ifidx_find(struct table_info *ti, void *key)
673*68394ec8SAlexander V. Chernikov {
674*68394ec8SAlexander V. Chernikov 	struct ifidx *ifi;
675*68394ec8SAlexander V. Chernikov 
676*68394ec8SAlexander V. Chernikov 	ifi = bsearch(key, ti->state, ti->data, sizeof(struct ifidx),
677*68394ec8SAlexander V. Chernikov 	    compare_ifidx);
678*68394ec8SAlexander V. Chernikov 
679*68394ec8SAlexander V. Chernikov 	return (ifi);
680*68394ec8SAlexander V. Chernikov }
681*68394ec8SAlexander V. Chernikov 
682*68394ec8SAlexander V. Chernikov static int
683*68394ec8SAlexander V. Chernikov ta_lookup_ifidx(struct table_info *ti, void *key, uint32_t keylen,
6849f7d47b0SAlexander V. Chernikov     uint32_t *val)
6859f7d47b0SAlexander V. Chernikov {
686*68394ec8SAlexander V. Chernikov 	struct ifidx *ifi;
6879f7d47b0SAlexander V. Chernikov 
688*68394ec8SAlexander V. Chernikov 	ifi = ifidx_find(ti, key);
6899f7d47b0SAlexander V. Chernikov 
690*68394ec8SAlexander V. Chernikov 	if (ifi != NULL) {
691*68394ec8SAlexander V. Chernikov 		*val = ifi->value;
6929f7d47b0SAlexander V. Chernikov 		return (1);
6939f7d47b0SAlexander V. Chernikov 	}
6949f7d47b0SAlexander V. Chernikov 
6959f7d47b0SAlexander V. Chernikov 	return (0);
6969f7d47b0SAlexander V. Chernikov }
6979f7d47b0SAlexander V. Chernikov 
6989f7d47b0SAlexander V. Chernikov static int
699*68394ec8SAlexander V. Chernikov ta_init_ifidx(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
700*68394ec8SAlexander V. Chernikov     char *data)
7019f7d47b0SAlexander V. Chernikov {
702*68394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
7039f7d47b0SAlexander V. Chernikov 
704*68394ec8SAlexander V. Chernikov 	icfg = malloc(sizeof(struct iftable_cfg), M_IPFW, M_WAITOK | M_ZERO);
7059f7d47b0SAlexander V. Chernikov 
706*68394ec8SAlexander V. Chernikov 	icfg->ii = ipfw_objhash_create(16);
707*68394ec8SAlexander V. Chernikov 	icfg->main_ptr = malloc(sizeof(struct ifidx) * IFIDX_CHUNK,  M_IPFW,
708*68394ec8SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
709*68394ec8SAlexander V. Chernikov 	icfg->size = IFIDX_CHUNK;
710*68394ec8SAlexander V. Chernikov 	icfg->ch = ch;
7119f7d47b0SAlexander V. Chernikov 
712*68394ec8SAlexander V. Chernikov 	*ta_state = icfg;
713*68394ec8SAlexander V. Chernikov 	ti->state = icfg->main_ptr;
714*68394ec8SAlexander V. Chernikov 	ti->lookup = ta_lookup_ifidx;
7159f7d47b0SAlexander V. Chernikov 
7169f7d47b0SAlexander V. Chernikov 	return (0);
7179f7d47b0SAlexander V. Chernikov }
7189f7d47b0SAlexander V. Chernikov 
719*68394ec8SAlexander V. Chernikov /*
720*68394ec8SAlexander V. Chernikov  * Handle tableinfo @ti pointer change (on table array resize).
721*68394ec8SAlexander V. Chernikov  */
722*68394ec8SAlexander V. Chernikov static void
723*68394ec8SAlexander V. Chernikov ta_change_ti_ifidx(void *ta_state, struct table_info *ti)
724*68394ec8SAlexander V. Chernikov {
725*68394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
726*68394ec8SAlexander V. Chernikov 
727*68394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
728*68394ec8SAlexander V. Chernikov 	icfg->ti = ti;
729*68394ec8SAlexander V. Chernikov }
7309f7d47b0SAlexander V. Chernikov 
7319f7d47b0SAlexander V. Chernikov static void
732*68394ec8SAlexander V. Chernikov destroy_ifidx_locked(struct namedobj_instance *ii, struct named_object *no,
733*68394ec8SAlexander V. Chernikov     void *arg)
7349f7d47b0SAlexander V. Chernikov {
735*68394ec8SAlexander V. Chernikov 	struct ifentry *ife;
736*68394ec8SAlexander V. Chernikov 	struct ip_fw_chain *ch;
7379f7d47b0SAlexander V. Chernikov 
738*68394ec8SAlexander V. Chernikov 	ch = (struct ip_fw_chain *)arg;
739*68394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)no;
740*68394ec8SAlexander V. Chernikov 
741*68394ec8SAlexander V. Chernikov 	ipfw_iface_del_notify(ch, &ife->ic);
742*68394ec8SAlexander V. Chernikov 	free(ife, M_IPFW_TBL);
7439f7d47b0SAlexander V. Chernikov }
7449f7d47b0SAlexander V. Chernikov 
745*68394ec8SAlexander V. Chernikov 
746*68394ec8SAlexander V. Chernikov /*
747*68394ec8SAlexander V. Chernikov  * Destroys table @ti
748*68394ec8SAlexander V. Chernikov  */
749*68394ec8SAlexander V. Chernikov static void
750*68394ec8SAlexander V. Chernikov ta_destroy_ifidx(void *ta_state, struct table_info *ti)
7519f7d47b0SAlexander V. Chernikov {
752*68394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
753*68394ec8SAlexander V. Chernikov 	struct ip_fw_chain *ch;
754*68394ec8SAlexander V. Chernikov 
755*68394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
756*68394ec8SAlexander V. Chernikov 	ch = icfg->ch;
757*68394ec8SAlexander V. Chernikov 
758*68394ec8SAlexander V. Chernikov 	if (icfg->main_ptr != NULL)
759*68394ec8SAlexander V. Chernikov 		free(icfg->main_ptr, M_IPFW);
760*68394ec8SAlexander V. Chernikov 
761*68394ec8SAlexander V. Chernikov 	ipfw_objhash_foreach(icfg->ii, destroy_ifidx_locked, ch);
762*68394ec8SAlexander V. Chernikov 
763*68394ec8SAlexander V. Chernikov 	ipfw_objhash_destroy(icfg->ii);
764*68394ec8SAlexander V. Chernikov 
765*68394ec8SAlexander V. Chernikov 	free(icfg, M_IPFW);
766*68394ec8SAlexander V. Chernikov }
767*68394ec8SAlexander V. Chernikov 
768*68394ec8SAlexander V. Chernikov struct ta_buf_ifidx
769*68394ec8SAlexander V. Chernikov {
770*68394ec8SAlexander V. Chernikov 	struct ifentry *ife;
771*68394ec8SAlexander V. Chernikov 	uint32_t value;
7729f7d47b0SAlexander V. Chernikov };
7739f7d47b0SAlexander V. Chernikov 
774*68394ec8SAlexander V. Chernikov /*
775*68394ec8SAlexander V. Chernikov  * Prepare state to add to the table:
776*68394ec8SAlexander V. Chernikov  * allocate ifentry and reference needed interface.
777*68394ec8SAlexander V. Chernikov  */
7789f7d47b0SAlexander V. Chernikov static int
779*68394ec8SAlexander V. Chernikov ta_prepare_add_ifidx(struct ip_fw_chain *ch, struct tentry_info *tei,
780*68394ec8SAlexander V. Chernikov     void *ta_buf)
781*68394ec8SAlexander V. Chernikov {
782*68394ec8SAlexander V. Chernikov 	struct ta_buf_ifidx *tb;
783*68394ec8SAlexander V. Chernikov 	char *ifname;
784*68394ec8SAlexander V. Chernikov 	struct ifentry *ife;
785*68394ec8SAlexander V. Chernikov 
786*68394ec8SAlexander V. Chernikov 	tb = (struct ta_buf_ifidx *)ta_buf;
787*68394ec8SAlexander V. Chernikov 	memset(tb, 0, sizeof(struct ta_buf_cidr));
788*68394ec8SAlexander V. Chernikov 
789*68394ec8SAlexander V. Chernikov 	/* Check if string is terminated */
790*68394ec8SAlexander V. Chernikov 	ifname = (char *)tei->paddr;
791*68394ec8SAlexander V. Chernikov 	if (strnlen(ifname, IF_NAMESIZE) == IF_NAMESIZE)
792*68394ec8SAlexander V. Chernikov 		return (EINVAL);
793*68394ec8SAlexander V. Chernikov 
794*68394ec8SAlexander V. Chernikov 	ife = malloc(sizeof(struct ifentry), M_IPFW_TBL, M_WAITOK | M_ZERO);
795*68394ec8SAlexander V. Chernikov 	ife->value = tei->value;
796*68394ec8SAlexander V. Chernikov 	ife->ic.cb = if_notifier;
797*68394ec8SAlexander V. Chernikov 	ife->ic.cbdata = ife;
798*68394ec8SAlexander V. Chernikov 
799*68394ec8SAlexander V. Chernikov 	if (ipfw_iface_ref(ch, ifname, &ife->ic) != 0)
800*68394ec8SAlexander V. Chernikov 		return (EINVAL);
801*68394ec8SAlexander V. Chernikov 
802*68394ec8SAlexander V. Chernikov 	/* Use ipfw_iface 'ifname' field as stable storage */
803*68394ec8SAlexander V. Chernikov 	ife->no.name = ife->ic.iface->ifname;
804*68394ec8SAlexander V. Chernikov 
805*68394ec8SAlexander V. Chernikov 	tb->ife = ife;
806*68394ec8SAlexander V. Chernikov 
807*68394ec8SAlexander V. Chernikov 	return (0);
808*68394ec8SAlexander V. Chernikov }
809*68394ec8SAlexander V. Chernikov 
810*68394ec8SAlexander V. Chernikov static int
811*68394ec8SAlexander V. Chernikov ta_add_ifidx(void *ta_state, struct table_info *ti,
812*68394ec8SAlexander V. Chernikov     struct tentry_info *tei, void *ta_buf, uint64_t *pflags)
813*68394ec8SAlexander V. Chernikov {
814*68394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
815*68394ec8SAlexander V. Chernikov 	struct ifentry *ife, *tmp;
816*68394ec8SAlexander V. Chernikov 	struct ta_buf_ifidx *tb;
817*68394ec8SAlexander V. Chernikov 	struct ipfw_iface *iif;
818*68394ec8SAlexander V. Chernikov 	struct ifidx *ifi;
819*68394ec8SAlexander V. Chernikov 	char *ifname;
820*68394ec8SAlexander V. Chernikov 
821*68394ec8SAlexander V. Chernikov 	tb = (struct ta_buf_ifidx *)ta_buf;
822*68394ec8SAlexander V. Chernikov 	ifname = (char *)tei->paddr;
823*68394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
824*68394ec8SAlexander V. Chernikov 	ife = tb->ife;
825*68394ec8SAlexander V. Chernikov 
826*68394ec8SAlexander V. Chernikov 	ife->icfg = icfg;
827*68394ec8SAlexander V. Chernikov 
828*68394ec8SAlexander V. Chernikov 	tmp = (struct ifentry *)ipfw_objhash_lookup_name(icfg->ii, 0, ifname);
829*68394ec8SAlexander V. Chernikov 
830*68394ec8SAlexander V. Chernikov 	if (tmp != NULL) {
831*68394ec8SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
832*68394ec8SAlexander V. Chernikov 			return (EEXIST);
833*68394ec8SAlexander V. Chernikov 
834*68394ec8SAlexander V. Chernikov 		/* We need to update value */
835*68394ec8SAlexander V. Chernikov 		iif = tmp->ic.iface;
836*68394ec8SAlexander V. Chernikov 		tmp->value = ife->value;
837*68394ec8SAlexander V. Chernikov 
838*68394ec8SAlexander V. Chernikov 		if (iif->resolved != 0) {
839*68394ec8SAlexander V. Chernikov 			/* We need to update runtime value, too */
840*68394ec8SAlexander V. Chernikov 			ifi = ifidx_find(ti, &iif->ifindex);
841*68394ec8SAlexander V. Chernikov 			ifi->value = ife->value;
842*68394ec8SAlexander V. Chernikov 		}
843*68394ec8SAlexander V. Chernikov 
844*68394ec8SAlexander V. Chernikov 		/* Indicate that update has happened instead of addition */
845*68394ec8SAlexander V. Chernikov 		tei->flags |= TEI_FLAGS_UPDATED;
846*68394ec8SAlexander V. Chernikov 		return (0);
847*68394ec8SAlexander V. Chernikov 	}
848*68394ec8SAlexander V. Chernikov 
849*68394ec8SAlexander V. Chernikov 	/* Link to internal list */
850*68394ec8SAlexander V. Chernikov 	ipfw_objhash_add(icfg->ii, &ife->no);
851*68394ec8SAlexander V. Chernikov 
852*68394ec8SAlexander V. Chernikov 	/* Link notifier (possible running its callback) */
853*68394ec8SAlexander V. Chernikov 	ipfw_iface_add_notify(icfg->ch, &ife->ic);
854*68394ec8SAlexander V. Chernikov 	icfg->count++;
855*68394ec8SAlexander V. Chernikov 
856*68394ec8SAlexander V. Chernikov 	if (icfg->count + 1 == icfg->size) {
857*68394ec8SAlexander V. Chernikov 		/* Notify core we need to grow */
858*68394ec8SAlexander V. Chernikov 		*pflags = icfg->size + IFIDX_CHUNK;
859*68394ec8SAlexander V. Chernikov 	}
860*68394ec8SAlexander V. Chernikov 
861*68394ec8SAlexander V. Chernikov 	tb->ife = NULL;
862*68394ec8SAlexander V. Chernikov 
863*68394ec8SAlexander V. Chernikov 	return (0);
864*68394ec8SAlexander V. Chernikov }
865*68394ec8SAlexander V. Chernikov 
866*68394ec8SAlexander V. Chernikov /*
867*68394ec8SAlexander V. Chernikov  * Prepare to delete key from table.
868*68394ec8SAlexander V. Chernikov  * Do basic interface name checks.
869*68394ec8SAlexander V. Chernikov  */
870*68394ec8SAlexander V. Chernikov static int
871*68394ec8SAlexander V. Chernikov ta_prepare_del_ifidx(struct ip_fw_chain *ch, struct tentry_info *tei,
872*68394ec8SAlexander V. Chernikov     void *ta_buf)
8739f7d47b0SAlexander V. Chernikov {
8749f7d47b0SAlexander V. Chernikov 	struct ta_buf_iface *tb;
875e0a8b9eeSAlexander V. Chernikov 	char *ifname;
8769f7d47b0SAlexander V. Chernikov 
8779f7d47b0SAlexander V. Chernikov 	tb = (struct ta_buf_iface *)ta_buf;
8789f7d47b0SAlexander V. Chernikov 	memset(tb, 0, sizeof(struct ta_buf_cidr));
8799f7d47b0SAlexander V. Chernikov 
8809f7d47b0SAlexander V. Chernikov 	/* Check if string is terminated */
881e0a8b9eeSAlexander V. Chernikov 	ifname = (char *)tei->paddr;
882e0a8b9eeSAlexander V. Chernikov 	if (strnlen(ifname, IF_NAMESIZE) == IF_NAMESIZE)
8839f7d47b0SAlexander V. Chernikov 		return (EINVAL);
8849f7d47b0SAlexander V. Chernikov 
8859f7d47b0SAlexander V. Chernikov 	return (0);
8869f7d47b0SAlexander V. Chernikov }
8879f7d47b0SAlexander V. Chernikov 
888*68394ec8SAlexander V. Chernikov /*
889*68394ec8SAlexander V. Chernikov  * Remove key from both configuration list and
890*68394ec8SAlexander V. Chernikov  * runtime array. Removed interface notification.
891*68394ec8SAlexander V. Chernikov  */
8929f7d47b0SAlexander V. Chernikov static int
893*68394ec8SAlexander V. Chernikov ta_del_ifidx(void *ta_state, struct table_info *ti,
894db785d31SAlexander V. Chernikov     struct tentry_info *tei, void *ta_buf, uint64_t *pflags)
8959f7d47b0SAlexander V. Chernikov {
896*68394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
897*68394ec8SAlexander V. Chernikov 	struct ifentry *ife;
898*68394ec8SAlexander V. Chernikov 	struct ta_buf_ifidx *tb;
899*68394ec8SAlexander V. Chernikov 	char *ifname;
900*68394ec8SAlexander V. Chernikov 	uint16_t ifindex;
901*68394ec8SAlexander V. Chernikov 	int res;
9029f7d47b0SAlexander V. Chernikov 
903*68394ec8SAlexander V. Chernikov 	tb = (struct ta_buf_ifidx *)ta_buf;
904*68394ec8SAlexander V. Chernikov 	ifname = (char *)tei->paddr;
905*68394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
906*68394ec8SAlexander V. Chernikov 	ife = tb->ife;
9079f7d47b0SAlexander V. Chernikov 
908*68394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)ipfw_objhash_lookup_name(icfg->ii, 0, ifname);
9099f7d47b0SAlexander V. Chernikov 
910*68394ec8SAlexander V. Chernikov 	if (ife == NULL)
91181d3153dSAlexander V. Chernikov 		return (ENOENT);
9129f7d47b0SAlexander V. Chernikov 
913*68394ec8SAlexander V. Chernikov 	if (ife->linked != 0) {
914*68394ec8SAlexander V. Chernikov 		/* We have to remove item from runtime */
915*68394ec8SAlexander V. Chernikov 		ifindex = ife->ic.iface->ifindex;
916*68394ec8SAlexander V. Chernikov 
917*68394ec8SAlexander V. Chernikov 		res = bdel(&ifindex, icfg->main_ptr, icfg->used,
918*68394ec8SAlexander V. Chernikov 		    sizeof(struct ifidx), compare_ifidx);
919*68394ec8SAlexander V. Chernikov 
920*68394ec8SAlexander V. Chernikov 		KASSERT(res == 1, ("index %d does not exist", ifindex));
921*68394ec8SAlexander V. Chernikov 		icfg->used--;
922*68394ec8SAlexander V. Chernikov 		ti->data = icfg->used;
923*68394ec8SAlexander V. Chernikov 		ife->linked = 0;
924*68394ec8SAlexander V. Chernikov 	}
925*68394ec8SAlexander V. Chernikov 
926*68394ec8SAlexander V. Chernikov 	/* Unlink from local list */
927*68394ec8SAlexander V. Chernikov 	ipfw_objhash_del(icfg->ii, &ife->no);
928*68394ec8SAlexander V. Chernikov 	/* Unlink notifier */
929*68394ec8SAlexander V. Chernikov 	ipfw_iface_del_notify(icfg->ch, &ife->ic);
930*68394ec8SAlexander V. Chernikov 
931*68394ec8SAlexander V. Chernikov 	icfg->count--;
932*68394ec8SAlexander V. Chernikov 
933*68394ec8SAlexander V. Chernikov 	tb->ife = ife;
934*68394ec8SAlexander V. Chernikov 
9359f7d47b0SAlexander V. Chernikov 	return (0);
9369f7d47b0SAlexander V. Chernikov }
9379f7d47b0SAlexander V. Chernikov 
938*68394ec8SAlexander V. Chernikov /*
939*68394ec8SAlexander V. Chernikov  * Flush deleted entry.
940*68394ec8SAlexander V. Chernikov  * Drops interface reference and frees entry.
941*68394ec8SAlexander V. Chernikov  */
9429f7d47b0SAlexander V. Chernikov static void
943*68394ec8SAlexander V. Chernikov ta_flush_ifidx_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
944*68394ec8SAlexander V. Chernikov     void *ta_buf)
9459f7d47b0SAlexander V. Chernikov {
946*68394ec8SAlexander V. Chernikov 	struct ta_buf_ifidx *tb;
9479f7d47b0SAlexander V. Chernikov 
948*68394ec8SAlexander V. Chernikov 	tb = (struct ta_buf_ifidx *)ta_buf;
9499f7d47b0SAlexander V. Chernikov 
950*68394ec8SAlexander V. Chernikov 	if (tb->ife != NULL) {
951*68394ec8SAlexander V. Chernikov 		/* Unlink first */
952*68394ec8SAlexander V. Chernikov 		ipfw_iface_unref(ch, &tb->ife->ic);
953*68394ec8SAlexander V. Chernikov 		free(tb->ife, M_IPFW_TBL);
954*68394ec8SAlexander V. Chernikov 	}
955*68394ec8SAlexander V. Chernikov }
956*68394ec8SAlexander V. Chernikov 
957*68394ec8SAlexander V. Chernikov 
958*68394ec8SAlexander V. Chernikov /*
959*68394ec8SAlexander V. Chernikov  * Handle interface announce/withdrawal for particular table.
960*68394ec8SAlexander V. Chernikov  * Every real runtime array modification happens here.
961*68394ec8SAlexander V. Chernikov  */
962*68394ec8SAlexander V. Chernikov static void
963*68394ec8SAlexander V. Chernikov if_notifier(struct ip_fw_chain *ch, void *cbdata, uint16_t ifindex)
964*68394ec8SAlexander V. Chernikov {
965*68394ec8SAlexander V. Chernikov 	struct ifentry *ife;
966*68394ec8SAlexander V. Chernikov 	struct ifidx ifi;
967*68394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
968*68394ec8SAlexander V. Chernikov 	struct table_info *ti;
969*68394ec8SAlexander V. Chernikov 	int res;
970*68394ec8SAlexander V. Chernikov 
971*68394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)cbdata;
972*68394ec8SAlexander V. Chernikov 	icfg = ife->icfg;
973*68394ec8SAlexander V. Chernikov 	ti = icfg->ti;
974*68394ec8SAlexander V. Chernikov 
975*68394ec8SAlexander V. Chernikov 	KASSERT(ti != NULL, ("ti=NULL, check change_ti handler"));
976*68394ec8SAlexander V. Chernikov 
977*68394ec8SAlexander V. Chernikov 	if (ife->linked == 0 && ifindex != 0) {
978*68394ec8SAlexander V. Chernikov 		/* Interface announce */
979*68394ec8SAlexander V. Chernikov 		ifi.kidx = ifindex;
980*68394ec8SAlexander V. Chernikov 		ifi.spare = 0;
981*68394ec8SAlexander V. Chernikov 		ifi.value = ife->value;
982*68394ec8SAlexander V. Chernikov 		res = badd(&ifindex, &ifi, icfg->main_ptr, icfg->used,
983*68394ec8SAlexander V. Chernikov 		    sizeof(struct ifidx), compare_ifidx);
984*68394ec8SAlexander V. Chernikov 		KASSERT(res == 1, ("index %d already exists", ifindex));
985*68394ec8SAlexander V. Chernikov 		icfg->used++;
986*68394ec8SAlexander V. Chernikov 		ti->data = icfg->used;
987*68394ec8SAlexander V. Chernikov 		ife->linked = 1;
988*68394ec8SAlexander V. Chernikov 	} else if (ife->linked != 0 && ifindex == 0) {
989*68394ec8SAlexander V. Chernikov 		/* Interface withdrawal */
990*68394ec8SAlexander V. Chernikov 		ifindex = ife->ic.iface->ifindex;
991*68394ec8SAlexander V. Chernikov 
992*68394ec8SAlexander V. Chernikov 		res = bdel(&ifindex, icfg->main_ptr, icfg->used,
993*68394ec8SAlexander V. Chernikov 		    sizeof(struct ifidx), compare_ifidx);
994*68394ec8SAlexander V. Chernikov 
995*68394ec8SAlexander V. Chernikov 		KASSERT(res == 1, ("index %d does not exist", ifindex));
996*68394ec8SAlexander V. Chernikov 		icfg->used--;
997*68394ec8SAlexander V. Chernikov 		ti->data = icfg->used;
998*68394ec8SAlexander V. Chernikov 		ife->linked = 0;
999*68394ec8SAlexander V. Chernikov 	}
1000*68394ec8SAlexander V. Chernikov }
1001*68394ec8SAlexander V. Chernikov 
1002*68394ec8SAlexander V. Chernikov 
1003*68394ec8SAlexander V. Chernikov /*
1004*68394ec8SAlexander V. Chernikov  * Table growing callbacks.
1005*68394ec8SAlexander V. Chernikov  */
1006*68394ec8SAlexander V. Chernikov 
1007*68394ec8SAlexander V. Chernikov struct mod_ifidx {
1008*68394ec8SAlexander V. Chernikov 	void	*main_ptr;
1009*68394ec8SAlexander V. Chernikov 	size_t	size;
1010*68394ec8SAlexander V. Chernikov };
1011*68394ec8SAlexander V. Chernikov 
1012*68394ec8SAlexander V. Chernikov /*
1013*68394ec8SAlexander V. Chernikov  * Allocate ned, larger runtime ifidx array.
1014*68394ec8SAlexander V. Chernikov  */
1015*68394ec8SAlexander V. Chernikov static int
1016*68394ec8SAlexander V. Chernikov ta_prepare_mod_ifidx(void *ta_buf, uint64_t *pflags)
1017*68394ec8SAlexander V. Chernikov {
1018*68394ec8SAlexander V. Chernikov 	struct mod_ifidx *mi;
1019*68394ec8SAlexander V. Chernikov 
1020*68394ec8SAlexander V. Chernikov 	mi = (struct mod_ifidx *)ta_buf;
1021*68394ec8SAlexander V. Chernikov 
1022*68394ec8SAlexander V. Chernikov 	memset(mi, 0, sizeof(struct mod_ifidx));
1023*68394ec8SAlexander V. Chernikov 	mi->size = *pflags;
1024*68394ec8SAlexander V. Chernikov 	mi->main_ptr = malloc(sizeof(struct ifidx) * mi->size, M_IPFW,
1025*68394ec8SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
1026*68394ec8SAlexander V. Chernikov 
1027*68394ec8SAlexander V. Chernikov 	return (0);
1028*68394ec8SAlexander V. Chernikov }
1029*68394ec8SAlexander V. Chernikov 
1030*68394ec8SAlexander V. Chernikov /*
1031*68394ec8SAlexander V. Chernikov  * Copy data from old runtime array to new one.
1032*68394ec8SAlexander V. Chernikov  */
1033*68394ec8SAlexander V. Chernikov static int
1034*68394ec8SAlexander V. Chernikov ta_fill_mod_ifidx(void *ta_state, struct table_info *ti, void *ta_buf,
1035*68394ec8SAlexander V. Chernikov     uint64_t *pflags)
1036*68394ec8SAlexander V. Chernikov {
1037*68394ec8SAlexander V. Chernikov 	struct mod_ifidx *mi;
1038*68394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
1039*68394ec8SAlexander V. Chernikov 
1040*68394ec8SAlexander V. Chernikov 	mi = (struct mod_ifidx *)ta_buf;
1041*68394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
1042*68394ec8SAlexander V. Chernikov 
1043*68394ec8SAlexander V. Chernikov 	/* Check if we still need to grow array */
1044*68394ec8SAlexander V. Chernikov 	if (icfg->size >= mi->size) {
1045*68394ec8SAlexander V. Chernikov 		*pflags = 0;
1046*68394ec8SAlexander V. Chernikov 		return (0);
1047*68394ec8SAlexander V. Chernikov 	}
1048*68394ec8SAlexander V. Chernikov 
1049*68394ec8SAlexander V. Chernikov 	memcpy(mi->main_ptr, icfg->main_ptr, icfg->used * sizeof(struct ifidx));
1050*68394ec8SAlexander V. Chernikov 
1051*68394ec8SAlexander V. Chernikov 	return (0);
1052*68394ec8SAlexander V. Chernikov }
1053*68394ec8SAlexander V. Chernikov 
1054*68394ec8SAlexander V. Chernikov /*
1055*68394ec8SAlexander V. Chernikov  * Switch old & new arrays.
1056*68394ec8SAlexander V. Chernikov  */
1057*68394ec8SAlexander V. Chernikov static int
1058*68394ec8SAlexander V. Chernikov ta_modify_ifidx(void *ta_state, struct table_info *ti, void *ta_buf,
1059*68394ec8SAlexander V. Chernikov     uint64_t pflags)
1060*68394ec8SAlexander V. Chernikov {
1061*68394ec8SAlexander V. Chernikov 	struct mod_ifidx *mi;
1062*68394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
1063*68394ec8SAlexander V. Chernikov 	void *old_ptr;
1064*68394ec8SAlexander V. Chernikov 
1065*68394ec8SAlexander V. Chernikov 	mi = (struct mod_ifidx *)ta_buf;
1066*68394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
1067*68394ec8SAlexander V. Chernikov 
1068*68394ec8SAlexander V. Chernikov 	old_ptr = icfg->main_ptr;
1069*68394ec8SAlexander V. Chernikov 	icfg->main_ptr = mi->main_ptr;
1070*68394ec8SAlexander V. Chernikov 	icfg->size = mi->size;
1071*68394ec8SAlexander V. Chernikov 	ti->state = icfg->main_ptr;
1072*68394ec8SAlexander V. Chernikov 
1073*68394ec8SAlexander V. Chernikov 	mi->main_ptr = old_ptr;
1074*68394ec8SAlexander V. Chernikov 
1075*68394ec8SAlexander V. Chernikov 	return (0);
1076*68394ec8SAlexander V. Chernikov }
1077*68394ec8SAlexander V. Chernikov 
1078*68394ec8SAlexander V. Chernikov /*
1079*68394ec8SAlexander V. Chernikov  * Free unneded array.
1080*68394ec8SAlexander V. Chernikov  */
1081*68394ec8SAlexander V. Chernikov static void
1082*68394ec8SAlexander V. Chernikov ta_flush_mod_ifidx(void *ta_buf)
1083*68394ec8SAlexander V. Chernikov {
1084*68394ec8SAlexander V. Chernikov 	struct mod_ifidx *mi;
1085*68394ec8SAlexander V. Chernikov 
1086*68394ec8SAlexander V. Chernikov 	mi = (struct mod_ifidx *)ta_buf;
1087*68394ec8SAlexander V. Chernikov 	if (mi->main_ptr != NULL)
1088*68394ec8SAlexander V. Chernikov 		free(mi->main_ptr, M_IPFW);
10899f7d47b0SAlexander V. Chernikov }
10909f7d47b0SAlexander V. Chernikov 
10919f7d47b0SAlexander V. Chernikov static int
1092*68394ec8SAlexander V. Chernikov ta_dump_ifidx_tentry(void *ta_state, struct table_info *ti, void *e,
109381d3153dSAlexander V. Chernikov     ipfw_obj_tentry *tent)
10949f7d47b0SAlexander V. Chernikov {
1095*68394ec8SAlexander V. Chernikov 	struct ifentry *ife;
10969f7d47b0SAlexander V. Chernikov 
1097*68394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)e;
1098*68394ec8SAlexander V. Chernikov 
109981d3153dSAlexander V. Chernikov 	tent->masklen = 8 * IF_NAMESIZE;
1100*68394ec8SAlexander V. Chernikov 	memcpy(&tent->k, ife->no.name, IF_NAMESIZE);
1101*68394ec8SAlexander V. Chernikov 	tent->value = ife->value;
11029f7d47b0SAlexander V. Chernikov 
11039f7d47b0SAlexander V. Chernikov 	return (0);
11049f7d47b0SAlexander V. Chernikov }
11059f7d47b0SAlexander V. Chernikov 
110681d3153dSAlexander V. Chernikov static int
1107*68394ec8SAlexander V. Chernikov ta_find_ifidx_tentry(void *ta_state, struct table_info *ti, void *key,
110881d3153dSAlexander V. Chernikov     uint32_t keylen, ipfw_obj_tentry *tent)
110981d3153dSAlexander V. Chernikov {
1110*68394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
1111*68394ec8SAlexander V. Chernikov 	struct ifentry *ife;
1112*68394ec8SAlexander V. Chernikov 	char *ifname;
111381d3153dSAlexander V. Chernikov 
1114*68394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
1115*68394ec8SAlexander V. Chernikov 	ifname = (char *)key;
111681d3153dSAlexander V. Chernikov 
1117*68394ec8SAlexander V. Chernikov 	if (strnlen(ifname, IF_NAMESIZE) == IF_NAMESIZE)
1118*68394ec8SAlexander V. Chernikov 		return (EINVAL);
111981d3153dSAlexander V. Chernikov 
1120*68394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)ipfw_objhash_lookup_name(icfg->ii, 0, ifname);
1121*68394ec8SAlexander V. Chernikov 
1122*68394ec8SAlexander V. Chernikov 	if (ife != NULL) {
1123*68394ec8SAlexander V. Chernikov 		ta_dump_ifidx_tentry(ta_state, ti, ife, tent);
112481d3153dSAlexander V. Chernikov 		return (0);
112581d3153dSAlexander V. Chernikov 	}
112681d3153dSAlexander V. Chernikov 
112781d3153dSAlexander V. Chernikov 	return (ENOENT);
112881d3153dSAlexander V. Chernikov }
112981d3153dSAlexander V. Chernikov 
1130*68394ec8SAlexander V. Chernikov struct wa_ifidx {
1131*68394ec8SAlexander V. Chernikov 	ta_foreach_f	*f;
1132*68394ec8SAlexander V. Chernikov 	void		*arg;
1133*68394ec8SAlexander V. Chernikov };
1134*68394ec8SAlexander V. Chernikov 
11359f7d47b0SAlexander V. Chernikov static void
1136*68394ec8SAlexander V. Chernikov foreach_ifidx(struct namedobj_instance *ii, struct named_object *no,
11379f7d47b0SAlexander V. Chernikov     void *arg)
11389f7d47b0SAlexander V. Chernikov {
1139*68394ec8SAlexander V. Chernikov 	struct ifentry *ife;
1140*68394ec8SAlexander V. Chernikov 	struct wa_ifidx *wa;
11419f7d47b0SAlexander V. Chernikov 
1142*68394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)no;
1143*68394ec8SAlexander V. Chernikov 	wa = (struct wa_ifidx *)arg;
1144*68394ec8SAlexander V. Chernikov 
1145*68394ec8SAlexander V. Chernikov 	wa->f(ife, wa->arg);
11469f7d47b0SAlexander V. Chernikov }
11479f7d47b0SAlexander V. Chernikov 
1148*68394ec8SAlexander V. Chernikov static void
1149*68394ec8SAlexander V. Chernikov ta_foreach_ifidx(void *ta_state, struct table_info *ti, ta_foreach_f *f,
1150*68394ec8SAlexander V. Chernikov     void *arg)
1151*68394ec8SAlexander V. Chernikov {
1152*68394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
1153*68394ec8SAlexander V. Chernikov 	struct wa_ifidx wa;
1154*68394ec8SAlexander V. Chernikov 
1155*68394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
1156*68394ec8SAlexander V. Chernikov 
1157*68394ec8SAlexander V. Chernikov 	wa.f = f;
1158*68394ec8SAlexander V. Chernikov 	wa.arg = arg;
1159*68394ec8SAlexander V. Chernikov 
1160*68394ec8SAlexander V. Chernikov 	ipfw_objhash_foreach(icfg->ii, foreach_ifidx, &wa);
1161*68394ec8SAlexander V. Chernikov }
1162*68394ec8SAlexander V. Chernikov 
1163*68394ec8SAlexander V. Chernikov struct table_algo idx_iface = {
1164*68394ec8SAlexander V. Chernikov 	.name		= "idx_iface",
1165*68394ec8SAlexander V. Chernikov 	.lookup		= ta_lookup_ifidx,
1166*68394ec8SAlexander V. Chernikov 	.init		= ta_init_ifidx,
1167*68394ec8SAlexander V. Chernikov 	.destroy	= ta_destroy_ifidx,
1168*68394ec8SAlexander V. Chernikov 	.prepare_add	= ta_prepare_add_ifidx,
1169*68394ec8SAlexander V. Chernikov 	.prepare_del	= ta_prepare_del_ifidx,
1170*68394ec8SAlexander V. Chernikov 	.add		= ta_add_ifidx,
1171*68394ec8SAlexander V. Chernikov 	.del		= ta_del_ifidx,
1172*68394ec8SAlexander V. Chernikov 	.flush_entry	= ta_flush_ifidx_entry,
1173*68394ec8SAlexander V. Chernikov 	.foreach	= ta_foreach_ifidx,
1174*68394ec8SAlexander V. Chernikov 	.dump_tentry	= ta_dump_ifidx_tentry,
1175*68394ec8SAlexander V. Chernikov 	.find_tentry	= ta_find_ifidx_tentry,
1176*68394ec8SAlexander V. Chernikov 	.prepare_mod	= ta_prepare_mod_ifidx,
1177*68394ec8SAlexander V. Chernikov 	.fill_mod	= ta_fill_mod_ifidx,
1178*68394ec8SAlexander V. Chernikov 	.modify		= ta_modify_ifidx,
1179*68394ec8SAlexander V. Chernikov 	.flush_mod	= ta_flush_mod_ifidx,
1180*68394ec8SAlexander V. Chernikov 	.change_ti	= ta_change_ti_ifidx,
11819f7d47b0SAlexander V. Chernikov };
11829f7d47b0SAlexander V. Chernikov 
11839f7d47b0SAlexander V. Chernikov void
11849f7d47b0SAlexander V. Chernikov ipfw_table_algo_init(struct ip_fw_chain *chain)
11859f7d47b0SAlexander V. Chernikov {
11869f7d47b0SAlexander V. Chernikov 	/*
11879f7d47b0SAlexander V. Chernikov 	 * Register all algorithms presented here.
11889f7d47b0SAlexander V. Chernikov 	 */
11899f7d47b0SAlexander V. Chernikov 	ipfw_add_table_algo(chain, &radix_cidr);
1190*68394ec8SAlexander V. Chernikov 	ipfw_add_table_algo(chain, &idx_iface);
11919f7d47b0SAlexander V. Chernikov }
11929f7d47b0SAlexander V. Chernikov 
11939f7d47b0SAlexander V. Chernikov void
11949f7d47b0SAlexander V. Chernikov ipfw_table_algo_destroy(struct ip_fw_chain *chain)
11959f7d47b0SAlexander V. Chernikov {
11969f7d47b0SAlexander V. Chernikov 	/* Do nothing */
11979f7d47b0SAlexander V. Chernikov }
11989f7d47b0SAlexander V. Chernikov 
11999f7d47b0SAlexander V. Chernikov 
1200