xref: /freebsd/sys/netpfil/ipfw/ip_fw_table_algo.c (revision 8ebca97f5ef134b3d7eb67626cb70283347e529f)
19f7d47b0SAlexander V. Chernikov /*-
21a33e799SAlexander V. Chernikov  * Copyright (c) 2014 Yandex LLC
31a33e799SAlexander V. Chernikov  * Copyright (c) 2014 Alexander V. Chernikov
49f7d47b0SAlexander V. Chernikov  *
59f7d47b0SAlexander V. Chernikov  * Redistribution and use in source and binary forms, with or without
69f7d47b0SAlexander V. Chernikov  * modification, are permitted provided that the following conditions
79f7d47b0SAlexander V. Chernikov  * are met:
89f7d47b0SAlexander V. Chernikov  * 1. Redistributions of source code must retain the above copyright
99f7d47b0SAlexander V. Chernikov  *    notice, this list of conditions and the following disclaimer.
109f7d47b0SAlexander V. Chernikov  * 2. Redistributions in binary form must reproduce the above copyright
119f7d47b0SAlexander V. Chernikov  *    notice, this list of conditions and the following disclaimer in the
129f7d47b0SAlexander V. Chernikov  *    documentation and/or other materials provided with the distribution.
139f7d47b0SAlexander V. Chernikov  *
149f7d47b0SAlexander V. Chernikov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
159f7d47b0SAlexander V. Chernikov  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
169f7d47b0SAlexander V. Chernikov  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
179f7d47b0SAlexander V. Chernikov  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
189f7d47b0SAlexander V. Chernikov  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
199f7d47b0SAlexander V. Chernikov  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
209f7d47b0SAlexander V. Chernikov  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
219f7d47b0SAlexander V. Chernikov  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
229f7d47b0SAlexander V. Chernikov  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
239f7d47b0SAlexander V. Chernikov  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
249f7d47b0SAlexander V. Chernikov  * SUCH DAMAGE.
259f7d47b0SAlexander V. Chernikov  */
269f7d47b0SAlexander V. Chernikov 
279f7d47b0SAlexander V. Chernikov #include <sys/cdefs.h>
289f7d47b0SAlexander V. Chernikov __FBSDID("$FreeBSD: projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c 267384 2014-06-12 09:59:11Z melifaro $");
299f7d47b0SAlexander V. Chernikov 
309f7d47b0SAlexander V. Chernikov /*
319f7d47b0SAlexander V. Chernikov  * Lookup table algorithms.
329f7d47b0SAlexander V. Chernikov  *
339f7d47b0SAlexander V. Chernikov  */
349f7d47b0SAlexander V. Chernikov 
359f7d47b0SAlexander V. Chernikov #include "opt_ipfw.h"
369f7d47b0SAlexander V. Chernikov #include "opt_inet.h"
379f7d47b0SAlexander V. Chernikov #ifndef INET
389f7d47b0SAlexander V. Chernikov #error IPFIREWALL requires INET.
399f7d47b0SAlexander V. Chernikov #endif /* INET */
409f7d47b0SAlexander V. Chernikov #include "opt_inet6.h"
419f7d47b0SAlexander V. Chernikov 
429f7d47b0SAlexander V. Chernikov #include <sys/param.h>
439f7d47b0SAlexander V. Chernikov #include <sys/systm.h>
449f7d47b0SAlexander V. Chernikov #include <sys/malloc.h>
459f7d47b0SAlexander V. Chernikov #include <sys/kernel.h>
469f7d47b0SAlexander V. Chernikov #include <sys/lock.h>
479f7d47b0SAlexander V. Chernikov #include <sys/rwlock.h>
48d4e1b515SAlexander V. Chernikov #include <sys/rmlock.h>
499f7d47b0SAlexander V. Chernikov #include <sys/socket.h>
509f7d47b0SAlexander V. Chernikov #include <sys/queue.h>
519f7d47b0SAlexander V. Chernikov #include <net/if.h>	/* ip_fw.h requires IFNAMSIZ */
529f7d47b0SAlexander V. Chernikov #include <net/radix.h>
53d3b00c08SAlexander V. Chernikov #include <net/route.h>
549f7d47b0SAlexander V. Chernikov 
559f7d47b0SAlexander V. Chernikov #include <netinet/in.h>
569f7d47b0SAlexander V. Chernikov #include <netinet/ip_var.h>	/* struct ipfw_rule_ref */
579f7d47b0SAlexander V. Chernikov #include <netinet/ip_fw.h>
589f7d47b0SAlexander V. Chernikov 
599f7d47b0SAlexander V. Chernikov #include <netpfil/ipfw/ip_fw_private.h>
60ea761a5dSAlexander V. Chernikov #include <netpfil/ipfw/ip_fw_table.h>
619f7d47b0SAlexander V. Chernikov 
62301290bcSAlexander V. Chernikov 
63301290bcSAlexander V. Chernikov /*
64301290bcSAlexander V. Chernikov  * IPFW table lookup algorithms.
65301290bcSAlexander V. Chernikov  *
66301290bcSAlexander V. Chernikov  * What is needed to add another table algo?
67301290bcSAlexander V. Chernikov  *
68301290bcSAlexander V. Chernikov  * Algo init:
69301290bcSAlexander V. Chernikov  * * struct table_algo has to be filled with:
70c21034b7SAlexander V. Chernikov  *   name: "type:algoname" format, e.g. "addr:radix". Currently
71c21034b7SAlexander V. Chernikov  *     there are the following types: "addr", "iface", "number" and "flow".
72301290bcSAlexander V. Chernikov  *   type: one of IPFW_TABLE_* types
73301290bcSAlexander V. Chernikov  *   flags: one or more TA_FLAGS_*
74301290bcSAlexander V. Chernikov  *   ta_buf_size: size of structure used to store add/del item state.
75301290bcSAlexander V. Chernikov  *     Needs to be less than TA_BUF_SZ.
76301290bcSAlexander V. Chernikov  *   callbacks: see below for description.
77301290bcSAlexander V. Chernikov  * * ipfw_add_table_algo / ipfw_del_table_algo has to be called
78301290bcSAlexander V. Chernikov  *
79301290bcSAlexander V. Chernikov  * Callbacks description:
80301290bcSAlexander V. Chernikov  *
81301290bcSAlexander V. Chernikov  * -init: request to initialize new table instance.
82301290bcSAlexander V. Chernikov  * typedef int (ta_init)(struct ip_fw_chain *ch, void **ta_state,
83301290bcSAlexander V. Chernikov  *     struct table_info *ti, char *data, uint8_t tflags);
84301290bcSAlexander V. Chernikov  * MANDATORY, unlocked. (M_WAITOK). Returns 0 on success.
85301290bcSAlexander V. Chernikov  *
86301290bcSAlexander V. Chernikov  *  Allocate all structures needed for normal operations.
87301290bcSAlexander V. Chernikov  *  * Caller may want to parse @data for some algo-specific
88301290bcSAlexander V. Chernikov  *    options provided by userland.
89301290bcSAlexander V. Chernikov  *  * Caller may want to save configuration state pointer to @ta_state
90301290bcSAlexander V. Chernikov  *  * Caller needs to save desired runtime structure pointer(s)
91301290bcSAlexander V. Chernikov  *    inside @ti fields. Note that it is not correct to save
92301290bcSAlexander V. Chernikov  *    @ti pointer at this moment. Use -change_ti hook for that.
93301290bcSAlexander V. Chernikov  *  * Caller has to fill in ti->lookup to appropriate function
94301290bcSAlexander V. Chernikov  *    pointer.
95301290bcSAlexander V. Chernikov  *
96301290bcSAlexander V. Chernikov  *
97301290bcSAlexander V. Chernikov  *
98301290bcSAlexander V. Chernikov  * -destroy: request to destroy table instance.
99301290bcSAlexander V. Chernikov  * typedef void (ta_destroy)(void *ta_state, struct table_info *ti);
100301290bcSAlexander V. Chernikov  * MANDATORY, may be locked (UH+WLOCK). (M_NOWAIT).
101301290bcSAlexander V. Chernikov  *
102301290bcSAlexander V. Chernikov  * Frees all table entries and all tables structures allocated by -init.
103301290bcSAlexander V. Chernikov  *
104301290bcSAlexander V. Chernikov  *
105301290bcSAlexander V. Chernikov  *
106301290bcSAlexander V. Chernikov  * -prepare_add: request to allocate state for adding new entry.
107301290bcSAlexander V. Chernikov  * typedef int (ta_prepare_add)(struct ip_fw_chain *ch, struct tentry_info *tei,
108301290bcSAlexander V. Chernikov  *     void *ta_buf);
109301290bcSAlexander V. Chernikov  * MANDATORY, unlocked. (M_WAITOK). Returns 0 on success.
110301290bcSAlexander V. Chernikov  *
11113263632SAlexander V. Chernikov  * Allocates state and fills it in with all necessary data (EXCEPT value)
11213263632SAlexander V. Chernikov  * from @tei to minimize operations needed to be done under WLOCK.
11313263632SAlexander V. Chernikov  * "value" field has to be copied to new entry in @add callback.
114301290bcSAlexander V. Chernikov  * Buffer ta_buf of size ta->ta_buf_sz may be used to store
115301290bcSAlexander V. Chernikov  * allocated state.
116301290bcSAlexander V. Chernikov  *
117301290bcSAlexander V. Chernikov  *
118301290bcSAlexander V. Chernikov  *
119301290bcSAlexander V. Chernikov  * -prepare_del: request to set state for deleting existing entry.
120301290bcSAlexander V. Chernikov  * typedef int (ta_prepare_del)(struct ip_fw_chain *ch, struct tentry_info *tei,
121301290bcSAlexander V. Chernikov  *     void *ta_buf);
122301290bcSAlexander V. Chernikov  * MANDATORY, locked, UH. (M_NOWAIT). Returns 0 on success.
123301290bcSAlexander V. Chernikov  *
124301290bcSAlexander V. Chernikov  * Buffer ta_buf of size ta->ta_buf_sz may be used to store
125301290bcSAlexander V. Chernikov  * allocated state. Caller should use on-stack ta_buf allocation
126301290bcSAlexander V. Chernikov  * instead of doing malloc().
127301290bcSAlexander V. Chernikov  *
128301290bcSAlexander V. Chernikov  *
129301290bcSAlexander V. Chernikov  *
130301290bcSAlexander V. Chernikov  * -add: request to insert new entry into runtime/config structures.
131301290bcSAlexander V. Chernikov  *  typedef int (ta_add)(void *ta_state, struct table_info *ti,
132301290bcSAlexander V. Chernikov  *     struct tentry_info *tei, void *ta_buf, uint32_t *pnum);
133301290bcSAlexander V. Chernikov  * MANDATORY, UH+WLOCK. (M_NOWAIT). Returns 0 on success.
134301290bcSAlexander V. Chernikov  *
135301290bcSAlexander V. Chernikov  * Insert new entry using previously-allocated state in @ta_buf.
136301290bcSAlexander V. Chernikov  * * @tei may have the following flags:
137301290bcSAlexander V. Chernikov  *   TEI_FLAGS_UPDATE: request to add or update entry.
138301290bcSAlexander V. Chernikov  *   TEI_FLAGS_DONTADD: request to update (but not add) entry.
139301290bcSAlexander V. Chernikov  * * Caller is required to do the following:
14013263632SAlexander V. Chernikov  *   copy real entry value from @tei
141301290bcSAlexander V. Chernikov  *   entry added: return 0, set 1 to @pnum
142301290bcSAlexander V. Chernikov  *   entry updated: return 0, store 0 to @pnum, store old value in @tei,
143301290bcSAlexander V. Chernikov  *     add TEI_FLAGS_UPDATED flag to @tei.
144301290bcSAlexander V. Chernikov  *   entry exists: return EEXIST
145301290bcSAlexander V. Chernikov  *   entry not found: return ENOENT
146301290bcSAlexander V. Chernikov  *   other error: return non-zero error code.
147301290bcSAlexander V. Chernikov  *
148301290bcSAlexander V. Chernikov  *
149301290bcSAlexander V. Chernikov  *
150301290bcSAlexander V. Chernikov  * -del: request to delete existing entry from runtime/config structures.
151301290bcSAlexander V. Chernikov  *  typedef int (ta_del)(void *ta_state, struct table_info *ti,
152301290bcSAlexander V. Chernikov  *     struct tentry_info *tei, void *ta_buf, uint32_t *pnum);
153301290bcSAlexander V. Chernikov  *  MANDATORY, UH+WLOCK. (M_NOWAIT). Returns 0 on success.
154301290bcSAlexander V. Chernikov  *
155301290bcSAlexander V. Chernikov  *  Delete entry using previously set up in @ta_buf.
156301290bcSAlexander V. Chernikov  * * Caller is required to do the following:
15713263632SAlexander V. Chernikov  *   entry deleted: return 0, set 1 to @pnum, store old value in @tei.
158301290bcSAlexander V. Chernikov  *   entry not found: return ENOENT
159301290bcSAlexander V. Chernikov  *   other error: return non-zero error code.
160301290bcSAlexander V. Chernikov  *
161301290bcSAlexander V. Chernikov  *
162301290bcSAlexander V. Chernikov  *
163301290bcSAlexander V. Chernikov  * -flush_entry: flush entry state created by -prepare_add / -del / others
164301290bcSAlexander V. Chernikov  *  typedef void (ta_flush_entry)(struct ip_fw_chain *ch,
165301290bcSAlexander V. Chernikov  *      struct tentry_info *tei, void *ta_buf);
166301290bcSAlexander V. Chernikov  *  MANDATORY, may be locked. (M_NOWAIT).
167301290bcSAlexander V. Chernikov  *
168301290bcSAlexander V. Chernikov  *  Delete state allocated by:
169301290bcSAlexander V. Chernikov  *  -prepare_add (-add returned EEXIST|UPDATED)
170301290bcSAlexander V. Chernikov  *  -prepare_del (if any)
171301290bcSAlexander V. Chernikov  *  -del
172301290bcSAlexander V. Chernikov  *  * Caller is required to handle empty @ta_buf correctly.
173301290bcSAlexander V. Chernikov  *
174301290bcSAlexander V. Chernikov  *
175301290bcSAlexander V. Chernikov  * -find_tentry: finds entry specified by key @tei
176301290bcSAlexander V. Chernikov  *  typedef int ta_find_tentry(void *ta_state, struct table_info *ti,
177301290bcSAlexander V. Chernikov  *      ipfw_obj_tentry *tent);
178301290bcSAlexander V. Chernikov  *  OPTIONAL, locked (UH). (M_NOWAIT). Returns 0 on success.
179301290bcSAlexander V. Chernikov  *
180301290bcSAlexander V. Chernikov  *  Finds entry specified by given key.
181301290bcSAlexander V. Chernikov  *  * Caller is requred to do the following:
182301290bcSAlexander V. Chernikov  *    entry found: returns 0, export entry to @tent
183301290bcSAlexander V. Chernikov  *    entry not found: returns ENOENT
184301290bcSAlexander V. Chernikov  *
185301290bcSAlexander V. Chernikov  *
186301290bcSAlexander V. Chernikov  * -need_modify: checks if @ti has enough space to hold another @count items.
187301290bcSAlexander V. Chernikov  *  typedef int (ta_need_modify)(void *ta_state, struct table_info *ti,
188301290bcSAlexander V. Chernikov  *      uint32_t count, uint64_t *pflags);
189fd0869d5SAlexander V. Chernikov  *  OPTIONAL, locked (UH). (M_NOWAIT). Returns 0 if has.
190301290bcSAlexander V. Chernikov  *
191301290bcSAlexander V. Chernikov  *  Checks if given table has enough space to add @count items without
192301290bcSAlexander V. Chernikov  *  resize. Caller may use @pflags to store desired modification data.
193301290bcSAlexander V. Chernikov  *
194301290bcSAlexander V. Chernikov  *
195301290bcSAlexander V. Chernikov  *
196301290bcSAlexander V. Chernikov  * -prepare_mod: allocate structures for table modification.
197301290bcSAlexander V. Chernikov  *  typedef int (ta_prepare_mod)(void *ta_buf, uint64_t *pflags);
198fd0869d5SAlexander V. Chernikov  * OPTIONAL(need_modify), unlocked. (M_WAITOK). Returns 0 on success.
199301290bcSAlexander V. Chernikov  *
200301290bcSAlexander V. Chernikov  * Allocate all needed state for table modification. Caller
201301290bcSAlexander V. Chernikov  * should use `struct mod_item` to store new state in @ta_buf.
202301290bcSAlexander V. Chernikov  * Up to TA_BUF_SZ (128 bytes) can be stored in @ta_buf.
203301290bcSAlexander V. Chernikov  *
204301290bcSAlexander V. Chernikov  *
205301290bcSAlexander V. Chernikov  *
206301290bcSAlexander V. Chernikov  * -fill_mod: copy some data to new state/
207301290bcSAlexander V. Chernikov  *  typedef int (ta_fill_mod)(void *ta_state, struct table_info *ti,
208301290bcSAlexander V. Chernikov  *      void *ta_buf, uint64_t *pflags);
209fd0869d5SAlexander V. Chernikov  * OPTIONAL(need_modify), locked (UH). (M_NOWAIT). Returns 0 on success.
210301290bcSAlexander V. Chernikov  *
211301290bcSAlexander V. Chernikov  * Copy as much data as we can to minimize changes under WLOCK.
212301290bcSAlexander V. Chernikov  * For example, array can be merged inside this callback.
213301290bcSAlexander V. Chernikov  *
214301290bcSAlexander V. Chernikov  *
215301290bcSAlexander V. Chernikov  *
216301290bcSAlexander V. Chernikov  * -modify: perform final modification.
217301290bcSAlexander V. Chernikov  *  typedef void (ta_modify)(void *ta_state, struct table_info *ti,
218301290bcSAlexander V. Chernikov  *      void *ta_buf, uint64_t pflags);
219fd0869d5SAlexander V. Chernikov  * OPTIONAL(need_modify), locked (UH+WLOCK). (M_NOWAIT).
220301290bcSAlexander V. Chernikov  *
221301290bcSAlexander V. Chernikov  * Performs all changes necessary to switch to new structures.
222301290bcSAlexander V. Chernikov  * * Caller should save old pointers to @ta_buf storage.
223301290bcSAlexander V. Chernikov  *
224301290bcSAlexander V. Chernikov  *
225301290bcSAlexander V. Chernikov  *
226301290bcSAlexander V. Chernikov  * -flush_mod: flush table modification state.
227301290bcSAlexander V. Chernikov  *  typedef void (ta_flush_mod)(void *ta_buf);
228fd0869d5SAlexander V. Chernikov  * OPTIONAL(need_modify), unlocked. (M_WAITOK).
229301290bcSAlexander V. Chernikov  *
230301290bcSAlexander V. Chernikov  * Performs flush for the following:
231301290bcSAlexander V. Chernikov  *   - prepare_mod (modification was not necessary)
232301290bcSAlexander V. Chernikov  *   - modify (for the old state)
233301290bcSAlexander V. Chernikov  *
234301290bcSAlexander V. Chernikov  *
235301290bcSAlexander V. Chernikov  *
236301290bcSAlexander V. Chernikov  * -change_gi: monitor table info pointer changes
237301290bcSAlexander V. Chernikov  * typedef void (ta_change_ti)(void *ta_state, struct table_info *ti);
238301290bcSAlexander V. Chernikov  * OPTIONAL, locked (UH). (M_NOWAIT).
239301290bcSAlexander V. Chernikov  *
240301290bcSAlexander V. Chernikov  * Called on @ti pointer changed. Called immediately after -init
241301290bcSAlexander V. Chernikov  * to set initial state.
242301290bcSAlexander V. Chernikov  *
243301290bcSAlexander V. Chernikov  *
244301290bcSAlexander V. Chernikov  *
245301290bcSAlexander V. Chernikov  * -foreach: calls @f for each table entry
246301290bcSAlexander V. Chernikov  *  typedef void ta_foreach(void *ta_state, struct table_info *ti,
247301290bcSAlexander V. Chernikov  *      ta_foreach_f *f, void *arg);
248301290bcSAlexander V. Chernikov  * MANDATORY, locked(UH). (M_NOWAIT).
249301290bcSAlexander V. Chernikov  *
250301290bcSAlexander V. Chernikov  * Runs callback with specified argument for each table entry,
251301290bcSAlexander V. Chernikov  * Typically used for dumping table entries.
252301290bcSAlexander V. Chernikov  *
253301290bcSAlexander V. Chernikov  *
254301290bcSAlexander V. Chernikov  *
255301290bcSAlexander V. Chernikov  * -dump_tentry: dump table entry in current @tentry format.
256301290bcSAlexander V. Chernikov  *  typedef int ta_dump_tentry(void *ta_state, struct table_info *ti, void *e,
257301290bcSAlexander V. Chernikov  *      ipfw_obj_tentry *tent);
258301290bcSAlexander V. Chernikov  * MANDATORY, locked(UH). (M_NOWAIT). Returns 0 on success.
259301290bcSAlexander V. Chernikov  *
260301290bcSAlexander V. Chernikov  * Dumps entry @e to @tent.
261301290bcSAlexander V. Chernikov  *
262301290bcSAlexander V. Chernikov  *
263301290bcSAlexander V. Chernikov  * -print_config: prints custom algoritm options into buffer.
264301290bcSAlexander V. Chernikov  *  typedef void (ta_print_config)(void *ta_state, struct table_info *ti,
265301290bcSAlexander V. Chernikov  *      char *buf, size_t bufsize);
266301290bcSAlexander V. Chernikov  * OPTIONAL. locked(UH). (M_NOWAIT).
267301290bcSAlexander V. Chernikov  *
268301290bcSAlexander V. Chernikov  * Prints custom algorithm options in the format suitable to pass
269301290bcSAlexander V. Chernikov  * back to -init callback.
270301290bcSAlexander V. Chernikov  *
271301290bcSAlexander V. Chernikov  *
272301290bcSAlexander V. Chernikov  *
273301290bcSAlexander V. Chernikov  * -dump_tinfo: dumps algo-specific info.
274301290bcSAlexander V. Chernikov  *  typedef void ta_dump_tinfo(void *ta_state, struct table_info *ti,
275301290bcSAlexander V. Chernikov  *      ipfw_ta_tinfo *tinfo);
276301290bcSAlexander V. Chernikov  * OPTIONAL. locked(UH). (M_NOWAIT).
277301290bcSAlexander V. Chernikov  *
278301290bcSAlexander V. Chernikov  * Dumps options like items size/hash size, etc.
279301290bcSAlexander V. Chernikov  */
280301290bcSAlexander V. Chernikov 
281b1d105bcSAlexander V. Chernikov MALLOC_DEFINE(M_IPFW_TBL, "ipfw_tbl", "IpFw tables");
2829f7d47b0SAlexander V. Chernikov 
2830bce0c23SAlexander V. Chernikov /*
2840bce0c23SAlexander V. Chernikov  * Utility structures/functions common to more than one algo
2850bce0c23SAlexander V. Chernikov  */
2860bce0c23SAlexander V. Chernikov 
2870bce0c23SAlexander V. Chernikov struct mod_item {
2880bce0c23SAlexander V. Chernikov 	void	*main_ptr;
2890bce0c23SAlexander V. Chernikov 	size_t	size;
2900bce0c23SAlexander V. Chernikov 	void	*main_ptr6;
2910bce0c23SAlexander V. Chernikov 	size_t	size6;
2920bce0c23SAlexander V. Chernikov };
2930bce0c23SAlexander V. Chernikov 
29468394ec8SAlexander V. Chernikov static int badd(const void *key, void *item, void *base, size_t nmemb,
29568394ec8SAlexander V. Chernikov     size_t size, int (*compar) (const void *, const void *));
29668394ec8SAlexander V. Chernikov static int bdel(const void *key, void *base, size_t nmemb, size_t size,
29768394ec8SAlexander V. Chernikov     int (*compar) (const void *, const void *));
29868394ec8SAlexander V. Chernikov 
29968394ec8SAlexander V. Chernikov 
30068394ec8SAlexander V. Chernikov /*
301c21034b7SAlexander V. Chernikov  * ADDR implementation using radix
30268394ec8SAlexander V. Chernikov  *
30368394ec8SAlexander V. Chernikov  */
30468394ec8SAlexander V. Chernikov 
3059f7d47b0SAlexander V. Chernikov /*
3069f7d47b0SAlexander V. Chernikov  * The radix code expects addr and mask to be array of bytes,
3079f7d47b0SAlexander V. Chernikov  * with the first byte being the length of the array. rn_inithead
3089f7d47b0SAlexander V. Chernikov  * is called with the offset in bits of the lookup key within the
3099f7d47b0SAlexander V. Chernikov  * array. If we use a sockaddr_in as the underlying type,
3109f7d47b0SAlexander V. Chernikov  * sin_len is conveniently located at offset 0, sin_addr is at
3119f7d47b0SAlexander V. Chernikov  * offset 4 and normally aligned.
3129f7d47b0SAlexander V. Chernikov  * But for portability, let's avoid assumption and make the code explicit
3139f7d47b0SAlexander V. Chernikov  */
3149f7d47b0SAlexander V. Chernikov #define KEY_LEN(v)	*((uint8_t *)&(v))
3159f7d47b0SAlexander V. Chernikov /*
3169f7d47b0SAlexander V. Chernikov  * Do not require radix to compare more than actual IPv4/IPv6 address
3179f7d47b0SAlexander V. Chernikov  */
3189f7d47b0SAlexander V. Chernikov #define KEY_LEN_INET	(offsetof(struct sockaddr_in, sin_addr) + sizeof(in_addr_t))
319e0a8b9eeSAlexander V. Chernikov #define KEY_LEN_INET6	(offsetof(struct sa_in6, sin6_addr) + sizeof(struct in6_addr))
3209f7d47b0SAlexander V. Chernikov 
3219f7d47b0SAlexander V. Chernikov #define OFF_LEN_INET	(8 * offsetof(struct sockaddr_in, sin_addr))
322e0a8b9eeSAlexander V. Chernikov #define OFF_LEN_INET6	(8 * offsetof(struct sa_in6, sin6_addr))
3239f7d47b0SAlexander V. Chernikov 
324c21034b7SAlexander V. Chernikov struct radix_addr_entry {
3259f7d47b0SAlexander V. Chernikov 	struct radix_node	rn[2];
326e0a8b9eeSAlexander V. Chernikov 	struct sockaddr_in	addr;
327e0a8b9eeSAlexander V. Chernikov 	uint32_t		value;
328e0a8b9eeSAlexander V. Chernikov 	uint8_t			masklen;
329e0a8b9eeSAlexander V. Chernikov };
330e0a8b9eeSAlexander V. Chernikov 
331e0a8b9eeSAlexander V. Chernikov struct sa_in6 {
332e0a8b9eeSAlexander V. Chernikov 	uint8_t			sin6_len;
333e0a8b9eeSAlexander V. Chernikov 	uint8_t			sin6_family;
334e0a8b9eeSAlexander V. Chernikov 	uint8_t			pad[2];
335e0a8b9eeSAlexander V. Chernikov 	struct in6_addr		sin6_addr;
336e0a8b9eeSAlexander V. Chernikov };
337e0a8b9eeSAlexander V. Chernikov 
338c21034b7SAlexander V. Chernikov struct radix_addr_xentry {
339e0a8b9eeSAlexander V. Chernikov 	struct radix_node	rn[2];
340e0a8b9eeSAlexander V. Chernikov 	struct sa_in6		addr6;
341e0a8b9eeSAlexander V. Chernikov 	uint32_t		value;
342e0a8b9eeSAlexander V. Chernikov 	uint8_t			masklen;
3439f7d47b0SAlexander V. Chernikov };
3449f7d47b0SAlexander V. Chernikov 
3455f379342SAlexander V. Chernikov struct radix_cfg {
3465f379342SAlexander V. Chernikov 	struct radix_node_head	*head4;
3475f379342SAlexander V. Chernikov 	struct radix_node_head	*head6;
3485f379342SAlexander V. Chernikov 	size_t			count4;
3495f379342SAlexander V. Chernikov 	size_t			count6;
3505f379342SAlexander V. Chernikov };
3515f379342SAlexander V. Chernikov 
352c21034b7SAlexander V. Chernikov struct ta_buf_radix
3530bce0c23SAlexander V. Chernikov {
3540bce0c23SAlexander V. Chernikov 	void *ent_ptr;
3550bce0c23SAlexander V. Chernikov 	struct sockaddr	*addr_ptr;
3560bce0c23SAlexander V. Chernikov 	struct sockaddr	*mask_ptr;
3570bce0c23SAlexander V. Chernikov 	union {
3580bce0c23SAlexander V. Chernikov 		struct {
3590bce0c23SAlexander V. Chernikov 			struct sockaddr_in sa;
3600bce0c23SAlexander V. Chernikov 			struct sockaddr_in ma;
3610bce0c23SAlexander V. Chernikov 		} a4;
3620bce0c23SAlexander V. Chernikov 		struct {
3630bce0c23SAlexander V. Chernikov 			struct sa_in6 sa;
3640bce0c23SAlexander V. Chernikov 			struct sa_in6 ma;
3650bce0c23SAlexander V. Chernikov 		} a6;
3660bce0c23SAlexander V. Chernikov 	} addr;
3670bce0c23SAlexander V. Chernikov };
3680bce0c23SAlexander V. Chernikov 
3699f7d47b0SAlexander V. Chernikov static int
3709f7d47b0SAlexander V. Chernikov ta_lookup_radix(struct table_info *ti, void *key, uint32_t keylen,
3719f7d47b0SAlexander V. Chernikov     uint32_t *val)
3729f7d47b0SAlexander V. Chernikov {
3739f7d47b0SAlexander V. Chernikov 	struct radix_node_head *rnh;
3749f7d47b0SAlexander V. Chernikov 
3759f7d47b0SAlexander V. Chernikov 	if (keylen == sizeof(in_addr_t)) {
376c21034b7SAlexander V. Chernikov 		struct radix_addr_entry *ent;
3779f7d47b0SAlexander V. Chernikov 		struct sockaddr_in sa;
3789f7d47b0SAlexander V. Chernikov 		KEY_LEN(sa) = KEY_LEN_INET;
3799f7d47b0SAlexander V. Chernikov 		sa.sin_addr.s_addr = *((in_addr_t *)key);
3809f7d47b0SAlexander V. Chernikov 		rnh = (struct radix_node_head *)ti->state;
381c21034b7SAlexander V. Chernikov 		ent = (struct radix_addr_entry *)(rnh->rnh_matchaddr(&sa, rnh));
3829f7d47b0SAlexander V. Chernikov 		if (ent != NULL) {
3839f7d47b0SAlexander V. Chernikov 			*val = ent->value;
3849f7d47b0SAlexander V. Chernikov 			return (1);
3859f7d47b0SAlexander V. Chernikov 		}
3869f7d47b0SAlexander V. Chernikov 	} else {
387c21034b7SAlexander V. Chernikov 		struct radix_addr_xentry *xent;
388e0a8b9eeSAlexander V. Chernikov 		struct sa_in6 sa6;
3899f7d47b0SAlexander V. Chernikov 		KEY_LEN(sa6) = KEY_LEN_INET6;
3909f7d47b0SAlexander V. Chernikov 		memcpy(&sa6.sin6_addr, key, sizeof(struct in6_addr));
3919f7d47b0SAlexander V. Chernikov 		rnh = (struct radix_node_head *)ti->xstate;
392c21034b7SAlexander V. Chernikov 		xent = (struct radix_addr_xentry *)(rnh->rnh_matchaddr(&sa6, rnh));
3939f7d47b0SAlexander V. Chernikov 		if (xent != NULL) {
3949f7d47b0SAlexander V. Chernikov 			*val = xent->value;
3959f7d47b0SAlexander V. Chernikov 			return (1);
3969f7d47b0SAlexander V. Chernikov 		}
3979f7d47b0SAlexander V. Chernikov 	}
3989f7d47b0SAlexander V. Chernikov 
3999f7d47b0SAlexander V. Chernikov 	return (0);
4009f7d47b0SAlexander V. Chernikov }
4019f7d47b0SAlexander V. Chernikov 
4029f7d47b0SAlexander V. Chernikov /*
4039f7d47b0SAlexander V. Chernikov  * New table
4049f7d47b0SAlexander V. Chernikov  */
4059f7d47b0SAlexander V. Chernikov static int
40668394ec8SAlexander V. Chernikov ta_init_radix(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
407914bffb6SAlexander V. Chernikov     char *data, uint8_t tflags)
4089f7d47b0SAlexander V. Chernikov {
4095f379342SAlexander V. Chernikov 	struct radix_cfg *cfg;
4109f7d47b0SAlexander V. Chernikov 
4119f7d47b0SAlexander V. Chernikov 	if (!rn_inithead(&ti->state, OFF_LEN_INET))
4129f7d47b0SAlexander V. Chernikov 		return (ENOMEM);
4139f7d47b0SAlexander V. Chernikov 	if (!rn_inithead(&ti->xstate, OFF_LEN_INET6)) {
4149f7d47b0SAlexander V. Chernikov 		rn_detachhead(&ti->state);
4159f7d47b0SAlexander V. Chernikov 		return (ENOMEM);
4169f7d47b0SAlexander V. Chernikov 	}
4179f7d47b0SAlexander V. Chernikov 
4185f379342SAlexander V. Chernikov 	cfg = malloc(sizeof(struct radix_cfg), M_IPFW, M_WAITOK | M_ZERO);
4195f379342SAlexander V. Chernikov 
4205f379342SAlexander V. Chernikov 	*ta_state = cfg;
4219f7d47b0SAlexander V. Chernikov 	ti->lookup = ta_lookup_radix;
4229f7d47b0SAlexander V. Chernikov 
4239f7d47b0SAlexander V. Chernikov 	return (0);
4249f7d47b0SAlexander V. Chernikov }
4259f7d47b0SAlexander V. Chernikov 
4269f7d47b0SAlexander V. Chernikov static int
427a399f8beSAlexander V. Chernikov flush_radix_entry(struct radix_node *rn, void *arg)
4289f7d47b0SAlexander V. Chernikov {
4299f7d47b0SAlexander V. Chernikov 	struct radix_node_head * const rnh = arg;
430c21034b7SAlexander V. Chernikov 	struct radix_addr_entry *ent;
4319f7d47b0SAlexander V. Chernikov 
432c21034b7SAlexander V. Chernikov 	ent = (struct radix_addr_entry *)
4339f7d47b0SAlexander V. Chernikov 	    rnh->rnh_deladdr(rn->rn_key, rn->rn_mask, rnh);
4349f7d47b0SAlexander V. Chernikov 	if (ent != NULL)
4359f7d47b0SAlexander V. Chernikov 		free(ent, M_IPFW_TBL);
4369f7d47b0SAlexander V. Chernikov 	return (0);
4379f7d47b0SAlexander V. Chernikov }
4389f7d47b0SAlexander V. Chernikov 
4399f7d47b0SAlexander V. Chernikov static void
4409f7d47b0SAlexander V. Chernikov ta_destroy_radix(void *ta_state, struct table_info *ti)
4419f7d47b0SAlexander V. Chernikov {
4425f379342SAlexander V. Chernikov 	struct radix_cfg *cfg;
4439f7d47b0SAlexander V. Chernikov 	struct radix_node_head *rnh;
4449f7d47b0SAlexander V. Chernikov 
4455f379342SAlexander V. Chernikov 	cfg = (struct radix_cfg *)ta_state;
4465f379342SAlexander V. Chernikov 
4479f7d47b0SAlexander V. Chernikov 	rnh = (struct radix_node_head *)(ti->state);
448a399f8beSAlexander V. Chernikov 	rnh->rnh_walktree(rnh, flush_radix_entry, rnh);
4499f7d47b0SAlexander V. Chernikov 	rn_detachhead(&ti->state);
4509f7d47b0SAlexander V. Chernikov 
4519f7d47b0SAlexander V. Chernikov 	rnh = (struct radix_node_head *)(ti->xstate);
452a399f8beSAlexander V. Chernikov 	rnh->rnh_walktree(rnh, flush_radix_entry, rnh);
4539f7d47b0SAlexander V. Chernikov 	rn_detachhead(&ti->xstate);
4545f379342SAlexander V. Chernikov 
4555f379342SAlexander V. Chernikov 	free(cfg, M_IPFW);
4565f379342SAlexander V. Chernikov }
4575f379342SAlexander V. Chernikov 
4585f379342SAlexander V. Chernikov /*
4595f379342SAlexander V. Chernikov  * Provide algo-specific table info
4605f379342SAlexander V. Chernikov  */
4615f379342SAlexander V. Chernikov static void
4625f379342SAlexander V. Chernikov ta_dump_radix_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo)
4635f379342SAlexander V. Chernikov {
4645f379342SAlexander V. Chernikov 	struct radix_cfg *cfg;
4655f379342SAlexander V. Chernikov 
4665f379342SAlexander V. Chernikov 	cfg = (struct radix_cfg *)ta_state;
4675f379342SAlexander V. Chernikov 
4685f379342SAlexander V. Chernikov 	tinfo->flags = IPFW_TATFLAGS_AFDATA | IPFW_TATFLAGS_AFITEM;
4695f379342SAlexander V. Chernikov 	tinfo->taclass4 = IPFW_TACLASS_RADIX;
4705f379342SAlexander V. Chernikov 	tinfo->count4 = cfg->count4;
471c21034b7SAlexander V. Chernikov 	tinfo->itemsize4 = sizeof(struct radix_addr_entry);
4725f379342SAlexander V. Chernikov 	tinfo->taclass6 = IPFW_TACLASS_RADIX;
4735f379342SAlexander V. Chernikov 	tinfo->count6 = cfg->count6;
474c21034b7SAlexander V. Chernikov 	tinfo->itemsize6 = sizeof(struct radix_addr_xentry);
4759f7d47b0SAlexander V. Chernikov }
4769f7d47b0SAlexander V. Chernikov 
4779f7d47b0SAlexander V. Chernikov static int
47881d3153dSAlexander V. Chernikov ta_dump_radix_tentry(void *ta_state, struct table_info *ti, void *e,
47981d3153dSAlexander V. Chernikov     ipfw_obj_tentry *tent)
4809f7d47b0SAlexander V. Chernikov {
481c21034b7SAlexander V. Chernikov 	struct radix_addr_entry *n;
482c21034b7SAlexander V. Chernikov 	struct radix_addr_xentry *xn;
4839f7d47b0SAlexander V. Chernikov 
484c21034b7SAlexander V. Chernikov 	n = (struct radix_addr_entry *)e;
4859f7d47b0SAlexander V. Chernikov 
4869f7d47b0SAlexander V. Chernikov 	/* Guess IPv4/IPv6 radix by sockaddr family */
4879f7d47b0SAlexander V. Chernikov 	if (n->addr.sin_family == AF_INET) {
48881d3153dSAlexander V. Chernikov 		tent->k.addr.s_addr = n->addr.sin_addr.s_addr;
489e0a8b9eeSAlexander V. Chernikov 		tent->masklen = n->masklen;
49081d3153dSAlexander V. Chernikov 		tent->subtype = AF_INET;
4910cba2b28SAlexander V. Chernikov 		tent->v.kidx = n->value;
4929f7d47b0SAlexander V. Chernikov #ifdef INET6
4939f7d47b0SAlexander V. Chernikov 	} else {
494c21034b7SAlexander V. Chernikov 		xn = (struct radix_addr_xentry *)e;
495e0a8b9eeSAlexander V. Chernikov 		memcpy(&tent->k, &xn->addr6.sin6_addr, sizeof(struct in6_addr));
496e0a8b9eeSAlexander V. Chernikov 		tent->masklen = xn->masklen;
49781d3153dSAlexander V. Chernikov 		tent->subtype = AF_INET6;
4980cba2b28SAlexander V. Chernikov 		tent->v.kidx = xn->value;
4999f7d47b0SAlexander V. Chernikov #endif
5009f7d47b0SAlexander V. Chernikov 	}
5019f7d47b0SAlexander V. Chernikov 
5029f7d47b0SAlexander V. Chernikov 	return (0);
5039f7d47b0SAlexander V. Chernikov }
5049f7d47b0SAlexander V. Chernikov 
50581d3153dSAlexander V. Chernikov static int
506914bffb6SAlexander V. Chernikov ta_find_radix_tentry(void *ta_state, struct table_info *ti,
507914bffb6SAlexander V. Chernikov     ipfw_obj_tentry *tent)
50881d3153dSAlexander V. Chernikov {
50981d3153dSAlexander V. Chernikov 	struct radix_node_head *rnh;
51081d3153dSAlexander V. Chernikov 	void *e;
51181d3153dSAlexander V. Chernikov 
51281d3153dSAlexander V. Chernikov 	e = NULL;
513914bffb6SAlexander V. Chernikov 	if (tent->subtype == AF_INET) {
51481d3153dSAlexander V. Chernikov 		struct sockaddr_in sa;
51581d3153dSAlexander V. Chernikov 		KEY_LEN(sa) = KEY_LEN_INET;
516914bffb6SAlexander V. Chernikov 		sa.sin_addr.s_addr = tent->k.addr.s_addr;
51781d3153dSAlexander V. Chernikov 		rnh = (struct radix_node_head *)ti->state;
51881d3153dSAlexander V. Chernikov 		e = rnh->rnh_matchaddr(&sa, rnh);
51981d3153dSAlexander V. Chernikov 	} else {
520e0a8b9eeSAlexander V. Chernikov 		struct sa_in6 sa6;
52181d3153dSAlexander V. Chernikov 		KEY_LEN(sa6) = KEY_LEN_INET6;
522914bffb6SAlexander V. Chernikov 		memcpy(&sa6.sin6_addr, &tent->k.addr6, sizeof(struct in6_addr));
52381d3153dSAlexander V. Chernikov 		rnh = (struct radix_node_head *)ti->xstate;
52481d3153dSAlexander V. Chernikov 		e = rnh->rnh_matchaddr(&sa6, rnh);
52581d3153dSAlexander V. Chernikov 	}
52681d3153dSAlexander V. Chernikov 
52781d3153dSAlexander V. Chernikov 	if (e != NULL) {
52881d3153dSAlexander V. Chernikov 		ta_dump_radix_tentry(ta_state, ti, e, tent);
52981d3153dSAlexander V. Chernikov 		return (0);
53081d3153dSAlexander V. Chernikov 	}
53181d3153dSAlexander V. Chernikov 
53281d3153dSAlexander V. Chernikov 	return (ENOENT);
53381d3153dSAlexander V. Chernikov }
53481d3153dSAlexander V. Chernikov 
5359f7d47b0SAlexander V. Chernikov static void
5369f7d47b0SAlexander V. Chernikov ta_foreach_radix(void *ta_state, struct table_info *ti, ta_foreach_f *f,
5379f7d47b0SAlexander V. Chernikov     void *arg)
5389f7d47b0SAlexander V. Chernikov {
5399f7d47b0SAlexander V. Chernikov 	struct radix_node_head *rnh;
5409f7d47b0SAlexander V. Chernikov 
5419f7d47b0SAlexander V. Chernikov 	rnh = (struct radix_node_head *)(ti->state);
5429f7d47b0SAlexander V. Chernikov 	rnh->rnh_walktree(rnh, (walktree_f_t *)f, arg);
5439f7d47b0SAlexander V. Chernikov 
5449f7d47b0SAlexander V. Chernikov 	rnh = (struct radix_node_head *)(ti->xstate);
5459f7d47b0SAlexander V. Chernikov 	rnh->rnh_walktree(rnh, (walktree_f_t *)f, arg);
5469f7d47b0SAlexander V. Chernikov }
5479f7d47b0SAlexander V. Chernikov 
5489f7d47b0SAlexander V. Chernikov 
5499f7d47b0SAlexander V. Chernikov #ifdef INET6
5509f7d47b0SAlexander V. Chernikov static inline void
5519f7d47b0SAlexander V. Chernikov ipv6_writemask(struct in6_addr *addr6, uint8_t mask)
5529f7d47b0SAlexander V. Chernikov {
5539f7d47b0SAlexander V. Chernikov 	uint32_t *cp;
5549f7d47b0SAlexander V. Chernikov 
5559f7d47b0SAlexander V. Chernikov 	for (cp = (uint32_t *)addr6; mask >= 32; mask -= 32)
5569f7d47b0SAlexander V. Chernikov 		*cp++ = 0xFFFFFFFF;
5579f7d47b0SAlexander V. Chernikov 	*cp = htonl(mask ? ~((1 << (32 - mask)) - 1) : 0);
5589f7d47b0SAlexander V. Chernikov }
5599f7d47b0SAlexander V. Chernikov #endif
5609f7d47b0SAlexander V. Chernikov 
5612e324d29SAlexander V. Chernikov static void
5622e324d29SAlexander V. Chernikov tei_to_sockaddr_ent(struct tentry_info *tei, struct sockaddr *sa,
5632e324d29SAlexander V. Chernikov     struct sockaddr *ma, int *set_mask)
5642e324d29SAlexander V. Chernikov {
5652e324d29SAlexander V. Chernikov 	int mlen;
5662e324d29SAlexander V. Chernikov 	struct sockaddr_in *addr, *mask;
567720ee730SAlexander V. Chernikov 	struct sa_in6 *addr6, *mask6;
5682e324d29SAlexander V. Chernikov 	in_addr_t a4;
5692e324d29SAlexander V. Chernikov 
5702e324d29SAlexander V. Chernikov 	mlen = tei->masklen;
5712e324d29SAlexander V. Chernikov 
5722e324d29SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
5732e324d29SAlexander V. Chernikov #ifdef INET
5742e324d29SAlexander V. Chernikov 		addr = (struct sockaddr_in *)sa;
5752e324d29SAlexander V. Chernikov 		mask = (struct sockaddr_in *)ma;
5762e324d29SAlexander V. Chernikov 		/* Set 'total' structure length */
5772e324d29SAlexander V. Chernikov 		KEY_LEN(*addr) = KEY_LEN_INET;
5782e324d29SAlexander V. Chernikov 		KEY_LEN(*mask) = KEY_LEN_INET;
5792e324d29SAlexander V. Chernikov 		addr->sin_family = AF_INET;
5802e324d29SAlexander V. Chernikov 		mask->sin_addr.s_addr =
5812e324d29SAlexander V. Chernikov 		    htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);
5822e324d29SAlexander V. Chernikov 		a4 = *((in_addr_t *)tei->paddr);
5832e324d29SAlexander V. Chernikov 		addr->sin_addr.s_addr = a4 & mask->sin_addr.s_addr;
5842e324d29SAlexander V. Chernikov 		if (mlen != 32)
5852e324d29SAlexander V. Chernikov 			*set_mask = 1;
5862e324d29SAlexander V. Chernikov 		else
5872e324d29SAlexander V. Chernikov 			*set_mask = 0;
5882e324d29SAlexander V. Chernikov #endif
5892e324d29SAlexander V. Chernikov #ifdef INET6
5902e324d29SAlexander V. Chernikov 	} else if (tei->subtype == AF_INET6) {
5912e324d29SAlexander V. Chernikov 		/* IPv6 case */
592720ee730SAlexander V. Chernikov 		addr6 = (struct sa_in6 *)sa;
593720ee730SAlexander V. Chernikov 		mask6 = (struct sa_in6 *)ma;
5942e324d29SAlexander V. Chernikov 		/* Set 'total' structure length */
5952e324d29SAlexander V. Chernikov 		KEY_LEN(*addr6) = KEY_LEN_INET6;
5962e324d29SAlexander V. Chernikov 		KEY_LEN(*mask6) = KEY_LEN_INET6;
5972e324d29SAlexander V. Chernikov 		addr6->sin6_family = AF_INET6;
5982e324d29SAlexander V. Chernikov 		ipv6_writemask(&mask6->sin6_addr, mlen);
5992e324d29SAlexander V. Chernikov 		memcpy(&addr6->sin6_addr, tei->paddr, sizeof(struct in6_addr));
6002e324d29SAlexander V. Chernikov 		APPLY_MASK(&addr6->sin6_addr, &mask6->sin6_addr);
6012e324d29SAlexander V. Chernikov 		if (mlen != 128)
6022e324d29SAlexander V. Chernikov 			*set_mask = 1;
6032e324d29SAlexander V. Chernikov 		else
6042e324d29SAlexander V. Chernikov 			*set_mask = 0;
6052e324d29SAlexander V. Chernikov 	}
6062e324d29SAlexander V. Chernikov #endif
6072e324d29SAlexander V. Chernikov }
6089f7d47b0SAlexander V. Chernikov 
6099f7d47b0SAlexander V. Chernikov static int
610a399f8beSAlexander V. Chernikov ta_prepare_add_radix(struct ip_fw_chain *ch, struct tentry_info *tei,
61168394ec8SAlexander V. Chernikov     void *ta_buf)
6129f7d47b0SAlexander V. Chernikov {
613c21034b7SAlexander V. Chernikov 	struct ta_buf_radix *tb;
614c21034b7SAlexander V. Chernikov 	struct radix_addr_entry *ent;
615c21034b7SAlexander V. Chernikov 	struct radix_addr_xentry *xent;
6162e324d29SAlexander V. Chernikov 	struct sockaddr *addr, *mask;
6172e324d29SAlexander V. Chernikov 	int mlen, set_mask;
6189f7d47b0SAlexander V. Chernikov 
619c21034b7SAlexander V. Chernikov 	tb = (struct ta_buf_radix *)ta_buf;
6209f7d47b0SAlexander V. Chernikov 
6219f7d47b0SAlexander V. Chernikov 	mlen = tei->masklen;
6222e324d29SAlexander V. Chernikov 	set_mask = 0;
6239f7d47b0SAlexander V. Chernikov 
624ac35ff17SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
6259f7d47b0SAlexander V. Chernikov #ifdef INET
6269f7d47b0SAlexander V. Chernikov 		if (mlen > 32)
6279f7d47b0SAlexander V. Chernikov 			return (EINVAL);
6289f7d47b0SAlexander V. Chernikov 		ent = malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO);
629e0a8b9eeSAlexander V. Chernikov 		ent->masklen = mlen;
6302e324d29SAlexander V. Chernikov 
6312e324d29SAlexander V. Chernikov 		addr = (struct sockaddr *)&ent->addr;
6322e324d29SAlexander V. Chernikov 		mask = (struct sockaddr *)&tb->addr.a4.ma;
6339f7d47b0SAlexander V. Chernikov 		tb->ent_ptr = ent;
6349f7d47b0SAlexander V. Chernikov #endif
6359f7d47b0SAlexander V. Chernikov #ifdef INET6
636ac35ff17SAlexander V. Chernikov 	} else if (tei->subtype == AF_INET6) {
6379f7d47b0SAlexander V. Chernikov 		/* IPv6 case */
6389f7d47b0SAlexander V. Chernikov 		if (mlen > 128)
6399f7d47b0SAlexander V. Chernikov 			return (EINVAL);
6409f7d47b0SAlexander V. Chernikov 		xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO);
641e0a8b9eeSAlexander V. Chernikov 		xent->masklen = mlen;
6422e324d29SAlexander V. Chernikov 
6432e324d29SAlexander V. Chernikov 		addr = (struct sockaddr *)&xent->addr6;
6442e324d29SAlexander V. Chernikov 		mask = (struct sockaddr *)&tb->addr.a6.ma;
6459f7d47b0SAlexander V. Chernikov 		tb->ent_ptr = xent;
6469f7d47b0SAlexander V. Chernikov #endif
6479f7d47b0SAlexander V. Chernikov 	} else {
6489f7d47b0SAlexander V. Chernikov 		/* Unknown CIDR type */
6499f7d47b0SAlexander V. Chernikov 		return (EINVAL);
6509f7d47b0SAlexander V. Chernikov 	}
6519f7d47b0SAlexander V. Chernikov 
6522e324d29SAlexander V. Chernikov 	tei_to_sockaddr_ent(tei, addr, mask, &set_mask);
6532e324d29SAlexander V. Chernikov 	/* Set pointers */
6542e324d29SAlexander V. Chernikov 	tb->addr_ptr = addr;
6552e324d29SAlexander V. Chernikov 	if (set_mask != 0)
6562e324d29SAlexander V. Chernikov 		tb->mask_ptr = mask;
6572e324d29SAlexander V. Chernikov 
6589f7d47b0SAlexander V. Chernikov 	return (0);
6599f7d47b0SAlexander V. Chernikov }
6609f7d47b0SAlexander V. Chernikov 
6619f7d47b0SAlexander V. Chernikov static int
662a399f8beSAlexander V. Chernikov ta_add_radix(void *ta_state, struct table_info *ti, struct tentry_info *tei,
663b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
6649f7d47b0SAlexander V. Chernikov {
6655f379342SAlexander V. Chernikov 	struct radix_cfg *cfg;
6669f7d47b0SAlexander V. Chernikov 	struct radix_node_head *rnh;
6679f7d47b0SAlexander V. Chernikov 	struct radix_node *rn;
668c21034b7SAlexander V. Chernikov 	struct ta_buf_radix *tb;
669648e8380SAlexander V. Chernikov 	uint32_t *old_value, value;
6709f7d47b0SAlexander V. Chernikov 
6715f379342SAlexander V. Chernikov 	cfg = (struct radix_cfg *)ta_state;
672c21034b7SAlexander V. Chernikov 	tb = (struct ta_buf_radix *)ta_buf;
6739f7d47b0SAlexander V. Chernikov 
67413263632SAlexander V. Chernikov 	/* Save current entry value from @tei */
67513263632SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
6769f7d47b0SAlexander V. Chernikov 		rnh = ti->state;
67713263632SAlexander V. Chernikov 		((struct radix_addr_entry *)tb->ent_ptr)->value = tei->value;
67813263632SAlexander V. Chernikov 	} else {
6799f7d47b0SAlexander V. Chernikov 		rnh = ti->xstate;
68013263632SAlexander V. Chernikov 		((struct radix_addr_xentry *)tb->ent_ptr)->value = tei->value;
68113263632SAlexander V. Chernikov 	}
6829f7d47b0SAlexander V. Chernikov 
6834c0c07a5SAlexander V. Chernikov 	/* Search for an entry first */
6844c0c07a5SAlexander V. Chernikov 	rn = rnh->rnh_lookup(tb->addr_ptr, tb->mask_ptr, rnh);
6854c0c07a5SAlexander V. Chernikov 	if (rn != NULL) {
686ac35ff17SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
6879f7d47b0SAlexander V. Chernikov 			return (EEXIST);
688ac35ff17SAlexander V. Chernikov 		/* Record already exists. Update value if we're asked to */
689648e8380SAlexander V. Chernikov 		if (tei->subtype == AF_INET)
690c21034b7SAlexander V. Chernikov 			old_value = &((struct radix_addr_entry *)rn)->value;
691648e8380SAlexander V. Chernikov 		else
692c21034b7SAlexander V. Chernikov 			old_value = &((struct radix_addr_xentry *)rn)->value;
693648e8380SAlexander V. Chernikov 
694648e8380SAlexander V. Chernikov 		value = *old_value;
695648e8380SAlexander V. Chernikov 		*old_value = tei->value;
696648e8380SAlexander V. Chernikov 		tei->value = value;
697ac35ff17SAlexander V. Chernikov 
698ac35ff17SAlexander V. Chernikov 		/* Indicate that update has happened instead of addition */
699ac35ff17SAlexander V. Chernikov 		tei->flags |= TEI_FLAGS_UPDATED;
700adea6201SAlexander V. Chernikov 		*pnum = 0;
701e0a8b9eeSAlexander V. Chernikov 
702e0a8b9eeSAlexander V. Chernikov 		return (0);
703ac35ff17SAlexander V. Chernikov 	}
704ac35ff17SAlexander V. Chernikov 
7054c0c07a5SAlexander V. Chernikov 	if ((tei->flags & TEI_FLAGS_DONTADD) != 0)
7064c0c07a5SAlexander V. Chernikov 		return (EFBIG);
7074c0c07a5SAlexander V. Chernikov 
7084c0c07a5SAlexander V. Chernikov 	rn = rnh->rnh_addaddr(tb->addr_ptr, tb->mask_ptr, rnh, tb->ent_ptr);
7094c0c07a5SAlexander V. Chernikov 	if (rn == NULL) {
7104c0c07a5SAlexander V. Chernikov 		/* Unknown error */
7114c0c07a5SAlexander V. Chernikov 		return (EINVAL);
7124c0c07a5SAlexander V. Chernikov 	}
7134c0c07a5SAlexander V. Chernikov 
7145f379342SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
7155f379342SAlexander V. Chernikov 		cfg->count4++;
7165f379342SAlexander V. Chernikov 	else
7175f379342SAlexander V. Chernikov 		cfg->count6++;
718ac35ff17SAlexander V. Chernikov 	tb->ent_ptr = NULL;
719adea6201SAlexander V. Chernikov 	*pnum = 1;
7209f7d47b0SAlexander V. Chernikov 
7219f7d47b0SAlexander V. Chernikov 	return (0);
7229f7d47b0SAlexander V. Chernikov }
7239f7d47b0SAlexander V. Chernikov 
7249f7d47b0SAlexander V. Chernikov static int
725a399f8beSAlexander V. Chernikov ta_prepare_del_radix(struct ip_fw_chain *ch, struct tentry_info *tei,
72668394ec8SAlexander V. Chernikov     void *ta_buf)
7279f7d47b0SAlexander V. Chernikov {
728c21034b7SAlexander V. Chernikov 	struct ta_buf_radix *tb;
7292e324d29SAlexander V. Chernikov 	struct sockaddr *addr, *mask;
7302e324d29SAlexander V. Chernikov 	int mlen, set_mask;
7319f7d47b0SAlexander V. Chernikov 
732c21034b7SAlexander V. Chernikov 	tb = (struct ta_buf_radix *)ta_buf;
7339f7d47b0SAlexander V. Chernikov 
7349f7d47b0SAlexander V. Chernikov 	mlen = tei->masklen;
7352e324d29SAlexander V. Chernikov 	set_mask = 0;
7369f7d47b0SAlexander V. Chernikov 
737ac35ff17SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
738e0a8b9eeSAlexander V. Chernikov 		if (mlen > 32)
739e0a8b9eeSAlexander V. Chernikov 			return (EINVAL);
7402e324d29SAlexander V. Chernikov 
7412e324d29SAlexander V. Chernikov 		addr = (struct sockaddr *)&tb->addr.a4.sa;
7422e324d29SAlexander V. Chernikov 		mask = (struct sockaddr *)&tb->addr.a4.ma;
7439f7d47b0SAlexander V. Chernikov #ifdef INET6
744ac35ff17SAlexander V. Chernikov 	} else if (tei->subtype == AF_INET6) {
7459f7d47b0SAlexander V. Chernikov 		if (mlen > 128)
7469f7d47b0SAlexander V. Chernikov 			return (EINVAL);
7472e324d29SAlexander V. Chernikov 
7482e324d29SAlexander V. Chernikov 		addr = (struct sockaddr *)&tb->addr.a6.sa;
7492e324d29SAlexander V. Chernikov 		mask = (struct sockaddr *)&tb->addr.a6.ma;
7509f7d47b0SAlexander V. Chernikov #endif
7519f7d47b0SAlexander V. Chernikov 	} else
7529f7d47b0SAlexander V. Chernikov 		return (EINVAL);
7539f7d47b0SAlexander V. Chernikov 
7542e324d29SAlexander V. Chernikov 	tei_to_sockaddr_ent(tei, addr, mask, &set_mask);
7552e324d29SAlexander V. Chernikov 	tb->addr_ptr = addr;
7562e324d29SAlexander V. Chernikov 	if (set_mask != 0)
7572e324d29SAlexander V. Chernikov 		tb->mask_ptr = mask;
7582e324d29SAlexander V. Chernikov 
7599f7d47b0SAlexander V. Chernikov 	return (0);
7609f7d47b0SAlexander V. Chernikov }
7619f7d47b0SAlexander V. Chernikov 
7629f7d47b0SAlexander V. Chernikov static int
763a399f8beSAlexander V. Chernikov ta_del_radix(void *ta_state, struct table_info *ti, struct tentry_info *tei,
764b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
7659f7d47b0SAlexander V. Chernikov {
7665f379342SAlexander V. Chernikov 	struct radix_cfg *cfg;
7679f7d47b0SAlexander V. Chernikov 	struct radix_node_head *rnh;
7689f7d47b0SAlexander V. Chernikov 	struct radix_node *rn;
769c21034b7SAlexander V. Chernikov 	struct ta_buf_radix *tb;
7709f7d47b0SAlexander V. Chernikov 
7715f379342SAlexander V. Chernikov 	cfg = (struct radix_cfg *)ta_state;
772c21034b7SAlexander V. Chernikov 	tb = (struct ta_buf_radix *)ta_buf;
7739f7d47b0SAlexander V. Chernikov 
774ac35ff17SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
7759f7d47b0SAlexander V. Chernikov 		rnh = ti->state;
7769f7d47b0SAlexander V. Chernikov 	else
7779f7d47b0SAlexander V. Chernikov 		rnh = ti->xstate;
7789f7d47b0SAlexander V. Chernikov 
7799f7d47b0SAlexander V. Chernikov 	rn = rnh->rnh_deladdr(tb->addr_ptr, tb->mask_ptr, rnh);
7809f7d47b0SAlexander V. Chernikov 
7813a845e10SAlexander V. Chernikov 	if (rn == NULL)
7823a845e10SAlexander V. Chernikov 		return (ENOENT);
7833a845e10SAlexander V. Chernikov 
784648e8380SAlexander V. Chernikov 	/* Save entry value to @tei */
785648e8380SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
786c21034b7SAlexander V. Chernikov 		tei->value = ((struct radix_addr_entry *)rn)->value;
787648e8380SAlexander V. Chernikov 	else
788c21034b7SAlexander V. Chernikov 		tei->value = ((struct radix_addr_xentry *)rn)->value;
789648e8380SAlexander V. Chernikov 
7909f7d47b0SAlexander V. Chernikov 	tb->ent_ptr = rn;
7919f7d47b0SAlexander V. Chernikov 
7925f379342SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
7935f379342SAlexander V. Chernikov 		cfg->count4--;
7945f379342SAlexander V. Chernikov 	else
7955f379342SAlexander V. Chernikov 		cfg->count6--;
796adea6201SAlexander V. Chernikov 	*pnum = 1;
797adea6201SAlexander V. Chernikov 
7989f7d47b0SAlexander V. Chernikov 	return (0);
7999f7d47b0SAlexander V. Chernikov }
8009f7d47b0SAlexander V. Chernikov 
8019f7d47b0SAlexander V. Chernikov static void
802a399f8beSAlexander V. Chernikov ta_flush_radix_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
80368394ec8SAlexander V. Chernikov     void *ta_buf)
8049f7d47b0SAlexander V. Chernikov {
805c21034b7SAlexander V. Chernikov 	struct ta_buf_radix *tb;
8069f7d47b0SAlexander V. Chernikov 
807c21034b7SAlexander V. Chernikov 	tb = (struct ta_buf_radix *)ta_buf;
8089f7d47b0SAlexander V. Chernikov 
809ac35ff17SAlexander V. Chernikov 	if (tb->ent_ptr != NULL)
8109f7d47b0SAlexander V. Chernikov 		free(tb->ent_ptr, M_IPFW_TBL);
8119f7d47b0SAlexander V. Chernikov }
8129f7d47b0SAlexander V. Chernikov 
813b6ee846eSAlexander V. Chernikov static int
814301290bcSAlexander V. Chernikov ta_need_modify_radix(void *ta_state, struct table_info *ti, uint32_t count,
815b6ee846eSAlexander V. Chernikov     uint64_t *pflags)
816b6ee846eSAlexander V. Chernikov {
817b6ee846eSAlexander V. Chernikov 
818b6ee846eSAlexander V. Chernikov 	/*
8190bce0c23SAlexander V. Chernikov 	 * radix does not require additional memory allocations
820b6ee846eSAlexander V. Chernikov 	 * other than nodes itself. Adding new masks to the tree do
821b6ee846eSAlexander V. Chernikov 	 * but we don't have any API to call (and we don't known which
822b6ee846eSAlexander V. Chernikov 	 * sizes do we need).
823b6ee846eSAlexander V. Chernikov 	 */
824301290bcSAlexander V. Chernikov 	return (0);
825b6ee846eSAlexander V. Chernikov }
826b6ee846eSAlexander V. Chernikov 
827c21034b7SAlexander V. Chernikov struct table_algo addr_radix = {
828c21034b7SAlexander V. Chernikov 	.name		= "addr:radix",
829c21034b7SAlexander V. Chernikov 	.type		= IPFW_TABLE_ADDR,
83057a1cf95SAlexander V. Chernikov 	.flags		= TA_FLAG_DEFAULT,
831c21034b7SAlexander V. Chernikov 	.ta_buf_size	= sizeof(struct ta_buf_radix),
8329f7d47b0SAlexander V. Chernikov 	.init		= ta_init_radix,
8339f7d47b0SAlexander V. Chernikov 	.destroy	= ta_destroy_radix,
834a399f8beSAlexander V. Chernikov 	.prepare_add	= ta_prepare_add_radix,
835a399f8beSAlexander V. Chernikov 	.prepare_del	= ta_prepare_del_radix,
836a399f8beSAlexander V. Chernikov 	.add		= ta_add_radix,
837a399f8beSAlexander V. Chernikov 	.del		= ta_del_radix,
838a399f8beSAlexander V. Chernikov 	.flush_entry	= ta_flush_radix_entry,
8399f7d47b0SAlexander V. Chernikov 	.foreach	= ta_foreach_radix,
84081d3153dSAlexander V. Chernikov 	.dump_tentry	= ta_dump_radix_tentry,
84181d3153dSAlexander V. Chernikov 	.find_tentry	= ta_find_radix_tentry,
8425f379342SAlexander V. Chernikov 	.dump_tinfo	= ta_dump_radix_tinfo,
843301290bcSAlexander V. Chernikov 	.need_modify	= ta_need_modify_radix,
8449f7d47b0SAlexander V. Chernikov };
8459f7d47b0SAlexander V. Chernikov 
8469f7d47b0SAlexander V. Chernikov 
8479f7d47b0SAlexander V. Chernikov /*
848c21034b7SAlexander V. Chernikov  * addr:hash cmds
84974b941f0SAlexander V. Chernikov  *
85074b941f0SAlexander V. Chernikov  *
85174b941f0SAlexander V. Chernikov  * ti->data:
85274b941f0SAlexander V. Chernikov  * [inv.mask4][inv.mask6][log2hsize4][log2hsize6]
85374b941f0SAlexander V. Chernikov  * [        8][        8[          8][         8]
85474b941f0SAlexander V. Chernikov  *
85574b941f0SAlexander V. Chernikov  * inv.mask4: 32 - mask
85674b941f0SAlexander V. Chernikov  * inv.mask6:
85774b941f0SAlexander V. Chernikov  * 1) _slow lookup: mask
85874b941f0SAlexander V. Chernikov  * 2) _aligned: (128 - mask) / 8
85974b941f0SAlexander V. Chernikov  * 3) _64: 8
860ce2817b5SAlexander V. Chernikov  *
861ce2817b5SAlexander V. Chernikov  *
862ce2817b5SAlexander V. Chernikov  * pflags:
863ce2817b5SAlexander V. Chernikov  * [v4=1/v6=0][hsize]
864ce2817b5SAlexander V. Chernikov  * [       32][   32]
86574b941f0SAlexander V. Chernikov  */
86674b941f0SAlexander V. Chernikov 
86774b941f0SAlexander V. Chernikov struct chashentry;
86874b941f0SAlexander V. Chernikov 
86974b941f0SAlexander V. Chernikov SLIST_HEAD(chashbhead, chashentry);
87074b941f0SAlexander V. Chernikov 
87174b941f0SAlexander V. Chernikov struct chash_cfg {
87274b941f0SAlexander V. Chernikov 	struct chashbhead *head4;
87374b941f0SAlexander V. Chernikov 	struct chashbhead *head6;
87474b941f0SAlexander V. Chernikov 	size_t	size4;
87574b941f0SAlexander V. Chernikov 	size_t	size6;
876ce2817b5SAlexander V. Chernikov 	size_t	items4;
877ce2817b5SAlexander V. Chernikov 	size_t	items6;
87874b941f0SAlexander V. Chernikov 	uint8_t	mask4;
87974b941f0SAlexander V. Chernikov 	uint8_t	mask6;
88074b941f0SAlexander V. Chernikov };
88174b941f0SAlexander V. Chernikov 
88274b941f0SAlexander V. Chernikov struct chashentry {
88374b941f0SAlexander V. Chernikov 	SLIST_ENTRY(chashentry)	next;
88474b941f0SAlexander V. Chernikov 	uint32_t	value;
88574b941f0SAlexander V. Chernikov 	uint32_t	type;
88674b941f0SAlexander V. Chernikov 	union {
88774b941f0SAlexander V. Chernikov 		uint32_t	a4;	/* Host format */
88874b941f0SAlexander V. Chernikov 		struct in6_addr	a6;	/* Network format */
88974b941f0SAlexander V. Chernikov 	} a;
89074b941f0SAlexander V. Chernikov };
89174b941f0SAlexander V. Chernikov 
8920bce0c23SAlexander V. Chernikov struct ta_buf_chash
8930bce0c23SAlexander V. Chernikov {
8940bce0c23SAlexander V. Chernikov 	void *ent_ptr;
8950bce0c23SAlexander V. Chernikov 	struct chashentry ent;
8960bce0c23SAlexander V. Chernikov };
8970bce0c23SAlexander V. Chernikov 
8980bce0c23SAlexander V. Chernikov 
89974b941f0SAlexander V. Chernikov static __inline uint32_t
90074b941f0SAlexander V. Chernikov hash_ip(uint32_t addr, int hsize)
90174b941f0SAlexander V. Chernikov {
90274b941f0SAlexander V. Chernikov 
90374b941f0SAlexander V. Chernikov 	return (addr % (hsize - 1));
90474b941f0SAlexander V. Chernikov }
90574b941f0SAlexander V. Chernikov 
90674b941f0SAlexander V. Chernikov static __inline uint32_t
90774b941f0SAlexander V. Chernikov hash_ip6(struct in6_addr *addr6, int hsize)
90874b941f0SAlexander V. Chernikov {
90974b941f0SAlexander V. Chernikov 	uint32_t i;
91074b941f0SAlexander V. Chernikov 
91174b941f0SAlexander V. Chernikov 	i = addr6->s6_addr32[0] ^ addr6->s6_addr32[1] ^
91274b941f0SAlexander V. Chernikov 	    addr6->s6_addr32[2] ^ addr6->s6_addr32[3];
91374b941f0SAlexander V. Chernikov 
91474b941f0SAlexander V. Chernikov 	return (i % (hsize - 1));
91574b941f0SAlexander V. Chernikov }
91674b941f0SAlexander V. Chernikov 
91774b941f0SAlexander V. Chernikov 
91874b941f0SAlexander V. Chernikov static __inline uint16_t
91974b941f0SAlexander V. Chernikov hash_ip64(struct in6_addr *addr6, int hsize)
92074b941f0SAlexander V. Chernikov {
92174b941f0SAlexander V. Chernikov 	uint32_t i;
92274b941f0SAlexander V. Chernikov 
92374b941f0SAlexander V. Chernikov 	i = addr6->s6_addr32[0] ^ addr6->s6_addr32[1];
92474b941f0SAlexander V. Chernikov 
92574b941f0SAlexander V. Chernikov 	return (i % (hsize - 1));
92674b941f0SAlexander V. Chernikov }
92774b941f0SAlexander V. Chernikov 
92874b941f0SAlexander V. Chernikov 
92974b941f0SAlexander V. Chernikov static __inline uint32_t
93074b941f0SAlexander V. Chernikov hash_ip6_slow(struct in6_addr *addr6, void *key, int mask, int hsize)
93174b941f0SAlexander V. Chernikov {
93274b941f0SAlexander V. Chernikov 	struct in6_addr mask6;
93374b941f0SAlexander V. Chernikov 
93474b941f0SAlexander V. Chernikov 	ipv6_writemask(&mask6, mask);
93574b941f0SAlexander V. Chernikov 	memcpy(addr6, key, sizeof(struct in6_addr));
93674b941f0SAlexander V. Chernikov 	APPLY_MASK(addr6, &mask6);
93774b941f0SAlexander V. Chernikov 	return (hash_ip6(addr6, hsize));
93874b941f0SAlexander V. Chernikov }
93974b941f0SAlexander V. Chernikov 
94074b941f0SAlexander V. Chernikov static __inline uint32_t
94174b941f0SAlexander V. Chernikov hash_ip6_al(struct in6_addr *addr6, void *key, int mask, int hsize)
94274b941f0SAlexander V. Chernikov {
94374b941f0SAlexander V. Chernikov 	uint64_t *paddr;
94474b941f0SAlexander V. Chernikov 
94574b941f0SAlexander V. Chernikov 	paddr = (uint64_t *)addr6;
94674b941f0SAlexander V. Chernikov 	*paddr = 0;
94774b941f0SAlexander V. Chernikov 	*(paddr + 1) = 0;
94874b941f0SAlexander V. Chernikov 	memcpy(addr6, key, mask);
94974b941f0SAlexander V. Chernikov 	return (hash_ip6(addr6, hsize));
95074b941f0SAlexander V. Chernikov }
95174b941f0SAlexander V. Chernikov 
95274b941f0SAlexander V. Chernikov static int
95374b941f0SAlexander V. Chernikov ta_lookup_chash_slow(struct table_info *ti, void *key, uint32_t keylen,
95474b941f0SAlexander V. Chernikov     uint32_t *val)
95574b941f0SAlexander V. Chernikov {
95674b941f0SAlexander V. Chernikov 	struct chashbhead *head;
95774b941f0SAlexander V. Chernikov 	struct chashentry *ent;
95874b941f0SAlexander V. Chernikov 	uint16_t hash, hsize;
95974b941f0SAlexander V. Chernikov 	uint8_t imask;
96074b941f0SAlexander V. Chernikov 
96174b941f0SAlexander V. Chernikov 	if (keylen == sizeof(in_addr_t)) {
96274b941f0SAlexander V. Chernikov 		head = (struct chashbhead *)ti->state;
96374b941f0SAlexander V. Chernikov 		imask = ti->data >> 24;
96474b941f0SAlexander V. Chernikov 		hsize = 1 << ((ti->data & 0xFFFF) >> 8);
96574b941f0SAlexander V. Chernikov 		uint32_t a;
96674b941f0SAlexander V. Chernikov 		a = ntohl(*((in_addr_t *)key));
96774b941f0SAlexander V. Chernikov 		a = a >> imask;
96874b941f0SAlexander V. Chernikov 		hash = hash_ip(a, hsize);
96974b941f0SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
97074b941f0SAlexander V. Chernikov 			if (ent->a.a4 == a) {
97174b941f0SAlexander V. Chernikov 				*val = ent->value;
97274b941f0SAlexander V. Chernikov 				return (1);
97374b941f0SAlexander V. Chernikov 			}
97474b941f0SAlexander V. Chernikov 		}
97574b941f0SAlexander V. Chernikov 	} else {
97674b941f0SAlexander V. Chernikov 		/* IPv6: worst scenario: non-round mask */
97774b941f0SAlexander V. Chernikov 		struct in6_addr addr6;
97874b941f0SAlexander V. Chernikov 		head = (struct chashbhead *)ti->xstate;
97974b941f0SAlexander V. Chernikov 		imask = (ti->data & 0xFF0000) >> 16;
98074b941f0SAlexander V. Chernikov 		hsize = 1 << (ti->data & 0xFF);
98174b941f0SAlexander V. Chernikov 		hash = hash_ip6_slow(&addr6, key, imask, hsize);
98274b941f0SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
98374b941f0SAlexander V. Chernikov 			if (memcmp(&ent->a.a6, &addr6, 16) == 0) {
98474b941f0SAlexander V. Chernikov 				*val = ent->value;
98574b941f0SAlexander V. Chernikov 				return (1);
98674b941f0SAlexander V. Chernikov 			}
98774b941f0SAlexander V. Chernikov 		}
98874b941f0SAlexander V. Chernikov 	}
98974b941f0SAlexander V. Chernikov 
99074b941f0SAlexander V. Chernikov 	return (0);
99174b941f0SAlexander V. Chernikov }
99274b941f0SAlexander V. Chernikov 
99374b941f0SAlexander V. Chernikov static int
99474b941f0SAlexander V. Chernikov ta_lookup_chash_aligned(struct table_info *ti, void *key, uint32_t keylen,
99574b941f0SAlexander V. Chernikov     uint32_t *val)
99674b941f0SAlexander V. Chernikov {
99774b941f0SAlexander V. Chernikov 	struct chashbhead *head;
99874b941f0SAlexander V. Chernikov 	struct chashentry *ent;
99974b941f0SAlexander V. Chernikov 	uint16_t hash, hsize;
100074b941f0SAlexander V. Chernikov 	uint8_t imask;
100174b941f0SAlexander V. Chernikov 
100274b941f0SAlexander V. Chernikov 	if (keylen == sizeof(in_addr_t)) {
100374b941f0SAlexander V. Chernikov 		head = (struct chashbhead *)ti->state;
100474b941f0SAlexander V. Chernikov 		imask = ti->data >> 24;
100574b941f0SAlexander V. Chernikov 		hsize = 1 << ((ti->data & 0xFFFF) >> 8);
100674b941f0SAlexander V. Chernikov 		uint32_t a;
100774b941f0SAlexander V. Chernikov 		a = ntohl(*((in_addr_t *)key));
100874b941f0SAlexander V. Chernikov 		a = a >> imask;
100974b941f0SAlexander V. Chernikov 		hash = hash_ip(a, hsize);
101074b941f0SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
101174b941f0SAlexander V. Chernikov 			if (ent->a.a4 == a) {
101274b941f0SAlexander V. Chernikov 				*val = ent->value;
101374b941f0SAlexander V. Chernikov 				return (1);
101474b941f0SAlexander V. Chernikov 			}
101574b941f0SAlexander V. Chernikov 		}
101674b941f0SAlexander V. Chernikov 	} else {
101774b941f0SAlexander V. Chernikov 		/* IPv6: aligned to 8bit mask */
101874b941f0SAlexander V. Chernikov 		struct in6_addr addr6;
101974b941f0SAlexander V. Chernikov 		uint64_t *paddr, *ptmp;
102074b941f0SAlexander V. Chernikov 		head = (struct chashbhead *)ti->xstate;
102174b941f0SAlexander V. Chernikov 		imask = (ti->data & 0xFF0000) >> 16;
102274b941f0SAlexander V. Chernikov 		hsize = 1 << (ti->data & 0xFF);
102374b941f0SAlexander V. Chernikov 
102474b941f0SAlexander V. Chernikov 		hash = hash_ip6_al(&addr6, key, imask, hsize);
102574b941f0SAlexander V. Chernikov 		paddr = (uint64_t *)&addr6;
102674b941f0SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
102774b941f0SAlexander V. Chernikov 			ptmp = (uint64_t *)&ent->a.a6;
102874b941f0SAlexander V. Chernikov 			if (paddr[0] == ptmp[0] && paddr[1] == ptmp[1]) {
102974b941f0SAlexander V. Chernikov 				*val = ent->value;
103074b941f0SAlexander V. Chernikov 				return (1);
103174b941f0SAlexander V. Chernikov 			}
103274b941f0SAlexander V. Chernikov 		}
103374b941f0SAlexander V. Chernikov 	}
103474b941f0SAlexander V. Chernikov 
103574b941f0SAlexander V. Chernikov 	return (0);
103674b941f0SAlexander V. Chernikov }
103774b941f0SAlexander V. Chernikov 
103874b941f0SAlexander V. Chernikov static int
103974b941f0SAlexander V. Chernikov ta_lookup_chash_64(struct table_info *ti, void *key, uint32_t keylen,
104074b941f0SAlexander V. Chernikov     uint32_t *val)
104174b941f0SAlexander V. Chernikov {
104274b941f0SAlexander V. Chernikov 	struct chashbhead *head;
104374b941f0SAlexander V. Chernikov 	struct chashentry *ent;
104474b941f0SAlexander V. Chernikov 	uint16_t hash, hsize;
104574b941f0SAlexander V. Chernikov 	uint8_t imask;
104674b941f0SAlexander V. Chernikov 
104774b941f0SAlexander V. Chernikov 	if (keylen == sizeof(in_addr_t)) {
104874b941f0SAlexander V. Chernikov 		head = (struct chashbhead *)ti->state;
104974b941f0SAlexander V. Chernikov 		imask = ti->data >> 24;
105074b941f0SAlexander V. Chernikov 		hsize = 1 << ((ti->data & 0xFFFF) >> 8);
105174b941f0SAlexander V. Chernikov 		uint32_t a;
105274b941f0SAlexander V. Chernikov 		a = ntohl(*((in_addr_t *)key));
105374b941f0SAlexander V. Chernikov 		a = a >> imask;
105474b941f0SAlexander V. Chernikov 		hash = hash_ip(a, hsize);
105574b941f0SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
105674b941f0SAlexander V. Chernikov 			if (ent->a.a4 == a) {
105774b941f0SAlexander V. Chernikov 				*val = ent->value;
105874b941f0SAlexander V. Chernikov 				return (1);
105974b941f0SAlexander V. Chernikov 			}
106074b941f0SAlexander V. Chernikov 		}
106174b941f0SAlexander V. Chernikov 	} else {
106274b941f0SAlexander V. Chernikov 		/* IPv6: /64 */
106374b941f0SAlexander V. Chernikov 		uint64_t a6, *paddr;
106474b941f0SAlexander V. Chernikov 		head = (struct chashbhead *)ti->xstate;
106574b941f0SAlexander V. Chernikov 		paddr = (uint64_t *)key;
106674b941f0SAlexander V. Chernikov 		hsize = 1 << (ti->data & 0xFF);
106774b941f0SAlexander V. Chernikov 		a6 = *paddr;
106874b941f0SAlexander V. Chernikov 		hash = hash_ip64((struct in6_addr *)key, hsize);
106974b941f0SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
107074b941f0SAlexander V. Chernikov 			paddr = (uint64_t *)&ent->a.a6;
107174b941f0SAlexander V. Chernikov 			if (a6 == *paddr) {
107274b941f0SAlexander V. Chernikov 				*val = ent->value;
107374b941f0SAlexander V. Chernikov 				return (1);
107474b941f0SAlexander V. Chernikov 			}
107574b941f0SAlexander V. Chernikov 		}
107674b941f0SAlexander V. Chernikov 	}
107774b941f0SAlexander V. Chernikov 
107874b941f0SAlexander V. Chernikov 	return (0);
107974b941f0SAlexander V. Chernikov }
108074b941f0SAlexander V. Chernikov 
108174b941f0SAlexander V. Chernikov static int
10820bce0c23SAlexander V. Chernikov chash_parse_opts(struct chash_cfg *cfg, char *data)
108374b941f0SAlexander V. Chernikov {
108474b941f0SAlexander V. Chernikov 	char *pdel, *pend, *s;
108574b941f0SAlexander V. Chernikov 	int mask4, mask6;
108674b941f0SAlexander V. Chernikov 
10870bce0c23SAlexander V. Chernikov 	mask4 = cfg->mask4;
10880bce0c23SAlexander V. Chernikov 	mask6 = cfg->mask6;
108974b941f0SAlexander V. Chernikov 
109074b941f0SAlexander V. Chernikov 	if (data == NULL)
109174b941f0SAlexander V. Chernikov 		return (0);
109274b941f0SAlexander V. Chernikov 	if ((pdel = strchr(data, ' ')) == NULL)
109374b941f0SAlexander V. Chernikov 		return (0);
109474b941f0SAlexander V. Chernikov 	while (*pdel == ' ')
109574b941f0SAlexander V. Chernikov 		pdel++;
109674b941f0SAlexander V. Chernikov 	if (strncmp(pdel, "masks=", 6) != 0)
109774b941f0SAlexander V. Chernikov 		return (EINVAL);
109874b941f0SAlexander V. Chernikov 	if ((s = strchr(pdel, ' ')) != NULL)
109974b941f0SAlexander V. Chernikov 		*s++ = '\0';
110074b941f0SAlexander V. Chernikov 
110174b941f0SAlexander V. Chernikov 	pdel += 6;
110274b941f0SAlexander V. Chernikov 	/* Need /XX[,/YY] */
110374b941f0SAlexander V. Chernikov 	if (*pdel++ != '/')
110474b941f0SAlexander V. Chernikov 		return (EINVAL);
110574b941f0SAlexander V. Chernikov 	mask4 = strtol(pdel, &pend, 10);
110674b941f0SAlexander V. Chernikov 	if (*pend == ',') {
110774b941f0SAlexander V. Chernikov 		/* ,/YY */
110874b941f0SAlexander V. Chernikov 		pdel = pend + 1;
110974b941f0SAlexander V. Chernikov 		if (*pdel++ != '/')
111074b941f0SAlexander V. Chernikov 			return (EINVAL);
111174b941f0SAlexander V. Chernikov 		mask6 = strtol(pdel, &pend, 10);
111274b941f0SAlexander V. Chernikov 		if (*pend != '\0')
111374b941f0SAlexander V. Chernikov 			return (EINVAL);
111474b941f0SAlexander V. Chernikov 	} else if (*pend != '\0')
111574b941f0SAlexander V. Chernikov 		return (EINVAL);
111674b941f0SAlexander V. Chernikov 
111774b941f0SAlexander V. Chernikov 	if (mask4 < 0 || mask4 > 32 || mask6 < 0 || mask6 > 128)
111874b941f0SAlexander V. Chernikov 		return (EINVAL);
111974b941f0SAlexander V. Chernikov 
11200bce0c23SAlexander V. Chernikov 	cfg->mask4 = mask4;
11210bce0c23SAlexander V. Chernikov 	cfg->mask6 = mask6;
112274b941f0SAlexander V. Chernikov 
112374b941f0SAlexander V. Chernikov 	return (0);
112474b941f0SAlexander V. Chernikov }
112574b941f0SAlexander V. Chernikov 
112674b941f0SAlexander V. Chernikov static void
112774b941f0SAlexander V. Chernikov ta_print_chash_config(void *ta_state, struct table_info *ti, char *buf,
112874b941f0SAlexander V. Chernikov     size_t bufsize)
112974b941f0SAlexander V. Chernikov {
11300bce0c23SAlexander V. Chernikov 	struct chash_cfg *cfg;
113174b941f0SAlexander V. Chernikov 
11320bce0c23SAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
113374b941f0SAlexander V. Chernikov 
11340bce0c23SAlexander V. Chernikov 	if (cfg->mask4 != 32 || cfg->mask6 != 128)
1135c21034b7SAlexander V. Chernikov 		snprintf(buf, bufsize, "%s masks=/%d,/%d", "addr:hash",
11360bce0c23SAlexander V. Chernikov 		    cfg->mask4, cfg->mask6);
113774b941f0SAlexander V. Chernikov 	else
1138c21034b7SAlexander V. Chernikov 		snprintf(buf, bufsize, "%s", "addr:hash");
113974b941f0SAlexander V. Chernikov }
114074b941f0SAlexander V. Chernikov 
1141914bffb6SAlexander V. Chernikov static int
1142914bffb6SAlexander V. Chernikov log2(uint32_t v)
1143914bffb6SAlexander V. Chernikov {
1144914bffb6SAlexander V. Chernikov 	uint32_t r;
1145914bffb6SAlexander V. Chernikov 
1146914bffb6SAlexander V. Chernikov 	r = 0;
1147914bffb6SAlexander V. Chernikov 	while (v >>= 1)
1148914bffb6SAlexander V. Chernikov 		r++;
1149914bffb6SAlexander V. Chernikov 
1150914bffb6SAlexander V. Chernikov 	return (r);
1151914bffb6SAlexander V. Chernikov }
115274b941f0SAlexander V. Chernikov 
115374b941f0SAlexander V. Chernikov /*
115474b941f0SAlexander V. Chernikov  * New table.
115574b941f0SAlexander V. Chernikov  * We assume 'data' to be either NULL or the following format:
1156c21034b7SAlexander V. Chernikov  * 'addr:hash [masks=/32[,/128]]'
115774b941f0SAlexander V. Chernikov  */
115874b941f0SAlexander V. Chernikov static int
115974b941f0SAlexander V. Chernikov ta_init_chash(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
1160914bffb6SAlexander V. Chernikov     char *data, uint8_t tflags)
116174b941f0SAlexander V. Chernikov {
116274b941f0SAlexander V. Chernikov 	int error, i;
1163914bffb6SAlexander V. Chernikov 	uint32_t hsize;
11640bce0c23SAlexander V. Chernikov 	struct chash_cfg *cfg;
116574b941f0SAlexander V. Chernikov 
11660bce0c23SAlexander V. Chernikov 	cfg = malloc(sizeof(struct chash_cfg), M_IPFW, M_WAITOK | M_ZERO);
116774b941f0SAlexander V. Chernikov 
11680bce0c23SAlexander V. Chernikov 	cfg->mask4 = 32;
11690bce0c23SAlexander V. Chernikov 	cfg->mask6 = 128;
117074b941f0SAlexander V. Chernikov 
11710bce0c23SAlexander V. Chernikov 	if ((error = chash_parse_opts(cfg, data)) != 0) {
11720bce0c23SAlexander V. Chernikov 		free(cfg, M_IPFW);
117374b941f0SAlexander V. Chernikov 		return (error);
117474b941f0SAlexander V. Chernikov 	}
117574b941f0SAlexander V. Chernikov 
11760bce0c23SAlexander V. Chernikov 	cfg->size4 = 128;
11770bce0c23SAlexander V. Chernikov 	cfg->size6 = 128;
117874b941f0SAlexander V. Chernikov 
11790bce0c23SAlexander V. Chernikov 	cfg->head4 = malloc(sizeof(struct chashbhead) * cfg->size4, M_IPFW,
118074b941f0SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
11810bce0c23SAlexander V. Chernikov 	cfg->head6 = malloc(sizeof(struct chashbhead) * cfg->size6, M_IPFW,
118274b941f0SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
11830bce0c23SAlexander V. Chernikov 	for (i = 0; i < cfg->size4; i++)
11840bce0c23SAlexander V. Chernikov 		SLIST_INIT(&cfg->head4[i]);
11850bce0c23SAlexander V. Chernikov 	for (i = 0; i < cfg->size6; i++)
11860bce0c23SAlexander V. Chernikov 		SLIST_INIT(&cfg->head6[i]);
118774b941f0SAlexander V. Chernikov 
118874b941f0SAlexander V. Chernikov 
11890bce0c23SAlexander V. Chernikov 	*ta_state = cfg;
11900bce0c23SAlexander V. Chernikov 	ti->state = cfg->head4;
11910bce0c23SAlexander V. Chernikov 	ti->xstate = cfg->head6;
119274b941f0SAlexander V. Chernikov 
119374b941f0SAlexander V. Chernikov 	/* Store data depending on v6 mask length */
11940bce0c23SAlexander V. Chernikov 	hsize = log2(cfg->size4) << 8 | log2(cfg->size6);
11950bce0c23SAlexander V. Chernikov 	if (cfg->mask6 == 64) {
11960bce0c23SAlexander V. Chernikov 		ti->data = (32 - cfg->mask4) << 24 | (128 - cfg->mask6) << 16|
1197914bffb6SAlexander V. Chernikov 		    hsize;
119874b941f0SAlexander V. Chernikov 		ti->lookup = ta_lookup_chash_64;
11990bce0c23SAlexander V. Chernikov 	} else if ((cfg->mask6  % 8) == 0) {
12000bce0c23SAlexander V. Chernikov 		ti->data = (32 - cfg->mask4) << 24 |
12010bce0c23SAlexander V. Chernikov 		    cfg->mask6 << 13 | hsize;
120274b941f0SAlexander V. Chernikov 		ti->lookup = ta_lookup_chash_aligned;
120374b941f0SAlexander V. Chernikov 	} else {
120474b941f0SAlexander V. Chernikov 		/* don't do that! */
12050bce0c23SAlexander V. Chernikov 		ti->data = (32 - cfg->mask4) << 24 |
12060bce0c23SAlexander V. Chernikov 		    cfg->mask6 << 16 | hsize;
120774b941f0SAlexander V. Chernikov 		ti->lookup = ta_lookup_chash_slow;
120874b941f0SAlexander V. Chernikov 	}
120974b941f0SAlexander V. Chernikov 
121074b941f0SAlexander V. Chernikov 	return (0);
121174b941f0SAlexander V. Chernikov }
121274b941f0SAlexander V. Chernikov 
121374b941f0SAlexander V. Chernikov static void
121474b941f0SAlexander V. Chernikov ta_destroy_chash(void *ta_state, struct table_info *ti)
121574b941f0SAlexander V. Chernikov {
12160bce0c23SAlexander V. Chernikov 	struct chash_cfg *cfg;
121774b941f0SAlexander V. Chernikov 	struct chashentry *ent, *ent_next;
121874b941f0SAlexander V. Chernikov 	int i;
121974b941f0SAlexander V. Chernikov 
12200bce0c23SAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
122174b941f0SAlexander V. Chernikov 
12220bce0c23SAlexander V. Chernikov 	for (i = 0; i < cfg->size4; i++)
12230bce0c23SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &cfg->head4[i], next, ent_next)
122474b941f0SAlexander V. Chernikov 			free(ent, M_IPFW_TBL);
122574b941f0SAlexander V. Chernikov 
12260bce0c23SAlexander V. Chernikov 	for (i = 0; i < cfg->size6; i++)
12270bce0c23SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &cfg->head6[i], next, ent_next)
122874b941f0SAlexander V. Chernikov 			free(ent, M_IPFW_TBL);
122974b941f0SAlexander V. Chernikov 
12300bce0c23SAlexander V. Chernikov 	free(cfg->head4, M_IPFW);
12310bce0c23SAlexander V. Chernikov 	free(cfg->head6, M_IPFW);
1232ce2817b5SAlexander V. Chernikov 
12330bce0c23SAlexander V. Chernikov 	free(cfg, M_IPFW);
123474b941f0SAlexander V. Chernikov }
123574b941f0SAlexander V. Chernikov 
12365f379342SAlexander V. Chernikov static void
12375f379342SAlexander V. Chernikov ta_dump_chash_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo)
12385f379342SAlexander V. Chernikov {
12395f379342SAlexander V. Chernikov 	struct chash_cfg *cfg;
12405f379342SAlexander V. Chernikov 
12415f379342SAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
12425f379342SAlexander V. Chernikov 
12435f379342SAlexander V. Chernikov 	tinfo->flags = IPFW_TATFLAGS_AFDATA | IPFW_TATFLAGS_AFITEM;
12445f379342SAlexander V. Chernikov 	tinfo->taclass4 = IPFW_TACLASS_HASH;
12455f379342SAlexander V. Chernikov 	tinfo->size4 = cfg->size4;
12465f379342SAlexander V. Chernikov 	tinfo->count4 = cfg->items4;
12475f379342SAlexander V. Chernikov 	tinfo->itemsize4 = sizeof(struct chashentry);
12485f379342SAlexander V. Chernikov 	tinfo->taclass6 = IPFW_TACLASS_HASH;
12495f379342SAlexander V. Chernikov 	tinfo->size6 = cfg->size6;
12505f379342SAlexander V. Chernikov 	tinfo->count6 = cfg->items6;
12515f379342SAlexander V. Chernikov 	tinfo->itemsize6 = sizeof(struct chashentry);
12525f379342SAlexander V. Chernikov }
12535f379342SAlexander V. Chernikov 
125474b941f0SAlexander V. Chernikov static int
125574b941f0SAlexander V. Chernikov ta_dump_chash_tentry(void *ta_state, struct table_info *ti, void *e,
125674b941f0SAlexander V. Chernikov     ipfw_obj_tentry *tent)
125774b941f0SAlexander V. Chernikov {
12580bce0c23SAlexander V. Chernikov 	struct chash_cfg *cfg;
125974b941f0SAlexander V. Chernikov 	struct chashentry *ent;
126074b941f0SAlexander V. Chernikov 
12610bce0c23SAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
126274b941f0SAlexander V. Chernikov 	ent = (struct chashentry *)e;
126374b941f0SAlexander V. Chernikov 
126474b941f0SAlexander V. Chernikov 	if (ent->type == AF_INET) {
12650bce0c23SAlexander V. Chernikov 		tent->k.addr.s_addr = htonl(ent->a.a4 << (32 - cfg->mask4));
12660bce0c23SAlexander V. Chernikov 		tent->masklen = cfg->mask4;
126774b941f0SAlexander V. Chernikov 		tent->subtype = AF_INET;
12680cba2b28SAlexander V. Chernikov 		tent->v.kidx = ent->value;
126974b941f0SAlexander V. Chernikov #ifdef INET6
127074b941f0SAlexander V. Chernikov 	} else {
127174b941f0SAlexander V. Chernikov 		memcpy(&tent->k, &ent->a.a6, sizeof(struct in6_addr));
12720bce0c23SAlexander V. Chernikov 		tent->masklen = cfg->mask6;
127374b941f0SAlexander V. Chernikov 		tent->subtype = AF_INET6;
12740cba2b28SAlexander V. Chernikov 		tent->v.kidx = ent->value;
127574b941f0SAlexander V. Chernikov #endif
127674b941f0SAlexander V. Chernikov 	}
127774b941f0SAlexander V. Chernikov 
127874b941f0SAlexander V. Chernikov 	return (0);
127974b941f0SAlexander V. Chernikov }
128074b941f0SAlexander V. Chernikov 
1281ce2817b5SAlexander V. Chernikov static uint32_t
1282ce2817b5SAlexander V. Chernikov hash_ent(struct chashentry *ent, int af, int mlen, uint32_t size)
1283ce2817b5SAlexander V. Chernikov {
1284ce2817b5SAlexander V. Chernikov 	uint32_t hash;
1285ce2817b5SAlexander V. Chernikov 
1286ce2817b5SAlexander V. Chernikov 	if (af == AF_INET) {
1287ce2817b5SAlexander V. Chernikov 		hash = hash_ip(ent->a.a4, size);
1288ce2817b5SAlexander V. Chernikov 	} else {
1289ce2817b5SAlexander V. Chernikov 		if (mlen == 64)
1290ce2817b5SAlexander V. Chernikov 			hash = hash_ip64(&ent->a.a6, size);
1291ce2817b5SAlexander V. Chernikov 		else
1292ce2817b5SAlexander V. Chernikov 			hash = hash_ip6(&ent->a.a6, size);
1293ce2817b5SAlexander V. Chernikov 	}
1294ce2817b5SAlexander V. Chernikov 
1295ce2817b5SAlexander V. Chernikov 	return (hash);
1296ce2817b5SAlexander V. Chernikov }
1297ce2817b5SAlexander V. Chernikov 
1298ce2817b5SAlexander V. Chernikov static int
1299ce2817b5SAlexander V. Chernikov tei_to_chash_ent(struct tentry_info *tei, struct chashentry *ent)
1300ce2817b5SAlexander V. Chernikov {
1301ce2817b5SAlexander V. Chernikov 	struct in6_addr mask6;
1302ce2817b5SAlexander V. Chernikov 	int mlen;
1303ce2817b5SAlexander V. Chernikov 
1304ce2817b5SAlexander V. Chernikov 
1305ce2817b5SAlexander V. Chernikov 	mlen = tei->masklen;
1306ce2817b5SAlexander V. Chernikov 
1307ce2817b5SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
1308ce2817b5SAlexander V. Chernikov #ifdef INET
1309ce2817b5SAlexander V. Chernikov 		if (mlen > 32)
1310ce2817b5SAlexander V. Chernikov 			return (EINVAL);
1311ce2817b5SAlexander V. Chernikov 		ent->type = AF_INET;
1312ce2817b5SAlexander V. Chernikov 
1313ce2817b5SAlexander V. Chernikov 		/* Calculate masked address */
1314ce2817b5SAlexander V. Chernikov 		ent->a.a4 = ntohl(*((in_addr_t *)tei->paddr)) >> (32 - mlen);
1315ce2817b5SAlexander V. Chernikov #endif
1316ce2817b5SAlexander V. Chernikov #ifdef INET6
1317ce2817b5SAlexander V. Chernikov 	} else if (tei->subtype == AF_INET6) {
1318ce2817b5SAlexander V. Chernikov 		/* IPv6 case */
1319ce2817b5SAlexander V. Chernikov 		if (mlen > 128)
1320ce2817b5SAlexander V. Chernikov 			return (EINVAL);
1321ce2817b5SAlexander V. Chernikov 		ent->type = AF_INET6;
1322ce2817b5SAlexander V. Chernikov 
1323ce2817b5SAlexander V. Chernikov 		ipv6_writemask(&mask6, mlen);
1324ce2817b5SAlexander V. Chernikov 		memcpy(&ent->a.a6, tei->paddr, sizeof(struct in6_addr));
1325ce2817b5SAlexander V. Chernikov 		APPLY_MASK(&ent->a.a6, &mask6);
1326ce2817b5SAlexander V. Chernikov #endif
1327ce2817b5SAlexander V. Chernikov 	} else {
1328ce2817b5SAlexander V. Chernikov 		/* Unknown CIDR type */
1329ce2817b5SAlexander V. Chernikov 		return (EINVAL);
1330ce2817b5SAlexander V. Chernikov 	}
1331ce2817b5SAlexander V. Chernikov 
1332ce2817b5SAlexander V. Chernikov 	return (0);
1333ce2817b5SAlexander V. Chernikov }
1334ce2817b5SAlexander V. Chernikov 
133574b941f0SAlexander V. Chernikov static int
1336914bffb6SAlexander V. Chernikov ta_find_chash_tentry(void *ta_state, struct table_info *ti,
1337914bffb6SAlexander V. Chernikov     ipfw_obj_tentry *tent)
133874b941f0SAlexander V. Chernikov {
13390bce0c23SAlexander V. Chernikov 	struct chash_cfg *cfg;
1340ce2817b5SAlexander V. Chernikov 	struct chashbhead *head;
1341ce2817b5SAlexander V. Chernikov 	struct chashentry ent, *tmp;
1342ce2817b5SAlexander V. Chernikov 	struct tentry_info tei;
1343ce2817b5SAlexander V. Chernikov 	int error;
1344ce2817b5SAlexander V. Chernikov 	uint32_t hash;
134574b941f0SAlexander V. Chernikov 
13460bce0c23SAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
1347ce2817b5SAlexander V. Chernikov 
1348ce2817b5SAlexander V. Chernikov 	memset(&ent, 0, sizeof(ent));
1349ce2817b5SAlexander V. Chernikov 	memset(&tei, 0, sizeof(tei));
1350ce2817b5SAlexander V. Chernikov 
1351914bffb6SAlexander V. Chernikov 	if (tent->subtype == AF_INET) {
1352914bffb6SAlexander V. Chernikov 		tei.paddr = &tent->k.addr;
13530bce0c23SAlexander V. Chernikov 		tei.masklen = cfg->mask4;
1354ce2817b5SAlexander V. Chernikov 		tei.subtype = AF_INET;
135574b941f0SAlexander V. Chernikov 
1356ce2817b5SAlexander V. Chernikov 		if ((error = tei_to_chash_ent(&tei, &ent)) != 0)
1357ce2817b5SAlexander V. Chernikov 			return (error);
1358ce2817b5SAlexander V. Chernikov 
13590bce0c23SAlexander V. Chernikov 		head = cfg->head4;
13600bce0c23SAlexander V. Chernikov 		hash = hash_ent(&ent, AF_INET, cfg->mask4, cfg->size4);
1361ce2817b5SAlexander V. Chernikov 		/* Check for existence */
1362ce2817b5SAlexander V. Chernikov 		SLIST_FOREACH(tmp, &head[hash], next) {
1363ce2817b5SAlexander V. Chernikov 			if (tmp->a.a4 != ent.a.a4)
1364ce2817b5SAlexander V. Chernikov 				continue;
1365ce2817b5SAlexander V. Chernikov 
1366ce2817b5SAlexander V. Chernikov 			ta_dump_chash_tentry(ta_state, ti, tmp, tent);
136774b941f0SAlexander V. Chernikov 			return (0);
136874b941f0SAlexander V. Chernikov 		}
1369ce2817b5SAlexander V. Chernikov 	} else {
1370914bffb6SAlexander V. Chernikov 		tei.paddr = &tent->k.addr6;
13710bce0c23SAlexander V. Chernikov 		tei.masklen = cfg->mask6;
1372ce2817b5SAlexander V. Chernikov 		tei.subtype = AF_INET6;
1373ce2817b5SAlexander V. Chernikov 
1374ce2817b5SAlexander V. Chernikov 		if ((error = tei_to_chash_ent(&tei, &ent)) != 0)
1375ce2817b5SAlexander V. Chernikov 			return (error);
1376ce2817b5SAlexander V. Chernikov 
13770bce0c23SAlexander V. Chernikov 		head = cfg->head6;
13780bce0c23SAlexander V. Chernikov 		hash = hash_ent(&ent, AF_INET6, cfg->mask6, cfg->size6);
1379ce2817b5SAlexander V. Chernikov 		/* Check for existence */
1380ce2817b5SAlexander V. Chernikov 		SLIST_FOREACH(tmp, &head[hash], next) {
1381ce2817b5SAlexander V. Chernikov 			if (memcmp(&tmp->a.a6, &ent.a.a6, 16) != 0)
1382ce2817b5SAlexander V. Chernikov 				continue;
1383ce2817b5SAlexander V. Chernikov 			ta_dump_chash_tentry(ta_state, ti, tmp, tent);
1384ce2817b5SAlexander V. Chernikov 			return (0);
1385ce2817b5SAlexander V. Chernikov 		}
1386ce2817b5SAlexander V. Chernikov 	}
1387ce2817b5SAlexander V. Chernikov 
138874b941f0SAlexander V. Chernikov 	return (ENOENT);
138974b941f0SAlexander V. Chernikov }
139074b941f0SAlexander V. Chernikov 
139174b941f0SAlexander V. Chernikov static void
139274b941f0SAlexander V. Chernikov ta_foreach_chash(void *ta_state, struct table_info *ti, ta_foreach_f *f,
139374b941f0SAlexander V. Chernikov     void *arg)
139474b941f0SAlexander V. Chernikov {
13950bce0c23SAlexander V. Chernikov 	struct chash_cfg *cfg;
139674b941f0SAlexander V. Chernikov 	struct chashentry *ent, *ent_next;
139774b941f0SAlexander V. Chernikov 	int i;
139874b941f0SAlexander V. Chernikov 
13990bce0c23SAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
140074b941f0SAlexander V. Chernikov 
14010bce0c23SAlexander V. Chernikov 	for (i = 0; i < cfg->size4; i++)
14020bce0c23SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &cfg->head4[i], next, ent_next)
140374b941f0SAlexander V. Chernikov 			f(ent, arg);
140474b941f0SAlexander V. Chernikov 
14050bce0c23SAlexander V. Chernikov 	for (i = 0; i < cfg->size6; i++)
14060bce0c23SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &cfg->head6[i], next, ent_next)
140774b941f0SAlexander V. Chernikov 			f(ent, arg);
140874b941f0SAlexander V. Chernikov }
140974b941f0SAlexander V. Chernikov 
141074b941f0SAlexander V. Chernikov static int
141174b941f0SAlexander V. Chernikov ta_prepare_add_chash(struct ip_fw_chain *ch, struct tentry_info *tei,
141274b941f0SAlexander V. Chernikov     void *ta_buf)
141374b941f0SAlexander V. Chernikov {
141474b941f0SAlexander V. Chernikov 	struct ta_buf_chash *tb;
141574b941f0SAlexander V. Chernikov 	struct chashentry *ent;
1416ce2817b5SAlexander V. Chernikov 	int error;
141774b941f0SAlexander V. Chernikov 
141874b941f0SAlexander V. Chernikov 	tb = (struct ta_buf_chash *)ta_buf;
141974b941f0SAlexander V. Chernikov 
142074b941f0SAlexander V. Chernikov 	ent = malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO);
142174b941f0SAlexander V. Chernikov 
1422ce2817b5SAlexander V. Chernikov 	error = tei_to_chash_ent(tei, ent);
1423ce2817b5SAlexander V. Chernikov 	if (error != 0) {
1424ce2817b5SAlexander V. Chernikov 		free(ent, M_IPFW_TBL);
1425ce2817b5SAlexander V. Chernikov 		return (error);
142674b941f0SAlexander V. Chernikov 	}
1427ce2817b5SAlexander V. Chernikov 	tb->ent_ptr = ent;
142874b941f0SAlexander V. Chernikov 
142974b941f0SAlexander V. Chernikov 	return (0);
143074b941f0SAlexander V. Chernikov }
143174b941f0SAlexander V. Chernikov 
143274b941f0SAlexander V. Chernikov static int
143374b941f0SAlexander V. Chernikov ta_add_chash(void *ta_state, struct table_info *ti, struct tentry_info *tei,
1434b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
143574b941f0SAlexander V. Chernikov {
14360bce0c23SAlexander V. Chernikov 	struct chash_cfg *cfg;
143774b941f0SAlexander V. Chernikov 	struct chashbhead *head;
143874b941f0SAlexander V. Chernikov 	struct chashentry *ent, *tmp;
143974b941f0SAlexander V. Chernikov 	struct ta_buf_chash *tb;
144074b941f0SAlexander V. Chernikov 	int exists;
1441648e8380SAlexander V. Chernikov 	uint32_t hash, value;
144274b941f0SAlexander V. Chernikov 
14430bce0c23SAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
144474b941f0SAlexander V. Chernikov 	tb = (struct ta_buf_chash *)ta_buf;
144574b941f0SAlexander V. Chernikov 	ent = (struct chashentry *)tb->ent_ptr;
144674b941f0SAlexander V. Chernikov 	hash = 0;
144774b941f0SAlexander V. Chernikov 	exists = 0;
144874b941f0SAlexander V. Chernikov 
144913263632SAlexander V. Chernikov 	/* Read current value from @tei */
145013263632SAlexander V. Chernikov 	ent->value = tei->value;
145113263632SAlexander V. Chernikov 
145213263632SAlexander V. Chernikov 	/* Read cuurrent value */
145374b941f0SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
14540bce0c23SAlexander V. Chernikov 		if (tei->masklen != cfg->mask4)
145574b941f0SAlexander V. Chernikov 			return (EINVAL);
14560bce0c23SAlexander V. Chernikov 		head = cfg->head4;
14570bce0c23SAlexander V. Chernikov 		hash = hash_ent(ent, AF_INET, cfg->mask4, cfg->size4);
1458ce2817b5SAlexander V. Chernikov 
145974b941f0SAlexander V. Chernikov 		/* Check for existence */
146074b941f0SAlexander V. Chernikov 		SLIST_FOREACH(tmp, &head[hash], next) {
146174b941f0SAlexander V. Chernikov 			if (tmp->a.a4 == ent->a.a4) {
146274b941f0SAlexander V. Chernikov 				exists = 1;
146374b941f0SAlexander V. Chernikov 				break;
146474b941f0SAlexander V. Chernikov 			}
146574b941f0SAlexander V. Chernikov 		}
146674b941f0SAlexander V. Chernikov 	} else {
14670bce0c23SAlexander V. Chernikov 		if (tei->masklen != cfg->mask6)
146874b941f0SAlexander V. Chernikov 			return (EINVAL);
14690bce0c23SAlexander V. Chernikov 		head = cfg->head6;
14700bce0c23SAlexander V. Chernikov 		hash = hash_ent(ent, AF_INET6, cfg->mask6, cfg->size6);
147174b941f0SAlexander V. Chernikov 		/* Check for existence */
147274b941f0SAlexander V. Chernikov 		SLIST_FOREACH(tmp, &head[hash], next) {
1473ce2817b5SAlexander V. Chernikov 			if (memcmp(&tmp->a.a6, &ent->a.a6, 16) == 0) {
147474b941f0SAlexander V. Chernikov 				exists = 1;
147574b941f0SAlexander V. Chernikov 				break;
147674b941f0SAlexander V. Chernikov 			}
147774b941f0SAlexander V. Chernikov 		}
147874b941f0SAlexander V. Chernikov 	}
147974b941f0SAlexander V. Chernikov 
148074b941f0SAlexander V. Chernikov 	if (exists == 1) {
148174b941f0SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
148274b941f0SAlexander V. Chernikov 			return (EEXIST);
148374b941f0SAlexander V. Chernikov 		/* Record already exists. Update value if we're asked to */
1484648e8380SAlexander V. Chernikov 		value = tmp->value;
148574b941f0SAlexander V. Chernikov 		tmp->value = tei->value;
1486648e8380SAlexander V. Chernikov 		tei->value = value;
148774b941f0SAlexander V. Chernikov 		/* Indicate that update has happened instead of addition */
148874b941f0SAlexander V. Chernikov 		tei->flags |= TEI_FLAGS_UPDATED;
148974b941f0SAlexander V. Chernikov 		*pnum = 0;
149074b941f0SAlexander V. Chernikov 	} else {
14914c0c07a5SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_DONTADD) != 0)
14924c0c07a5SAlexander V. Chernikov 			return (EFBIG);
149374b941f0SAlexander V. Chernikov 		SLIST_INSERT_HEAD(&head[hash], ent, next);
149474b941f0SAlexander V. Chernikov 		tb->ent_ptr = NULL;
149574b941f0SAlexander V. Chernikov 		*pnum = 1;
1496ce2817b5SAlexander V. Chernikov 
1497b6ee846eSAlexander V. Chernikov 		/* Update counters */
1498b6ee846eSAlexander V. Chernikov 		if (tei->subtype == AF_INET)
14990bce0c23SAlexander V. Chernikov 			cfg->items4++;
1500b6ee846eSAlexander V. Chernikov 		else
15010bce0c23SAlexander V. Chernikov 			cfg->items6++;
150274b941f0SAlexander V. Chernikov 	}
150374b941f0SAlexander V. Chernikov 
150474b941f0SAlexander V. Chernikov 	return (0);
150574b941f0SAlexander V. Chernikov }
150674b941f0SAlexander V. Chernikov 
150774b941f0SAlexander V. Chernikov static int
150874b941f0SAlexander V. Chernikov ta_prepare_del_chash(struct ip_fw_chain *ch, struct tentry_info *tei,
150974b941f0SAlexander V. Chernikov     void *ta_buf)
151074b941f0SAlexander V. Chernikov {
151174b941f0SAlexander V. Chernikov 	struct ta_buf_chash *tb;
151274b941f0SAlexander V. Chernikov 
151374b941f0SAlexander V. Chernikov 	tb = (struct ta_buf_chash *)ta_buf;
151474b941f0SAlexander V. Chernikov 
1515ce2817b5SAlexander V. Chernikov 	return (tei_to_chash_ent(tei, &tb->ent));
151674b941f0SAlexander V. Chernikov }
151774b941f0SAlexander V. Chernikov 
151874b941f0SAlexander V. Chernikov static int
151974b941f0SAlexander V. Chernikov ta_del_chash(void *ta_state, struct table_info *ti, struct tentry_info *tei,
1520b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
152174b941f0SAlexander V. Chernikov {
15220bce0c23SAlexander V. Chernikov 	struct chash_cfg *cfg;
152374b941f0SAlexander V. Chernikov 	struct chashbhead *head;
15240bce0c23SAlexander V. Chernikov 	struct chashentry *tmp, *tmp_next, *ent;
152574b941f0SAlexander V. Chernikov 	struct ta_buf_chash *tb;
152674b941f0SAlexander V. Chernikov 	uint32_t hash;
152774b941f0SAlexander V. Chernikov 
15280bce0c23SAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
152974b941f0SAlexander V. Chernikov 	tb = (struct ta_buf_chash *)ta_buf;
15300bce0c23SAlexander V. Chernikov 	ent = &tb->ent;
153174b941f0SAlexander V. Chernikov 
153274b941f0SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
15330bce0c23SAlexander V. Chernikov 		if (tei->masklen != cfg->mask4)
153474b941f0SAlexander V. Chernikov 			return (EINVAL);
15350bce0c23SAlexander V. Chernikov 		head = cfg->head4;
15360bce0c23SAlexander V. Chernikov 		hash = hash_ent(ent, AF_INET, cfg->mask4, cfg->size4);
153774b941f0SAlexander V. Chernikov 
15380bce0c23SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(tmp, &head[hash], next, tmp_next) {
15390bce0c23SAlexander V. Chernikov 			if (tmp->a.a4 != ent->a.a4)
1540648e8380SAlexander V. Chernikov 				continue;
1541648e8380SAlexander V. Chernikov 
15420bce0c23SAlexander V. Chernikov 			SLIST_REMOVE(&head[hash], tmp, chashentry, next);
15430bce0c23SAlexander V. Chernikov 			cfg->items4--;
15440bce0c23SAlexander V. Chernikov 			tb->ent_ptr = tmp;
15450bce0c23SAlexander V. Chernikov 			tei->value = tmp->value;
1546648e8380SAlexander V. Chernikov 			*pnum = 1;
154774b941f0SAlexander V. Chernikov 			return (0);
154874b941f0SAlexander V. Chernikov 		}
154974b941f0SAlexander V. Chernikov 	} else {
15500bce0c23SAlexander V. Chernikov 		if (tei->masklen != cfg->mask6)
155174b941f0SAlexander V. Chernikov 			return (EINVAL);
15520bce0c23SAlexander V. Chernikov 		head = cfg->head6;
15530bce0c23SAlexander V. Chernikov 		hash = hash_ent(ent, AF_INET6, cfg->mask6, cfg->size6);
15540bce0c23SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(tmp, &head[hash], next, tmp_next) {
15550bce0c23SAlexander V. Chernikov 			if (memcmp(&tmp->a.a6, &ent->a.a6, 16) != 0)
1556648e8380SAlexander V. Chernikov 				continue;
1557648e8380SAlexander V. Chernikov 
15580bce0c23SAlexander V. Chernikov 			SLIST_REMOVE(&head[hash], tmp, chashentry, next);
15590bce0c23SAlexander V. Chernikov 			cfg->items6--;
15600bce0c23SAlexander V. Chernikov 			tb->ent_ptr = tmp;
15610bce0c23SAlexander V. Chernikov 			tei->value = tmp->value;
156274b941f0SAlexander V. Chernikov 			*pnum = 1;
156374b941f0SAlexander V. Chernikov 			return (0);
156474b941f0SAlexander V. Chernikov 		}
156574b941f0SAlexander V. Chernikov 	}
156674b941f0SAlexander V. Chernikov 
156774b941f0SAlexander V. Chernikov 	return (ENOENT);
156874b941f0SAlexander V. Chernikov }
156974b941f0SAlexander V. Chernikov 
157074b941f0SAlexander V. Chernikov static void
157174b941f0SAlexander V. Chernikov ta_flush_chash_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
157274b941f0SAlexander V. Chernikov     void *ta_buf)
157374b941f0SAlexander V. Chernikov {
157474b941f0SAlexander V. Chernikov 	struct ta_buf_chash *tb;
157574b941f0SAlexander V. Chernikov 
157674b941f0SAlexander V. Chernikov 	tb = (struct ta_buf_chash *)ta_buf;
157774b941f0SAlexander V. Chernikov 
157874b941f0SAlexander V. Chernikov 	if (tb->ent_ptr != NULL)
157974b941f0SAlexander V. Chernikov 		free(tb->ent_ptr, M_IPFW_TBL);
158074b941f0SAlexander V. Chernikov }
158174b941f0SAlexander V. Chernikov 
1582ce2817b5SAlexander V. Chernikov /*
1583ce2817b5SAlexander V. Chernikov  * Hash growing callbacks.
1584ce2817b5SAlexander V. Chernikov  */
1585ce2817b5SAlexander V. Chernikov 
1586b6ee846eSAlexander V. Chernikov static int
1587301290bcSAlexander V. Chernikov ta_need_modify_chash(void *ta_state, struct table_info *ti, uint32_t count,
1588b6ee846eSAlexander V. Chernikov     uint64_t *pflags)
1589b6ee846eSAlexander V. Chernikov {
1590b6ee846eSAlexander V. Chernikov 	struct chash_cfg *cfg;
1591b6ee846eSAlexander V. Chernikov 	uint64_t data;
1592b6ee846eSAlexander V. Chernikov 
1593b6ee846eSAlexander V. Chernikov 	/*
1594b6ee846eSAlexander V. Chernikov 	 * Since we don't know exact number of IPv4/IPv6 records in @count,
1595b6ee846eSAlexander V. Chernikov 	 * ignore non-zero @count value at all. Check current hash sizes
1596b6ee846eSAlexander V. Chernikov 	 * and return appropriate data.
1597b6ee846eSAlexander V. Chernikov 	 */
1598b6ee846eSAlexander V. Chernikov 
1599b6ee846eSAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
1600b6ee846eSAlexander V. Chernikov 
1601b6ee846eSAlexander V. Chernikov 	data = 0;
1602b6ee846eSAlexander V. Chernikov 	if (cfg->items4 > cfg->size4 && cfg->size4 < 65536)
1603b6ee846eSAlexander V. Chernikov 		data |= (cfg->size4 * 2) << 16;
1604b6ee846eSAlexander V. Chernikov 	if (cfg->items6 > cfg->size6 && cfg->size6 < 65536)
1605b6ee846eSAlexander V. Chernikov 		data |= cfg->size6 * 2;
1606b6ee846eSAlexander V. Chernikov 
1607b6ee846eSAlexander V. Chernikov 	if (data != 0) {
1608b6ee846eSAlexander V. Chernikov 		*pflags = data;
1609301290bcSAlexander V. Chernikov 		return (1);
1610b6ee846eSAlexander V. Chernikov 	}
1611b6ee846eSAlexander V. Chernikov 
1612301290bcSAlexander V. Chernikov 	return (0);
1613b6ee846eSAlexander V. Chernikov }
1614b6ee846eSAlexander V. Chernikov 
1615ce2817b5SAlexander V. Chernikov /*
1616ce2817b5SAlexander V. Chernikov  * Allocate new, larger chash.
1617ce2817b5SAlexander V. Chernikov  */
1618ce2817b5SAlexander V. Chernikov static int
1619ce2817b5SAlexander V. Chernikov ta_prepare_mod_chash(void *ta_buf, uint64_t *pflags)
1620ce2817b5SAlexander V. Chernikov {
1621ce2817b5SAlexander V. Chernikov 	struct mod_item *mi;
1622ce2817b5SAlexander V. Chernikov 	struct chashbhead *head;
1623ce2817b5SAlexander V. Chernikov 	int i;
1624ce2817b5SAlexander V. Chernikov 
1625ce2817b5SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
1626ce2817b5SAlexander V. Chernikov 
1627ce2817b5SAlexander V. Chernikov 	memset(mi, 0, sizeof(struct mod_item));
1628b6ee846eSAlexander V. Chernikov 	mi->size = (*pflags >> 16) & 0xFFFF;
1629b6ee846eSAlexander V. Chernikov 	mi->size6 = *pflags & 0xFFFF;
1630b6ee846eSAlexander V. Chernikov 	if (mi->size > 0) {
1631b6ee846eSAlexander V. Chernikov 		head = malloc(sizeof(struct chashbhead) * mi->size,
1632b6ee846eSAlexander V. Chernikov 		    M_IPFW, M_WAITOK | M_ZERO);
1633ce2817b5SAlexander V. Chernikov 		for (i = 0; i < mi->size; i++)
1634ce2817b5SAlexander V. Chernikov 			SLIST_INIT(&head[i]);
1635ce2817b5SAlexander V. Chernikov 		mi->main_ptr = head;
1636b6ee846eSAlexander V. Chernikov 	}
1637b6ee846eSAlexander V. Chernikov 
1638b6ee846eSAlexander V. Chernikov 	if (mi->size6 > 0) {
1639b6ee846eSAlexander V. Chernikov 		head = malloc(sizeof(struct chashbhead) * mi->size6,
1640b6ee846eSAlexander V. Chernikov 		    M_IPFW, M_WAITOK | M_ZERO);
1641b6ee846eSAlexander V. Chernikov 		for (i = 0; i < mi->size6; i++)
1642b6ee846eSAlexander V. Chernikov 			SLIST_INIT(&head[i]);
1643b6ee846eSAlexander V. Chernikov 		mi->main_ptr6 = head;
1644b6ee846eSAlexander V. Chernikov 	}
1645ce2817b5SAlexander V. Chernikov 
1646ce2817b5SAlexander V. Chernikov 	return (0);
1647ce2817b5SAlexander V. Chernikov }
1648ce2817b5SAlexander V. Chernikov 
1649ce2817b5SAlexander V. Chernikov /*
1650ce2817b5SAlexander V. Chernikov  * Copy data from old runtime array to new one.
1651ce2817b5SAlexander V. Chernikov  */
1652ce2817b5SAlexander V. Chernikov static int
1653ce2817b5SAlexander V. Chernikov ta_fill_mod_chash(void *ta_state, struct table_info *ti, void *ta_buf,
1654ce2817b5SAlexander V. Chernikov     uint64_t *pflags)
1655ce2817b5SAlexander V. Chernikov {
1656ce2817b5SAlexander V. Chernikov 
1657ce2817b5SAlexander V. Chernikov 	/* In is not possible to do rehash if we're not holidng WLOCK. */
1658ce2817b5SAlexander V. Chernikov 	return (0);
1659ce2817b5SAlexander V. Chernikov }
1660ce2817b5SAlexander V. Chernikov 
1661ce2817b5SAlexander V. Chernikov /*
1662ce2817b5SAlexander V. Chernikov  * Switch old & new arrays.
1663ce2817b5SAlexander V. Chernikov  */
1664301290bcSAlexander V. Chernikov static void
1665ce2817b5SAlexander V. Chernikov ta_modify_chash(void *ta_state, struct table_info *ti, void *ta_buf,
1666ce2817b5SAlexander V. Chernikov     uint64_t pflags)
1667ce2817b5SAlexander V. Chernikov {
1668ce2817b5SAlexander V. Chernikov 	struct mod_item *mi;
1669b6ee846eSAlexander V. Chernikov 	struct chash_cfg *cfg;
1670ce2817b5SAlexander V. Chernikov 	struct chashbhead *old_head, *new_head;
1671ce2817b5SAlexander V. Chernikov 	struct chashentry *ent, *ent_next;
1672ce2817b5SAlexander V. Chernikov 	int af, i, mlen;
1673ce2817b5SAlexander V. Chernikov 	uint32_t nhash;
1674b6ee846eSAlexander V. Chernikov 	size_t old_size, new_size;
1675ce2817b5SAlexander V. Chernikov 
1676ce2817b5SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
1677b6ee846eSAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
1678ce2817b5SAlexander V. Chernikov 
1679ce2817b5SAlexander V. Chernikov 	/* Check which hash we need to grow and do we still need that */
1680b6ee846eSAlexander V. Chernikov 	if (mi->size > 0 && cfg->size4 < mi->size) {
1681ce2817b5SAlexander V. Chernikov 		new_head = (struct chashbhead *)mi->main_ptr;
1682b6ee846eSAlexander V. Chernikov 		new_size = mi->size;
1683b6ee846eSAlexander V. Chernikov 		old_size = cfg->size4;
1684b6ee846eSAlexander V. Chernikov 		old_head = ti->state;
1685b6ee846eSAlexander V. Chernikov 		mlen = cfg->mask4;
1686b6ee846eSAlexander V. Chernikov 		af = AF_INET;
1687b6ee846eSAlexander V. Chernikov 
1688ce2817b5SAlexander V. Chernikov 		for (i = 0; i < old_size; i++) {
1689ce2817b5SAlexander V. Chernikov 			SLIST_FOREACH_SAFE(ent, &old_head[i], next, ent_next) {
1690b6ee846eSAlexander V. Chernikov 				nhash = hash_ent(ent, af, mlen, new_size);
1691ce2817b5SAlexander V. Chernikov 				SLIST_INSERT_HEAD(&new_head[nhash], ent, next);
1692ce2817b5SAlexander V. Chernikov 			}
1693ce2817b5SAlexander V. Chernikov 		}
1694ce2817b5SAlexander V. Chernikov 
1695ce2817b5SAlexander V. Chernikov 		ti->state = new_head;
1696b6ee846eSAlexander V. Chernikov 		cfg->head4 = new_head;
1697b6ee846eSAlexander V. Chernikov 		cfg->size4 = mi->size;
1698b6ee846eSAlexander V. Chernikov 		mi->main_ptr = old_head;
1699ce2817b5SAlexander V. Chernikov 	}
1700ce2817b5SAlexander V. Chernikov 
1701b6ee846eSAlexander V. Chernikov 	if (mi->size6 > 0 && cfg->size6 < mi->size6) {
1702b6ee846eSAlexander V. Chernikov 		new_head = (struct chashbhead *)mi->main_ptr6;
1703b6ee846eSAlexander V. Chernikov 		new_size = mi->size6;
1704b6ee846eSAlexander V. Chernikov 		old_size = cfg->size6;
1705b6ee846eSAlexander V. Chernikov 		old_head = ti->xstate;
1706b6ee846eSAlexander V. Chernikov 		mlen = cfg->mask6;
1707b6ee846eSAlexander V. Chernikov 		af = AF_INET6;
1708914bffb6SAlexander V. Chernikov 
1709b6ee846eSAlexander V. Chernikov 		for (i = 0; i < old_size; i++) {
1710b6ee846eSAlexander V. Chernikov 			SLIST_FOREACH_SAFE(ent, &old_head[i], next, ent_next) {
1711b6ee846eSAlexander V. Chernikov 				nhash = hash_ent(ent, af, mlen, new_size);
1712b6ee846eSAlexander V. Chernikov 				SLIST_INSERT_HEAD(&new_head[nhash], ent, next);
1713b6ee846eSAlexander V. Chernikov 			}
1714b6ee846eSAlexander V. Chernikov 		}
1715b6ee846eSAlexander V. Chernikov 
1716b6ee846eSAlexander V. Chernikov 		ti->xstate = new_head;
1717b6ee846eSAlexander V. Chernikov 		cfg->head6 = new_head;
1718b6ee846eSAlexander V. Chernikov 		cfg->size6 = mi->size6;
1719b6ee846eSAlexander V. Chernikov 		mi->main_ptr6 = old_head;
1720b6ee846eSAlexander V. Chernikov 	}
1721b6ee846eSAlexander V. Chernikov 
1722b6ee846eSAlexander V. Chernikov 	/* Update lower 32 bits with new values */
1723b6ee846eSAlexander V. Chernikov 	ti->data &= 0xFFFFFFFF00000000;
1724b6ee846eSAlexander V. Chernikov 	ti->data |= log2(cfg->size4) << 8 | log2(cfg->size6);
1725ce2817b5SAlexander V. Chernikov }
1726ce2817b5SAlexander V. Chernikov 
1727ce2817b5SAlexander V. Chernikov /*
1728ce2817b5SAlexander V. Chernikov  * Free unneded array.
1729ce2817b5SAlexander V. Chernikov  */
1730ce2817b5SAlexander V. Chernikov static void
1731ce2817b5SAlexander V. Chernikov ta_flush_mod_chash(void *ta_buf)
1732ce2817b5SAlexander V. Chernikov {
1733ce2817b5SAlexander V. Chernikov 	struct mod_item *mi;
1734ce2817b5SAlexander V. Chernikov 
1735ce2817b5SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
1736ce2817b5SAlexander V. Chernikov 	if (mi->main_ptr != NULL)
1737ce2817b5SAlexander V. Chernikov 		free(mi->main_ptr, M_IPFW);
1738b6ee846eSAlexander V. Chernikov 	if (mi->main_ptr6 != NULL)
1739b6ee846eSAlexander V. Chernikov 		free(mi->main_ptr6, M_IPFW);
1740ce2817b5SAlexander V. Chernikov }
1741ce2817b5SAlexander V. Chernikov 
1742c21034b7SAlexander V. Chernikov struct table_algo addr_hash = {
1743c21034b7SAlexander V. Chernikov 	.name		= "addr:hash",
1744c21034b7SAlexander V. Chernikov 	.type		= IPFW_TABLE_ADDR,
174557a1cf95SAlexander V. Chernikov 	.ta_buf_size	= sizeof(struct ta_buf_chash),
174674b941f0SAlexander V. Chernikov 	.init		= ta_init_chash,
174774b941f0SAlexander V. Chernikov 	.destroy	= ta_destroy_chash,
174874b941f0SAlexander V. Chernikov 	.prepare_add	= ta_prepare_add_chash,
174974b941f0SAlexander V. Chernikov 	.prepare_del	= ta_prepare_del_chash,
175074b941f0SAlexander V. Chernikov 	.add		= ta_add_chash,
175174b941f0SAlexander V. Chernikov 	.del		= ta_del_chash,
175274b941f0SAlexander V. Chernikov 	.flush_entry	= ta_flush_chash_entry,
175374b941f0SAlexander V. Chernikov 	.foreach	= ta_foreach_chash,
175474b941f0SAlexander V. Chernikov 	.dump_tentry	= ta_dump_chash_tentry,
175574b941f0SAlexander V. Chernikov 	.find_tentry	= ta_find_chash_tentry,
175674b941f0SAlexander V. Chernikov 	.print_config	= ta_print_chash_config,
17575f379342SAlexander V. Chernikov 	.dump_tinfo	= ta_dump_chash_tinfo,
1758301290bcSAlexander V. Chernikov 	.need_modify	= ta_need_modify_chash,
1759ce2817b5SAlexander V. Chernikov 	.prepare_mod	= ta_prepare_mod_chash,
1760ce2817b5SAlexander V. Chernikov 	.fill_mod	= ta_fill_mod_chash,
1761ce2817b5SAlexander V. Chernikov 	.modify		= ta_modify_chash,
1762ce2817b5SAlexander V. Chernikov 	.flush_mod	= ta_flush_mod_chash,
176374b941f0SAlexander V. Chernikov };
176474b941f0SAlexander V. Chernikov 
176574b941f0SAlexander V. Chernikov 
176674b941f0SAlexander V. Chernikov /*
176768394ec8SAlexander V. Chernikov  * Iface table cmds.
176868394ec8SAlexander V. Chernikov  *
176968394ec8SAlexander V. Chernikov  * Implementation:
177068394ec8SAlexander V. Chernikov  *
177168394ec8SAlexander V. Chernikov  * Runtime part:
177268394ec8SAlexander V. Chernikov  * - sorted array of "struct ifidx" pointed by ti->state.
1773b23d5de9SAlexander V. Chernikov  *   Array is allocated with rounding up to IFIDX_CHUNK. Only existing
177468394ec8SAlexander V. Chernikov  *   interfaces are stored in array, however its allocated size is
177568394ec8SAlexander V. Chernikov  *   sufficient to hold all table records if needed.
177668394ec8SAlexander V. Chernikov  * - current array size is stored in ti->data
177768394ec8SAlexander V. Chernikov  *
177868394ec8SAlexander V. Chernikov  * Table data:
177968394ec8SAlexander V. Chernikov  * - "struct iftable_cfg" is allocated to store table state (ta_state).
178068394ec8SAlexander V. Chernikov  * - All table records are stored inside namedobj instance.
17819f7d47b0SAlexander V. Chernikov  *
17829f7d47b0SAlexander V. Chernikov  */
17839f7d47b0SAlexander V. Chernikov 
178468394ec8SAlexander V. Chernikov struct ifidx {
178568394ec8SAlexander V. Chernikov 	uint16_t	kidx;
178668394ec8SAlexander V. Chernikov 	uint16_t	spare;
178768394ec8SAlexander V. Chernikov 	uint32_t	value;
178868394ec8SAlexander V. Chernikov };
17890cba2b28SAlexander V. Chernikov #define	DEFAULT_IFIDX_SIZE	64
179068394ec8SAlexander V. Chernikov 
179168394ec8SAlexander V. Chernikov struct iftable_cfg;
179268394ec8SAlexander V. Chernikov 
179368394ec8SAlexander V. Chernikov struct ifentry {
179468394ec8SAlexander V. Chernikov 	struct named_object	no;
179568394ec8SAlexander V. Chernikov 	struct ipfw_ifc		ic;
179668394ec8SAlexander V. Chernikov 	struct iftable_cfg	*icfg;
179768394ec8SAlexander V. Chernikov 	uint32_t		value;
179868394ec8SAlexander V. Chernikov 	int			linked;
179968394ec8SAlexander V. Chernikov };
180068394ec8SAlexander V. Chernikov 
180168394ec8SAlexander V. Chernikov struct iftable_cfg {
180268394ec8SAlexander V. Chernikov 	struct namedobj_instance	*ii;
180368394ec8SAlexander V. Chernikov 	struct ip_fw_chain	*ch;
180468394ec8SAlexander V. Chernikov 	struct table_info	*ti;
180568394ec8SAlexander V. Chernikov 	void	*main_ptr;
180668394ec8SAlexander V. Chernikov 	size_t	size;	/* Number of items allocated in array */
180768394ec8SAlexander V. Chernikov 	size_t	count;	/* Number of all items */
180868394ec8SAlexander V. Chernikov 	size_t	used;	/* Number of items _active_ now */
180968394ec8SAlexander V. Chernikov };
181068394ec8SAlexander V. Chernikov 
18110bce0c23SAlexander V. Chernikov struct ta_buf_ifidx
18120bce0c23SAlexander V. Chernikov {
18130bce0c23SAlexander V. Chernikov 	struct ifentry *ife;
18140bce0c23SAlexander V. Chernikov 	uint32_t value;
18150bce0c23SAlexander V. Chernikov };
181668394ec8SAlexander V. Chernikov 
181768394ec8SAlexander V. Chernikov int compare_ifidx(const void *k, const void *v);
181868394ec8SAlexander V. Chernikov static void if_notifier(struct ip_fw_chain *ch, void *cbdata, uint16_t ifindex);
181968394ec8SAlexander V. Chernikov 
182068394ec8SAlexander V. Chernikov int
182168394ec8SAlexander V. Chernikov compare_ifidx(const void *k, const void *v)
182268394ec8SAlexander V. Chernikov {
1823d4e1b515SAlexander V. Chernikov 	const struct ifidx *ifidx;
182468394ec8SAlexander V. Chernikov 	uint16_t key;
182568394ec8SAlexander V. Chernikov 
1826d4e1b515SAlexander V. Chernikov 	key = *((const uint16_t *)k);
1827d4e1b515SAlexander V. Chernikov 	ifidx = (const struct ifidx *)v;
182868394ec8SAlexander V. Chernikov 
182968394ec8SAlexander V. Chernikov 	if (key < ifidx->kidx)
183068394ec8SAlexander V. Chernikov 		return (-1);
183168394ec8SAlexander V. Chernikov 	else if (key > ifidx->kidx)
183268394ec8SAlexander V. Chernikov 		return (1);
183368394ec8SAlexander V. Chernikov 
183468394ec8SAlexander V. Chernikov 	return (0);
183568394ec8SAlexander V. Chernikov }
183668394ec8SAlexander V. Chernikov 
183768394ec8SAlexander V. Chernikov /*
183868394ec8SAlexander V. Chernikov  * Adds item @item with key @key into ascending-sorted array @base.
183968394ec8SAlexander V. Chernikov  * Assumes @base has enough additional storage.
184068394ec8SAlexander V. Chernikov  *
184168394ec8SAlexander V. Chernikov  * Returns 1 on success, 0 on duplicate key.
184268394ec8SAlexander V. Chernikov  */
18439f7d47b0SAlexander V. Chernikov static int
184468394ec8SAlexander V. Chernikov badd(const void *key, void *item, void *base, size_t nmemb,
184568394ec8SAlexander V. Chernikov     size_t size, int (*compar) (const void *, const void *))
184668394ec8SAlexander V. Chernikov {
184768394ec8SAlexander V. Chernikov 	int min, max, mid, shift, res;
184868394ec8SAlexander V. Chernikov 	caddr_t paddr;
184968394ec8SAlexander V. Chernikov 
185068394ec8SAlexander V. Chernikov 	if (nmemb == 0) {
185168394ec8SAlexander V. Chernikov 		memcpy(base, item, size);
185268394ec8SAlexander V. Chernikov 		return (1);
185368394ec8SAlexander V. Chernikov 	}
185468394ec8SAlexander V. Chernikov 
185568394ec8SAlexander V. Chernikov 	/* Binary search */
185668394ec8SAlexander V. Chernikov 	min = 0;
185768394ec8SAlexander V. Chernikov 	max = nmemb - 1;
185868394ec8SAlexander V. Chernikov 	mid = 0;
185968394ec8SAlexander V. Chernikov 	while (min <= max) {
186068394ec8SAlexander V. Chernikov 		mid = (min + max) / 2;
186168394ec8SAlexander V. Chernikov 		res = compar(key, (const void *)((caddr_t)base + mid * size));
186268394ec8SAlexander V. Chernikov 		if (res == 0)
186368394ec8SAlexander V. Chernikov 			return (0);
186468394ec8SAlexander V. Chernikov 
186568394ec8SAlexander V. Chernikov 		if (res > 0)
186668394ec8SAlexander V. Chernikov 			min = mid + 1;
186768394ec8SAlexander V. Chernikov 		else
186868394ec8SAlexander V. Chernikov 			max = mid - 1;
186968394ec8SAlexander V. Chernikov 	}
187068394ec8SAlexander V. Chernikov 
187168394ec8SAlexander V. Chernikov 	/* Item not found. */
187268394ec8SAlexander V. Chernikov 	res = compar(key, (const void *)((caddr_t)base + mid * size));
187368394ec8SAlexander V. Chernikov 	if (res > 0)
187468394ec8SAlexander V. Chernikov 		shift = mid + 1;
187568394ec8SAlexander V. Chernikov 	else
187668394ec8SAlexander V. Chernikov 		shift = mid;
187768394ec8SAlexander V. Chernikov 
187868394ec8SAlexander V. Chernikov 	paddr = (caddr_t)base + shift * size;
187968394ec8SAlexander V. Chernikov 	if (nmemb > shift)
188068394ec8SAlexander V. Chernikov 		memmove(paddr + size, paddr, (nmemb - shift) * size);
188168394ec8SAlexander V. Chernikov 
188268394ec8SAlexander V. Chernikov 	memcpy(paddr, item, size);
188368394ec8SAlexander V. Chernikov 
188468394ec8SAlexander V. Chernikov 	return (1);
188568394ec8SAlexander V. Chernikov }
188668394ec8SAlexander V. Chernikov 
188768394ec8SAlexander V. Chernikov /*
188868394ec8SAlexander V. Chernikov  * Deletes item with key @key from ascending-sorted array @base.
188968394ec8SAlexander V. Chernikov  *
189068394ec8SAlexander V. Chernikov  * Returns 1 on success, 0 for non-existent key.
189168394ec8SAlexander V. Chernikov  */
189268394ec8SAlexander V. Chernikov static int
189368394ec8SAlexander V. Chernikov bdel(const void *key, void *base, size_t nmemb, size_t size,
189468394ec8SAlexander V. Chernikov     int (*compar) (const void *, const void *))
189568394ec8SAlexander V. Chernikov {
189668394ec8SAlexander V. Chernikov 	caddr_t item;
189768394ec8SAlexander V. Chernikov 	size_t sz;
189868394ec8SAlexander V. Chernikov 
189968394ec8SAlexander V. Chernikov 	item = (caddr_t)bsearch(key, base, nmemb, size, compar);
190068394ec8SAlexander V. Chernikov 
190168394ec8SAlexander V. Chernikov 	if (item == NULL)
190268394ec8SAlexander V. Chernikov 		return (0);
190368394ec8SAlexander V. Chernikov 
190468394ec8SAlexander V. Chernikov 	sz = (caddr_t)base + nmemb * size - item;
190568394ec8SAlexander V. Chernikov 
190668394ec8SAlexander V. Chernikov 	if (sz > 0)
190768394ec8SAlexander V. Chernikov 		memmove(item, item + size, sz);
190868394ec8SAlexander V. Chernikov 
190968394ec8SAlexander V. Chernikov 	return (1);
191068394ec8SAlexander V. Chernikov }
191168394ec8SAlexander V. Chernikov 
191268394ec8SAlexander V. Chernikov static struct ifidx *
191368394ec8SAlexander V. Chernikov ifidx_find(struct table_info *ti, void *key)
191468394ec8SAlexander V. Chernikov {
191568394ec8SAlexander V. Chernikov 	struct ifidx *ifi;
191668394ec8SAlexander V. Chernikov 
191768394ec8SAlexander V. Chernikov 	ifi = bsearch(key, ti->state, ti->data, sizeof(struct ifidx),
191868394ec8SAlexander V. Chernikov 	    compare_ifidx);
191968394ec8SAlexander V. Chernikov 
192068394ec8SAlexander V. Chernikov 	return (ifi);
192168394ec8SAlexander V. Chernikov }
192268394ec8SAlexander V. Chernikov 
192368394ec8SAlexander V. Chernikov static int
192468394ec8SAlexander V. Chernikov ta_lookup_ifidx(struct table_info *ti, void *key, uint32_t keylen,
19259f7d47b0SAlexander V. Chernikov     uint32_t *val)
19269f7d47b0SAlexander V. Chernikov {
192768394ec8SAlexander V. Chernikov 	struct ifidx *ifi;
19289f7d47b0SAlexander V. Chernikov 
192968394ec8SAlexander V. Chernikov 	ifi = ifidx_find(ti, key);
19309f7d47b0SAlexander V. Chernikov 
193168394ec8SAlexander V. Chernikov 	if (ifi != NULL) {
193268394ec8SAlexander V. Chernikov 		*val = ifi->value;
19339f7d47b0SAlexander V. Chernikov 		return (1);
19349f7d47b0SAlexander V. Chernikov 	}
19359f7d47b0SAlexander V. Chernikov 
19369f7d47b0SAlexander V. Chernikov 	return (0);
19379f7d47b0SAlexander V. Chernikov }
19389f7d47b0SAlexander V. Chernikov 
19399f7d47b0SAlexander V. Chernikov static int
194068394ec8SAlexander V. Chernikov ta_init_ifidx(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
1941914bffb6SAlexander V. Chernikov     char *data, uint8_t tflags)
19429f7d47b0SAlexander V. Chernikov {
194368394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
19449f7d47b0SAlexander V. Chernikov 
194568394ec8SAlexander V. Chernikov 	icfg = malloc(sizeof(struct iftable_cfg), M_IPFW, M_WAITOK | M_ZERO);
19469f7d47b0SAlexander V. Chernikov 
19470cba2b28SAlexander V. Chernikov 	icfg->ii = ipfw_objhash_create(DEFAULT_IFIDX_SIZE);
19480cba2b28SAlexander V. Chernikov 	icfg->size = DEFAULT_IFIDX_SIZE;
19490bce0c23SAlexander V. Chernikov 	icfg->main_ptr = malloc(sizeof(struct ifidx) * icfg->size, M_IPFW,
195068394ec8SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
195168394ec8SAlexander V. Chernikov 	icfg->ch = ch;
19529f7d47b0SAlexander V. Chernikov 
195368394ec8SAlexander V. Chernikov 	*ta_state = icfg;
195468394ec8SAlexander V. Chernikov 	ti->state = icfg->main_ptr;
195568394ec8SAlexander V. Chernikov 	ti->lookup = ta_lookup_ifidx;
19569f7d47b0SAlexander V. Chernikov 
19579f7d47b0SAlexander V. Chernikov 	return (0);
19589f7d47b0SAlexander V. Chernikov }
19599f7d47b0SAlexander V. Chernikov 
196068394ec8SAlexander V. Chernikov /*
196168394ec8SAlexander V. Chernikov  * Handle tableinfo @ti pointer change (on table array resize).
196268394ec8SAlexander V. Chernikov  */
196368394ec8SAlexander V. Chernikov static void
196468394ec8SAlexander V. Chernikov ta_change_ti_ifidx(void *ta_state, struct table_info *ti)
196568394ec8SAlexander V. Chernikov {
196668394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
196768394ec8SAlexander V. Chernikov 
196868394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
196968394ec8SAlexander V. Chernikov 	icfg->ti = ti;
197068394ec8SAlexander V. Chernikov }
19719f7d47b0SAlexander V. Chernikov 
19729f7d47b0SAlexander V. Chernikov static void
197368394ec8SAlexander V. Chernikov destroy_ifidx_locked(struct namedobj_instance *ii, struct named_object *no,
197468394ec8SAlexander V. Chernikov     void *arg)
19759f7d47b0SAlexander V. Chernikov {
197668394ec8SAlexander V. Chernikov 	struct ifentry *ife;
197768394ec8SAlexander V. Chernikov 	struct ip_fw_chain *ch;
19789f7d47b0SAlexander V. Chernikov 
197968394ec8SAlexander V. Chernikov 	ch = (struct ip_fw_chain *)arg;
198068394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)no;
198168394ec8SAlexander V. Chernikov 
198268394ec8SAlexander V. Chernikov 	ipfw_iface_del_notify(ch, &ife->ic);
198368394ec8SAlexander V. Chernikov 	free(ife, M_IPFW_TBL);
19849f7d47b0SAlexander V. Chernikov }
19859f7d47b0SAlexander V. Chernikov 
198668394ec8SAlexander V. Chernikov 
198768394ec8SAlexander V. Chernikov /*
198868394ec8SAlexander V. Chernikov  * Destroys table @ti
198968394ec8SAlexander V. Chernikov  */
199068394ec8SAlexander V. Chernikov static void
199168394ec8SAlexander V. Chernikov ta_destroy_ifidx(void *ta_state, struct table_info *ti)
19929f7d47b0SAlexander V. Chernikov {
199368394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
199468394ec8SAlexander V. Chernikov 	struct ip_fw_chain *ch;
199568394ec8SAlexander V. Chernikov 
199668394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
199768394ec8SAlexander V. Chernikov 	ch = icfg->ch;
199868394ec8SAlexander V. Chernikov 
199968394ec8SAlexander V. Chernikov 	if (icfg->main_ptr != NULL)
200068394ec8SAlexander V. Chernikov 		free(icfg->main_ptr, M_IPFW);
200168394ec8SAlexander V. Chernikov 
200268394ec8SAlexander V. Chernikov 	ipfw_objhash_foreach(icfg->ii, destroy_ifidx_locked, ch);
200368394ec8SAlexander V. Chernikov 
200468394ec8SAlexander V. Chernikov 	ipfw_objhash_destroy(icfg->ii);
200568394ec8SAlexander V. Chernikov 
200668394ec8SAlexander V. Chernikov 	free(icfg, M_IPFW);
200768394ec8SAlexander V. Chernikov }
200868394ec8SAlexander V. Chernikov 
200968394ec8SAlexander V. Chernikov /*
20105f379342SAlexander V. Chernikov  * Provide algo-specific table info
20115f379342SAlexander V. Chernikov  */
20125f379342SAlexander V. Chernikov static void
20135f379342SAlexander V. Chernikov ta_dump_ifidx_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo)
20145f379342SAlexander V. Chernikov {
20155f379342SAlexander V. Chernikov 	struct iftable_cfg *cfg;
20165f379342SAlexander V. Chernikov 
20175f379342SAlexander V. Chernikov 	cfg = (struct iftable_cfg *)ta_state;
20185f379342SAlexander V. Chernikov 
20195f379342SAlexander V. Chernikov 	tinfo->taclass4 = IPFW_TACLASS_ARRAY;
20205f379342SAlexander V. Chernikov 	tinfo->size4 = cfg->size;
20215f379342SAlexander V. Chernikov 	tinfo->count4 = cfg->used;
20225f379342SAlexander V. Chernikov 	tinfo->itemsize4 = sizeof(struct ifidx);
20235f379342SAlexander V. Chernikov }
20245f379342SAlexander V. Chernikov 
20255f379342SAlexander V. Chernikov /*
202668394ec8SAlexander V. Chernikov  * Prepare state to add to the table:
202768394ec8SAlexander V. Chernikov  * allocate ifentry and reference needed interface.
202868394ec8SAlexander V. Chernikov  */
20299f7d47b0SAlexander V. Chernikov static int
203068394ec8SAlexander V. Chernikov ta_prepare_add_ifidx(struct ip_fw_chain *ch, struct tentry_info *tei,
203168394ec8SAlexander V. Chernikov     void *ta_buf)
203268394ec8SAlexander V. Chernikov {
203368394ec8SAlexander V. Chernikov 	struct ta_buf_ifidx *tb;
203468394ec8SAlexander V. Chernikov 	char *ifname;
203568394ec8SAlexander V. Chernikov 	struct ifentry *ife;
203668394ec8SAlexander V. Chernikov 
203768394ec8SAlexander V. Chernikov 	tb = (struct ta_buf_ifidx *)ta_buf;
203868394ec8SAlexander V. Chernikov 
203968394ec8SAlexander V. Chernikov 	/* Check if string is terminated */
204068394ec8SAlexander V. Chernikov 	ifname = (char *)tei->paddr;
204168394ec8SAlexander V. Chernikov 	if (strnlen(ifname, IF_NAMESIZE) == IF_NAMESIZE)
204268394ec8SAlexander V. Chernikov 		return (EINVAL);
204368394ec8SAlexander V. Chernikov 
204468394ec8SAlexander V. Chernikov 	ife = malloc(sizeof(struct ifentry), M_IPFW_TBL, M_WAITOK | M_ZERO);
204568394ec8SAlexander V. Chernikov 	ife->ic.cb = if_notifier;
204668394ec8SAlexander V. Chernikov 	ife->ic.cbdata = ife;
204768394ec8SAlexander V. Chernikov 
2048*8ebca97fSAlexander V. Chernikov 	if (ipfw_iface_ref(ch, ifname, &ife->ic) != 0) {
2049*8ebca97fSAlexander V. Chernikov 		free(ife, M_IPFW_TBL);
205068394ec8SAlexander V. Chernikov 		return (EINVAL);
2051*8ebca97fSAlexander V. Chernikov 	}
205268394ec8SAlexander V. Chernikov 
205368394ec8SAlexander V. Chernikov 	/* Use ipfw_iface 'ifname' field as stable storage */
205468394ec8SAlexander V. Chernikov 	ife->no.name = ife->ic.iface->ifname;
205568394ec8SAlexander V. Chernikov 
205668394ec8SAlexander V. Chernikov 	tb->ife = ife;
205768394ec8SAlexander V. Chernikov 
205868394ec8SAlexander V. Chernikov 	return (0);
205968394ec8SAlexander V. Chernikov }
206068394ec8SAlexander V. Chernikov 
206168394ec8SAlexander V. Chernikov static int
2062adea6201SAlexander V. Chernikov ta_add_ifidx(void *ta_state, struct table_info *ti, struct tentry_info *tei,
2063b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
206468394ec8SAlexander V. Chernikov {
206568394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
206668394ec8SAlexander V. Chernikov 	struct ifentry *ife, *tmp;
206768394ec8SAlexander V. Chernikov 	struct ta_buf_ifidx *tb;
206868394ec8SAlexander V. Chernikov 	struct ipfw_iface *iif;
206968394ec8SAlexander V. Chernikov 	struct ifidx *ifi;
207068394ec8SAlexander V. Chernikov 	char *ifname;
2071648e8380SAlexander V. Chernikov 	uint32_t value;
207268394ec8SAlexander V. Chernikov 
207368394ec8SAlexander V. Chernikov 	tb = (struct ta_buf_ifidx *)ta_buf;
207468394ec8SAlexander V. Chernikov 	ifname = (char *)tei->paddr;
207568394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
207668394ec8SAlexander V. Chernikov 	ife = tb->ife;
207768394ec8SAlexander V. Chernikov 
207868394ec8SAlexander V. Chernikov 	ife->icfg = icfg;
207913263632SAlexander V. Chernikov 	ife->value = tei->value;
208068394ec8SAlexander V. Chernikov 
208168394ec8SAlexander V. Chernikov 	tmp = (struct ifentry *)ipfw_objhash_lookup_name(icfg->ii, 0, ifname);
208268394ec8SAlexander V. Chernikov 
208368394ec8SAlexander V. Chernikov 	if (tmp != NULL) {
208468394ec8SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
208568394ec8SAlexander V. Chernikov 			return (EEXIST);
208668394ec8SAlexander V. Chernikov 
2087648e8380SAlexander V. Chernikov 		/* Exchange values in @tmp and @tei */
2088648e8380SAlexander V. Chernikov 		value = tmp->value;
2089648e8380SAlexander V. Chernikov 		tmp->value = tei->value;
2090648e8380SAlexander V. Chernikov 		tei->value = value;
209168394ec8SAlexander V. Chernikov 
2092648e8380SAlexander V. Chernikov 		iif = tmp->ic.iface;
209368394ec8SAlexander V. Chernikov 		if (iif->resolved != 0) {
2094648e8380SAlexander V. Chernikov 			/* We have to update runtime value, too */
209568394ec8SAlexander V. Chernikov 			ifi = ifidx_find(ti, &iif->ifindex);
209668394ec8SAlexander V. Chernikov 			ifi->value = ife->value;
209768394ec8SAlexander V. Chernikov 		}
209868394ec8SAlexander V. Chernikov 
209968394ec8SAlexander V. Chernikov 		/* Indicate that update has happened instead of addition */
210068394ec8SAlexander V. Chernikov 		tei->flags |= TEI_FLAGS_UPDATED;
2101adea6201SAlexander V. Chernikov 		*pnum = 0;
210268394ec8SAlexander V. Chernikov 		return (0);
210368394ec8SAlexander V. Chernikov 	}
210468394ec8SAlexander V. Chernikov 
21054c0c07a5SAlexander V. Chernikov 	if ((tei->flags & TEI_FLAGS_DONTADD) != 0)
21064c0c07a5SAlexander V. Chernikov 		return (EFBIG);
21074c0c07a5SAlexander V. Chernikov 
210868394ec8SAlexander V. Chernikov 	/* Link to internal list */
210968394ec8SAlexander V. Chernikov 	ipfw_objhash_add(icfg->ii, &ife->no);
211068394ec8SAlexander V. Chernikov 
211168394ec8SAlexander V. Chernikov 	/* Link notifier (possible running its callback) */
211268394ec8SAlexander V. Chernikov 	ipfw_iface_add_notify(icfg->ch, &ife->ic);
211368394ec8SAlexander V. Chernikov 	icfg->count++;
211468394ec8SAlexander V. Chernikov 
211568394ec8SAlexander V. Chernikov 	tb->ife = NULL;
2116adea6201SAlexander V. Chernikov 	*pnum = 1;
211768394ec8SAlexander V. Chernikov 
211868394ec8SAlexander V. Chernikov 	return (0);
211968394ec8SAlexander V. Chernikov }
212068394ec8SAlexander V. Chernikov 
212168394ec8SAlexander V. Chernikov /*
212268394ec8SAlexander V. Chernikov  * Prepare to delete key from table.
212368394ec8SAlexander V. Chernikov  * Do basic interface name checks.
212468394ec8SAlexander V. Chernikov  */
212568394ec8SAlexander V. Chernikov static int
212668394ec8SAlexander V. Chernikov ta_prepare_del_ifidx(struct ip_fw_chain *ch, struct tentry_info *tei,
212768394ec8SAlexander V. Chernikov     void *ta_buf)
21289f7d47b0SAlexander V. Chernikov {
212974b941f0SAlexander V. Chernikov 	struct ta_buf_ifidx *tb;
2130e0a8b9eeSAlexander V. Chernikov 	char *ifname;
21319f7d47b0SAlexander V. Chernikov 
213274b941f0SAlexander V. Chernikov 	tb = (struct ta_buf_ifidx *)ta_buf;
21339f7d47b0SAlexander V. Chernikov 
21349f7d47b0SAlexander V. Chernikov 	/* Check if string is terminated */
2135e0a8b9eeSAlexander V. Chernikov 	ifname = (char *)tei->paddr;
2136e0a8b9eeSAlexander V. Chernikov 	if (strnlen(ifname, IF_NAMESIZE) == IF_NAMESIZE)
21379f7d47b0SAlexander V. Chernikov 		return (EINVAL);
21389f7d47b0SAlexander V. Chernikov 
21399f7d47b0SAlexander V. Chernikov 	return (0);
21409f7d47b0SAlexander V. Chernikov }
21419f7d47b0SAlexander V. Chernikov 
214268394ec8SAlexander V. Chernikov /*
214368394ec8SAlexander V. Chernikov  * Remove key from both configuration list and
214468394ec8SAlexander V. Chernikov  * runtime array. Removed interface notification.
214568394ec8SAlexander V. Chernikov  */
21469f7d47b0SAlexander V. Chernikov static int
2147adea6201SAlexander V. Chernikov ta_del_ifidx(void *ta_state, struct table_info *ti, struct tentry_info *tei,
2148b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
21499f7d47b0SAlexander V. Chernikov {
215068394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
215168394ec8SAlexander V. Chernikov 	struct ifentry *ife;
215268394ec8SAlexander V. Chernikov 	struct ta_buf_ifidx *tb;
215368394ec8SAlexander V. Chernikov 	char *ifname;
215468394ec8SAlexander V. Chernikov 	uint16_t ifindex;
215568394ec8SAlexander V. Chernikov 	int res;
21569f7d47b0SAlexander V. Chernikov 
215768394ec8SAlexander V. Chernikov 	tb = (struct ta_buf_ifidx *)ta_buf;
215868394ec8SAlexander V. Chernikov 	ifname = (char *)tei->paddr;
215968394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
216068394ec8SAlexander V. Chernikov 	ife = tb->ife;
21619f7d47b0SAlexander V. Chernikov 
216268394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)ipfw_objhash_lookup_name(icfg->ii, 0, ifname);
21639f7d47b0SAlexander V. Chernikov 
216468394ec8SAlexander V. Chernikov 	if (ife == NULL)
216581d3153dSAlexander V. Chernikov 		return (ENOENT);
21669f7d47b0SAlexander V. Chernikov 
216768394ec8SAlexander V. Chernikov 	if (ife->linked != 0) {
216868394ec8SAlexander V. Chernikov 		/* We have to remove item from runtime */
216968394ec8SAlexander V. Chernikov 		ifindex = ife->ic.iface->ifindex;
217068394ec8SAlexander V. Chernikov 
217168394ec8SAlexander V. Chernikov 		res = bdel(&ifindex, icfg->main_ptr, icfg->used,
217268394ec8SAlexander V. Chernikov 		    sizeof(struct ifidx), compare_ifidx);
217368394ec8SAlexander V. Chernikov 
217468394ec8SAlexander V. Chernikov 		KASSERT(res == 1, ("index %d does not exist", ifindex));
217568394ec8SAlexander V. Chernikov 		icfg->used--;
217668394ec8SAlexander V. Chernikov 		ti->data = icfg->used;
217768394ec8SAlexander V. Chernikov 		ife->linked = 0;
217868394ec8SAlexander V. Chernikov 	}
217968394ec8SAlexander V. Chernikov 
218068394ec8SAlexander V. Chernikov 	/* Unlink from local list */
218168394ec8SAlexander V. Chernikov 	ipfw_objhash_del(icfg->ii, &ife->no);
218268394ec8SAlexander V. Chernikov 	/* Unlink notifier */
218368394ec8SAlexander V. Chernikov 	ipfw_iface_del_notify(icfg->ch, &ife->ic);
218468394ec8SAlexander V. Chernikov 
218568394ec8SAlexander V. Chernikov 	icfg->count--;
2186648e8380SAlexander V. Chernikov 	tei->value = ife->value;
218768394ec8SAlexander V. Chernikov 
218868394ec8SAlexander V. Chernikov 	tb->ife = ife;
2189adea6201SAlexander V. Chernikov 	*pnum = 1;
219068394ec8SAlexander V. Chernikov 
21919f7d47b0SAlexander V. Chernikov 	return (0);
21929f7d47b0SAlexander V. Chernikov }
21939f7d47b0SAlexander V. Chernikov 
219468394ec8SAlexander V. Chernikov /*
219568394ec8SAlexander V. Chernikov  * Flush deleted entry.
219668394ec8SAlexander V. Chernikov  * Drops interface reference and frees entry.
219768394ec8SAlexander V. Chernikov  */
21989f7d47b0SAlexander V. Chernikov static void
219968394ec8SAlexander V. Chernikov ta_flush_ifidx_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
220068394ec8SAlexander V. Chernikov     void *ta_buf)
22019f7d47b0SAlexander V. Chernikov {
220268394ec8SAlexander V. Chernikov 	struct ta_buf_ifidx *tb;
22039f7d47b0SAlexander V. Chernikov 
220468394ec8SAlexander V. Chernikov 	tb = (struct ta_buf_ifidx *)ta_buf;
22059f7d47b0SAlexander V. Chernikov 
220668394ec8SAlexander V. Chernikov 	if (tb->ife != NULL) {
220768394ec8SAlexander V. Chernikov 		/* Unlink first */
220868394ec8SAlexander V. Chernikov 		ipfw_iface_unref(ch, &tb->ife->ic);
220968394ec8SAlexander V. Chernikov 		free(tb->ife, M_IPFW_TBL);
221068394ec8SAlexander V. Chernikov 	}
221168394ec8SAlexander V. Chernikov }
221268394ec8SAlexander V. Chernikov 
221368394ec8SAlexander V. Chernikov 
221468394ec8SAlexander V. Chernikov /*
221568394ec8SAlexander V. Chernikov  * Handle interface announce/withdrawal for particular table.
221668394ec8SAlexander V. Chernikov  * Every real runtime array modification happens here.
221768394ec8SAlexander V. Chernikov  */
221868394ec8SAlexander V. Chernikov static void
221968394ec8SAlexander V. Chernikov if_notifier(struct ip_fw_chain *ch, void *cbdata, uint16_t ifindex)
222068394ec8SAlexander V. Chernikov {
222168394ec8SAlexander V. Chernikov 	struct ifentry *ife;
222268394ec8SAlexander V. Chernikov 	struct ifidx ifi;
222368394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
222468394ec8SAlexander V. Chernikov 	struct table_info *ti;
222568394ec8SAlexander V. Chernikov 	int res;
222668394ec8SAlexander V. Chernikov 
222768394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)cbdata;
222868394ec8SAlexander V. Chernikov 	icfg = ife->icfg;
222968394ec8SAlexander V. Chernikov 	ti = icfg->ti;
223068394ec8SAlexander V. Chernikov 
223168394ec8SAlexander V. Chernikov 	KASSERT(ti != NULL, ("ti=NULL, check change_ti handler"));
223268394ec8SAlexander V. Chernikov 
223368394ec8SAlexander V. Chernikov 	if (ife->linked == 0 && ifindex != 0) {
223468394ec8SAlexander V. Chernikov 		/* Interface announce */
223568394ec8SAlexander V. Chernikov 		ifi.kidx = ifindex;
223668394ec8SAlexander V. Chernikov 		ifi.spare = 0;
223768394ec8SAlexander V. Chernikov 		ifi.value = ife->value;
223868394ec8SAlexander V. Chernikov 		res = badd(&ifindex, &ifi, icfg->main_ptr, icfg->used,
223968394ec8SAlexander V. Chernikov 		    sizeof(struct ifidx), compare_ifidx);
224068394ec8SAlexander V. Chernikov 		KASSERT(res == 1, ("index %d already exists", ifindex));
224168394ec8SAlexander V. Chernikov 		icfg->used++;
224268394ec8SAlexander V. Chernikov 		ti->data = icfg->used;
224368394ec8SAlexander V. Chernikov 		ife->linked = 1;
224468394ec8SAlexander V. Chernikov 	} else if (ife->linked != 0 && ifindex == 0) {
224568394ec8SAlexander V. Chernikov 		/* Interface withdrawal */
224668394ec8SAlexander V. Chernikov 		ifindex = ife->ic.iface->ifindex;
224768394ec8SAlexander V. Chernikov 
224868394ec8SAlexander V. Chernikov 		res = bdel(&ifindex, icfg->main_ptr, icfg->used,
224968394ec8SAlexander V. Chernikov 		    sizeof(struct ifidx), compare_ifidx);
225068394ec8SAlexander V. Chernikov 
225168394ec8SAlexander V. Chernikov 		KASSERT(res == 1, ("index %d does not exist", ifindex));
225268394ec8SAlexander V. Chernikov 		icfg->used--;
225368394ec8SAlexander V. Chernikov 		ti->data = icfg->used;
225468394ec8SAlexander V. Chernikov 		ife->linked = 0;
225568394ec8SAlexander V. Chernikov 	}
225668394ec8SAlexander V. Chernikov }
225768394ec8SAlexander V. Chernikov 
225868394ec8SAlexander V. Chernikov 
225968394ec8SAlexander V. Chernikov /*
226068394ec8SAlexander V. Chernikov  * Table growing callbacks.
226168394ec8SAlexander V. Chernikov  */
226268394ec8SAlexander V. Chernikov 
2263b6ee846eSAlexander V. Chernikov static int
2264301290bcSAlexander V. Chernikov ta_need_modify_ifidx(void *ta_state, struct table_info *ti, uint32_t count,
2265b6ee846eSAlexander V. Chernikov     uint64_t *pflags)
2266b6ee846eSAlexander V. Chernikov {
2267b6ee846eSAlexander V. Chernikov 	struct iftable_cfg *cfg;
22680bce0c23SAlexander V. Chernikov 	uint32_t size;
2269b6ee846eSAlexander V. Chernikov 
2270b6ee846eSAlexander V. Chernikov 	cfg = (struct iftable_cfg *)ta_state;
2271b6ee846eSAlexander V. Chernikov 
22720bce0c23SAlexander V. Chernikov 	size = cfg->size;
22730bce0c23SAlexander V. Chernikov 	while (size < cfg->count + count)
22740bce0c23SAlexander V. Chernikov 		size *= 2;
22750bce0c23SAlexander V. Chernikov 
22760bce0c23SAlexander V. Chernikov 	if (size != cfg->size) {
22770bce0c23SAlexander V. Chernikov 		*pflags = size;
2278301290bcSAlexander V. Chernikov 		return (1);
2279b6ee846eSAlexander V. Chernikov 	}
2280b6ee846eSAlexander V. Chernikov 
2281301290bcSAlexander V. Chernikov 	return (0);
2282b6ee846eSAlexander V. Chernikov }
2283b6ee846eSAlexander V. Chernikov 
228468394ec8SAlexander V. Chernikov /*
228568394ec8SAlexander V. Chernikov  * Allocate ned, larger runtime ifidx array.
228668394ec8SAlexander V. Chernikov  */
228768394ec8SAlexander V. Chernikov static int
228868394ec8SAlexander V. Chernikov ta_prepare_mod_ifidx(void *ta_buf, uint64_t *pflags)
228968394ec8SAlexander V. Chernikov {
22900bce0c23SAlexander V. Chernikov 	struct mod_item *mi;
229168394ec8SAlexander V. Chernikov 
22920bce0c23SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
229368394ec8SAlexander V. Chernikov 
22940bce0c23SAlexander V. Chernikov 	memset(mi, 0, sizeof(struct mod_item));
229568394ec8SAlexander V. Chernikov 	mi->size = *pflags;
229668394ec8SAlexander V. Chernikov 	mi->main_ptr = malloc(sizeof(struct ifidx) * mi->size, M_IPFW,
229768394ec8SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
229868394ec8SAlexander V. Chernikov 
229968394ec8SAlexander V. Chernikov 	return (0);
230068394ec8SAlexander V. Chernikov }
230168394ec8SAlexander V. Chernikov 
230268394ec8SAlexander V. Chernikov /*
230368394ec8SAlexander V. Chernikov  * Copy data from old runtime array to new one.
230468394ec8SAlexander V. Chernikov  */
230568394ec8SAlexander V. Chernikov static int
230668394ec8SAlexander V. Chernikov ta_fill_mod_ifidx(void *ta_state, struct table_info *ti, void *ta_buf,
230768394ec8SAlexander V. Chernikov     uint64_t *pflags)
230868394ec8SAlexander V. Chernikov {
23090bce0c23SAlexander V. Chernikov 	struct mod_item *mi;
231068394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
231168394ec8SAlexander V. Chernikov 
23120bce0c23SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
231368394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
231468394ec8SAlexander V. Chernikov 
231568394ec8SAlexander V. Chernikov 	/* Check if we still need to grow array */
231668394ec8SAlexander V. Chernikov 	if (icfg->size >= mi->size) {
231768394ec8SAlexander V. Chernikov 		*pflags = 0;
231868394ec8SAlexander V. Chernikov 		return (0);
231968394ec8SAlexander V. Chernikov 	}
232068394ec8SAlexander V. Chernikov 
232168394ec8SAlexander V. Chernikov 	memcpy(mi->main_ptr, icfg->main_ptr, icfg->used * sizeof(struct ifidx));
232268394ec8SAlexander V. Chernikov 
232368394ec8SAlexander V. Chernikov 	return (0);
232468394ec8SAlexander V. Chernikov }
232568394ec8SAlexander V. Chernikov 
232668394ec8SAlexander V. Chernikov /*
232768394ec8SAlexander V. Chernikov  * Switch old & new arrays.
232868394ec8SAlexander V. Chernikov  */
2329301290bcSAlexander V. Chernikov static void
233068394ec8SAlexander V. Chernikov ta_modify_ifidx(void *ta_state, struct table_info *ti, void *ta_buf,
233168394ec8SAlexander V. Chernikov     uint64_t pflags)
233268394ec8SAlexander V. Chernikov {
23330bce0c23SAlexander V. Chernikov 	struct mod_item *mi;
233468394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
233568394ec8SAlexander V. Chernikov 	void *old_ptr;
233668394ec8SAlexander V. Chernikov 
23370bce0c23SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
233868394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
233968394ec8SAlexander V. Chernikov 
234068394ec8SAlexander V. Chernikov 	old_ptr = icfg->main_ptr;
234168394ec8SAlexander V. Chernikov 	icfg->main_ptr = mi->main_ptr;
234268394ec8SAlexander V. Chernikov 	icfg->size = mi->size;
234368394ec8SAlexander V. Chernikov 	ti->state = icfg->main_ptr;
234468394ec8SAlexander V. Chernikov 
234568394ec8SAlexander V. Chernikov 	mi->main_ptr = old_ptr;
234668394ec8SAlexander V. Chernikov }
234768394ec8SAlexander V. Chernikov 
234868394ec8SAlexander V. Chernikov /*
234968394ec8SAlexander V. Chernikov  * Free unneded array.
235068394ec8SAlexander V. Chernikov  */
235168394ec8SAlexander V. Chernikov static void
235268394ec8SAlexander V. Chernikov ta_flush_mod_ifidx(void *ta_buf)
235368394ec8SAlexander V. Chernikov {
23540bce0c23SAlexander V. Chernikov 	struct mod_item *mi;
235568394ec8SAlexander V. Chernikov 
23560bce0c23SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
235768394ec8SAlexander V. Chernikov 	if (mi->main_ptr != NULL)
235868394ec8SAlexander V. Chernikov 		free(mi->main_ptr, M_IPFW);
23599f7d47b0SAlexander V. Chernikov }
23609f7d47b0SAlexander V. Chernikov 
23619f7d47b0SAlexander V. Chernikov static int
236268394ec8SAlexander V. Chernikov ta_dump_ifidx_tentry(void *ta_state, struct table_info *ti, void *e,
236381d3153dSAlexander V. Chernikov     ipfw_obj_tentry *tent)
23649f7d47b0SAlexander V. Chernikov {
236568394ec8SAlexander V. Chernikov 	struct ifentry *ife;
23669f7d47b0SAlexander V. Chernikov 
236768394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)e;
236868394ec8SAlexander V. Chernikov 
236981d3153dSAlexander V. Chernikov 	tent->masklen = 8 * IF_NAMESIZE;
237068394ec8SAlexander V. Chernikov 	memcpy(&tent->k, ife->no.name, IF_NAMESIZE);
23710cba2b28SAlexander V. Chernikov 	tent->v.kidx = ife->value;
23729f7d47b0SAlexander V. Chernikov 
23739f7d47b0SAlexander V. Chernikov 	return (0);
23749f7d47b0SAlexander V. Chernikov }
23759f7d47b0SAlexander V. Chernikov 
237681d3153dSAlexander V. Chernikov static int
2377914bffb6SAlexander V. Chernikov ta_find_ifidx_tentry(void *ta_state, struct table_info *ti,
2378914bffb6SAlexander V. Chernikov     ipfw_obj_tentry *tent)
237981d3153dSAlexander V. Chernikov {
238068394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
238168394ec8SAlexander V. Chernikov 	struct ifentry *ife;
238268394ec8SAlexander V. Chernikov 	char *ifname;
238381d3153dSAlexander V. Chernikov 
238468394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
2385914bffb6SAlexander V. Chernikov 	ifname = tent->k.iface;
238681d3153dSAlexander V. Chernikov 
238768394ec8SAlexander V. Chernikov 	if (strnlen(ifname, IF_NAMESIZE) == IF_NAMESIZE)
238868394ec8SAlexander V. Chernikov 		return (EINVAL);
238981d3153dSAlexander V. Chernikov 
239068394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)ipfw_objhash_lookup_name(icfg->ii, 0, ifname);
239168394ec8SAlexander V. Chernikov 
239268394ec8SAlexander V. Chernikov 	if (ife != NULL) {
239368394ec8SAlexander V. Chernikov 		ta_dump_ifidx_tentry(ta_state, ti, ife, tent);
239481d3153dSAlexander V. Chernikov 		return (0);
239581d3153dSAlexander V. Chernikov 	}
239681d3153dSAlexander V. Chernikov 
239781d3153dSAlexander V. Chernikov 	return (ENOENT);
239881d3153dSAlexander V. Chernikov }
239981d3153dSAlexander V. Chernikov 
240068394ec8SAlexander V. Chernikov struct wa_ifidx {
240168394ec8SAlexander V. Chernikov 	ta_foreach_f	*f;
240268394ec8SAlexander V. Chernikov 	void		*arg;
240368394ec8SAlexander V. Chernikov };
240468394ec8SAlexander V. Chernikov 
24059f7d47b0SAlexander V. Chernikov static void
240668394ec8SAlexander V. Chernikov foreach_ifidx(struct namedobj_instance *ii, struct named_object *no,
24079f7d47b0SAlexander V. Chernikov     void *arg)
24089f7d47b0SAlexander V. Chernikov {
240968394ec8SAlexander V. Chernikov 	struct ifentry *ife;
241068394ec8SAlexander V. Chernikov 	struct wa_ifidx *wa;
24119f7d47b0SAlexander V. Chernikov 
241268394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)no;
241368394ec8SAlexander V. Chernikov 	wa = (struct wa_ifidx *)arg;
241468394ec8SAlexander V. Chernikov 
241568394ec8SAlexander V. Chernikov 	wa->f(ife, wa->arg);
24169f7d47b0SAlexander V. Chernikov }
24179f7d47b0SAlexander V. Chernikov 
241868394ec8SAlexander V. Chernikov static void
241968394ec8SAlexander V. Chernikov ta_foreach_ifidx(void *ta_state, struct table_info *ti, ta_foreach_f *f,
242068394ec8SAlexander V. Chernikov     void *arg)
242168394ec8SAlexander V. Chernikov {
242268394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
242368394ec8SAlexander V. Chernikov 	struct wa_ifidx wa;
242468394ec8SAlexander V. Chernikov 
242568394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
242668394ec8SAlexander V. Chernikov 
242768394ec8SAlexander V. Chernikov 	wa.f = f;
242868394ec8SAlexander V. Chernikov 	wa.arg = arg;
242968394ec8SAlexander V. Chernikov 
243068394ec8SAlexander V. Chernikov 	ipfw_objhash_foreach(icfg->ii, foreach_ifidx, &wa);
243168394ec8SAlexander V. Chernikov }
243268394ec8SAlexander V. Chernikov 
243374b941f0SAlexander V. Chernikov struct table_algo iface_idx = {
2434adea6201SAlexander V. Chernikov 	.name		= "iface:array",
24359d099b4fSAlexander V. Chernikov 	.type		= IPFW_TABLE_INTERFACE,
243657a1cf95SAlexander V. Chernikov 	.flags		= TA_FLAG_DEFAULT,
243757a1cf95SAlexander V. Chernikov 	.ta_buf_size	= sizeof(struct ta_buf_ifidx),
243868394ec8SAlexander V. Chernikov 	.init		= ta_init_ifidx,
243968394ec8SAlexander V. Chernikov 	.destroy	= ta_destroy_ifidx,
244068394ec8SAlexander V. Chernikov 	.prepare_add	= ta_prepare_add_ifidx,
244168394ec8SAlexander V. Chernikov 	.prepare_del	= ta_prepare_del_ifidx,
244268394ec8SAlexander V. Chernikov 	.add		= ta_add_ifidx,
244368394ec8SAlexander V. Chernikov 	.del		= ta_del_ifidx,
244468394ec8SAlexander V. Chernikov 	.flush_entry	= ta_flush_ifidx_entry,
244568394ec8SAlexander V. Chernikov 	.foreach	= ta_foreach_ifidx,
244668394ec8SAlexander V. Chernikov 	.dump_tentry	= ta_dump_ifidx_tentry,
244768394ec8SAlexander V. Chernikov 	.find_tentry	= ta_find_ifidx_tentry,
24485f379342SAlexander V. Chernikov 	.dump_tinfo	= ta_dump_ifidx_tinfo,
2449301290bcSAlexander V. Chernikov 	.need_modify	= ta_need_modify_ifidx,
245068394ec8SAlexander V. Chernikov 	.prepare_mod	= ta_prepare_mod_ifidx,
245168394ec8SAlexander V. Chernikov 	.fill_mod	= ta_fill_mod_ifidx,
245268394ec8SAlexander V. Chernikov 	.modify		= ta_modify_ifidx,
245368394ec8SAlexander V. Chernikov 	.flush_mod	= ta_flush_mod_ifidx,
245468394ec8SAlexander V. Chernikov 	.change_ti	= ta_change_ti_ifidx,
24559f7d47b0SAlexander V. Chernikov };
24569f7d47b0SAlexander V. Chernikov 
2457b23d5de9SAlexander V. Chernikov /*
2458b23d5de9SAlexander V. Chernikov  * Number array cmds.
2459b23d5de9SAlexander V. Chernikov  *
2460b23d5de9SAlexander V. Chernikov  * Implementation:
2461b23d5de9SAlexander V. Chernikov  *
2462b23d5de9SAlexander V. Chernikov  * Runtime part:
2463b23d5de9SAlexander V. Chernikov  * - sorted array of "struct numarray" pointed by ti->state.
2464b23d5de9SAlexander V. Chernikov  *   Array is allocated with rounding up to NUMARRAY_CHUNK.
2465b23d5de9SAlexander V. Chernikov  * - current array size is stored in ti->data
2466b23d5de9SAlexander V. Chernikov  *
2467b23d5de9SAlexander V. Chernikov  */
2468b23d5de9SAlexander V. Chernikov 
2469b23d5de9SAlexander V. Chernikov struct numarray {
2470b23d5de9SAlexander V. Chernikov 	uint32_t	number;
2471b23d5de9SAlexander V. Chernikov 	uint32_t	value;
2472b23d5de9SAlexander V. Chernikov };
2473b23d5de9SAlexander V. Chernikov 
2474b23d5de9SAlexander V. Chernikov struct numarray_cfg {
2475b23d5de9SAlexander V. Chernikov 	void	*main_ptr;
2476b23d5de9SAlexander V. Chernikov 	size_t	size;	/* Number of items allocated in array */
2477b23d5de9SAlexander V. Chernikov 	size_t	used;	/* Number of items _active_ now */
2478b23d5de9SAlexander V. Chernikov };
2479b23d5de9SAlexander V. Chernikov 
24800bce0c23SAlexander V. Chernikov struct ta_buf_numarray
24810bce0c23SAlexander V. Chernikov {
24820bce0c23SAlexander V. Chernikov 	struct numarray na;
24830bce0c23SAlexander V. Chernikov };
2484b23d5de9SAlexander V. Chernikov 
2485b23d5de9SAlexander V. Chernikov int compare_numarray(const void *k, const void *v);
2486b23d5de9SAlexander V. Chernikov 
2487b23d5de9SAlexander V. Chernikov int
2488b23d5de9SAlexander V. Chernikov compare_numarray(const void *k, const void *v)
2489b23d5de9SAlexander V. Chernikov {
2490d4e1b515SAlexander V. Chernikov 	const struct numarray *na;
2491b23d5de9SAlexander V. Chernikov 	uint32_t key;
2492b23d5de9SAlexander V. Chernikov 
2493d4e1b515SAlexander V. Chernikov 	key = *((const uint32_t *)k);
2494d4e1b515SAlexander V. Chernikov 	na = (const struct numarray *)v;
2495b23d5de9SAlexander V. Chernikov 
2496b23d5de9SAlexander V. Chernikov 	if (key < na->number)
2497b23d5de9SAlexander V. Chernikov 		return (-1);
2498b23d5de9SAlexander V. Chernikov 	else if (key > na->number)
2499b23d5de9SAlexander V. Chernikov 		return (1);
2500b23d5de9SAlexander V. Chernikov 
2501b23d5de9SAlexander V. Chernikov 	return (0);
2502b23d5de9SAlexander V. Chernikov }
2503b23d5de9SAlexander V. Chernikov 
2504b23d5de9SAlexander V. Chernikov static struct numarray *
2505b23d5de9SAlexander V. Chernikov numarray_find(struct table_info *ti, void *key)
2506b23d5de9SAlexander V. Chernikov {
2507b23d5de9SAlexander V. Chernikov 	struct numarray *ri;
2508b23d5de9SAlexander V. Chernikov 
2509b23d5de9SAlexander V. Chernikov 	ri = bsearch(key, ti->state, ti->data, sizeof(struct numarray),
2510b23d5de9SAlexander V. Chernikov 	    compare_ifidx);
2511b23d5de9SAlexander V. Chernikov 
2512b23d5de9SAlexander V. Chernikov 	return (ri);
2513b23d5de9SAlexander V. Chernikov }
2514b23d5de9SAlexander V. Chernikov 
2515b23d5de9SAlexander V. Chernikov static int
2516b23d5de9SAlexander V. Chernikov ta_lookup_numarray(struct table_info *ti, void *key, uint32_t keylen,
2517b23d5de9SAlexander V. Chernikov     uint32_t *val)
2518b23d5de9SAlexander V. Chernikov {
2519b23d5de9SAlexander V. Chernikov 	struct numarray *ri;
2520b23d5de9SAlexander V. Chernikov 
2521b23d5de9SAlexander V. Chernikov 	ri = numarray_find(ti, key);
2522b23d5de9SAlexander V. Chernikov 
2523b23d5de9SAlexander V. Chernikov 	if (ri != NULL) {
2524b23d5de9SAlexander V. Chernikov 		*val = ri->value;
2525b23d5de9SAlexander V. Chernikov 		return (1);
2526b23d5de9SAlexander V. Chernikov 	}
2527b23d5de9SAlexander V. Chernikov 
2528b23d5de9SAlexander V. Chernikov 	return (0);
2529b23d5de9SAlexander V. Chernikov }
2530b23d5de9SAlexander V. Chernikov 
2531b23d5de9SAlexander V. Chernikov static int
2532b23d5de9SAlexander V. Chernikov ta_init_numarray(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
2533914bffb6SAlexander V. Chernikov     char *data, uint8_t tflags)
2534b23d5de9SAlexander V. Chernikov {
2535b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2536b23d5de9SAlexander V. Chernikov 
2537b23d5de9SAlexander V. Chernikov 	cfg = malloc(sizeof(*cfg), M_IPFW, M_WAITOK | M_ZERO);
2538b23d5de9SAlexander V. Chernikov 
25390bce0c23SAlexander V. Chernikov 	cfg->size = 16;
2540b23d5de9SAlexander V. Chernikov 	cfg->main_ptr = malloc(sizeof(struct numarray) * cfg->size, M_IPFW,
2541b23d5de9SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
2542b23d5de9SAlexander V. Chernikov 
2543b23d5de9SAlexander V. Chernikov 	*ta_state = cfg;
2544b23d5de9SAlexander V. Chernikov 	ti->state = cfg->main_ptr;
2545b23d5de9SAlexander V. Chernikov 	ti->lookup = ta_lookup_numarray;
2546b23d5de9SAlexander V. Chernikov 
2547b23d5de9SAlexander V. Chernikov 	return (0);
2548b23d5de9SAlexander V. Chernikov }
2549b23d5de9SAlexander V. Chernikov 
2550b23d5de9SAlexander V. Chernikov /*
2551b23d5de9SAlexander V. Chernikov  * Destroys table @ti
2552b23d5de9SAlexander V. Chernikov  */
2553b23d5de9SAlexander V. Chernikov static void
2554b23d5de9SAlexander V. Chernikov ta_destroy_numarray(void *ta_state, struct table_info *ti)
2555b23d5de9SAlexander V. Chernikov {
2556b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2557b23d5de9SAlexander V. Chernikov 
2558b23d5de9SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2559b23d5de9SAlexander V. Chernikov 
2560b23d5de9SAlexander V. Chernikov 	if (cfg->main_ptr != NULL)
2561b23d5de9SAlexander V. Chernikov 		free(cfg->main_ptr, M_IPFW);
2562b23d5de9SAlexander V. Chernikov 
2563b23d5de9SAlexander V. Chernikov 	free(cfg, M_IPFW);
2564b23d5de9SAlexander V. Chernikov }
2565b23d5de9SAlexander V. Chernikov 
2566b23d5de9SAlexander V. Chernikov /*
25675f379342SAlexander V. Chernikov  * Provide algo-specific table info
25685f379342SAlexander V. Chernikov  */
25695f379342SAlexander V. Chernikov static void
25705f379342SAlexander V. Chernikov ta_dump_numarray_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo)
25715f379342SAlexander V. Chernikov {
25725f379342SAlexander V. Chernikov 	struct numarray_cfg *cfg;
25735f379342SAlexander V. Chernikov 
25745f379342SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
25755f379342SAlexander V. Chernikov 
25765f379342SAlexander V. Chernikov 	tinfo->taclass4 = IPFW_TACLASS_ARRAY;
25775f379342SAlexander V. Chernikov 	tinfo->size4 = cfg->size;
25785f379342SAlexander V. Chernikov 	tinfo->count4 = cfg->used;
25795f379342SAlexander V. Chernikov 	tinfo->itemsize4 = sizeof(struct numarray);
25805f379342SAlexander V. Chernikov }
25815f379342SAlexander V. Chernikov 
25825f379342SAlexander V. Chernikov /*
2583b23d5de9SAlexander V. Chernikov  * Prepare for addition/deletion to an array.
2584b23d5de9SAlexander V. Chernikov  */
2585b23d5de9SAlexander V. Chernikov static int
2586b23d5de9SAlexander V. Chernikov ta_prepare_add_numarray(struct ip_fw_chain *ch, struct tentry_info *tei,
2587b23d5de9SAlexander V. Chernikov     void *ta_buf)
2588b23d5de9SAlexander V. Chernikov {
2589b23d5de9SAlexander V. Chernikov 	struct ta_buf_numarray *tb;
2590b23d5de9SAlexander V. Chernikov 
2591b23d5de9SAlexander V. Chernikov 	tb = (struct ta_buf_numarray *)ta_buf;
2592b23d5de9SAlexander V. Chernikov 
2593b23d5de9SAlexander V. Chernikov 	tb->na.number = *((uint32_t *)tei->paddr);
2594b23d5de9SAlexander V. Chernikov 
2595b23d5de9SAlexander V. Chernikov 	return (0);
2596b23d5de9SAlexander V. Chernikov }
2597b23d5de9SAlexander V. Chernikov 
2598b23d5de9SAlexander V. Chernikov static int
2599b23d5de9SAlexander V. Chernikov ta_add_numarray(void *ta_state, struct table_info *ti, struct tentry_info *tei,
2600b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
2601b23d5de9SAlexander V. Chernikov {
2602b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2603b23d5de9SAlexander V. Chernikov 	struct ta_buf_numarray *tb;
2604b23d5de9SAlexander V. Chernikov 	struct numarray *ri;
2605b23d5de9SAlexander V. Chernikov 	int res;
2606648e8380SAlexander V. Chernikov 	uint32_t value;
2607b23d5de9SAlexander V. Chernikov 
2608b23d5de9SAlexander V. Chernikov 	tb = (struct ta_buf_numarray *)ta_buf;
2609b23d5de9SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2610b23d5de9SAlexander V. Chernikov 
261113263632SAlexander V. Chernikov 	/* Read current value from @tei */
261213263632SAlexander V. Chernikov 	tb->na.value = tei->value;
261313263632SAlexander V. Chernikov 
2614b23d5de9SAlexander V. Chernikov 	ri = numarray_find(ti, &tb->na.number);
2615b23d5de9SAlexander V. Chernikov 
2616b23d5de9SAlexander V. Chernikov 	if (ri != NULL) {
2617b23d5de9SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
2618b23d5de9SAlexander V. Chernikov 			return (EEXIST);
2619b23d5de9SAlexander V. Chernikov 
2620648e8380SAlexander V. Chernikov 		/* Exchange values between ri and @tei */
2621648e8380SAlexander V. Chernikov 		value = ri->value;
2622648e8380SAlexander V. Chernikov 		ri->value = tei->value;
2623648e8380SAlexander V. Chernikov 		tei->value = value;
2624b23d5de9SAlexander V. Chernikov 		/* Indicate that update has happened instead of addition */
2625b23d5de9SAlexander V. Chernikov 		tei->flags |= TEI_FLAGS_UPDATED;
2626b23d5de9SAlexander V. Chernikov 		*pnum = 0;
2627b23d5de9SAlexander V. Chernikov 		return (0);
2628b23d5de9SAlexander V. Chernikov 	}
2629b23d5de9SAlexander V. Chernikov 
26304c0c07a5SAlexander V. Chernikov 	if ((tei->flags & TEI_FLAGS_DONTADD) != 0)
26314c0c07a5SAlexander V. Chernikov 		return (EFBIG);
26324c0c07a5SAlexander V. Chernikov 
2633b23d5de9SAlexander V. Chernikov 	res = badd(&tb->na.number, &tb->na, cfg->main_ptr, cfg->used,
2634b23d5de9SAlexander V. Chernikov 	    sizeof(struct numarray), compare_numarray);
2635b23d5de9SAlexander V. Chernikov 
2636b23d5de9SAlexander V. Chernikov 	KASSERT(res == 1, ("number %d already exists", tb->na.number));
2637b23d5de9SAlexander V. Chernikov 	cfg->used++;
2638b23d5de9SAlexander V. Chernikov 	ti->data = cfg->used;
2639b23d5de9SAlexander V. Chernikov 	*pnum = 1;
2640b23d5de9SAlexander V. Chernikov 
2641b23d5de9SAlexander V. Chernikov 	return (0);
2642b23d5de9SAlexander V. Chernikov }
2643b23d5de9SAlexander V. Chernikov 
2644b23d5de9SAlexander V. Chernikov /*
2645b23d5de9SAlexander V. Chernikov  * Remove key from both configuration list and
2646b23d5de9SAlexander V. Chernikov  * runtime array. Removed interface notification.
2647b23d5de9SAlexander V. Chernikov  */
2648b23d5de9SAlexander V. Chernikov static int
2649b23d5de9SAlexander V. Chernikov ta_del_numarray(void *ta_state, struct table_info *ti, struct tentry_info *tei,
2650b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
2651b23d5de9SAlexander V. Chernikov {
2652b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2653b23d5de9SAlexander V. Chernikov 	struct ta_buf_numarray *tb;
2654b23d5de9SAlexander V. Chernikov 	struct numarray *ri;
2655b23d5de9SAlexander V. Chernikov 	int res;
2656b23d5de9SAlexander V. Chernikov 
2657b23d5de9SAlexander V. Chernikov 	tb = (struct ta_buf_numarray *)ta_buf;
2658b23d5de9SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2659b23d5de9SAlexander V. Chernikov 
2660b23d5de9SAlexander V. Chernikov 	ri = numarray_find(ti, &tb->na.number);
2661b23d5de9SAlexander V. Chernikov 	if (ri == NULL)
2662b23d5de9SAlexander V. Chernikov 		return (ENOENT);
2663b23d5de9SAlexander V. Chernikov 
2664648e8380SAlexander V. Chernikov 	tei->value = ri->value;
2665648e8380SAlexander V. Chernikov 
2666b23d5de9SAlexander V. Chernikov 	res = bdel(&tb->na.number, cfg->main_ptr, cfg->used,
2667b23d5de9SAlexander V. Chernikov 	    sizeof(struct numarray), compare_numarray);
2668b23d5de9SAlexander V. Chernikov 
2669b23d5de9SAlexander V. Chernikov 	KASSERT(res == 1, ("number %u does not exist", tb->na.number));
2670b23d5de9SAlexander V. Chernikov 	cfg->used--;
2671b23d5de9SAlexander V. Chernikov 	ti->data = cfg->used;
2672b23d5de9SAlexander V. Chernikov 	*pnum = 1;
2673b23d5de9SAlexander V. Chernikov 
2674b23d5de9SAlexander V. Chernikov 	return (0);
2675b23d5de9SAlexander V. Chernikov }
2676b23d5de9SAlexander V. Chernikov 
2677b23d5de9SAlexander V. Chernikov static void
2678b23d5de9SAlexander V. Chernikov ta_flush_numarray_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
2679b23d5de9SAlexander V. Chernikov     void *ta_buf)
2680b23d5de9SAlexander V. Chernikov {
2681b23d5de9SAlexander V. Chernikov 
26820bce0c23SAlexander V. Chernikov 	/* We don't have any state, do nothing */
2683b23d5de9SAlexander V. Chernikov }
2684b23d5de9SAlexander V. Chernikov 
2685b23d5de9SAlexander V. Chernikov 
2686b23d5de9SAlexander V. Chernikov /*
2687b23d5de9SAlexander V. Chernikov  * Table growing callbacks.
2688b23d5de9SAlexander V. Chernikov  */
2689b23d5de9SAlexander V. Chernikov 
2690b6ee846eSAlexander V. Chernikov static int
2691301290bcSAlexander V. Chernikov ta_need_modify_numarray(void *ta_state, struct table_info *ti, uint32_t count,
2692b6ee846eSAlexander V. Chernikov     uint64_t *pflags)
2693b6ee846eSAlexander V. Chernikov {
2694b6ee846eSAlexander V. Chernikov 	struct numarray_cfg *cfg;
26950bce0c23SAlexander V. Chernikov 	size_t size;
2696b6ee846eSAlexander V. Chernikov 
2697b6ee846eSAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2698b6ee846eSAlexander V. Chernikov 
26990bce0c23SAlexander V. Chernikov 	size = cfg->size;
27000bce0c23SAlexander V. Chernikov 	while (size < cfg->used + count)
27010bce0c23SAlexander V. Chernikov 		size *= 2;
27020bce0c23SAlexander V. Chernikov 
27030bce0c23SAlexander V. Chernikov 	if (size != cfg->size) {
27040bce0c23SAlexander V. Chernikov 		*pflags = size;
2705301290bcSAlexander V. Chernikov 		return (1);
2706b6ee846eSAlexander V. Chernikov 	}
2707b6ee846eSAlexander V. Chernikov 
2708301290bcSAlexander V. Chernikov 	return (0);
2709b6ee846eSAlexander V. Chernikov }
2710b6ee846eSAlexander V. Chernikov 
2711b23d5de9SAlexander V. Chernikov /*
2712b6ee846eSAlexander V. Chernikov  * Allocate new, larger runtime array.
2713b23d5de9SAlexander V. Chernikov  */
2714b23d5de9SAlexander V. Chernikov static int
2715b23d5de9SAlexander V. Chernikov ta_prepare_mod_numarray(void *ta_buf, uint64_t *pflags)
2716b23d5de9SAlexander V. Chernikov {
2717b23d5de9SAlexander V. Chernikov 	struct mod_item *mi;
2718b23d5de9SAlexander V. Chernikov 
2719b23d5de9SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
2720b23d5de9SAlexander V. Chernikov 
2721b23d5de9SAlexander V. Chernikov 	memset(mi, 0, sizeof(struct mod_item));
2722b23d5de9SAlexander V. Chernikov 	mi->size = *pflags;
2723b23d5de9SAlexander V. Chernikov 	mi->main_ptr = malloc(sizeof(struct numarray) * mi->size, M_IPFW,
2724b23d5de9SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
2725b23d5de9SAlexander V. Chernikov 
2726b23d5de9SAlexander V. Chernikov 	return (0);
2727b23d5de9SAlexander V. Chernikov }
2728b23d5de9SAlexander V. Chernikov 
2729b23d5de9SAlexander V. Chernikov /*
2730b23d5de9SAlexander V. Chernikov  * Copy data from old runtime array to new one.
2731b23d5de9SAlexander V. Chernikov  */
2732b23d5de9SAlexander V. Chernikov static int
2733b23d5de9SAlexander V. Chernikov ta_fill_mod_numarray(void *ta_state, struct table_info *ti, void *ta_buf,
2734b23d5de9SAlexander V. Chernikov     uint64_t *pflags)
2735b23d5de9SAlexander V. Chernikov {
2736b23d5de9SAlexander V. Chernikov 	struct mod_item *mi;
2737b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2738b23d5de9SAlexander V. Chernikov 
2739b23d5de9SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
2740b23d5de9SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2741b23d5de9SAlexander V. Chernikov 
2742b23d5de9SAlexander V. Chernikov 	/* Check if we still need to grow array */
2743b23d5de9SAlexander V. Chernikov 	if (cfg->size >= mi->size) {
2744b23d5de9SAlexander V. Chernikov 		*pflags = 0;
2745b23d5de9SAlexander V. Chernikov 		return (0);
2746b23d5de9SAlexander V. Chernikov 	}
2747b23d5de9SAlexander V. Chernikov 
2748b23d5de9SAlexander V. Chernikov 	memcpy(mi->main_ptr, cfg->main_ptr, cfg->used * sizeof(struct numarray));
2749b23d5de9SAlexander V. Chernikov 
2750b23d5de9SAlexander V. Chernikov 	return (0);
2751b23d5de9SAlexander V. Chernikov }
2752b23d5de9SAlexander V. Chernikov 
2753b23d5de9SAlexander V. Chernikov /*
2754b23d5de9SAlexander V. Chernikov  * Switch old & new arrays.
2755b23d5de9SAlexander V. Chernikov  */
2756301290bcSAlexander V. Chernikov static void
2757b23d5de9SAlexander V. Chernikov ta_modify_numarray(void *ta_state, struct table_info *ti, void *ta_buf,
2758b23d5de9SAlexander V. Chernikov     uint64_t pflags)
2759b23d5de9SAlexander V. Chernikov {
2760b23d5de9SAlexander V. Chernikov 	struct mod_item *mi;
2761b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2762b23d5de9SAlexander V. Chernikov 	void *old_ptr;
2763b23d5de9SAlexander V. Chernikov 
2764b23d5de9SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
2765b23d5de9SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2766b23d5de9SAlexander V. Chernikov 
2767b23d5de9SAlexander V. Chernikov 	old_ptr = cfg->main_ptr;
2768b23d5de9SAlexander V. Chernikov 	cfg->main_ptr = mi->main_ptr;
2769b23d5de9SAlexander V. Chernikov 	cfg->size = mi->size;
2770b23d5de9SAlexander V. Chernikov 	ti->state = cfg->main_ptr;
2771b23d5de9SAlexander V. Chernikov 
2772b23d5de9SAlexander V. Chernikov 	mi->main_ptr = old_ptr;
2773b23d5de9SAlexander V. Chernikov }
2774b23d5de9SAlexander V. Chernikov 
2775b23d5de9SAlexander V. Chernikov /*
2776b23d5de9SAlexander V. Chernikov  * Free unneded array.
2777b23d5de9SAlexander V. Chernikov  */
2778b23d5de9SAlexander V. Chernikov static void
2779b23d5de9SAlexander V. Chernikov ta_flush_mod_numarray(void *ta_buf)
2780b23d5de9SAlexander V. Chernikov {
2781b23d5de9SAlexander V. Chernikov 	struct mod_item *mi;
2782b23d5de9SAlexander V. Chernikov 
2783b23d5de9SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
2784b23d5de9SAlexander V. Chernikov 	if (mi->main_ptr != NULL)
2785b23d5de9SAlexander V. Chernikov 		free(mi->main_ptr, M_IPFW);
2786b23d5de9SAlexander V. Chernikov }
2787b23d5de9SAlexander V. Chernikov 
2788b23d5de9SAlexander V. Chernikov static int
2789b23d5de9SAlexander V. Chernikov ta_dump_numarray_tentry(void *ta_state, struct table_info *ti, void *e,
2790b23d5de9SAlexander V. Chernikov     ipfw_obj_tentry *tent)
2791b23d5de9SAlexander V. Chernikov {
2792b23d5de9SAlexander V. Chernikov 	struct numarray *na;
2793b23d5de9SAlexander V. Chernikov 
2794b23d5de9SAlexander V. Chernikov 	na = (struct numarray *)e;
2795b23d5de9SAlexander V. Chernikov 
2796b23d5de9SAlexander V. Chernikov 	tent->k.key = na->number;
27970cba2b28SAlexander V. Chernikov 	tent->v.kidx = na->value;
2798b23d5de9SAlexander V. Chernikov 
2799b23d5de9SAlexander V. Chernikov 	return (0);
2800b23d5de9SAlexander V. Chernikov }
2801b23d5de9SAlexander V. Chernikov 
2802b23d5de9SAlexander V. Chernikov static int
2803914bffb6SAlexander V. Chernikov ta_find_numarray_tentry(void *ta_state, struct table_info *ti,
2804914bffb6SAlexander V. Chernikov     ipfw_obj_tentry *tent)
2805b23d5de9SAlexander V. Chernikov {
2806b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2807b23d5de9SAlexander V. Chernikov 	struct numarray *ri;
2808b23d5de9SAlexander V. Chernikov 
2809b23d5de9SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2810b23d5de9SAlexander V. Chernikov 
2811914bffb6SAlexander V. Chernikov 	ri = numarray_find(ti, &tent->k.key);
2812b23d5de9SAlexander V. Chernikov 
2813b23d5de9SAlexander V. Chernikov 	if (ri != NULL) {
2814b23d5de9SAlexander V. Chernikov 		ta_dump_numarray_tentry(ta_state, ti, ri, tent);
2815b23d5de9SAlexander V. Chernikov 		return (0);
2816b23d5de9SAlexander V. Chernikov 	}
2817b23d5de9SAlexander V. Chernikov 
2818b23d5de9SAlexander V. Chernikov 	return (ENOENT);
2819b23d5de9SAlexander V. Chernikov }
2820b23d5de9SAlexander V. Chernikov 
2821b23d5de9SAlexander V. Chernikov static void
2822b23d5de9SAlexander V. Chernikov ta_foreach_numarray(void *ta_state, struct table_info *ti, ta_foreach_f *f,
2823b23d5de9SAlexander V. Chernikov     void *arg)
2824b23d5de9SAlexander V. Chernikov {
2825b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2826b23d5de9SAlexander V. Chernikov 	struct numarray *array;
2827b23d5de9SAlexander V. Chernikov 	int i;
2828b23d5de9SAlexander V. Chernikov 
2829b23d5de9SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2830b23d5de9SAlexander V. Chernikov 	array = cfg->main_ptr;
2831b23d5de9SAlexander V. Chernikov 
2832b23d5de9SAlexander V. Chernikov 	for (i = 0; i < cfg->used; i++)
2833b23d5de9SAlexander V. Chernikov 		f(&array[i], arg);
2834b23d5de9SAlexander V. Chernikov }
2835b23d5de9SAlexander V. Chernikov 
2836b23d5de9SAlexander V. Chernikov struct table_algo number_array = {
2837b23d5de9SAlexander V. Chernikov 	.name		= "number:array",
2838b23d5de9SAlexander V. Chernikov 	.type		= IPFW_TABLE_NUMBER,
283957a1cf95SAlexander V. Chernikov 	.ta_buf_size	= sizeof(struct ta_buf_numarray),
2840b23d5de9SAlexander V. Chernikov 	.init		= ta_init_numarray,
2841b23d5de9SAlexander V. Chernikov 	.destroy	= ta_destroy_numarray,
2842b23d5de9SAlexander V. Chernikov 	.prepare_add	= ta_prepare_add_numarray,
2843b23d5de9SAlexander V. Chernikov 	.prepare_del	= ta_prepare_add_numarray,
2844b23d5de9SAlexander V. Chernikov 	.add		= ta_add_numarray,
2845b23d5de9SAlexander V. Chernikov 	.del		= ta_del_numarray,
2846b23d5de9SAlexander V. Chernikov 	.flush_entry	= ta_flush_numarray_entry,
2847b23d5de9SAlexander V. Chernikov 	.foreach	= ta_foreach_numarray,
2848b23d5de9SAlexander V. Chernikov 	.dump_tentry	= ta_dump_numarray_tentry,
2849b23d5de9SAlexander V. Chernikov 	.find_tentry	= ta_find_numarray_tentry,
28505f379342SAlexander V. Chernikov 	.dump_tinfo	= ta_dump_numarray_tinfo,
2851301290bcSAlexander V. Chernikov 	.need_modify	= ta_need_modify_numarray,
2852b23d5de9SAlexander V. Chernikov 	.prepare_mod	= ta_prepare_mod_numarray,
2853b23d5de9SAlexander V. Chernikov 	.fill_mod	= ta_fill_mod_numarray,
2854b23d5de9SAlexander V. Chernikov 	.modify		= ta_modify_numarray,
2855b23d5de9SAlexander V. Chernikov 	.flush_mod	= ta_flush_mod_numarray,
2856b23d5de9SAlexander V. Chernikov };
2857b23d5de9SAlexander V. Chernikov 
2858914bffb6SAlexander V. Chernikov /*
2859914bffb6SAlexander V. Chernikov  * flow:hash cmds
2860914bffb6SAlexander V. Chernikov  *
2861914bffb6SAlexander V. Chernikov  *
2862914bffb6SAlexander V. Chernikov  * ti->data:
2863914bffb6SAlexander V. Chernikov  * [inv.mask4][inv.mask6][log2hsize4][log2hsize6]
2864914bffb6SAlexander V. Chernikov  * [        8][        8[          8][         8]
2865914bffb6SAlexander V. Chernikov  *
2866914bffb6SAlexander V. Chernikov  * inv.mask4: 32 - mask
2867914bffb6SAlexander V. Chernikov  * inv.mask6:
2868914bffb6SAlexander V. Chernikov  * 1) _slow lookup: mask
2869914bffb6SAlexander V. Chernikov  * 2) _aligned: (128 - mask) / 8
2870914bffb6SAlexander V. Chernikov  * 3) _64: 8
2871914bffb6SAlexander V. Chernikov  *
2872914bffb6SAlexander V. Chernikov  *
2873914bffb6SAlexander V. Chernikov  * pflags:
2874b6ee846eSAlexander V. Chernikov  * [hsize4][hsize6]
2875b6ee846eSAlexander V. Chernikov  * [    16][    16]
2876914bffb6SAlexander V. Chernikov  */
2877914bffb6SAlexander V. Chernikov 
2878914bffb6SAlexander V. Chernikov struct fhashentry;
2879914bffb6SAlexander V. Chernikov 
2880914bffb6SAlexander V. Chernikov SLIST_HEAD(fhashbhead, fhashentry);
2881914bffb6SAlexander V. Chernikov 
2882914bffb6SAlexander V. Chernikov struct fhashentry {
2883914bffb6SAlexander V. Chernikov 	SLIST_ENTRY(fhashentry)	next;
2884914bffb6SAlexander V. Chernikov 	uint8_t		af;
2885914bffb6SAlexander V. Chernikov 	uint8_t		proto;
2886914bffb6SAlexander V. Chernikov 	uint16_t	spare0;
2887914bffb6SAlexander V. Chernikov 	uint16_t	dport;
2888914bffb6SAlexander V. Chernikov 	uint16_t	sport;
2889914bffb6SAlexander V. Chernikov 	uint32_t	value;
2890914bffb6SAlexander V. Chernikov 	uint32_t	spare1;
2891914bffb6SAlexander V. Chernikov };
2892914bffb6SAlexander V. Chernikov 
2893914bffb6SAlexander V. Chernikov struct fhashentry4 {
2894914bffb6SAlexander V. Chernikov 	struct fhashentry	e;
2895914bffb6SAlexander V. Chernikov 	struct in_addr		dip;
2896914bffb6SAlexander V. Chernikov 	struct in_addr		sip;
2897914bffb6SAlexander V. Chernikov };
2898914bffb6SAlexander V. Chernikov 
2899914bffb6SAlexander V. Chernikov struct fhashentry6 {
2900914bffb6SAlexander V. Chernikov 	struct fhashentry	e;
2901914bffb6SAlexander V. Chernikov 	struct in6_addr		dip6;
2902914bffb6SAlexander V. Chernikov 	struct in6_addr		sip6;
2903914bffb6SAlexander V. Chernikov };
2904914bffb6SAlexander V. Chernikov 
2905914bffb6SAlexander V. Chernikov struct fhash_cfg {
2906914bffb6SAlexander V. Chernikov 	struct fhashbhead	*head;
2907914bffb6SAlexander V. Chernikov 	size_t			size;
2908914bffb6SAlexander V. Chernikov 	size_t			items;
2909914bffb6SAlexander V. Chernikov 	struct fhashentry4	fe4;
2910914bffb6SAlexander V. Chernikov 	struct fhashentry6	fe6;
2911914bffb6SAlexander V. Chernikov };
2912914bffb6SAlexander V. Chernikov 
29133fe2ef91SAlexander V. Chernikov struct ta_buf_fhash {
29140bce0c23SAlexander V. Chernikov 	void	*ent_ptr;
29150bce0c23SAlexander V. Chernikov 	struct fhashentry6 fe6;
29160bce0c23SAlexander V. Chernikov };
29170bce0c23SAlexander V. Chernikov 
2918914bffb6SAlexander V. Chernikov static __inline int
2919914bffb6SAlexander V. Chernikov cmp_flow_ent(struct fhashentry *a, struct fhashentry *b, size_t sz)
2920914bffb6SAlexander V. Chernikov {
2921914bffb6SAlexander V. Chernikov 	uint64_t *ka, *kb;
2922914bffb6SAlexander V. Chernikov 
2923914bffb6SAlexander V. Chernikov 	ka = (uint64_t *)(&a->next + 1);
2924914bffb6SAlexander V. Chernikov 	kb = (uint64_t *)(&b->next + 1);
2925914bffb6SAlexander V. Chernikov 
2926914bffb6SAlexander V. Chernikov 	if (*ka == *kb && (memcmp(a + 1, b + 1, sz) == 0))
2927914bffb6SAlexander V. Chernikov 		return (1);
2928914bffb6SAlexander V. Chernikov 
2929914bffb6SAlexander V. Chernikov 	return (0);
2930914bffb6SAlexander V. Chernikov }
2931914bffb6SAlexander V. Chernikov 
2932914bffb6SAlexander V. Chernikov static __inline uint32_t
2933914bffb6SAlexander V. Chernikov hash_flow4(struct fhashentry4 *f, int hsize)
2934914bffb6SAlexander V. Chernikov {
2935914bffb6SAlexander V. Chernikov 	uint32_t i;
2936914bffb6SAlexander V. Chernikov 
2937914bffb6SAlexander V. Chernikov 	i = (f->dip.s_addr) ^ (f->sip.s_addr) ^ (f->e.dport) ^ (f->e.sport);
2938914bffb6SAlexander V. Chernikov 
2939914bffb6SAlexander V. Chernikov 	return (i % (hsize - 1));
2940914bffb6SAlexander V. Chernikov }
2941914bffb6SAlexander V. Chernikov 
2942914bffb6SAlexander V. Chernikov static __inline uint32_t
2943914bffb6SAlexander V. Chernikov hash_flow6(struct fhashentry6 *f, int hsize)
2944914bffb6SAlexander V. Chernikov {
2945914bffb6SAlexander V. Chernikov 	uint32_t i;
2946914bffb6SAlexander V. Chernikov 
2947914bffb6SAlexander V. Chernikov 	i = (f->dip6.__u6_addr.__u6_addr32[2]) ^
2948914bffb6SAlexander V. Chernikov 	    (f->dip6.__u6_addr.__u6_addr32[3]) ^
2949914bffb6SAlexander V. Chernikov 	    (f->sip6.__u6_addr.__u6_addr32[2]) ^
2950914bffb6SAlexander V. Chernikov 	    (f->sip6.__u6_addr.__u6_addr32[3]) ^
2951914bffb6SAlexander V. Chernikov 	    (f->e.dport) ^ (f->e.sport);
2952914bffb6SAlexander V. Chernikov 
2953914bffb6SAlexander V. Chernikov 	return (i % (hsize - 1));
2954914bffb6SAlexander V. Chernikov }
2955914bffb6SAlexander V. Chernikov 
2956914bffb6SAlexander V. Chernikov static uint32_t
2957914bffb6SAlexander V. Chernikov hash_flow_ent(struct fhashentry *ent, uint32_t size)
2958914bffb6SAlexander V. Chernikov {
2959914bffb6SAlexander V. Chernikov 	uint32_t hash;
2960914bffb6SAlexander V. Chernikov 
2961914bffb6SAlexander V. Chernikov 	if (ent->af == AF_INET) {
2962914bffb6SAlexander V. Chernikov 		hash = hash_flow4((struct fhashentry4 *)ent, size);
2963914bffb6SAlexander V. Chernikov 	} else {
2964914bffb6SAlexander V. Chernikov 		hash = hash_flow6((struct fhashentry6 *)ent, size);
2965914bffb6SAlexander V. Chernikov 	}
2966914bffb6SAlexander V. Chernikov 
2967914bffb6SAlexander V. Chernikov 	return (hash);
2968914bffb6SAlexander V. Chernikov }
2969914bffb6SAlexander V. Chernikov 
2970914bffb6SAlexander V. Chernikov static int
2971914bffb6SAlexander V. Chernikov ta_lookup_fhash(struct table_info *ti, void *key, uint32_t keylen,
2972914bffb6SAlexander V. Chernikov     uint32_t *val)
2973914bffb6SAlexander V. Chernikov {
2974914bffb6SAlexander V. Chernikov 	struct fhashbhead *head;
2975914bffb6SAlexander V. Chernikov 	struct fhashentry *ent;
2976914bffb6SAlexander V. Chernikov 	struct fhashentry4 *m4;
2977914bffb6SAlexander V. Chernikov 	struct ipfw_flow_id *id;
2978914bffb6SAlexander V. Chernikov 	uint16_t hash, hsize;
2979914bffb6SAlexander V. Chernikov 
2980914bffb6SAlexander V. Chernikov 	id = (struct ipfw_flow_id *)key;
2981914bffb6SAlexander V. Chernikov 	head = (struct fhashbhead *)ti->state;
2982914bffb6SAlexander V. Chernikov 	hsize = ti->data;
2983914bffb6SAlexander V. Chernikov 	m4 = (struct fhashentry4 *)ti->xstate;
2984914bffb6SAlexander V. Chernikov 
2985914bffb6SAlexander V. Chernikov 	if (id->addr_type == 4) {
2986914bffb6SAlexander V. Chernikov 		struct fhashentry4 f;
2987914bffb6SAlexander V. Chernikov 
2988914bffb6SAlexander V. Chernikov 		/* Copy hash mask */
2989914bffb6SAlexander V. Chernikov 		f = *m4;
2990914bffb6SAlexander V. Chernikov 
2991914bffb6SAlexander V. Chernikov 		f.dip.s_addr &= id->dst_ip;
2992914bffb6SAlexander V. Chernikov 		f.sip.s_addr &= id->src_ip;
2993914bffb6SAlexander V. Chernikov 		f.e.dport &= id->dst_port;
2994914bffb6SAlexander V. Chernikov 		f.e.sport &= id->src_port;
2995914bffb6SAlexander V. Chernikov 		f.e.proto &= id->proto;
2996914bffb6SAlexander V. Chernikov 		hash = hash_flow4(&f, hsize);
2997914bffb6SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
2998914bffb6SAlexander V. Chernikov 			if (cmp_flow_ent(ent, &f.e, 2 * 4) != 0) {
2999914bffb6SAlexander V. Chernikov 				*val = ent->value;
3000914bffb6SAlexander V. Chernikov 				return (1);
3001914bffb6SAlexander V. Chernikov 			}
3002914bffb6SAlexander V. Chernikov 		}
3003914bffb6SAlexander V. Chernikov 	} else if (id->addr_type == 6) {
3004914bffb6SAlexander V. Chernikov 		struct fhashentry6 f;
3005914bffb6SAlexander V. Chernikov 		uint64_t *fp, *idp;
3006914bffb6SAlexander V. Chernikov 
3007914bffb6SAlexander V. Chernikov 		/* Copy hash mask */
3008914bffb6SAlexander V. Chernikov 		f = *((struct fhashentry6 *)(m4 + 1));
3009914bffb6SAlexander V. Chernikov 
3010914bffb6SAlexander V. Chernikov 		/* Handle lack of __u6_addr.__u6_addr64 */
3011914bffb6SAlexander V. Chernikov 		fp = (uint64_t *)&f.dip6;
3012914bffb6SAlexander V. Chernikov 		idp = (uint64_t *)&id->dst_ip6;
3013914bffb6SAlexander V. Chernikov 		/* src IPv6 is stored after dst IPv6 */
3014914bffb6SAlexander V. Chernikov 		*fp++ &= *idp++;
3015914bffb6SAlexander V. Chernikov 		*fp++ &= *idp++;
3016914bffb6SAlexander V. Chernikov 		*fp++ &= *idp++;
3017914bffb6SAlexander V. Chernikov 		*fp &= *idp;
3018914bffb6SAlexander V. Chernikov 		f.e.dport &= id->dst_port;
3019914bffb6SAlexander V. Chernikov 		f.e.sport &= id->src_port;
3020914bffb6SAlexander V. Chernikov 		f.e.proto &= id->proto;
3021914bffb6SAlexander V. Chernikov 		hash = hash_flow6(&f, hsize);
3022914bffb6SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
3023914bffb6SAlexander V. Chernikov 			if (cmp_flow_ent(ent, &f.e, 2 * 16) != 0) {
3024914bffb6SAlexander V. Chernikov 				*val = ent->value;
3025914bffb6SAlexander V. Chernikov 				return (1);
3026914bffb6SAlexander V. Chernikov 			}
3027914bffb6SAlexander V. Chernikov 		}
3028914bffb6SAlexander V. Chernikov 	}
3029914bffb6SAlexander V. Chernikov 
3030914bffb6SAlexander V. Chernikov 	return (0);
3031914bffb6SAlexander V. Chernikov }
3032914bffb6SAlexander V. Chernikov 
3033914bffb6SAlexander V. Chernikov /*
3034914bffb6SAlexander V. Chernikov  * New table.
3035914bffb6SAlexander V. Chernikov  */
3036914bffb6SAlexander V. Chernikov static int
3037914bffb6SAlexander V. Chernikov ta_init_fhash(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
3038914bffb6SAlexander V. Chernikov     char *data, uint8_t tflags)
3039914bffb6SAlexander V. Chernikov {
3040914bffb6SAlexander V. Chernikov 	int i;
3041914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
3042914bffb6SAlexander V. Chernikov 	struct fhashentry4 *fe4;
3043914bffb6SAlexander V. Chernikov 	struct fhashentry6 *fe6;
3044914bffb6SAlexander V. Chernikov 
3045914bffb6SAlexander V. Chernikov 	cfg = malloc(sizeof(struct fhash_cfg), M_IPFW, M_WAITOK | M_ZERO);
3046914bffb6SAlexander V. Chernikov 
3047914bffb6SAlexander V. Chernikov 	cfg->size = 512;
3048914bffb6SAlexander V. Chernikov 
3049914bffb6SAlexander V. Chernikov 	cfg->head = malloc(sizeof(struct fhashbhead) * cfg->size, M_IPFW,
3050914bffb6SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
3051914bffb6SAlexander V. Chernikov 	for (i = 0; i < cfg->size; i++)
3052914bffb6SAlexander V. Chernikov 		SLIST_INIT(&cfg->head[i]);
3053914bffb6SAlexander V. Chernikov 
3054914bffb6SAlexander V. Chernikov 	/* Fill in fe masks based on @tflags */
3055914bffb6SAlexander V. Chernikov 	fe4 = &cfg->fe4;
3056914bffb6SAlexander V. Chernikov 	fe6 = &cfg->fe6;
3057914bffb6SAlexander V. Chernikov 	if (tflags & IPFW_TFFLAG_SRCIP) {
3058914bffb6SAlexander V. Chernikov 		memset(&fe4->sip, 0xFF, sizeof(fe4->sip));
3059914bffb6SAlexander V. Chernikov 		memset(&fe6->sip6, 0xFF, sizeof(fe6->sip6));
3060914bffb6SAlexander V. Chernikov 	}
3061914bffb6SAlexander V. Chernikov 	if (tflags & IPFW_TFFLAG_DSTIP) {
3062914bffb6SAlexander V. Chernikov 		memset(&fe4->dip, 0xFF, sizeof(fe4->dip));
3063914bffb6SAlexander V. Chernikov 		memset(&fe6->dip6, 0xFF, sizeof(fe6->dip6));
3064914bffb6SAlexander V. Chernikov 	}
3065914bffb6SAlexander V. Chernikov 	if (tflags & IPFW_TFFLAG_SRCPORT) {
3066914bffb6SAlexander V. Chernikov 		memset(&fe4->e.sport, 0xFF, sizeof(fe4->e.sport));
3067914bffb6SAlexander V. Chernikov 		memset(&fe6->e.sport, 0xFF, sizeof(fe6->e.sport));
3068914bffb6SAlexander V. Chernikov 	}
3069914bffb6SAlexander V. Chernikov 	if (tflags & IPFW_TFFLAG_DSTPORT) {
3070914bffb6SAlexander V. Chernikov 		memset(&fe4->e.dport, 0xFF, sizeof(fe4->e.dport));
3071914bffb6SAlexander V. Chernikov 		memset(&fe6->e.dport, 0xFF, sizeof(fe6->e.dport));
3072914bffb6SAlexander V. Chernikov 	}
3073914bffb6SAlexander V. Chernikov 	if (tflags & IPFW_TFFLAG_PROTO) {
3074914bffb6SAlexander V. Chernikov 		memset(&fe4->e.proto, 0xFF, sizeof(fe4->e.proto));
3075914bffb6SAlexander V. Chernikov 		memset(&fe6->e.proto, 0xFF, sizeof(fe6->e.proto));
3076914bffb6SAlexander V. Chernikov 	}
3077914bffb6SAlexander V. Chernikov 
3078914bffb6SAlexander V. Chernikov 	fe4->e.af = AF_INET;
3079914bffb6SAlexander V. Chernikov 	fe6->e.af = AF_INET6;
3080914bffb6SAlexander V. Chernikov 
3081914bffb6SAlexander V. Chernikov 	*ta_state = cfg;
3082914bffb6SAlexander V. Chernikov 	ti->state = cfg->head;
3083914bffb6SAlexander V. Chernikov 	ti->xstate = &cfg->fe4;
3084914bffb6SAlexander V. Chernikov 	ti->data = cfg->size;
3085914bffb6SAlexander V. Chernikov 	ti->lookup = ta_lookup_fhash;
3086914bffb6SAlexander V. Chernikov 
3087914bffb6SAlexander V. Chernikov 	return (0);
3088914bffb6SAlexander V. Chernikov }
3089914bffb6SAlexander V. Chernikov 
3090914bffb6SAlexander V. Chernikov static void
3091914bffb6SAlexander V. Chernikov ta_destroy_fhash(void *ta_state, struct table_info *ti)
3092914bffb6SAlexander V. Chernikov {
3093914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
3094914bffb6SAlexander V. Chernikov 	struct fhashentry *ent, *ent_next;
3095914bffb6SAlexander V. Chernikov 	int i;
3096914bffb6SAlexander V. Chernikov 
3097914bffb6SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
3098914bffb6SAlexander V. Chernikov 
3099914bffb6SAlexander V. Chernikov 	for (i = 0; i < cfg->size; i++)
3100914bffb6SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &cfg->head[i], next, ent_next)
3101914bffb6SAlexander V. Chernikov 			free(ent, M_IPFW_TBL);
3102914bffb6SAlexander V. Chernikov 
3103914bffb6SAlexander V. Chernikov 	free(cfg->head, M_IPFW);
3104914bffb6SAlexander V. Chernikov 	free(cfg, M_IPFW);
3105914bffb6SAlexander V. Chernikov }
3106914bffb6SAlexander V. Chernikov 
31075f379342SAlexander V. Chernikov /*
31085f379342SAlexander V. Chernikov  * Provide algo-specific table info
31095f379342SAlexander V. Chernikov  */
31105f379342SAlexander V. Chernikov static void
31115f379342SAlexander V. Chernikov ta_dump_fhash_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo)
31125f379342SAlexander V. Chernikov {
31135f379342SAlexander V. Chernikov 	struct fhash_cfg *cfg;
31145f379342SAlexander V. Chernikov 
31155f379342SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
31165f379342SAlexander V. Chernikov 
31175f379342SAlexander V. Chernikov 	tinfo->flags = IPFW_TATFLAGS_AFITEM;
31185f379342SAlexander V. Chernikov 	tinfo->taclass4 = IPFW_TACLASS_HASH;
31195f379342SAlexander V. Chernikov 	tinfo->size4 = cfg->size;
31205f379342SAlexander V. Chernikov 	tinfo->count4 = cfg->items;
31215f379342SAlexander V. Chernikov 	tinfo->itemsize4 = sizeof(struct fhashentry4);
31225f379342SAlexander V. Chernikov 	tinfo->itemsize6 = sizeof(struct fhashentry6);
31235f379342SAlexander V. Chernikov }
31245f379342SAlexander V. Chernikov 
3125914bffb6SAlexander V. Chernikov static int
3126914bffb6SAlexander V. Chernikov ta_dump_fhash_tentry(void *ta_state, struct table_info *ti, void *e,
3127914bffb6SAlexander V. Chernikov     ipfw_obj_tentry *tent)
3128914bffb6SAlexander V. Chernikov {
3129914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
3130914bffb6SAlexander V. Chernikov 	struct fhashentry *ent;
3131914bffb6SAlexander V. Chernikov 	struct fhashentry4 *fe4;
3132914bffb6SAlexander V. Chernikov 	struct fhashentry6 *fe6;
3133914bffb6SAlexander V. Chernikov 	struct tflow_entry *tfe;
3134914bffb6SAlexander V. Chernikov 
3135914bffb6SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
3136914bffb6SAlexander V. Chernikov 	ent = (struct fhashentry *)e;
3137914bffb6SAlexander V. Chernikov 	tfe = &tent->k.flow;
3138914bffb6SAlexander V. Chernikov 
3139914bffb6SAlexander V. Chernikov 	tfe->af = ent->af;
3140914bffb6SAlexander V. Chernikov 	tfe->proto = ent->proto;
3141914bffb6SAlexander V. Chernikov 	tfe->dport = htons(ent->dport);
3142914bffb6SAlexander V. Chernikov 	tfe->sport = htons(ent->sport);
31430cba2b28SAlexander V. Chernikov 	tent->v.kidx = ent->value;
3144914bffb6SAlexander V. Chernikov 	tent->subtype = ent->af;
3145914bffb6SAlexander V. Chernikov 
3146914bffb6SAlexander V. Chernikov 	if (ent->af == AF_INET) {
3147914bffb6SAlexander V. Chernikov 		fe4 = (struct fhashentry4 *)ent;
3148914bffb6SAlexander V. Chernikov 		tfe->a.a4.sip.s_addr = htonl(fe4->sip.s_addr);
3149914bffb6SAlexander V. Chernikov 		tfe->a.a4.dip.s_addr = htonl(fe4->dip.s_addr);
3150914bffb6SAlexander V. Chernikov 		tent->masklen = 32;
3151914bffb6SAlexander V. Chernikov #ifdef INET6
3152914bffb6SAlexander V. Chernikov 	} else {
3153914bffb6SAlexander V. Chernikov 		fe6 = (struct fhashentry6 *)ent;
3154914bffb6SAlexander V. Chernikov 		tfe->a.a6.sip6 = fe6->sip6;
3155914bffb6SAlexander V. Chernikov 		tfe->a.a6.dip6 = fe6->dip6;
3156914bffb6SAlexander V. Chernikov 		tent->masklen = 128;
3157914bffb6SAlexander V. Chernikov #endif
3158914bffb6SAlexander V. Chernikov 	}
3159914bffb6SAlexander V. Chernikov 
3160914bffb6SAlexander V. Chernikov 	return (0);
3161914bffb6SAlexander V. Chernikov }
3162914bffb6SAlexander V. Chernikov 
3163914bffb6SAlexander V. Chernikov static int
3164914bffb6SAlexander V. Chernikov tei_to_fhash_ent(struct tentry_info *tei, struct fhashentry *ent)
3165914bffb6SAlexander V. Chernikov {
3166914bffb6SAlexander V. Chernikov 	struct fhashentry4 *fe4;
3167914bffb6SAlexander V. Chernikov 	struct fhashentry6 *fe6;
3168914bffb6SAlexander V. Chernikov 	struct tflow_entry *tfe;
3169914bffb6SAlexander V. Chernikov 
3170914bffb6SAlexander V. Chernikov 	tfe = (struct tflow_entry *)tei->paddr;
3171914bffb6SAlexander V. Chernikov 
3172914bffb6SAlexander V. Chernikov 	ent->af = tei->subtype;
3173914bffb6SAlexander V. Chernikov 	ent->proto = tfe->proto;
3174914bffb6SAlexander V. Chernikov 	ent->dport = ntohs(tfe->dport);
3175914bffb6SAlexander V. Chernikov 	ent->sport = ntohs(tfe->sport);
3176914bffb6SAlexander V. Chernikov 
3177914bffb6SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
3178914bffb6SAlexander V. Chernikov #ifdef INET
3179914bffb6SAlexander V. Chernikov 		fe4 = (struct fhashentry4 *)ent;
3180914bffb6SAlexander V. Chernikov 		fe4->sip.s_addr = ntohl(tfe->a.a4.sip.s_addr);
3181914bffb6SAlexander V. Chernikov 		fe4->dip.s_addr = ntohl(tfe->a.a4.dip.s_addr);
3182914bffb6SAlexander V. Chernikov #endif
3183914bffb6SAlexander V. Chernikov #ifdef INET6
3184914bffb6SAlexander V. Chernikov 	} else if (tei->subtype == AF_INET6) {
3185914bffb6SAlexander V. Chernikov 		fe6 = (struct fhashentry6 *)ent;
3186914bffb6SAlexander V. Chernikov 		fe6->sip6 = tfe->a.a6.sip6;
3187914bffb6SAlexander V. Chernikov 		fe6->dip6 = tfe->a.a6.dip6;
3188914bffb6SAlexander V. Chernikov #endif
3189914bffb6SAlexander V. Chernikov 	} else {
3190914bffb6SAlexander V. Chernikov 		/* Unknown CIDR type */
3191914bffb6SAlexander V. Chernikov 		return (EINVAL);
3192914bffb6SAlexander V. Chernikov 	}
3193914bffb6SAlexander V. Chernikov 
3194914bffb6SAlexander V. Chernikov 	return (0);
3195914bffb6SAlexander V. Chernikov }
3196914bffb6SAlexander V. Chernikov 
3197914bffb6SAlexander V. Chernikov 
3198914bffb6SAlexander V. Chernikov static int
3199914bffb6SAlexander V. Chernikov ta_find_fhash_tentry(void *ta_state, struct table_info *ti,
3200914bffb6SAlexander V. Chernikov     ipfw_obj_tentry *tent)
3201914bffb6SAlexander V. Chernikov {
3202914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
3203914bffb6SAlexander V. Chernikov 	struct fhashbhead *head;
3204914bffb6SAlexander V. Chernikov 	struct fhashentry *ent, *tmp;
3205914bffb6SAlexander V. Chernikov 	struct fhashentry6 fe6;
3206914bffb6SAlexander V. Chernikov 	struct tentry_info tei;
3207914bffb6SAlexander V. Chernikov 	int error;
3208914bffb6SAlexander V. Chernikov 	uint32_t hash;
3209914bffb6SAlexander V. Chernikov 	size_t sz;
3210914bffb6SAlexander V. Chernikov 
3211914bffb6SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
3212914bffb6SAlexander V. Chernikov 
3213914bffb6SAlexander V. Chernikov 	ent = &fe6.e;
3214914bffb6SAlexander V. Chernikov 
3215914bffb6SAlexander V. Chernikov 	memset(&fe6, 0, sizeof(fe6));
3216914bffb6SAlexander V. Chernikov 	memset(&tei, 0, sizeof(tei));
3217914bffb6SAlexander V. Chernikov 
3218914bffb6SAlexander V. Chernikov 	tei.paddr = &tent->k.flow;
3219914bffb6SAlexander V. Chernikov 	tei.subtype = tent->subtype;
3220914bffb6SAlexander V. Chernikov 
3221914bffb6SAlexander V. Chernikov 	if ((error = tei_to_fhash_ent(&tei, ent)) != 0)
3222914bffb6SAlexander V. Chernikov 		return (error);
3223914bffb6SAlexander V. Chernikov 
3224914bffb6SAlexander V. Chernikov 	head = cfg->head;
3225914bffb6SAlexander V. Chernikov 	hash = hash_flow_ent(ent, cfg->size);
3226914bffb6SAlexander V. Chernikov 
3227914bffb6SAlexander V. Chernikov 	if (tei.subtype == AF_INET)
3228914bffb6SAlexander V. Chernikov 		sz = 2 * sizeof(struct in_addr);
3229914bffb6SAlexander V. Chernikov 	else
3230914bffb6SAlexander V. Chernikov 		sz = 2 * sizeof(struct in6_addr);
3231914bffb6SAlexander V. Chernikov 
3232914bffb6SAlexander V. Chernikov 	/* Check for existence */
3233914bffb6SAlexander V. Chernikov 	SLIST_FOREACH(tmp, &head[hash], next) {
3234914bffb6SAlexander V. Chernikov 		if (cmp_flow_ent(tmp, ent, sz) != 0) {
3235914bffb6SAlexander V. Chernikov 			ta_dump_fhash_tentry(ta_state, ti, tmp, tent);
3236914bffb6SAlexander V. Chernikov 			return (0);
3237914bffb6SAlexander V. Chernikov 		}
3238914bffb6SAlexander V. Chernikov 	}
3239914bffb6SAlexander V. Chernikov 
3240914bffb6SAlexander V. Chernikov 	return (ENOENT);
3241914bffb6SAlexander V. Chernikov }
3242914bffb6SAlexander V. Chernikov 
3243914bffb6SAlexander V. Chernikov static void
3244914bffb6SAlexander V. Chernikov ta_foreach_fhash(void *ta_state, struct table_info *ti, ta_foreach_f *f,
3245914bffb6SAlexander V. Chernikov     void *arg)
3246914bffb6SAlexander V. Chernikov {
3247914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
3248914bffb6SAlexander V. Chernikov 	struct fhashentry *ent, *ent_next;
3249914bffb6SAlexander V. Chernikov 	int i;
3250914bffb6SAlexander V. Chernikov 
3251914bffb6SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
3252914bffb6SAlexander V. Chernikov 
3253914bffb6SAlexander V. Chernikov 	for (i = 0; i < cfg->size; i++)
3254914bffb6SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &cfg->head[i], next, ent_next)
3255914bffb6SAlexander V. Chernikov 			f(ent, arg);
3256914bffb6SAlexander V. Chernikov }
3257914bffb6SAlexander V. Chernikov 
3258914bffb6SAlexander V. Chernikov static int
3259914bffb6SAlexander V. Chernikov ta_prepare_add_fhash(struct ip_fw_chain *ch, struct tentry_info *tei,
3260914bffb6SAlexander V. Chernikov     void *ta_buf)
3261914bffb6SAlexander V. Chernikov {
3262914bffb6SAlexander V. Chernikov 	struct ta_buf_fhash *tb;
3263914bffb6SAlexander V. Chernikov 	struct fhashentry *ent;
3264914bffb6SAlexander V. Chernikov 	size_t sz;
3265914bffb6SAlexander V. Chernikov 	int error;
3266914bffb6SAlexander V. Chernikov 
3267914bffb6SAlexander V. Chernikov 	tb = (struct ta_buf_fhash *)ta_buf;
3268914bffb6SAlexander V. Chernikov 
3269914bffb6SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
3270914bffb6SAlexander V. Chernikov 		sz = sizeof(struct fhashentry4);
3271914bffb6SAlexander V. Chernikov 	else if (tei->subtype == AF_INET6)
3272914bffb6SAlexander V. Chernikov 		sz = sizeof(struct fhashentry6);
3273914bffb6SAlexander V. Chernikov 	else
3274914bffb6SAlexander V. Chernikov 		return (EINVAL);
3275914bffb6SAlexander V. Chernikov 
3276914bffb6SAlexander V. Chernikov 	ent = malloc(sz, M_IPFW_TBL, M_WAITOK | M_ZERO);
3277914bffb6SAlexander V. Chernikov 
3278914bffb6SAlexander V. Chernikov 	error = tei_to_fhash_ent(tei, ent);
3279914bffb6SAlexander V. Chernikov 	if (error != 0) {
3280914bffb6SAlexander V. Chernikov 		free(ent, M_IPFW_TBL);
3281914bffb6SAlexander V. Chernikov 		return (error);
3282914bffb6SAlexander V. Chernikov 	}
3283914bffb6SAlexander V. Chernikov 	tb->ent_ptr = ent;
3284914bffb6SAlexander V. Chernikov 
3285914bffb6SAlexander V. Chernikov 	return (0);
3286914bffb6SAlexander V. Chernikov }
3287914bffb6SAlexander V. Chernikov 
3288914bffb6SAlexander V. Chernikov static int
3289914bffb6SAlexander V. Chernikov ta_add_fhash(void *ta_state, struct table_info *ti, struct tentry_info *tei,
3290b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
3291914bffb6SAlexander V. Chernikov {
3292914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
3293914bffb6SAlexander V. Chernikov 	struct fhashbhead *head;
3294914bffb6SAlexander V. Chernikov 	struct fhashentry *ent, *tmp;
3295914bffb6SAlexander V. Chernikov 	struct ta_buf_fhash *tb;
3296914bffb6SAlexander V. Chernikov 	int exists;
3297648e8380SAlexander V. Chernikov 	uint32_t hash, value;
3298914bffb6SAlexander V. Chernikov 	size_t sz;
3299914bffb6SAlexander V. Chernikov 
3300914bffb6SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
3301914bffb6SAlexander V. Chernikov 	tb = (struct ta_buf_fhash *)ta_buf;
3302914bffb6SAlexander V. Chernikov 	ent = (struct fhashentry *)tb->ent_ptr;
3303914bffb6SAlexander V. Chernikov 	exists = 0;
3304914bffb6SAlexander V. Chernikov 
330513263632SAlexander V. Chernikov 	/* Read current value from @tei */
330613263632SAlexander V. Chernikov 	ent->value = tei->value;
330713263632SAlexander V. Chernikov 
3308914bffb6SAlexander V. Chernikov 	head = cfg->head;
3309914bffb6SAlexander V. Chernikov 	hash = hash_flow_ent(ent, cfg->size);
3310914bffb6SAlexander V. Chernikov 
3311914bffb6SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
3312914bffb6SAlexander V. Chernikov 		sz = 2 * sizeof(struct in_addr);
3313914bffb6SAlexander V. Chernikov 	else
3314914bffb6SAlexander V. Chernikov 		sz = 2 * sizeof(struct in6_addr);
3315914bffb6SAlexander V. Chernikov 
3316914bffb6SAlexander V. Chernikov 	/* Check for existence */
3317914bffb6SAlexander V. Chernikov 	SLIST_FOREACH(tmp, &head[hash], next) {
3318914bffb6SAlexander V. Chernikov 		if (cmp_flow_ent(tmp, ent, sz) != 0) {
3319914bffb6SAlexander V. Chernikov 			exists = 1;
3320914bffb6SAlexander V. Chernikov 			break;
3321914bffb6SAlexander V. Chernikov 		}
3322914bffb6SAlexander V. Chernikov 	}
3323914bffb6SAlexander V. Chernikov 
3324914bffb6SAlexander V. Chernikov 	if (exists == 1) {
3325914bffb6SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
3326914bffb6SAlexander V. Chernikov 			return (EEXIST);
3327914bffb6SAlexander V. Chernikov 		/* Record already exists. Update value if we're asked to */
3328648e8380SAlexander V. Chernikov 		/* Exchange values between tmp and @tei */
3329648e8380SAlexander V. Chernikov 		value = tmp->value;
3330914bffb6SAlexander V. Chernikov 		tmp->value = tei->value;
3331648e8380SAlexander V. Chernikov 		tei->value = value;
3332914bffb6SAlexander V. Chernikov 		/* Indicate that update has happened instead of addition */
3333914bffb6SAlexander V. Chernikov 		tei->flags |= TEI_FLAGS_UPDATED;
3334914bffb6SAlexander V. Chernikov 		*pnum = 0;
3335914bffb6SAlexander V. Chernikov 	} else {
33364c0c07a5SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_DONTADD) != 0)
33374c0c07a5SAlexander V. Chernikov 			return (EFBIG);
33384c0c07a5SAlexander V. Chernikov 
3339914bffb6SAlexander V. Chernikov 		SLIST_INSERT_HEAD(&head[hash], ent, next);
3340914bffb6SAlexander V. Chernikov 		tb->ent_ptr = NULL;
3341914bffb6SAlexander V. Chernikov 		*pnum = 1;
3342914bffb6SAlexander V. Chernikov 
3343914bffb6SAlexander V. Chernikov 		/* Update counters and check if we need to grow hash */
3344914bffb6SAlexander V. Chernikov 		cfg->items++;
3345914bffb6SAlexander V. Chernikov 	}
3346914bffb6SAlexander V. Chernikov 
3347914bffb6SAlexander V. Chernikov 	return (0);
3348914bffb6SAlexander V. Chernikov }
3349914bffb6SAlexander V. Chernikov 
3350914bffb6SAlexander V. Chernikov static int
3351914bffb6SAlexander V. Chernikov ta_prepare_del_fhash(struct ip_fw_chain *ch, struct tentry_info *tei,
3352914bffb6SAlexander V. Chernikov     void *ta_buf)
3353914bffb6SAlexander V. Chernikov {
3354914bffb6SAlexander V. Chernikov 	struct ta_buf_fhash *tb;
3355914bffb6SAlexander V. Chernikov 
3356914bffb6SAlexander V. Chernikov 	tb = (struct ta_buf_fhash *)ta_buf;
3357914bffb6SAlexander V. Chernikov 
3358914bffb6SAlexander V. Chernikov 	return (tei_to_fhash_ent(tei, &tb->fe6.e));
3359914bffb6SAlexander V. Chernikov }
3360914bffb6SAlexander V. Chernikov 
3361914bffb6SAlexander V. Chernikov static int
3362914bffb6SAlexander V. Chernikov ta_del_fhash(void *ta_state, struct table_info *ti, struct tentry_info *tei,
3363b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
3364914bffb6SAlexander V. Chernikov {
3365914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
3366914bffb6SAlexander V. Chernikov 	struct fhashbhead *head;
3367914bffb6SAlexander V. Chernikov 	struct fhashentry *ent, *tmp;
3368914bffb6SAlexander V. Chernikov 	struct ta_buf_fhash *tb;
3369914bffb6SAlexander V. Chernikov 	uint32_t hash;
3370914bffb6SAlexander V. Chernikov 	size_t sz;
3371914bffb6SAlexander V. Chernikov 
3372914bffb6SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
3373914bffb6SAlexander V. Chernikov 	tb = (struct ta_buf_fhash *)ta_buf;
3374914bffb6SAlexander V. Chernikov 	ent = &tb->fe6.e;
3375914bffb6SAlexander V. Chernikov 
3376914bffb6SAlexander V. Chernikov 	head = cfg->head;
3377914bffb6SAlexander V. Chernikov 	hash = hash_flow_ent(ent, cfg->size);
3378914bffb6SAlexander V. Chernikov 
3379914bffb6SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
3380914bffb6SAlexander V. Chernikov 		sz = 2 * sizeof(struct in_addr);
3381914bffb6SAlexander V. Chernikov 	else
3382914bffb6SAlexander V. Chernikov 		sz = 2 * sizeof(struct in6_addr);
3383914bffb6SAlexander V. Chernikov 
3384914bffb6SAlexander V. Chernikov 	/* Check for existence */
3385914bffb6SAlexander V. Chernikov 	SLIST_FOREACH(tmp, &head[hash], next) {
3386648e8380SAlexander V. Chernikov 		if (cmp_flow_ent(tmp, ent, sz) == 0)
3387648e8380SAlexander V. Chernikov 			continue;
3388648e8380SAlexander V. Chernikov 
3389914bffb6SAlexander V. Chernikov 		SLIST_REMOVE(&head[hash], tmp, fhashentry, next);
3390648e8380SAlexander V. Chernikov 		tei->value = tmp->value;
3391914bffb6SAlexander V. Chernikov 		*pnum = 1;
3392914bffb6SAlexander V. Chernikov 		cfg->items--;
3393648e8380SAlexander V. Chernikov 		tb->ent_ptr = tmp;
3394914bffb6SAlexander V. Chernikov 		return (0);
3395914bffb6SAlexander V. Chernikov 	}
3396914bffb6SAlexander V. Chernikov 
3397914bffb6SAlexander V. Chernikov 	return (ENOENT);
3398914bffb6SAlexander V. Chernikov }
3399914bffb6SAlexander V. Chernikov 
3400914bffb6SAlexander V. Chernikov static void
3401914bffb6SAlexander V. Chernikov ta_flush_fhash_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
3402914bffb6SAlexander V. Chernikov     void *ta_buf)
3403914bffb6SAlexander V. Chernikov {
3404914bffb6SAlexander V. Chernikov 	struct ta_buf_fhash *tb;
3405914bffb6SAlexander V. Chernikov 
3406914bffb6SAlexander V. Chernikov 	tb = (struct ta_buf_fhash *)ta_buf;
3407914bffb6SAlexander V. Chernikov 
3408914bffb6SAlexander V. Chernikov 	if (tb->ent_ptr != NULL)
3409914bffb6SAlexander V. Chernikov 		free(tb->ent_ptr, M_IPFW_TBL);
3410914bffb6SAlexander V. Chernikov }
3411914bffb6SAlexander V. Chernikov 
3412914bffb6SAlexander V. Chernikov /*
3413914bffb6SAlexander V. Chernikov  * Hash growing callbacks.
3414914bffb6SAlexander V. Chernikov  */
3415914bffb6SAlexander V. Chernikov 
3416b6ee846eSAlexander V. Chernikov static int
3417301290bcSAlexander V. Chernikov ta_need_modify_fhash(void *ta_state, struct table_info *ti, uint32_t count,
3418b6ee846eSAlexander V. Chernikov     uint64_t *pflags)
3419b6ee846eSAlexander V. Chernikov {
3420b6ee846eSAlexander V. Chernikov 	struct fhash_cfg *cfg;
3421b6ee846eSAlexander V. Chernikov 
3422b6ee846eSAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
3423b6ee846eSAlexander V. Chernikov 
3424b6ee846eSAlexander V. Chernikov 	if (cfg->items > cfg->size && cfg->size < 65536) {
3425b6ee846eSAlexander V. Chernikov 		*pflags = cfg->size * 2;
3426301290bcSAlexander V. Chernikov 		return (1);
3427b6ee846eSAlexander V. Chernikov 	}
3428b6ee846eSAlexander V. Chernikov 
3429301290bcSAlexander V. Chernikov 	return (0);
3430b6ee846eSAlexander V. Chernikov }
3431b6ee846eSAlexander V. Chernikov 
3432914bffb6SAlexander V. Chernikov /*
3433914bffb6SAlexander V. Chernikov  * Allocate new, larger fhash.
3434914bffb6SAlexander V. Chernikov  */
3435914bffb6SAlexander V. Chernikov static int
3436914bffb6SAlexander V. Chernikov ta_prepare_mod_fhash(void *ta_buf, uint64_t *pflags)
3437914bffb6SAlexander V. Chernikov {
3438914bffb6SAlexander V. Chernikov 	struct mod_item *mi;
3439914bffb6SAlexander V. Chernikov 	struct fhashbhead *head;
3440914bffb6SAlexander V. Chernikov 	int i;
3441914bffb6SAlexander V. Chernikov 
3442914bffb6SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
3443914bffb6SAlexander V. Chernikov 
3444914bffb6SAlexander V. Chernikov 	memset(mi, 0, sizeof(struct mod_item));
3445914bffb6SAlexander V. Chernikov 	mi->size = *pflags;
3446914bffb6SAlexander V. Chernikov 	head = malloc(sizeof(struct fhashbhead) * mi->size, M_IPFW,
3447914bffb6SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
3448914bffb6SAlexander V. Chernikov 	for (i = 0; i < mi->size; i++)
3449914bffb6SAlexander V. Chernikov 		SLIST_INIT(&head[i]);
3450914bffb6SAlexander V. Chernikov 
3451914bffb6SAlexander V. Chernikov 	mi->main_ptr = head;
3452914bffb6SAlexander V. Chernikov 
3453914bffb6SAlexander V. Chernikov 	return (0);
3454914bffb6SAlexander V. Chernikov }
3455914bffb6SAlexander V. Chernikov 
3456914bffb6SAlexander V. Chernikov /*
3457914bffb6SAlexander V. Chernikov  * Copy data from old runtime array to new one.
3458914bffb6SAlexander V. Chernikov  */
3459914bffb6SAlexander V. Chernikov static int
3460914bffb6SAlexander V. Chernikov ta_fill_mod_fhash(void *ta_state, struct table_info *ti, void *ta_buf,
3461914bffb6SAlexander V. Chernikov     uint64_t *pflags)
3462914bffb6SAlexander V. Chernikov {
3463914bffb6SAlexander V. Chernikov 
3464914bffb6SAlexander V. Chernikov 	/* In is not possible to do rehash if we're not holidng WLOCK. */
3465914bffb6SAlexander V. Chernikov 	return (0);
3466914bffb6SAlexander V. Chernikov }
3467914bffb6SAlexander V. Chernikov 
3468914bffb6SAlexander V. Chernikov /*
3469914bffb6SAlexander V. Chernikov  * Switch old & new arrays.
3470914bffb6SAlexander V. Chernikov  */
3471301290bcSAlexander V. Chernikov static void
3472914bffb6SAlexander V. Chernikov ta_modify_fhash(void *ta_state, struct table_info *ti, void *ta_buf,
3473914bffb6SAlexander V. Chernikov     uint64_t pflags)
3474914bffb6SAlexander V. Chernikov {
3475914bffb6SAlexander V. Chernikov 	struct mod_item *mi;
3476914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
3477914bffb6SAlexander V. Chernikov 	struct fhashbhead *old_head, *new_head;
3478914bffb6SAlexander V. Chernikov 	struct fhashentry *ent, *ent_next;
3479914bffb6SAlexander V. Chernikov 	int i;
3480914bffb6SAlexander V. Chernikov 	uint32_t nhash;
3481914bffb6SAlexander V. Chernikov 	size_t old_size;
3482914bffb6SAlexander V. Chernikov 
3483914bffb6SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
3484914bffb6SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
3485914bffb6SAlexander V. Chernikov 
3486914bffb6SAlexander V. Chernikov 	old_size = cfg->size;
3487914bffb6SAlexander V. Chernikov 	old_head = ti->state;
3488914bffb6SAlexander V. Chernikov 
3489914bffb6SAlexander V. Chernikov 	new_head = (struct fhashbhead *)mi->main_ptr;
3490914bffb6SAlexander V. Chernikov 	for (i = 0; i < old_size; i++) {
3491914bffb6SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &old_head[i], next, ent_next) {
3492914bffb6SAlexander V. Chernikov 			nhash = hash_flow_ent(ent, mi->size);
3493914bffb6SAlexander V. Chernikov 			SLIST_INSERT_HEAD(&new_head[nhash], ent, next);
3494914bffb6SAlexander V. Chernikov 		}
3495914bffb6SAlexander V. Chernikov 	}
3496914bffb6SAlexander V. Chernikov 
3497914bffb6SAlexander V. Chernikov 	ti->state = new_head;
3498914bffb6SAlexander V. Chernikov 	ti->data = mi->size;
3499914bffb6SAlexander V. Chernikov 	cfg->head = new_head;
3500914bffb6SAlexander V. Chernikov 	cfg->size = mi->size;
3501914bffb6SAlexander V. Chernikov 
3502914bffb6SAlexander V. Chernikov 	mi->main_ptr = old_head;
3503914bffb6SAlexander V. Chernikov }
3504914bffb6SAlexander V. Chernikov 
3505914bffb6SAlexander V. Chernikov /*
3506914bffb6SAlexander V. Chernikov  * Free unneded array.
3507914bffb6SAlexander V. Chernikov  */
3508914bffb6SAlexander V. Chernikov static void
3509914bffb6SAlexander V. Chernikov ta_flush_mod_fhash(void *ta_buf)
3510914bffb6SAlexander V. Chernikov {
3511914bffb6SAlexander V. Chernikov 	struct mod_item *mi;
3512914bffb6SAlexander V. Chernikov 
3513914bffb6SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
3514914bffb6SAlexander V. Chernikov 	if (mi->main_ptr != NULL)
3515914bffb6SAlexander V. Chernikov 		free(mi->main_ptr, M_IPFW);
3516914bffb6SAlexander V. Chernikov }
3517914bffb6SAlexander V. Chernikov 
3518914bffb6SAlexander V. Chernikov struct table_algo flow_hash = {
3519914bffb6SAlexander V. Chernikov 	.name		= "flow:hash",
3520914bffb6SAlexander V. Chernikov 	.type		= IPFW_TABLE_FLOW,
352157a1cf95SAlexander V. Chernikov 	.flags		= TA_FLAG_DEFAULT,
352257a1cf95SAlexander V. Chernikov 	.ta_buf_size	= sizeof(struct ta_buf_fhash),
3523914bffb6SAlexander V. Chernikov 	.init		= ta_init_fhash,
3524914bffb6SAlexander V. Chernikov 	.destroy	= ta_destroy_fhash,
3525914bffb6SAlexander V. Chernikov 	.prepare_add	= ta_prepare_add_fhash,
3526914bffb6SAlexander V. Chernikov 	.prepare_del	= ta_prepare_del_fhash,
3527914bffb6SAlexander V. Chernikov 	.add		= ta_add_fhash,
3528914bffb6SAlexander V. Chernikov 	.del		= ta_del_fhash,
3529914bffb6SAlexander V. Chernikov 	.flush_entry	= ta_flush_fhash_entry,
3530914bffb6SAlexander V. Chernikov 	.foreach	= ta_foreach_fhash,
3531914bffb6SAlexander V. Chernikov 	.dump_tentry	= ta_dump_fhash_tentry,
3532914bffb6SAlexander V. Chernikov 	.find_tentry	= ta_find_fhash_tentry,
35335f379342SAlexander V. Chernikov 	.dump_tinfo	= ta_dump_fhash_tinfo,
3534301290bcSAlexander V. Chernikov 	.need_modify	= ta_need_modify_fhash,
3535914bffb6SAlexander V. Chernikov 	.prepare_mod	= ta_prepare_mod_fhash,
3536914bffb6SAlexander V. Chernikov 	.fill_mod	= ta_fill_mod_fhash,
3537914bffb6SAlexander V. Chernikov 	.modify		= ta_modify_fhash,
3538914bffb6SAlexander V. Chernikov 	.flush_mod	= ta_flush_mod_fhash,
3539914bffb6SAlexander V. Chernikov };
35403fe2ef91SAlexander V. Chernikov 
3541d3b00c08SAlexander V. Chernikov /*
3542d3b00c08SAlexander V. Chernikov  * Kernel fibs bindings.
3543d3b00c08SAlexander V. Chernikov  *
3544d3b00c08SAlexander V. Chernikov  * Implementation:
3545d3b00c08SAlexander V. Chernikov  *
3546d3b00c08SAlexander V. Chernikov  * Runtime part:
3547d3b00c08SAlexander V. Chernikov  * - fully relies on route API
3548d3b00c08SAlexander V. Chernikov  * - fib number is stored in ti->data
3549d3b00c08SAlexander V. Chernikov  *
3550d3b00c08SAlexander V. Chernikov  */
3551d3b00c08SAlexander V. Chernikov 
3552d3b00c08SAlexander V. Chernikov static struct rtentry *
3553d3b00c08SAlexander V. Chernikov lookup_kfib(void *key, int keylen, int fib)
3554d3b00c08SAlexander V. Chernikov {
3555d3b00c08SAlexander V. Chernikov 	struct sockaddr *s;
3556d3b00c08SAlexander V. Chernikov 
3557d3b00c08SAlexander V. Chernikov 	if (keylen == 4) {
3558d3b00c08SAlexander V. Chernikov 		struct sockaddr_in sin;
3559d3b00c08SAlexander V. Chernikov 		bzero(&sin, sizeof(sin));
3560d3b00c08SAlexander V. Chernikov 		sin.sin_len = sizeof(struct sockaddr_in);
3561d3b00c08SAlexander V. Chernikov 		sin.sin_family = AF_INET;
3562d3b00c08SAlexander V. Chernikov 		sin.sin_addr.s_addr = *(in_addr_t *)key;
3563d3b00c08SAlexander V. Chernikov 		s = (struct sockaddr *)&sin;
3564d3b00c08SAlexander V. Chernikov 	} else {
3565d3b00c08SAlexander V. Chernikov 		struct sockaddr_in6 sin6;
3566d3b00c08SAlexander V. Chernikov 		bzero(&sin6, sizeof(sin6));
3567d3b00c08SAlexander V. Chernikov 		sin6.sin6_len = sizeof(struct sockaddr_in6);
3568d3b00c08SAlexander V. Chernikov 		sin6.sin6_family = AF_INET6;
3569d3b00c08SAlexander V. Chernikov 		sin6.sin6_addr = *(struct in6_addr *)key;
3570d3b00c08SAlexander V. Chernikov 		s = (struct sockaddr *)&sin6;
3571d3b00c08SAlexander V. Chernikov 	}
3572d3b00c08SAlexander V. Chernikov 
3573d3b00c08SAlexander V. Chernikov 	return (rtalloc1_fib(s, 0, 0, fib));
3574d3b00c08SAlexander V. Chernikov }
3575d3b00c08SAlexander V. Chernikov 
3576d3b00c08SAlexander V. Chernikov static int
3577d3b00c08SAlexander V. Chernikov ta_lookup_kfib(struct table_info *ti, void *key, uint32_t keylen,
3578d3b00c08SAlexander V. Chernikov     uint32_t *val)
3579d3b00c08SAlexander V. Chernikov {
3580d3b00c08SAlexander V. Chernikov 	struct rtentry *rte;
3581d3b00c08SAlexander V. Chernikov 
3582d3b00c08SAlexander V. Chernikov 	if ((rte = lookup_kfib(key, keylen, ti->data)) == NULL)
3583d3b00c08SAlexander V. Chernikov 		return (0);
3584d3b00c08SAlexander V. Chernikov 
3585d3b00c08SAlexander V. Chernikov 	*val = 0;
3586d3b00c08SAlexander V. Chernikov 	RTFREE_LOCKED(rte);
3587d3b00c08SAlexander V. Chernikov 
3588d3b00c08SAlexander V. Chernikov 	return (1);
3589d3b00c08SAlexander V. Chernikov }
3590d3b00c08SAlexander V. Chernikov 
3591d3b00c08SAlexander V. Chernikov /* Parse 'fib=%d' */
3592d3b00c08SAlexander V. Chernikov static int
3593d3b00c08SAlexander V. Chernikov kfib_parse_opts(int *pfib, char *data)
3594d3b00c08SAlexander V. Chernikov {
3595d3b00c08SAlexander V. Chernikov 	char *pdel, *pend, *s;
3596d3b00c08SAlexander V. Chernikov 	int fibnum;
3597d3b00c08SAlexander V. Chernikov 
3598d3b00c08SAlexander V. Chernikov 	if (data == NULL)
3599d3b00c08SAlexander V. Chernikov 		return (0);
3600d3b00c08SAlexander V. Chernikov 	if ((pdel = strchr(data, ' ')) == NULL)
3601d3b00c08SAlexander V. Chernikov 		return (0);
3602d3b00c08SAlexander V. Chernikov 	while (*pdel == ' ')
3603d3b00c08SAlexander V. Chernikov 		pdel++;
3604d3b00c08SAlexander V. Chernikov 	if (strncmp(pdel, "fib=", 4) != 0)
3605d3b00c08SAlexander V. Chernikov 		return (EINVAL);
3606d3b00c08SAlexander V. Chernikov 	if ((s = strchr(pdel, ' ')) != NULL)
3607d3b00c08SAlexander V. Chernikov 		*s++ = '\0';
3608d3b00c08SAlexander V. Chernikov 
3609d3b00c08SAlexander V. Chernikov 	pdel += 4;
3610d3b00c08SAlexander V. Chernikov 	/* Need \d+ */
3611d3b00c08SAlexander V. Chernikov 	fibnum = strtol(pdel, &pend, 10);
3612d3b00c08SAlexander V. Chernikov 	if (*pend != '\0')
3613d3b00c08SAlexander V. Chernikov 		return (EINVAL);
3614d3b00c08SAlexander V. Chernikov 
3615d3b00c08SAlexander V. Chernikov 	*pfib = fibnum;
3616d3b00c08SAlexander V. Chernikov 
3617d3b00c08SAlexander V. Chernikov 	return (0);
3618d3b00c08SAlexander V. Chernikov }
3619d3b00c08SAlexander V. Chernikov 
3620d3b00c08SAlexander V. Chernikov static void
3621d3b00c08SAlexander V. Chernikov ta_print_kfib_config(void *ta_state, struct table_info *ti, char *buf,
3622d3b00c08SAlexander V. Chernikov     size_t bufsize)
3623d3b00c08SAlexander V. Chernikov {
3624d3b00c08SAlexander V. Chernikov 
3625d3b00c08SAlexander V. Chernikov 	if (ti->data != 0)
3626c21034b7SAlexander V. Chernikov 		snprintf(buf, bufsize, "%s fib=%lu", "addr:kfib", ti->data);
3627d3b00c08SAlexander V. Chernikov 	else
3628c21034b7SAlexander V. Chernikov 		snprintf(buf, bufsize, "%s", "addr:kfib");
3629d3b00c08SAlexander V. Chernikov }
3630d3b00c08SAlexander V. Chernikov 
3631d3b00c08SAlexander V. Chernikov static int
3632d3b00c08SAlexander V. Chernikov ta_init_kfib(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
3633d3b00c08SAlexander V. Chernikov     char *data, uint8_t tflags)
3634d3b00c08SAlexander V. Chernikov {
3635d3b00c08SAlexander V. Chernikov 	int error, fibnum;
3636d3b00c08SAlexander V. Chernikov 
3637d3b00c08SAlexander V. Chernikov 	fibnum = 0;
3638d3b00c08SAlexander V. Chernikov 	if ((error = kfib_parse_opts(&fibnum, data)) != 0)
3639d3b00c08SAlexander V. Chernikov 		return (error);
3640d3b00c08SAlexander V. Chernikov 
3641d3b00c08SAlexander V. Chernikov 	if (fibnum >= rt_numfibs)
3642d3b00c08SAlexander V. Chernikov 		return (E2BIG);
3643d3b00c08SAlexander V. Chernikov 
3644d3b00c08SAlexander V. Chernikov 	ti->data = fibnum;
3645d3b00c08SAlexander V. Chernikov 	ti->lookup = ta_lookup_kfib;
3646d3b00c08SAlexander V. Chernikov 
3647d3b00c08SAlexander V. Chernikov 	return (0);
3648d3b00c08SAlexander V. Chernikov }
3649d3b00c08SAlexander V. Chernikov 
3650d3b00c08SAlexander V. Chernikov /*
3651d3b00c08SAlexander V. Chernikov  * Destroys table @ti
3652d3b00c08SAlexander V. Chernikov  */
3653d3b00c08SAlexander V. Chernikov static void
3654d3b00c08SAlexander V. Chernikov ta_destroy_kfib(void *ta_state, struct table_info *ti)
3655d3b00c08SAlexander V. Chernikov {
3656d3b00c08SAlexander V. Chernikov 
3657d3b00c08SAlexander V. Chernikov }
3658d3b00c08SAlexander V. Chernikov 
3659d3b00c08SAlexander V. Chernikov /*
3660d3b00c08SAlexander V. Chernikov  * Provide algo-specific table info
3661d3b00c08SAlexander V. Chernikov  */
3662d3b00c08SAlexander V. Chernikov static void
3663d3b00c08SAlexander V. Chernikov ta_dump_kfib_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo)
3664d3b00c08SAlexander V. Chernikov {
3665d3b00c08SAlexander V. Chernikov 
3666d3b00c08SAlexander V. Chernikov 	tinfo->flags = IPFW_TATFLAGS_AFDATA;
3667d3b00c08SAlexander V. Chernikov 	tinfo->taclass4 = IPFW_TACLASS_RADIX;
3668d3b00c08SAlexander V. Chernikov 	tinfo->count4 = 0;
3669d3b00c08SAlexander V. Chernikov 	tinfo->itemsize4 = sizeof(struct rtentry);
3670d3b00c08SAlexander V. Chernikov 	tinfo->taclass6 = IPFW_TACLASS_RADIX;
3671d3b00c08SAlexander V. Chernikov 	tinfo->count6 = 0;
3672d3b00c08SAlexander V. Chernikov 	tinfo->itemsize6 = sizeof(struct rtentry);
3673d3b00c08SAlexander V. Chernikov }
3674d3b00c08SAlexander V. Chernikov 
3675d3b00c08SAlexander V. Chernikov static int
3676d3b00c08SAlexander V. Chernikov contigmask(uint8_t *p, int len)
3677d3b00c08SAlexander V. Chernikov {
3678d3b00c08SAlexander V. Chernikov 	int i, n;
3679d3b00c08SAlexander V. Chernikov 
3680d3b00c08SAlexander V. Chernikov 	for (i = 0; i < len ; i++)
3681d3b00c08SAlexander V. Chernikov 		if ( (p[i/8] & (1 << (7 - (i%8)))) == 0) /* first bit unset */
3682d3b00c08SAlexander V. Chernikov 			break;
3683d3b00c08SAlexander V. Chernikov 	for (n= i + 1; n < len; n++)
3684d3b00c08SAlexander V. Chernikov 		if ( (p[n/8] & (1 << (7 - (n % 8)))) != 0)
3685d3b00c08SAlexander V. Chernikov 			return (-1); /* mask not contiguous */
3686d3b00c08SAlexander V. Chernikov 	return (i);
3687d3b00c08SAlexander V. Chernikov }
3688d3b00c08SAlexander V. Chernikov 
3689d3b00c08SAlexander V. Chernikov 
3690d3b00c08SAlexander V. Chernikov static int
3691d3b00c08SAlexander V. Chernikov ta_dump_kfib_tentry(void *ta_state, struct table_info *ti, void *e,
3692d3b00c08SAlexander V. Chernikov     ipfw_obj_tentry *tent)
3693d3b00c08SAlexander V. Chernikov {
3694d3b00c08SAlexander V. Chernikov 	struct rtentry *rte;
3695d3b00c08SAlexander V. Chernikov 	struct sockaddr_in *addr, *mask;
3696d3b00c08SAlexander V. Chernikov 	struct sockaddr_in6 *addr6, *mask6;
3697d3b00c08SAlexander V. Chernikov 	int len;
3698d3b00c08SAlexander V. Chernikov 
3699d3b00c08SAlexander V. Chernikov 	rte = (struct rtentry *)e;
3700d3b00c08SAlexander V. Chernikov 	addr = (struct sockaddr_in *)rt_key(rte);
3701d3b00c08SAlexander V. Chernikov 	mask = (struct sockaddr_in *)rt_mask(rte);
3702d3b00c08SAlexander V. Chernikov 	len = 0;
3703d3b00c08SAlexander V. Chernikov 
3704d3b00c08SAlexander V. Chernikov 	/* Guess IPv4/IPv6 radix by sockaddr family */
3705d3b00c08SAlexander V. Chernikov 	if (addr->sin_family == AF_INET) {
3706d3b00c08SAlexander V. Chernikov 		tent->k.addr.s_addr = addr->sin_addr.s_addr;
3707d3b00c08SAlexander V. Chernikov 		len = 32;
3708d3b00c08SAlexander V. Chernikov 		if (mask != NULL)
3709d3b00c08SAlexander V. Chernikov 			len = contigmask((uint8_t *)&mask->sin_addr, 32);
3710d3b00c08SAlexander V. Chernikov 		if (len == -1)
3711d3b00c08SAlexander V. Chernikov 			len = 0;
3712d3b00c08SAlexander V. Chernikov 		tent->masklen = len;
3713d3b00c08SAlexander V. Chernikov 		tent->subtype = AF_INET;
37140cba2b28SAlexander V. Chernikov 		tent->v.kidx = 0; /* Do we need to put GW here? */
3715d3b00c08SAlexander V. Chernikov #ifdef INET6
3716d3b00c08SAlexander V. Chernikov 	} else if (addr->sin_family == AF_INET6) {
3717d3b00c08SAlexander V. Chernikov 		addr6 = (struct sockaddr_in6 *)addr;
3718d3b00c08SAlexander V. Chernikov 		mask6 = (struct sockaddr_in6 *)mask;
3719d3b00c08SAlexander V. Chernikov 		memcpy(&tent->k, &addr6->sin6_addr, sizeof(struct in6_addr));
3720d3b00c08SAlexander V. Chernikov 		len = 128;
3721d3b00c08SAlexander V. Chernikov 		if (mask6 != NULL)
3722d3b00c08SAlexander V. Chernikov 			len = contigmask((uint8_t *)&mask6->sin6_addr, 128);
3723d3b00c08SAlexander V. Chernikov 		if (len == -1)
3724d3b00c08SAlexander V. Chernikov 			len = 0;
3725d3b00c08SAlexander V. Chernikov 		tent->masklen = len;
3726d3b00c08SAlexander V. Chernikov 		tent->subtype = AF_INET6;
37270cba2b28SAlexander V. Chernikov 		tent->v.kidx = 0;
3728d3b00c08SAlexander V. Chernikov #endif
3729d3b00c08SAlexander V. Chernikov 	}
3730d3b00c08SAlexander V. Chernikov 
3731d3b00c08SAlexander V. Chernikov 	return (0);
3732d3b00c08SAlexander V. Chernikov }
3733d3b00c08SAlexander V. Chernikov 
3734d3b00c08SAlexander V. Chernikov static int
3735d3b00c08SAlexander V. Chernikov ta_find_kfib_tentry(void *ta_state, struct table_info *ti,
3736d3b00c08SAlexander V. Chernikov     ipfw_obj_tentry *tent)
3737d3b00c08SAlexander V. Chernikov {
3738d3b00c08SAlexander V. Chernikov 	struct rtentry *rte;
3739d3b00c08SAlexander V. Chernikov 	void *key;
3740d3b00c08SAlexander V. Chernikov 	int keylen;
3741d3b00c08SAlexander V. Chernikov 
3742d3b00c08SAlexander V. Chernikov 	if (tent->subtype == AF_INET) {
3743d3b00c08SAlexander V. Chernikov 		key = &tent->k.addr;
3744d3b00c08SAlexander V. Chernikov 		keylen = sizeof(struct in_addr);
3745d3b00c08SAlexander V. Chernikov 	} else {
3746d3b00c08SAlexander V. Chernikov 		key = &tent->k.addr6;
3747d3b00c08SAlexander V. Chernikov 		keylen = sizeof(struct in6_addr);
3748d3b00c08SAlexander V. Chernikov 	}
3749d3b00c08SAlexander V. Chernikov 
3750d3b00c08SAlexander V. Chernikov 	if ((rte = lookup_kfib(key, keylen, ti->data)) == NULL)
3751d3b00c08SAlexander V. Chernikov 		return (0);
3752d3b00c08SAlexander V. Chernikov 
3753d3b00c08SAlexander V. Chernikov 	if (rte != NULL) {
3754d3b00c08SAlexander V. Chernikov 		ta_dump_kfib_tentry(ta_state, ti, rte, tent);
3755d3b00c08SAlexander V. Chernikov 		RTFREE_LOCKED(rte);
3756d3b00c08SAlexander V. Chernikov 		return (0);
3757d3b00c08SAlexander V. Chernikov 	}
3758d3b00c08SAlexander V. Chernikov 
3759d3b00c08SAlexander V. Chernikov 	return (ENOENT);
3760d3b00c08SAlexander V. Chernikov }
3761d3b00c08SAlexander V. Chernikov 
3762d3b00c08SAlexander V. Chernikov static void
3763d3b00c08SAlexander V. Chernikov ta_foreach_kfib(void *ta_state, struct table_info *ti, ta_foreach_f *f,
3764d3b00c08SAlexander V. Chernikov     void *arg)
3765d3b00c08SAlexander V. Chernikov {
3766d3b00c08SAlexander V. Chernikov 	struct radix_node_head *rnh;
3767d3b00c08SAlexander V. Chernikov 	int error;
3768d3b00c08SAlexander V. Chernikov 
3769d3b00c08SAlexander V. Chernikov 	rnh = rt_tables_get_rnh(ti->data, AF_INET);
3770d3b00c08SAlexander V. Chernikov 	if (rnh != NULL) {
3771d3b00c08SAlexander V. Chernikov 		RADIX_NODE_HEAD_RLOCK(rnh);
3772d3b00c08SAlexander V. Chernikov 		error = rnh->rnh_walktree(rnh, (walktree_f_t *)f, arg);
3773d3b00c08SAlexander V. Chernikov 		RADIX_NODE_HEAD_RUNLOCK(rnh);
3774d3b00c08SAlexander V. Chernikov 	}
3775d3b00c08SAlexander V. Chernikov 
3776d3b00c08SAlexander V. Chernikov 	rnh = rt_tables_get_rnh(ti->data, AF_INET6);
3777d3b00c08SAlexander V. Chernikov 	if (rnh != NULL) {
3778d3b00c08SAlexander V. Chernikov 		RADIX_NODE_HEAD_RLOCK(rnh);
3779d3b00c08SAlexander V. Chernikov 		error = rnh->rnh_walktree(rnh, (walktree_f_t *)f, arg);
3780d3b00c08SAlexander V. Chernikov 		RADIX_NODE_HEAD_RUNLOCK(rnh);
3781d3b00c08SAlexander V. Chernikov 	}
3782d3b00c08SAlexander V. Chernikov }
3783d3b00c08SAlexander V. Chernikov 
3784c21034b7SAlexander V. Chernikov struct table_algo addr_kfib = {
3785c21034b7SAlexander V. Chernikov 	.name		= "addr:kfib",
3786c21034b7SAlexander V. Chernikov 	.type		= IPFW_TABLE_ADDR,
3787d3b00c08SAlexander V. Chernikov 	.flags		= TA_FLAG_READONLY,
3788d3b00c08SAlexander V. Chernikov 	.ta_buf_size	= 0,
3789d3b00c08SAlexander V. Chernikov 	.init		= ta_init_kfib,
3790d3b00c08SAlexander V. Chernikov 	.destroy	= ta_destroy_kfib,
3791d3b00c08SAlexander V. Chernikov 	.foreach	= ta_foreach_kfib,
3792d3b00c08SAlexander V. Chernikov 	.dump_tentry	= ta_dump_kfib_tentry,
3793d3b00c08SAlexander V. Chernikov 	.find_tentry	= ta_find_kfib_tentry,
3794d3b00c08SAlexander V. Chernikov 	.dump_tinfo	= ta_dump_kfib_tinfo,
3795d3b00c08SAlexander V. Chernikov 	.print_config	= ta_print_kfib_config,
3796d3b00c08SAlexander V. Chernikov };
3797d3b00c08SAlexander V. Chernikov 
37989f7d47b0SAlexander V. Chernikov void
37990b565ac0SAlexander V. Chernikov ipfw_table_algo_init(struct ip_fw_chain *ch)
38009f7d47b0SAlexander V. Chernikov {
38010b565ac0SAlexander V. Chernikov 	size_t sz;
38020b565ac0SAlexander V. Chernikov 
38039f7d47b0SAlexander V. Chernikov 	/*
38049f7d47b0SAlexander V. Chernikov 	 * Register all algorithms presented here.
38059f7d47b0SAlexander V. Chernikov 	 */
38060b565ac0SAlexander V. Chernikov 	sz = sizeof(struct table_algo);
3807c21034b7SAlexander V. Chernikov 	ipfw_add_table_algo(ch, &addr_radix, sz, &addr_radix.idx);
3808c21034b7SAlexander V. Chernikov 	ipfw_add_table_algo(ch, &addr_hash, sz, &addr_hash.idx);
38090b565ac0SAlexander V. Chernikov 	ipfw_add_table_algo(ch, &iface_idx, sz, &iface_idx.idx);
3810b23d5de9SAlexander V. Chernikov 	ipfw_add_table_algo(ch, &number_array, sz, &number_array.idx);
3811914bffb6SAlexander V. Chernikov 	ipfw_add_table_algo(ch, &flow_hash, sz, &flow_hash.idx);
3812c21034b7SAlexander V. Chernikov 	ipfw_add_table_algo(ch, &addr_kfib, sz, &addr_kfib.idx);
3813b1d105bcSAlexander V. Chernikov 	ipfw_add_table_algo(ch, &addr_dxr, sz, &addr_dxr.idx);
38149f7d47b0SAlexander V. Chernikov }
38159f7d47b0SAlexander V. Chernikov 
38169f7d47b0SAlexander V. Chernikov void
38170b565ac0SAlexander V. Chernikov ipfw_table_algo_destroy(struct ip_fw_chain *ch)
38189f7d47b0SAlexander V. Chernikov {
38190b565ac0SAlexander V. Chernikov 
3820c21034b7SAlexander V. Chernikov 	ipfw_del_table_algo(ch, addr_radix.idx);
3821c21034b7SAlexander V. Chernikov 	ipfw_del_table_algo(ch, addr_hash.idx);
38220b565ac0SAlexander V. Chernikov 	ipfw_del_table_algo(ch, iface_idx.idx);
3823b23d5de9SAlexander V. Chernikov 	ipfw_del_table_algo(ch, number_array.idx);
3824914bffb6SAlexander V. Chernikov 	ipfw_del_table_algo(ch, flow_hash.idx);
3825c21034b7SAlexander V. Chernikov 	ipfw_del_table_algo(ch, addr_kfib.idx);
3826b1d105bcSAlexander V. Chernikov 	ipfw_del_table_algo(ch, addr_dxr.idx);
38279f7d47b0SAlexander V. Chernikov }
38289f7d47b0SAlexander V. Chernikov 
38299f7d47b0SAlexander V. Chernikov 
3830