xref: /freebsd/sys/netpfil/ipfw/ip_fw_table_algo.c (revision 60a28b091651d7cdea9d57862d2535c1f33c4be3)
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>
28a13a8216SAlexander V. Chernikov __FBSDID("$FreeBSD$");
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>
546ad7446cSAlexander V. Chernikov #include <net/route/nhop.h>
554451d893SAlexander V. Chernikov #include <net/route/route_ctl.h>
569f7d47b0SAlexander V. Chernikov 
579f7d47b0SAlexander V. Chernikov #include <netinet/in.h>
58004d3e30SAlexander V. Chernikov #include <netinet/in_fib.h>
599f7d47b0SAlexander V. Chernikov #include <netinet/ip_var.h>	/* struct ipfw_rule_ref */
609f7d47b0SAlexander V. Chernikov #include <netinet/ip_fw.h>
61004d3e30SAlexander V. Chernikov #include <netinet6/in6_fib.h>
629f7d47b0SAlexander V. Chernikov 
639f7d47b0SAlexander V. Chernikov #include <netpfil/ipfw/ip_fw_private.h>
64ea761a5dSAlexander V. Chernikov #include <netpfil/ipfw/ip_fw_table.h>
659f7d47b0SAlexander V. Chernikov 
66301290bcSAlexander V. Chernikov /*
67301290bcSAlexander V. Chernikov  * IPFW table lookup algorithms.
68301290bcSAlexander V. Chernikov  *
69301290bcSAlexander V. Chernikov  * What is needed to add another table algo?
70301290bcSAlexander V. Chernikov  *
71301290bcSAlexander V. Chernikov  * Algo init:
72301290bcSAlexander V. Chernikov  * * struct table_algo has to be filled with:
73c21034b7SAlexander V. Chernikov  *   name: "type:algoname" format, e.g. "addr:radix". Currently
74c21034b7SAlexander V. Chernikov  *     there are the following types: "addr", "iface", "number" and "flow".
75301290bcSAlexander V. Chernikov  *   type: one of IPFW_TABLE_* types
76301290bcSAlexander V. Chernikov  *   flags: one or more TA_FLAGS_*
77301290bcSAlexander V. Chernikov  *   ta_buf_size: size of structure used to store add/del item state.
78301290bcSAlexander V. Chernikov  *     Needs to be less than TA_BUF_SZ.
79301290bcSAlexander V. Chernikov  *   callbacks: see below for description.
80301290bcSAlexander V. Chernikov  * * ipfw_add_table_algo / ipfw_del_table_algo has to be called
81301290bcSAlexander V. Chernikov  *
82301290bcSAlexander V. Chernikov  * Callbacks description:
83301290bcSAlexander V. Chernikov  *
84301290bcSAlexander V. Chernikov  * -init: request to initialize new table instance.
85301290bcSAlexander V. Chernikov  * typedef int (ta_init)(struct ip_fw_chain *ch, void **ta_state,
86301290bcSAlexander V. Chernikov  *     struct table_info *ti, char *data, uint8_t tflags);
87301290bcSAlexander V. Chernikov  * MANDATORY, unlocked. (M_WAITOK). Returns 0 on success.
88301290bcSAlexander V. Chernikov  *
89301290bcSAlexander V. Chernikov  *  Allocate all structures needed for normal operations.
90301290bcSAlexander V. Chernikov  *  * Caller may want to parse @data for some algo-specific
91301290bcSAlexander V. Chernikov  *    options provided by userland.
92301290bcSAlexander V. Chernikov  *  * Caller may want to save configuration state pointer to @ta_state
93301290bcSAlexander V. Chernikov  *  * Caller needs to save desired runtime structure pointer(s)
94301290bcSAlexander V. Chernikov  *    inside @ti fields. Note that it is not correct to save
95301290bcSAlexander V. Chernikov  *    @ti pointer at this moment. Use -change_ti hook for that.
96301290bcSAlexander V. Chernikov  *  * Caller has to fill in ti->lookup to appropriate function
97301290bcSAlexander V. Chernikov  *    pointer.
98301290bcSAlexander V. Chernikov  *
99301290bcSAlexander V. Chernikov  *
100301290bcSAlexander V. Chernikov  *
101301290bcSAlexander V. Chernikov  * -destroy: request to destroy table instance.
102301290bcSAlexander V. Chernikov  * typedef void (ta_destroy)(void *ta_state, struct table_info *ti);
1030caab009SAlexander V. Chernikov  * MANDATORY, unlocked. (M_WAITOK).
104301290bcSAlexander V. Chernikov  *
105301290bcSAlexander V. Chernikov  * Frees all table entries and all tables structures allocated by -init.
106301290bcSAlexander V. Chernikov  *
107301290bcSAlexander V. Chernikov  *
108301290bcSAlexander V. Chernikov  *
109301290bcSAlexander V. Chernikov  * -prepare_add: request to allocate state for adding new entry.
110301290bcSAlexander V. Chernikov  * typedef int (ta_prepare_add)(struct ip_fw_chain *ch, struct tentry_info *tei,
111301290bcSAlexander V. Chernikov  *     void *ta_buf);
112301290bcSAlexander V. Chernikov  * MANDATORY, unlocked. (M_WAITOK). Returns 0 on success.
113301290bcSAlexander V. Chernikov  *
11413263632SAlexander V. Chernikov  * Allocates state and fills it in with all necessary data (EXCEPT value)
11513263632SAlexander V. Chernikov  * from @tei to minimize operations needed to be done under WLOCK.
11613263632SAlexander V. Chernikov  * "value" field has to be copied to new entry in @add callback.
117301290bcSAlexander V. Chernikov  * Buffer ta_buf of size ta->ta_buf_sz may be used to store
118301290bcSAlexander V. Chernikov  * allocated state.
119301290bcSAlexander V. Chernikov  *
120301290bcSAlexander V. Chernikov  *
121301290bcSAlexander V. Chernikov  *
122301290bcSAlexander V. Chernikov  * -prepare_del: request to set state for deleting existing entry.
123301290bcSAlexander V. Chernikov  * typedef int (ta_prepare_del)(struct ip_fw_chain *ch, struct tentry_info *tei,
124301290bcSAlexander V. Chernikov  *     void *ta_buf);
125301290bcSAlexander V. Chernikov  * MANDATORY, locked, UH. (M_NOWAIT). Returns 0 on success.
126301290bcSAlexander V. Chernikov  *
127301290bcSAlexander V. Chernikov  * Buffer ta_buf of size ta->ta_buf_sz may be used to store
128301290bcSAlexander V. Chernikov  * allocated state. Caller should use on-stack ta_buf allocation
129301290bcSAlexander V. Chernikov  * instead of doing malloc().
130301290bcSAlexander V. Chernikov  *
131301290bcSAlexander V. Chernikov  *
132301290bcSAlexander V. Chernikov  *
133301290bcSAlexander V. Chernikov  * -add: request to insert new entry into runtime/config structures.
134301290bcSAlexander V. Chernikov  *  typedef int (ta_add)(void *ta_state, struct table_info *ti,
135301290bcSAlexander V. Chernikov  *     struct tentry_info *tei, void *ta_buf, uint32_t *pnum);
136301290bcSAlexander V. Chernikov  * MANDATORY, UH+WLOCK. (M_NOWAIT). Returns 0 on success.
137301290bcSAlexander V. Chernikov  *
138301290bcSAlexander V. Chernikov  * Insert new entry using previously-allocated state in @ta_buf.
139301290bcSAlexander V. Chernikov  * * @tei may have the following flags:
140301290bcSAlexander V. Chernikov  *   TEI_FLAGS_UPDATE: request to add or update entry.
141301290bcSAlexander V. Chernikov  *   TEI_FLAGS_DONTADD: request to update (but not add) entry.
142301290bcSAlexander V. Chernikov  * * Caller is required to do the following:
14313263632SAlexander V. Chernikov  *   copy real entry value from @tei
144301290bcSAlexander V. Chernikov  *   entry added: return 0, set 1 to @pnum
145301290bcSAlexander V. Chernikov  *   entry updated: return 0, store 0 to @pnum, store old value in @tei,
146301290bcSAlexander V. Chernikov  *     add TEI_FLAGS_UPDATED flag to @tei.
147301290bcSAlexander V. Chernikov  *   entry exists: return EEXIST
148301290bcSAlexander V. Chernikov  *   entry not found: return ENOENT
149301290bcSAlexander V. Chernikov  *   other error: return non-zero error code.
150301290bcSAlexander V. Chernikov  *
151301290bcSAlexander V. Chernikov  *
152301290bcSAlexander V. Chernikov  *
153301290bcSAlexander V. Chernikov  * -del: request to delete existing entry from runtime/config structures.
154301290bcSAlexander V. Chernikov  *  typedef int (ta_del)(void *ta_state, struct table_info *ti,
155301290bcSAlexander V. Chernikov  *     struct tentry_info *tei, void *ta_buf, uint32_t *pnum);
156301290bcSAlexander V. Chernikov  *  MANDATORY, UH+WLOCK. (M_NOWAIT). Returns 0 on success.
157301290bcSAlexander V. Chernikov  *
158301290bcSAlexander V. Chernikov  *  Delete entry using previously set up in @ta_buf.
159301290bcSAlexander V. Chernikov  * * Caller is required to do the following:
16013263632SAlexander V. Chernikov  *   entry deleted: return 0, set 1 to @pnum, store old value in @tei.
161301290bcSAlexander V. Chernikov  *   entry not found: return ENOENT
162301290bcSAlexander V. Chernikov  *   other error: return non-zero error code.
163301290bcSAlexander V. Chernikov  *
164301290bcSAlexander V. Chernikov  *
165301290bcSAlexander V. Chernikov  *
166301290bcSAlexander V. Chernikov  * -flush_entry: flush entry state created by -prepare_add / -del / others
167301290bcSAlexander V. Chernikov  *  typedef void (ta_flush_entry)(struct ip_fw_chain *ch,
168301290bcSAlexander V. Chernikov  *      struct tentry_info *tei, void *ta_buf);
169301290bcSAlexander V. Chernikov  *  MANDATORY, may be locked. (M_NOWAIT).
170301290bcSAlexander V. Chernikov  *
171301290bcSAlexander V. Chernikov  *  Delete state allocated by:
172301290bcSAlexander V. Chernikov  *  -prepare_add (-add returned EEXIST|UPDATED)
173301290bcSAlexander V. Chernikov  *  -prepare_del (if any)
174301290bcSAlexander V. Chernikov  *  -del
175301290bcSAlexander V. Chernikov  *  * Caller is required to handle empty @ta_buf correctly.
176301290bcSAlexander V. Chernikov  *
177301290bcSAlexander V. Chernikov  *
178301290bcSAlexander V. Chernikov  * -find_tentry: finds entry specified by key @tei
179301290bcSAlexander V. Chernikov  *  typedef int ta_find_tentry(void *ta_state, struct table_info *ti,
180301290bcSAlexander V. Chernikov  *      ipfw_obj_tentry *tent);
181301290bcSAlexander V. Chernikov  *  OPTIONAL, locked (UH). (M_NOWAIT). Returns 0 on success.
182301290bcSAlexander V. Chernikov  *
183301290bcSAlexander V. Chernikov  *  Finds entry specified by given key.
184a4641f4eSPedro F. Giffuni  *  * Caller is required to do the following:
185301290bcSAlexander V. Chernikov  *    entry found: returns 0, export entry to @tent
186301290bcSAlexander V. Chernikov  *    entry not found: returns ENOENT
187301290bcSAlexander V. Chernikov  *
188301290bcSAlexander V. Chernikov  *
189301290bcSAlexander V. Chernikov  * -need_modify: checks if @ti has enough space to hold another @count items.
190301290bcSAlexander V. Chernikov  *  typedef int (ta_need_modify)(void *ta_state, struct table_info *ti,
191301290bcSAlexander V. Chernikov  *      uint32_t count, uint64_t *pflags);
192fd0869d5SAlexander V. Chernikov  *  OPTIONAL, locked (UH). (M_NOWAIT). Returns 0 if has.
193301290bcSAlexander V. Chernikov  *
194301290bcSAlexander V. Chernikov  *  Checks if given table has enough space to add @count items without
195301290bcSAlexander V. Chernikov  *  resize. Caller may use @pflags to store desired modification data.
196301290bcSAlexander V. Chernikov  *
197301290bcSAlexander V. Chernikov  *
198301290bcSAlexander V. Chernikov  *
199301290bcSAlexander V. Chernikov  * -prepare_mod: allocate structures for table modification.
200301290bcSAlexander V. Chernikov  *  typedef int (ta_prepare_mod)(void *ta_buf, uint64_t *pflags);
201fd0869d5SAlexander V. Chernikov  * OPTIONAL(need_modify), unlocked. (M_WAITOK). Returns 0 on success.
202301290bcSAlexander V. Chernikov  *
203301290bcSAlexander V. Chernikov  * Allocate all needed state for table modification. Caller
204301290bcSAlexander V. Chernikov  * should use `struct mod_item` to store new state in @ta_buf.
205301290bcSAlexander V. Chernikov  * Up to TA_BUF_SZ (128 bytes) can be stored in @ta_buf.
206301290bcSAlexander V. Chernikov  *
207301290bcSAlexander V. Chernikov  *
208301290bcSAlexander V. Chernikov  *
209301290bcSAlexander V. Chernikov  * -fill_mod: copy some data to new state/
210301290bcSAlexander V. Chernikov  *  typedef int (ta_fill_mod)(void *ta_state, struct table_info *ti,
211301290bcSAlexander V. Chernikov  *      void *ta_buf, uint64_t *pflags);
212fd0869d5SAlexander V. Chernikov  * OPTIONAL(need_modify), locked (UH). (M_NOWAIT). Returns 0 on success.
213301290bcSAlexander V. Chernikov  *
214301290bcSAlexander V. Chernikov  * Copy as much data as we can to minimize changes under WLOCK.
215301290bcSAlexander V. Chernikov  * For example, array can be merged inside this callback.
216301290bcSAlexander V. Chernikov  *
217301290bcSAlexander V. Chernikov  *
218301290bcSAlexander V. Chernikov  *
219301290bcSAlexander V. Chernikov  * -modify: perform final modification.
220301290bcSAlexander V. Chernikov  *  typedef void (ta_modify)(void *ta_state, struct table_info *ti,
221301290bcSAlexander V. Chernikov  *      void *ta_buf, uint64_t pflags);
222fd0869d5SAlexander V. Chernikov  * OPTIONAL(need_modify), locked (UH+WLOCK). (M_NOWAIT).
223301290bcSAlexander V. Chernikov  *
224301290bcSAlexander V. Chernikov  * Performs all changes necessary to switch to new structures.
225301290bcSAlexander V. Chernikov  * * Caller should save old pointers to @ta_buf storage.
226301290bcSAlexander V. Chernikov  *
227301290bcSAlexander V. Chernikov  *
228301290bcSAlexander V. Chernikov  *
229301290bcSAlexander V. Chernikov  * -flush_mod: flush table modification state.
230301290bcSAlexander V. Chernikov  *  typedef void (ta_flush_mod)(void *ta_buf);
231fd0869d5SAlexander V. Chernikov  * OPTIONAL(need_modify), unlocked. (M_WAITOK).
232301290bcSAlexander V. Chernikov  *
233301290bcSAlexander V. Chernikov  * Performs flush for the following:
234301290bcSAlexander V. Chernikov  *   - prepare_mod (modification was not necessary)
235301290bcSAlexander V. Chernikov  *   - modify (for the old state)
236301290bcSAlexander V. Chernikov  *
237301290bcSAlexander V. Chernikov  *
238301290bcSAlexander V. Chernikov  *
239301290bcSAlexander V. Chernikov  * -change_gi: monitor table info pointer changes
240301290bcSAlexander V. Chernikov  * typedef void (ta_change_ti)(void *ta_state, struct table_info *ti);
241301290bcSAlexander V. Chernikov  * OPTIONAL, locked (UH). (M_NOWAIT).
242301290bcSAlexander V. Chernikov  *
243301290bcSAlexander V. Chernikov  * Called on @ti pointer changed. Called immediately after -init
244301290bcSAlexander V. Chernikov  * to set initial state.
245301290bcSAlexander V. Chernikov  *
246301290bcSAlexander V. Chernikov  *
247301290bcSAlexander V. Chernikov  *
248301290bcSAlexander V. Chernikov  * -foreach: calls @f for each table entry
249301290bcSAlexander V. Chernikov  *  typedef void ta_foreach(void *ta_state, struct table_info *ti,
250301290bcSAlexander V. Chernikov  *      ta_foreach_f *f, void *arg);
251301290bcSAlexander V. Chernikov  * MANDATORY, locked(UH). (M_NOWAIT).
252301290bcSAlexander V. Chernikov  *
253301290bcSAlexander V. Chernikov  * Runs callback with specified argument for each table entry,
254301290bcSAlexander V. Chernikov  * Typically used for dumping table entries.
255301290bcSAlexander V. Chernikov  *
256301290bcSAlexander V. Chernikov  *
257301290bcSAlexander V. Chernikov  *
258301290bcSAlexander V. Chernikov  * -dump_tentry: dump table entry in current @tentry format.
259301290bcSAlexander V. Chernikov  *  typedef int ta_dump_tentry(void *ta_state, struct table_info *ti, void *e,
260301290bcSAlexander V. Chernikov  *      ipfw_obj_tentry *tent);
261301290bcSAlexander V. Chernikov  * MANDATORY, locked(UH). (M_NOWAIT). Returns 0 on success.
262301290bcSAlexander V. Chernikov  *
263301290bcSAlexander V. Chernikov  * Dumps entry @e to @tent.
264301290bcSAlexander V. Chernikov  *
265301290bcSAlexander V. Chernikov  *
266a4641f4eSPedro F. Giffuni  * -print_config: prints custom algorithm options into buffer.
267301290bcSAlexander V. Chernikov  *  typedef void (ta_print_config)(void *ta_state, struct table_info *ti,
268301290bcSAlexander V. Chernikov  *      char *buf, size_t bufsize);
269301290bcSAlexander V. Chernikov  * OPTIONAL. locked(UH). (M_NOWAIT).
270301290bcSAlexander V. Chernikov  *
271301290bcSAlexander V. Chernikov  * Prints custom algorithm options in the format suitable to pass
272301290bcSAlexander V. Chernikov  * back to -init callback.
273301290bcSAlexander V. Chernikov  *
274301290bcSAlexander V. Chernikov  *
275301290bcSAlexander V. Chernikov  *
276301290bcSAlexander V. Chernikov  * -dump_tinfo: dumps algo-specific info.
277301290bcSAlexander V. Chernikov  *  typedef void ta_dump_tinfo(void *ta_state, struct table_info *ti,
278301290bcSAlexander V. Chernikov  *      ipfw_ta_tinfo *tinfo);
279301290bcSAlexander V. Chernikov  * OPTIONAL. locked(UH). (M_NOWAIT).
280301290bcSAlexander V. Chernikov  *
281301290bcSAlexander V. Chernikov  * Dumps options like items size/hash size, etc.
282301290bcSAlexander V. Chernikov  */
283301290bcSAlexander V. Chernikov 
284b1d105bcSAlexander V. Chernikov MALLOC_DEFINE(M_IPFW_TBL, "ipfw_tbl", "IpFw tables");
2859f7d47b0SAlexander V. Chernikov 
2860bce0c23SAlexander V. Chernikov /*
2870bce0c23SAlexander V. Chernikov  * Utility structures/functions common to more than one algo
2880bce0c23SAlexander V. Chernikov  */
2890bce0c23SAlexander V. Chernikov 
2900bce0c23SAlexander V. Chernikov struct mod_item {
2910bce0c23SAlexander V. Chernikov 	void	*main_ptr;
2920bce0c23SAlexander V. Chernikov 	size_t	size;
2930bce0c23SAlexander V. Chernikov 	void	*main_ptr6;
2940bce0c23SAlexander V. Chernikov 	size_t	size6;
2950bce0c23SAlexander V. Chernikov };
2960bce0c23SAlexander V. Chernikov 
29768394ec8SAlexander V. Chernikov static int badd(const void *key, void *item, void *base, size_t nmemb,
29868394ec8SAlexander V. Chernikov     size_t size, int (*compar) (const void *, const void *));
29968394ec8SAlexander V. Chernikov static int bdel(const void *key, void *base, size_t nmemb, size_t size,
30068394ec8SAlexander V. Chernikov     int (*compar) (const void *, const void *));
30168394ec8SAlexander V. Chernikov 
30268394ec8SAlexander V. Chernikov /*
303c21034b7SAlexander V. Chernikov  * ADDR implementation using radix
30468394ec8SAlexander V. Chernikov  *
30568394ec8SAlexander V. Chernikov  */
30668394ec8SAlexander V. Chernikov 
3079f7d47b0SAlexander V. Chernikov /*
3089f7d47b0SAlexander V. Chernikov  * The radix code expects addr and mask to be array of bytes,
3099f7d47b0SAlexander V. Chernikov  * with the first byte being the length of the array. rn_inithead
3109f7d47b0SAlexander V. Chernikov  * is called with the offset in bits of the lookup key within the
3119f7d47b0SAlexander V. Chernikov  * array. If we use a sockaddr_in as the underlying type,
3129f7d47b0SAlexander V. Chernikov  * sin_len is conveniently located at offset 0, sin_addr is at
3139f7d47b0SAlexander V. Chernikov  * offset 4 and normally aligned.
3149f7d47b0SAlexander V. Chernikov  * But for portability, let's avoid assumption and make the code explicit
3159f7d47b0SAlexander V. Chernikov  */
3169f7d47b0SAlexander V. Chernikov #define KEY_LEN(v)	*((uint8_t *)&(v))
3179f7d47b0SAlexander V. Chernikov /*
3189f7d47b0SAlexander V. Chernikov  * Do not require radix to compare more than actual IPv4/IPv6 address
3199f7d47b0SAlexander V. Chernikov  */
3209f7d47b0SAlexander V. Chernikov #define KEY_LEN_INET	(offsetof(struct sockaddr_in, sin_addr) + sizeof(in_addr_t))
321e0a8b9eeSAlexander V. Chernikov #define KEY_LEN_INET6	(offsetof(struct sa_in6, sin6_addr) + sizeof(struct in6_addr))
3229f7d47b0SAlexander V. Chernikov 
3239f7d47b0SAlexander V. Chernikov #define OFF_LEN_INET	(8 * offsetof(struct sockaddr_in, sin_addr))
324e0a8b9eeSAlexander V. Chernikov #define OFF_LEN_INET6	(8 * offsetof(struct sa_in6, sin6_addr))
3259f7d47b0SAlexander V. Chernikov 
326c21034b7SAlexander V. Chernikov struct radix_addr_entry {
3279f7d47b0SAlexander V. Chernikov 	struct radix_node	rn[2];
328e0a8b9eeSAlexander V. Chernikov 	struct sockaddr_in	addr;
329e0a8b9eeSAlexander V. Chernikov 	uint32_t		value;
330e0a8b9eeSAlexander V. Chernikov 	uint8_t			masklen;
331e0a8b9eeSAlexander V. Chernikov };
332e0a8b9eeSAlexander V. Chernikov 
333e0a8b9eeSAlexander V. Chernikov struct sa_in6 {
334e0a8b9eeSAlexander V. Chernikov 	uint8_t			sin6_len;
335e0a8b9eeSAlexander V. Chernikov 	uint8_t			sin6_family;
336e0a8b9eeSAlexander V. Chernikov 	uint8_t			pad[2];
337e0a8b9eeSAlexander V. Chernikov 	struct in6_addr		sin6_addr;
338e0a8b9eeSAlexander V. Chernikov };
339e0a8b9eeSAlexander V. Chernikov 
340c21034b7SAlexander V. Chernikov struct radix_addr_xentry {
341e0a8b9eeSAlexander V. Chernikov 	struct radix_node	rn[2];
342e0a8b9eeSAlexander V. Chernikov 	struct sa_in6		addr6;
343e0a8b9eeSAlexander V. Chernikov 	uint32_t		value;
344e0a8b9eeSAlexander V. Chernikov 	uint8_t			masklen;
3459f7d47b0SAlexander V. Chernikov };
3469f7d47b0SAlexander V. Chernikov 
3475f379342SAlexander V. Chernikov struct radix_cfg {
3485f379342SAlexander V. Chernikov 	struct radix_node_head	*head4;
3495f379342SAlexander V. Chernikov 	struct radix_node_head	*head6;
3505f379342SAlexander V. Chernikov 	size_t			count4;
3515f379342SAlexander V. Chernikov 	size_t			count6;
3525f379342SAlexander V. Chernikov };
3535f379342SAlexander V. Chernikov 
354c21034b7SAlexander V. Chernikov struct ta_buf_radix
3550bce0c23SAlexander V. Chernikov {
3560bce0c23SAlexander V. Chernikov 	void *ent_ptr;
3570bce0c23SAlexander V. Chernikov 	struct sockaddr	*addr_ptr;
3580bce0c23SAlexander V. Chernikov 	struct sockaddr	*mask_ptr;
3590bce0c23SAlexander V. Chernikov 	union {
3600bce0c23SAlexander V. Chernikov 		struct {
3610bce0c23SAlexander V. Chernikov 			struct sockaddr_in sa;
3620bce0c23SAlexander V. Chernikov 			struct sockaddr_in ma;
3630bce0c23SAlexander V. Chernikov 		} a4;
3640bce0c23SAlexander V. Chernikov 		struct {
3650bce0c23SAlexander V. Chernikov 			struct sa_in6 sa;
3660bce0c23SAlexander V. Chernikov 			struct sa_in6 ma;
3670bce0c23SAlexander V. Chernikov 		} a6;
3680bce0c23SAlexander V. Chernikov 	} addr;
3690bce0c23SAlexander V. Chernikov };
3700bce0c23SAlexander V. Chernikov 
3719fe15d06SAlexander V. Chernikov static int ta_lookup_radix(struct table_info *ti, void *key, uint32_t keylen,
3729fe15d06SAlexander V. Chernikov     uint32_t *val);
3739fe15d06SAlexander V. Chernikov static int ta_init_radix(struct ip_fw_chain *ch, void **ta_state,
3749fe15d06SAlexander V. Chernikov     struct table_info *ti, char *data, uint8_t tflags);
3759fe15d06SAlexander V. Chernikov static int flush_radix_entry(struct radix_node *rn, void *arg);
3769fe15d06SAlexander V. Chernikov static void ta_destroy_radix(void *ta_state, struct table_info *ti);
3779fe15d06SAlexander V. Chernikov static void ta_dump_radix_tinfo(void *ta_state, struct table_info *ti,
3789fe15d06SAlexander V. Chernikov     ipfw_ta_tinfo *tinfo);
3799fe15d06SAlexander V. Chernikov static int ta_dump_radix_tentry(void *ta_state, struct table_info *ti,
3809fe15d06SAlexander V. Chernikov     void *e, ipfw_obj_tentry *tent);
3819fe15d06SAlexander V. Chernikov static int ta_find_radix_tentry(void *ta_state, struct table_info *ti,
3829fe15d06SAlexander V. Chernikov     ipfw_obj_tentry *tent);
3839fe15d06SAlexander V. Chernikov static void ta_foreach_radix(void *ta_state, struct table_info *ti,
3849fe15d06SAlexander V. Chernikov     ta_foreach_f *f, void *arg);
3859fe15d06SAlexander V. Chernikov static void tei_to_sockaddr_ent(struct tentry_info *tei, struct sockaddr *sa,
3869fe15d06SAlexander V. Chernikov     struct sockaddr *ma, int *set_mask);
3879fe15d06SAlexander V. Chernikov static int ta_prepare_add_radix(struct ip_fw_chain *ch, struct tentry_info *tei,
3889fe15d06SAlexander V. Chernikov     void *ta_buf);
3899fe15d06SAlexander V. Chernikov static int ta_add_radix(void *ta_state, struct table_info *ti,
3909fe15d06SAlexander V. Chernikov     struct tentry_info *tei, void *ta_buf, uint32_t *pnum);
3919fe15d06SAlexander V. Chernikov static int ta_prepare_del_radix(struct ip_fw_chain *ch, struct tentry_info *tei,
3929fe15d06SAlexander V. Chernikov     void *ta_buf);
3939fe15d06SAlexander V. Chernikov static int ta_del_radix(void *ta_state, struct table_info *ti,
3949fe15d06SAlexander V. Chernikov     struct tentry_info *tei, void *ta_buf, uint32_t *pnum);
3959fe15d06SAlexander V. Chernikov static void ta_flush_radix_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
3969fe15d06SAlexander V. Chernikov     void *ta_buf);
3979fe15d06SAlexander V. Chernikov static int ta_need_modify_radix(void *ta_state, struct table_info *ti,
3989fe15d06SAlexander V. Chernikov     uint32_t count, uint64_t *pflags);
3999fe15d06SAlexander V. Chernikov 
4009f7d47b0SAlexander V. Chernikov static int
4019f7d47b0SAlexander V. Chernikov ta_lookup_radix(struct table_info *ti, void *key, uint32_t keylen,
4029f7d47b0SAlexander V. Chernikov     uint32_t *val)
4039f7d47b0SAlexander V. Chernikov {
4049f7d47b0SAlexander V. Chernikov 	struct radix_node_head *rnh;
4059f7d47b0SAlexander V. Chernikov 
4069f7d47b0SAlexander V. Chernikov 	if (keylen == sizeof(in_addr_t)) {
407c21034b7SAlexander V. Chernikov 		struct radix_addr_entry *ent;
4089f7d47b0SAlexander V. Chernikov 		struct sockaddr_in sa;
4099f7d47b0SAlexander V. Chernikov 		KEY_LEN(sa) = KEY_LEN_INET;
4109f7d47b0SAlexander V. Chernikov 		sa.sin_addr.s_addr = *((in_addr_t *)key);
4119f7d47b0SAlexander V. Chernikov 		rnh = (struct radix_node_head *)ti->state;
41261eee0e2SAlexander V. Chernikov 		ent = (struct radix_addr_entry *)(rnh->rnh_matchaddr(&sa, &rnh->rh));
4139f7d47b0SAlexander V. Chernikov 		if (ent != NULL) {
4149f7d47b0SAlexander V. Chernikov 			*val = ent->value;
4159f7d47b0SAlexander V. Chernikov 			return (1);
4169f7d47b0SAlexander V. Chernikov 		}
4179f7d47b0SAlexander V. Chernikov 	} else {
418c21034b7SAlexander V. Chernikov 		struct radix_addr_xentry *xent;
419e0a8b9eeSAlexander V. Chernikov 		struct sa_in6 sa6;
4209f7d47b0SAlexander V. Chernikov 		KEY_LEN(sa6) = KEY_LEN_INET6;
4219f7d47b0SAlexander V. Chernikov 		memcpy(&sa6.sin6_addr, key, sizeof(struct in6_addr));
4229f7d47b0SAlexander V. Chernikov 		rnh = (struct radix_node_head *)ti->xstate;
42361eee0e2SAlexander V. Chernikov 		xent = (struct radix_addr_xentry *)(rnh->rnh_matchaddr(&sa6, &rnh->rh));
4249f7d47b0SAlexander V. Chernikov 		if (xent != NULL) {
4259f7d47b0SAlexander V. Chernikov 			*val = xent->value;
4269f7d47b0SAlexander V. Chernikov 			return (1);
4279f7d47b0SAlexander V. Chernikov 		}
4289f7d47b0SAlexander V. Chernikov 	}
4299f7d47b0SAlexander V. Chernikov 
4309f7d47b0SAlexander V. Chernikov 	return (0);
4319f7d47b0SAlexander V. Chernikov }
4329f7d47b0SAlexander V. Chernikov 
4339f7d47b0SAlexander V. Chernikov /*
4349f7d47b0SAlexander V. Chernikov  * New table
4359f7d47b0SAlexander V. Chernikov  */
4369f7d47b0SAlexander V. Chernikov static int
43768394ec8SAlexander V. Chernikov ta_init_radix(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
438914bffb6SAlexander V. Chernikov     char *data, uint8_t tflags)
4399f7d47b0SAlexander V. Chernikov {
4405f379342SAlexander V. Chernikov 	struct radix_cfg *cfg;
4419f7d47b0SAlexander V. Chernikov 
4429f7d47b0SAlexander V. Chernikov 	if (!rn_inithead(&ti->state, OFF_LEN_INET))
4439f7d47b0SAlexander V. Chernikov 		return (ENOMEM);
4449f7d47b0SAlexander V. Chernikov 	if (!rn_inithead(&ti->xstate, OFF_LEN_INET6)) {
4459f7d47b0SAlexander V. Chernikov 		rn_detachhead(&ti->state);
4469f7d47b0SAlexander V. Chernikov 		return (ENOMEM);
4479f7d47b0SAlexander V. Chernikov 	}
4489f7d47b0SAlexander V. Chernikov 
4495f379342SAlexander V. Chernikov 	cfg = malloc(sizeof(struct radix_cfg), M_IPFW, M_WAITOK | M_ZERO);
4505f379342SAlexander V. Chernikov 
4515f379342SAlexander V. Chernikov 	*ta_state = cfg;
4529f7d47b0SAlexander V. Chernikov 	ti->lookup = ta_lookup_radix;
4539f7d47b0SAlexander V. Chernikov 
4549f7d47b0SAlexander V. Chernikov 	return (0);
4559f7d47b0SAlexander V. Chernikov }
4569f7d47b0SAlexander V. Chernikov 
4579f7d47b0SAlexander V. Chernikov static int
458a399f8beSAlexander V. Chernikov flush_radix_entry(struct radix_node *rn, void *arg)
4599f7d47b0SAlexander V. Chernikov {
4609f7d47b0SAlexander V. Chernikov 	struct radix_node_head * const rnh = arg;
461c21034b7SAlexander V. Chernikov 	struct radix_addr_entry *ent;
4629f7d47b0SAlexander V. Chernikov 
463c21034b7SAlexander V. Chernikov 	ent = (struct radix_addr_entry *)
46461eee0e2SAlexander V. Chernikov 	    rnh->rnh_deladdr(rn->rn_key, rn->rn_mask, &rnh->rh);
4659f7d47b0SAlexander V. Chernikov 	if (ent != NULL)
4669f7d47b0SAlexander V. Chernikov 		free(ent, M_IPFW_TBL);
4679f7d47b0SAlexander V. Chernikov 	return (0);
4689f7d47b0SAlexander V. Chernikov }
4699f7d47b0SAlexander V. Chernikov 
4709f7d47b0SAlexander V. Chernikov static void
4719f7d47b0SAlexander V. Chernikov ta_destroy_radix(void *ta_state, struct table_info *ti)
4729f7d47b0SAlexander V. Chernikov {
4735f379342SAlexander V. Chernikov 	struct radix_cfg *cfg;
4749f7d47b0SAlexander V. Chernikov 	struct radix_node_head *rnh;
4759f7d47b0SAlexander V. Chernikov 
4765f379342SAlexander V. Chernikov 	cfg = (struct radix_cfg *)ta_state;
4775f379342SAlexander V. Chernikov 
4789f7d47b0SAlexander V. Chernikov 	rnh = (struct radix_node_head *)(ti->state);
47961eee0e2SAlexander V. Chernikov 	rnh->rnh_walktree(&rnh->rh, flush_radix_entry, rnh);
4809f7d47b0SAlexander V. Chernikov 	rn_detachhead(&ti->state);
4819f7d47b0SAlexander V. Chernikov 
4829f7d47b0SAlexander V. Chernikov 	rnh = (struct radix_node_head *)(ti->xstate);
48361eee0e2SAlexander V. Chernikov 	rnh->rnh_walktree(&rnh->rh, flush_radix_entry, rnh);
4849f7d47b0SAlexander V. Chernikov 	rn_detachhead(&ti->xstate);
4855f379342SAlexander V. Chernikov 
4865f379342SAlexander V. Chernikov 	free(cfg, M_IPFW);
4875f379342SAlexander V. Chernikov }
4885f379342SAlexander V. Chernikov 
4895f379342SAlexander V. Chernikov /*
4905f379342SAlexander V. Chernikov  * Provide algo-specific table info
4915f379342SAlexander V. Chernikov  */
4925f379342SAlexander V. Chernikov static void
4935f379342SAlexander V. Chernikov ta_dump_radix_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo)
4945f379342SAlexander V. Chernikov {
4955f379342SAlexander V. Chernikov 	struct radix_cfg *cfg;
4965f379342SAlexander V. Chernikov 
4975f379342SAlexander V. Chernikov 	cfg = (struct radix_cfg *)ta_state;
4985f379342SAlexander V. Chernikov 
4995f379342SAlexander V. Chernikov 	tinfo->flags = IPFW_TATFLAGS_AFDATA | IPFW_TATFLAGS_AFITEM;
5005f379342SAlexander V. Chernikov 	tinfo->taclass4 = IPFW_TACLASS_RADIX;
5015f379342SAlexander V. Chernikov 	tinfo->count4 = cfg->count4;
502c21034b7SAlexander V. Chernikov 	tinfo->itemsize4 = sizeof(struct radix_addr_entry);
5035f379342SAlexander V. Chernikov 	tinfo->taclass6 = IPFW_TACLASS_RADIX;
5045f379342SAlexander V. Chernikov 	tinfo->count6 = cfg->count6;
505c21034b7SAlexander V. Chernikov 	tinfo->itemsize6 = sizeof(struct radix_addr_xentry);
5069f7d47b0SAlexander V. Chernikov }
5079f7d47b0SAlexander V. Chernikov 
5089f7d47b0SAlexander V. Chernikov static int
50981d3153dSAlexander V. Chernikov ta_dump_radix_tentry(void *ta_state, struct table_info *ti, void *e,
51081d3153dSAlexander V. Chernikov     ipfw_obj_tentry *tent)
5119f7d47b0SAlexander V. Chernikov {
512c21034b7SAlexander V. Chernikov 	struct radix_addr_entry *n;
5139fe15d06SAlexander V. Chernikov #ifdef INET6
514c21034b7SAlexander V. Chernikov 	struct radix_addr_xentry *xn;
5159fe15d06SAlexander V. Chernikov #endif
5169f7d47b0SAlexander V. Chernikov 
517c21034b7SAlexander V. Chernikov 	n = (struct radix_addr_entry *)e;
5189f7d47b0SAlexander V. Chernikov 
5199f7d47b0SAlexander V. Chernikov 	/* Guess IPv4/IPv6 radix by sockaddr family */
5209f7d47b0SAlexander V. Chernikov 	if (n->addr.sin_family == AF_INET) {
52181d3153dSAlexander V. Chernikov 		tent->k.addr.s_addr = n->addr.sin_addr.s_addr;
522e0a8b9eeSAlexander V. Chernikov 		tent->masklen = n->masklen;
52381d3153dSAlexander V. Chernikov 		tent->subtype = AF_INET;
5240cba2b28SAlexander V. Chernikov 		tent->v.kidx = n->value;
5259f7d47b0SAlexander V. Chernikov #ifdef INET6
5269f7d47b0SAlexander V. Chernikov 	} else {
527c21034b7SAlexander V. Chernikov 		xn = (struct radix_addr_xentry *)e;
528ba3e1361SAndrey V. Elsukov 		memcpy(&tent->k.addr6, &xn->addr6.sin6_addr,
529ba3e1361SAndrey V. Elsukov 		    sizeof(struct in6_addr));
530e0a8b9eeSAlexander V. Chernikov 		tent->masklen = xn->masklen;
53181d3153dSAlexander V. Chernikov 		tent->subtype = AF_INET6;
5320cba2b28SAlexander V. Chernikov 		tent->v.kidx = xn->value;
5339f7d47b0SAlexander V. Chernikov #endif
5349f7d47b0SAlexander V. Chernikov 	}
5359f7d47b0SAlexander V. Chernikov 
5369f7d47b0SAlexander V. Chernikov 	return (0);
5379f7d47b0SAlexander V. Chernikov }
5389f7d47b0SAlexander V. Chernikov 
53981d3153dSAlexander V. Chernikov static int
540914bffb6SAlexander V. Chernikov ta_find_radix_tentry(void *ta_state, struct table_info *ti,
541914bffb6SAlexander V. Chernikov     ipfw_obj_tentry *tent)
54281d3153dSAlexander V. Chernikov {
54381d3153dSAlexander V. Chernikov 	struct radix_node_head *rnh;
54481d3153dSAlexander V. Chernikov 	void *e;
54581d3153dSAlexander V. Chernikov 
54681d3153dSAlexander V. Chernikov 	e = NULL;
547914bffb6SAlexander V. Chernikov 	if (tent->subtype == AF_INET) {
54881d3153dSAlexander V. Chernikov 		struct sockaddr_in sa;
54981d3153dSAlexander V. Chernikov 		KEY_LEN(sa) = KEY_LEN_INET;
550914bffb6SAlexander V. Chernikov 		sa.sin_addr.s_addr = tent->k.addr.s_addr;
55181d3153dSAlexander V. Chernikov 		rnh = (struct radix_node_head *)ti->state;
55261eee0e2SAlexander V. Chernikov 		e = rnh->rnh_matchaddr(&sa, &rnh->rh);
55381d3153dSAlexander V. Chernikov 	} else {
554e0a8b9eeSAlexander V. Chernikov 		struct sa_in6 sa6;
55581d3153dSAlexander V. Chernikov 		KEY_LEN(sa6) = KEY_LEN_INET6;
556914bffb6SAlexander V. Chernikov 		memcpy(&sa6.sin6_addr, &tent->k.addr6, sizeof(struct in6_addr));
55781d3153dSAlexander V. Chernikov 		rnh = (struct radix_node_head *)ti->xstate;
55861eee0e2SAlexander V. Chernikov 		e = rnh->rnh_matchaddr(&sa6, &rnh->rh);
55981d3153dSAlexander V. Chernikov 	}
56081d3153dSAlexander V. Chernikov 
56181d3153dSAlexander V. Chernikov 	if (e != NULL) {
56281d3153dSAlexander V. Chernikov 		ta_dump_radix_tentry(ta_state, ti, e, tent);
56381d3153dSAlexander V. Chernikov 		return (0);
56481d3153dSAlexander V. Chernikov 	}
56581d3153dSAlexander V. Chernikov 
56681d3153dSAlexander V. Chernikov 	return (ENOENT);
56781d3153dSAlexander V. Chernikov }
56881d3153dSAlexander V. Chernikov 
5699f7d47b0SAlexander V. Chernikov static void
5709f7d47b0SAlexander V. Chernikov ta_foreach_radix(void *ta_state, struct table_info *ti, ta_foreach_f *f,
5719f7d47b0SAlexander V. Chernikov     void *arg)
5729f7d47b0SAlexander V. Chernikov {
5739f7d47b0SAlexander V. Chernikov 	struct radix_node_head *rnh;
5749f7d47b0SAlexander V. Chernikov 
5759f7d47b0SAlexander V. Chernikov 	rnh = (struct radix_node_head *)(ti->state);
57661eee0e2SAlexander V. Chernikov 	rnh->rnh_walktree(&rnh->rh, (walktree_f_t *)f, arg);
5779f7d47b0SAlexander V. Chernikov 
5789f7d47b0SAlexander V. Chernikov 	rnh = (struct radix_node_head *)(ti->xstate);
57961eee0e2SAlexander V. Chernikov 	rnh->rnh_walktree(&rnh->rh, (walktree_f_t *)f, arg);
5809f7d47b0SAlexander V. Chernikov }
5819f7d47b0SAlexander V. Chernikov 
5829f7d47b0SAlexander V. Chernikov #ifdef INET6
583d699ee2dSAlexander V. Chernikov static inline void ipv6_writemask(struct in6_addr *addr6, uint8_t mask);
584d699ee2dSAlexander V. Chernikov 
5859f7d47b0SAlexander V. Chernikov static inline void
5869f7d47b0SAlexander V. Chernikov ipv6_writemask(struct in6_addr *addr6, uint8_t mask)
5879f7d47b0SAlexander V. Chernikov {
5889f7d47b0SAlexander V. Chernikov 	uint32_t *cp;
5899f7d47b0SAlexander V. Chernikov 
5909f7d47b0SAlexander V. Chernikov 	for (cp = (uint32_t *)addr6; mask >= 32; mask -= 32)
5919f7d47b0SAlexander V. Chernikov 		*cp++ = 0xFFFFFFFF;
59237aefa2aSAlexander V. Chernikov 	if (mask > 0)
5939f7d47b0SAlexander V. Chernikov 		*cp = htonl(mask ? ~((1 << (32 - mask)) - 1) : 0);
5949f7d47b0SAlexander V. Chernikov }
5959f7d47b0SAlexander V. Chernikov #endif
5969f7d47b0SAlexander V. Chernikov 
5972e324d29SAlexander V. Chernikov static void
5982e324d29SAlexander V. Chernikov tei_to_sockaddr_ent(struct tentry_info *tei, struct sockaddr *sa,
5992e324d29SAlexander V. Chernikov     struct sockaddr *ma, int *set_mask)
6002e324d29SAlexander V. Chernikov {
6012e324d29SAlexander V. Chernikov 	int mlen;
6029fe15d06SAlexander V. Chernikov #ifdef INET
6032e324d29SAlexander V. Chernikov 	struct sockaddr_in *addr, *mask;
6049fe15d06SAlexander V. Chernikov #endif
6059fe15d06SAlexander V. Chernikov #ifdef INET6
606720ee730SAlexander V. Chernikov 	struct sa_in6 *addr6, *mask6;
6079fe15d06SAlexander V. Chernikov #endif
6082e324d29SAlexander V. Chernikov 	in_addr_t a4;
6092e324d29SAlexander V. Chernikov 
6102e324d29SAlexander V. Chernikov 	mlen = tei->masklen;
6112e324d29SAlexander V. Chernikov 
6122e324d29SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
6132e324d29SAlexander V. Chernikov #ifdef INET
6142e324d29SAlexander V. Chernikov 		addr = (struct sockaddr_in *)sa;
6152e324d29SAlexander V. Chernikov 		mask = (struct sockaddr_in *)ma;
6162e324d29SAlexander V. Chernikov 		/* Set 'total' structure length */
6172e324d29SAlexander V. Chernikov 		KEY_LEN(*addr) = KEY_LEN_INET;
6182e324d29SAlexander V. Chernikov 		KEY_LEN(*mask) = KEY_LEN_INET;
6192e324d29SAlexander V. Chernikov 		addr->sin_family = AF_INET;
6202e324d29SAlexander V. Chernikov 		mask->sin_addr.s_addr =
6212e324d29SAlexander V. Chernikov 		    htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);
6222e324d29SAlexander V. Chernikov 		a4 = *((in_addr_t *)tei->paddr);
6232e324d29SAlexander V. Chernikov 		addr->sin_addr.s_addr = a4 & mask->sin_addr.s_addr;
6242e324d29SAlexander V. Chernikov 		if (mlen != 32)
6252e324d29SAlexander V. Chernikov 			*set_mask = 1;
6262e324d29SAlexander V. Chernikov 		else
6272e324d29SAlexander V. Chernikov 			*set_mask = 0;
6282e324d29SAlexander V. Chernikov #endif
6292e324d29SAlexander V. Chernikov #ifdef INET6
6302e324d29SAlexander V. Chernikov 	} else if (tei->subtype == AF_INET6) {
6312e324d29SAlexander V. Chernikov 		/* IPv6 case */
632720ee730SAlexander V. Chernikov 		addr6 = (struct sa_in6 *)sa;
633720ee730SAlexander V. Chernikov 		mask6 = (struct sa_in6 *)ma;
6342e324d29SAlexander V. Chernikov 		/* Set 'total' structure length */
6352e324d29SAlexander V. Chernikov 		KEY_LEN(*addr6) = KEY_LEN_INET6;
6362e324d29SAlexander V. Chernikov 		KEY_LEN(*mask6) = KEY_LEN_INET6;
6372e324d29SAlexander V. Chernikov 		addr6->sin6_family = AF_INET6;
6382e324d29SAlexander V. Chernikov 		ipv6_writemask(&mask6->sin6_addr, mlen);
6392e324d29SAlexander V. Chernikov 		memcpy(&addr6->sin6_addr, tei->paddr, sizeof(struct in6_addr));
6402e324d29SAlexander V. Chernikov 		APPLY_MASK(&addr6->sin6_addr, &mask6->sin6_addr);
6412e324d29SAlexander V. Chernikov 		if (mlen != 128)
6422e324d29SAlexander V. Chernikov 			*set_mask = 1;
6432e324d29SAlexander V. Chernikov 		else
6442e324d29SAlexander V. Chernikov 			*set_mask = 0;
6452e324d29SAlexander V. Chernikov #endif
6462e324d29SAlexander V. Chernikov 	}
647d699ee2dSAlexander V. Chernikov }
6489f7d47b0SAlexander V. Chernikov 
6499f7d47b0SAlexander V. Chernikov static int
650a399f8beSAlexander V. Chernikov ta_prepare_add_radix(struct ip_fw_chain *ch, struct tentry_info *tei,
65168394ec8SAlexander V. Chernikov     void *ta_buf)
6529f7d47b0SAlexander V. Chernikov {
653c21034b7SAlexander V. Chernikov 	struct ta_buf_radix *tb;
654c21034b7SAlexander V. Chernikov 	struct radix_addr_entry *ent;
655d699ee2dSAlexander V. Chernikov #ifdef INET6
656c21034b7SAlexander V. Chernikov 	struct radix_addr_xentry *xent;
657d699ee2dSAlexander V. Chernikov #endif
6582e324d29SAlexander V. Chernikov 	struct sockaddr *addr, *mask;
6592e324d29SAlexander V. Chernikov 	int mlen, set_mask;
6609f7d47b0SAlexander V. Chernikov 
661c21034b7SAlexander V. Chernikov 	tb = (struct ta_buf_radix *)ta_buf;
6629f7d47b0SAlexander V. Chernikov 
6639f7d47b0SAlexander V. Chernikov 	mlen = tei->masklen;
6642e324d29SAlexander V. Chernikov 	set_mask = 0;
6659f7d47b0SAlexander V. Chernikov 
666ac35ff17SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
6679f7d47b0SAlexander V. Chernikov #ifdef INET
6689f7d47b0SAlexander V. Chernikov 		if (mlen > 32)
6699f7d47b0SAlexander V. Chernikov 			return (EINVAL);
6709f7d47b0SAlexander V. Chernikov 		ent = malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO);
671e0a8b9eeSAlexander V. Chernikov 		ent->masklen = mlen;
6722e324d29SAlexander V. Chernikov 
6732e324d29SAlexander V. Chernikov 		addr = (struct sockaddr *)&ent->addr;
6742e324d29SAlexander V. Chernikov 		mask = (struct sockaddr *)&tb->addr.a4.ma;
6759f7d47b0SAlexander V. Chernikov 		tb->ent_ptr = ent;
6769f7d47b0SAlexander V. Chernikov #endif
6779f7d47b0SAlexander V. Chernikov #ifdef INET6
678ac35ff17SAlexander V. Chernikov 	} else if (tei->subtype == AF_INET6) {
6799f7d47b0SAlexander V. Chernikov 		/* IPv6 case */
6809f7d47b0SAlexander V. Chernikov 		if (mlen > 128)
6819f7d47b0SAlexander V. Chernikov 			return (EINVAL);
6829f7d47b0SAlexander V. Chernikov 		xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO);
683e0a8b9eeSAlexander V. Chernikov 		xent->masklen = mlen;
6842e324d29SAlexander V. Chernikov 
6852e324d29SAlexander V. Chernikov 		addr = (struct sockaddr *)&xent->addr6;
6862e324d29SAlexander V. Chernikov 		mask = (struct sockaddr *)&tb->addr.a6.ma;
6879f7d47b0SAlexander V. Chernikov 		tb->ent_ptr = xent;
6889f7d47b0SAlexander V. Chernikov #endif
6899f7d47b0SAlexander V. Chernikov 	} else {
6909f7d47b0SAlexander V. Chernikov 		/* Unknown CIDR type */
6919f7d47b0SAlexander V. Chernikov 		return (EINVAL);
6929f7d47b0SAlexander V. Chernikov 	}
6939f7d47b0SAlexander V. Chernikov 
6942e324d29SAlexander V. Chernikov 	tei_to_sockaddr_ent(tei, addr, mask, &set_mask);
6952e324d29SAlexander V. Chernikov 	/* Set pointers */
6962e324d29SAlexander V. Chernikov 	tb->addr_ptr = addr;
6972e324d29SAlexander V. Chernikov 	if (set_mask != 0)
6982e324d29SAlexander V. Chernikov 		tb->mask_ptr = mask;
6992e324d29SAlexander V. Chernikov 
7009f7d47b0SAlexander V. Chernikov 	return (0);
7019f7d47b0SAlexander V. Chernikov }
7029f7d47b0SAlexander V. Chernikov 
7039f7d47b0SAlexander V. Chernikov static int
704a399f8beSAlexander V. Chernikov ta_add_radix(void *ta_state, struct table_info *ti, struct tentry_info *tei,
705b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
7069f7d47b0SAlexander V. Chernikov {
7075f379342SAlexander V. Chernikov 	struct radix_cfg *cfg;
7089f7d47b0SAlexander V. Chernikov 	struct radix_node_head *rnh;
7099f7d47b0SAlexander V. Chernikov 	struct radix_node *rn;
710c21034b7SAlexander V. Chernikov 	struct ta_buf_radix *tb;
711648e8380SAlexander V. Chernikov 	uint32_t *old_value, value;
7129f7d47b0SAlexander V. Chernikov 
7135f379342SAlexander V. Chernikov 	cfg = (struct radix_cfg *)ta_state;
714c21034b7SAlexander V. Chernikov 	tb = (struct ta_buf_radix *)ta_buf;
7159f7d47b0SAlexander V. Chernikov 
71613263632SAlexander V. Chernikov 	/* Save current entry value from @tei */
71713263632SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
7189f7d47b0SAlexander V. Chernikov 		rnh = ti->state;
71913263632SAlexander V. Chernikov 		((struct radix_addr_entry *)tb->ent_ptr)->value = tei->value;
72013263632SAlexander V. Chernikov 	} else {
7219f7d47b0SAlexander V. Chernikov 		rnh = ti->xstate;
72213263632SAlexander V. Chernikov 		((struct radix_addr_xentry *)tb->ent_ptr)->value = tei->value;
72313263632SAlexander V. Chernikov 	}
7249f7d47b0SAlexander V. Chernikov 
7254c0c07a5SAlexander V. Chernikov 	/* Search for an entry first */
72661eee0e2SAlexander V. Chernikov 	rn = rnh->rnh_lookup(tb->addr_ptr, tb->mask_ptr, &rnh->rh);
7274c0c07a5SAlexander V. Chernikov 	if (rn != NULL) {
728ac35ff17SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
7299f7d47b0SAlexander V. Chernikov 			return (EEXIST);
730ac35ff17SAlexander V. Chernikov 		/* Record already exists. Update value if we're asked to */
731648e8380SAlexander V. Chernikov 		if (tei->subtype == AF_INET)
732c21034b7SAlexander V. Chernikov 			old_value = &((struct radix_addr_entry *)rn)->value;
733648e8380SAlexander V. Chernikov 		else
734c21034b7SAlexander V. Chernikov 			old_value = &((struct radix_addr_xentry *)rn)->value;
735648e8380SAlexander V. Chernikov 
736648e8380SAlexander V. Chernikov 		value = *old_value;
737648e8380SAlexander V. Chernikov 		*old_value = tei->value;
738648e8380SAlexander V. Chernikov 		tei->value = value;
739ac35ff17SAlexander V. Chernikov 
740ac35ff17SAlexander V. Chernikov 		/* Indicate that update has happened instead of addition */
741ac35ff17SAlexander V. Chernikov 		tei->flags |= TEI_FLAGS_UPDATED;
742adea6201SAlexander V. Chernikov 		*pnum = 0;
743e0a8b9eeSAlexander V. Chernikov 
744e0a8b9eeSAlexander V. Chernikov 		return (0);
745ac35ff17SAlexander V. Chernikov 	}
746ac35ff17SAlexander V. Chernikov 
7474c0c07a5SAlexander V. Chernikov 	if ((tei->flags & TEI_FLAGS_DONTADD) != 0)
7484c0c07a5SAlexander V. Chernikov 		return (EFBIG);
7494c0c07a5SAlexander V. Chernikov 
75061eee0e2SAlexander V. Chernikov 	rn = rnh->rnh_addaddr(tb->addr_ptr, tb->mask_ptr, &rnh->rh,tb->ent_ptr);
7514c0c07a5SAlexander V. Chernikov 	if (rn == NULL) {
7524c0c07a5SAlexander V. Chernikov 		/* Unknown error */
7534c0c07a5SAlexander V. Chernikov 		return (EINVAL);
7544c0c07a5SAlexander V. Chernikov 	}
7554c0c07a5SAlexander V. Chernikov 
7565f379342SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
7575f379342SAlexander V. Chernikov 		cfg->count4++;
7585f379342SAlexander V. Chernikov 	else
7595f379342SAlexander V. Chernikov 		cfg->count6++;
760ac35ff17SAlexander V. Chernikov 	tb->ent_ptr = NULL;
761adea6201SAlexander V. Chernikov 	*pnum = 1;
7629f7d47b0SAlexander V. Chernikov 
7639f7d47b0SAlexander V. Chernikov 	return (0);
7649f7d47b0SAlexander V. Chernikov }
7659f7d47b0SAlexander V. Chernikov 
7669f7d47b0SAlexander V. Chernikov static int
767a399f8beSAlexander V. Chernikov ta_prepare_del_radix(struct ip_fw_chain *ch, struct tentry_info *tei,
76868394ec8SAlexander V. Chernikov     void *ta_buf)
7699f7d47b0SAlexander V. Chernikov {
770c21034b7SAlexander V. Chernikov 	struct ta_buf_radix *tb;
7712e324d29SAlexander V. Chernikov 	struct sockaddr *addr, *mask;
7722e324d29SAlexander V. Chernikov 	int mlen, set_mask;
7739f7d47b0SAlexander V. Chernikov 
774c21034b7SAlexander V. Chernikov 	tb = (struct ta_buf_radix *)ta_buf;
7759f7d47b0SAlexander V. Chernikov 
7769f7d47b0SAlexander V. Chernikov 	mlen = tei->masklen;
7772e324d29SAlexander V. Chernikov 	set_mask = 0;
7789f7d47b0SAlexander V. Chernikov 
779ac35ff17SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
780e0a8b9eeSAlexander V. Chernikov 		if (mlen > 32)
781e0a8b9eeSAlexander V. Chernikov 			return (EINVAL);
7822e324d29SAlexander V. Chernikov 
7832e324d29SAlexander V. Chernikov 		addr = (struct sockaddr *)&tb->addr.a4.sa;
7842e324d29SAlexander V. Chernikov 		mask = (struct sockaddr *)&tb->addr.a4.ma;
7859f7d47b0SAlexander V. Chernikov #ifdef INET6
786ac35ff17SAlexander V. Chernikov 	} else if (tei->subtype == AF_INET6) {
7879f7d47b0SAlexander V. Chernikov 		if (mlen > 128)
7889f7d47b0SAlexander V. Chernikov 			return (EINVAL);
7892e324d29SAlexander V. Chernikov 
7902e324d29SAlexander V. Chernikov 		addr = (struct sockaddr *)&tb->addr.a6.sa;
7912e324d29SAlexander V. Chernikov 		mask = (struct sockaddr *)&tb->addr.a6.ma;
7929f7d47b0SAlexander V. Chernikov #endif
7939f7d47b0SAlexander V. Chernikov 	} else
7949f7d47b0SAlexander V. Chernikov 		return (EINVAL);
7959f7d47b0SAlexander V. Chernikov 
7962e324d29SAlexander V. Chernikov 	tei_to_sockaddr_ent(tei, addr, mask, &set_mask);
7972e324d29SAlexander V. Chernikov 	tb->addr_ptr = addr;
7982e324d29SAlexander V. Chernikov 	if (set_mask != 0)
7992e324d29SAlexander V. Chernikov 		tb->mask_ptr = mask;
8002e324d29SAlexander V. Chernikov 
8019f7d47b0SAlexander V. Chernikov 	return (0);
8029f7d47b0SAlexander V. Chernikov }
8039f7d47b0SAlexander V. Chernikov 
8049f7d47b0SAlexander V. Chernikov static int
805a399f8beSAlexander V. Chernikov ta_del_radix(void *ta_state, struct table_info *ti, struct tentry_info *tei,
806b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
8079f7d47b0SAlexander V. Chernikov {
8085f379342SAlexander V. Chernikov 	struct radix_cfg *cfg;
8099f7d47b0SAlexander V. Chernikov 	struct radix_node_head *rnh;
8109f7d47b0SAlexander V. Chernikov 	struct radix_node *rn;
811c21034b7SAlexander V. Chernikov 	struct ta_buf_radix *tb;
8129f7d47b0SAlexander V. Chernikov 
8135f379342SAlexander V. Chernikov 	cfg = (struct radix_cfg *)ta_state;
814c21034b7SAlexander V. Chernikov 	tb = (struct ta_buf_radix *)ta_buf;
8159f7d47b0SAlexander V. Chernikov 
816ac35ff17SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
8179f7d47b0SAlexander V. Chernikov 		rnh = ti->state;
8189f7d47b0SAlexander V. Chernikov 	else
8199f7d47b0SAlexander V. Chernikov 		rnh = ti->xstate;
8209f7d47b0SAlexander V. Chernikov 
82161eee0e2SAlexander V. Chernikov 	rn = rnh->rnh_deladdr(tb->addr_ptr, tb->mask_ptr, &rnh->rh);
8229f7d47b0SAlexander V. Chernikov 
8233a845e10SAlexander V. Chernikov 	if (rn == NULL)
8243a845e10SAlexander V. Chernikov 		return (ENOENT);
8253a845e10SAlexander V. Chernikov 
826648e8380SAlexander V. Chernikov 	/* Save entry value to @tei */
827648e8380SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
828c21034b7SAlexander V. Chernikov 		tei->value = ((struct radix_addr_entry *)rn)->value;
829648e8380SAlexander V. Chernikov 	else
830c21034b7SAlexander V. Chernikov 		tei->value = ((struct radix_addr_xentry *)rn)->value;
831648e8380SAlexander V. Chernikov 
8329f7d47b0SAlexander V. Chernikov 	tb->ent_ptr = rn;
8339f7d47b0SAlexander V. Chernikov 
8345f379342SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
8355f379342SAlexander V. Chernikov 		cfg->count4--;
8365f379342SAlexander V. Chernikov 	else
8375f379342SAlexander V. Chernikov 		cfg->count6--;
838adea6201SAlexander V. Chernikov 	*pnum = 1;
839adea6201SAlexander V. Chernikov 
8409f7d47b0SAlexander V. Chernikov 	return (0);
8419f7d47b0SAlexander V. Chernikov }
8429f7d47b0SAlexander V. Chernikov 
8439f7d47b0SAlexander V. Chernikov static void
844a399f8beSAlexander V. Chernikov ta_flush_radix_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
84568394ec8SAlexander V. Chernikov     void *ta_buf)
8469f7d47b0SAlexander V. Chernikov {
847c21034b7SAlexander V. Chernikov 	struct ta_buf_radix *tb;
8489f7d47b0SAlexander V. Chernikov 
849c21034b7SAlexander V. Chernikov 	tb = (struct ta_buf_radix *)ta_buf;
8509f7d47b0SAlexander V. Chernikov 
851ac35ff17SAlexander V. Chernikov 	if (tb->ent_ptr != NULL)
8529f7d47b0SAlexander V. Chernikov 		free(tb->ent_ptr, M_IPFW_TBL);
8539f7d47b0SAlexander V. Chernikov }
8549f7d47b0SAlexander V. Chernikov 
855b6ee846eSAlexander V. Chernikov static int
856301290bcSAlexander V. Chernikov ta_need_modify_radix(void *ta_state, struct table_info *ti, uint32_t count,
857b6ee846eSAlexander V. Chernikov     uint64_t *pflags)
858b6ee846eSAlexander V. Chernikov {
859b6ee846eSAlexander V. Chernikov 
860b6ee846eSAlexander V. Chernikov 	/*
8610bce0c23SAlexander V. Chernikov 	 * radix does not require additional memory allocations
862b6ee846eSAlexander V. Chernikov 	 * other than nodes itself. Adding new masks to the tree do
863b6ee846eSAlexander V. Chernikov 	 * but we don't have any API to call (and we don't known which
864b6ee846eSAlexander V. Chernikov 	 * sizes do we need).
865b6ee846eSAlexander V. Chernikov 	 */
866301290bcSAlexander V. Chernikov 	return (0);
867b6ee846eSAlexander V. Chernikov }
868b6ee846eSAlexander V. Chernikov 
869c21034b7SAlexander V. Chernikov struct table_algo addr_radix = {
870c21034b7SAlexander V. Chernikov 	.name		= "addr:radix",
871c21034b7SAlexander V. Chernikov 	.type		= IPFW_TABLE_ADDR,
87257a1cf95SAlexander V. Chernikov 	.flags		= TA_FLAG_DEFAULT,
873c21034b7SAlexander V. Chernikov 	.ta_buf_size	= sizeof(struct ta_buf_radix),
8749f7d47b0SAlexander V. Chernikov 	.init		= ta_init_radix,
8759f7d47b0SAlexander V. Chernikov 	.destroy	= ta_destroy_radix,
876a399f8beSAlexander V. Chernikov 	.prepare_add	= ta_prepare_add_radix,
877a399f8beSAlexander V. Chernikov 	.prepare_del	= ta_prepare_del_radix,
878a399f8beSAlexander V. Chernikov 	.add		= ta_add_radix,
879a399f8beSAlexander V. Chernikov 	.del		= ta_del_radix,
880a399f8beSAlexander V. Chernikov 	.flush_entry	= ta_flush_radix_entry,
8819f7d47b0SAlexander V. Chernikov 	.foreach	= ta_foreach_radix,
88281d3153dSAlexander V. Chernikov 	.dump_tentry	= ta_dump_radix_tentry,
88381d3153dSAlexander V. Chernikov 	.find_tentry	= ta_find_radix_tentry,
8845f379342SAlexander V. Chernikov 	.dump_tinfo	= ta_dump_radix_tinfo,
885301290bcSAlexander V. Chernikov 	.need_modify	= ta_need_modify_radix,
8869f7d47b0SAlexander V. Chernikov };
8879f7d47b0SAlexander V. Chernikov 
8889f7d47b0SAlexander V. Chernikov /*
889c21034b7SAlexander V. Chernikov  * addr:hash cmds
89074b941f0SAlexander V. Chernikov  *
89174b941f0SAlexander V. Chernikov  *
89274b941f0SAlexander V. Chernikov  * ti->data:
89374b941f0SAlexander V. Chernikov  * [inv.mask4][inv.mask6][log2hsize4][log2hsize6]
89474b941f0SAlexander V. Chernikov  * [        8][        8[          8][         8]
89574b941f0SAlexander V. Chernikov  *
89674b941f0SAlexander V. Chernikov  * inv.mask4: 32 - mask
89774b941f0SAlexander V. Chernikov  * inv.mask6:
89874b941f0SAlexander V. Chernikov  * 1) _slow lookup: mask
89974b941f0SAlexander V. Chernikov  * 2) _aligned: (128 - mask) / 8
90074b941f0SAlexander V. Chernikov  * 3) _64: 8
901ce2817b5SAlexander V. Chernikov  *
902ce2817b5SAlexander V. Chernikov  *
903ce2817b5SAlexander V. Chernikov  * pflags:
904ce2817b5SAlexander V. Chernikov  * [v4=1/v6=0][hsize]
905ce2817b5SAlexander V. Chernikov  * [       32][   32]
90674b941f0SAlexander V. Chernikov  */
90774b941f0SAlexander V. Chernikov 
90874b941f0SAlexander V. Chernikov struct chashentry;
90974b941f0SAlexander V. Chernikov 
91074b941f0SAlexander V. Chernikov SLIST_HEAD(chashbhead, chashentry);
91174b941f0SAlexander V. Chernikov 
91274b941f0SAlexander V. Chernikov struct chash_cfg {
91374b941f0SAlexander V. Chernikov 	struct chashbhead *head4;
91474b941f0SAlexander V. Chernikov 	struct chashbhead *head6;
91574b941f0SAlexander V. Chernikov 	size_t	size4;
91674b941f0SAlexander V. Chernikov 	size_t	size6;
917ce2817b5SAlexander V. Chernikov 	size_t	items4;
918ce2817b5SAlexander V. Chernikov 	size_t	items6;
91974b941f0SAlexander V. Chernikov 	uint8_t	mask4;
92074b941f0SAlexander V. Chernikov 	uint8_t	mask6;
92174b941f0SAlexander V. Chernikov };
92274b941f0SAlexander V. Chernikov 
92374b941f0SAlexander V. Chernikov struct chashentry {
92474b941f0SAlexander V. Chernikov 	SLIST_ENTRY(chashentry)	next;
92574b941f0SAlexander V. Chernikov 	uint32_t	value;
92674b941f0SAlexander V. Chernikov 	uint32_t	type;
92774b941f0SAlexander V. Chernikov 	union {
92874b941f0SAlexander V. Chernikov 		uint32_t	a4;	/* Host format */
92974b941f0SAlexander V. Chernikov 		struct in6_addr	a6;	/* Network format */
93074b941f0SAlexander V. Chernikov 	} a;
93174b941f0SAlexander V. Chernikov };
93274b941f0SAlexander V. Chernikov 
9330bce0c23SAlexander V. Chernikov struct ta_buf_chash
9340bce0c23SAlexander V. Chernikov {
9350bce0c23SAlexander V. Chernikov 	void *ent_ptr;
9360bce0c23SAlexander V. Chernikov 	struct chashentry ent;
9370bce0c23SAlexander V. Chernikov };
9380bce0c23SAlexander V. Chernikov 
939d699ee2dSAlexander V. Chernikov #ifdef INET
9409fe15d06SAlexander V. Chernikov static __inline uint32_t hash_ip(uint32_t addr, int hsize);
941d699ee2dSAlexander V. Chernikov #endif
942d699ee2dSAlexander V. Chernikov #ifdef INET6
9439fe15d06SAlexander V. Chernikov static __inline uint32_t hash_ip6(struct in6_addr *addr6, int hsize);
9449fe15d06SAlexander V. Chernikov static __inline uint16_t hash_ip64(struct in6_addr *addr6, int hsize);
9459fe15d06SAlexander V. Chernikov static __inline uint32_t hash_ip6_slow(struct in6_addr *addr6, void *key,
9469fe15d06SAlexander V. Chernikov     int mask, int hsize);
9479fe15d06SAlexander V. Chernikov static __inline uint32_t hash_ip6_al(struct in6_addr *addr6, void *key, int mask,
9489fe15d06SAlexander V. Chernikov     int hsize);
949d699ee2dSAlexander V. Chernikov #endif
9509fe15d06SAlexander V. Chernikov static int ta_lookup_chash_slow(struct table_info *ti, void *key, uint32_t keylen,
9519fe15d06SAlexander V. Chernikov     uint32_t *val);
9529fe15d06SAlexander V. Chernikov static int ta_lookup_chash_aligned(struct table_info *ti, void *key,
9539fe15d06SAlexander V. Chernikov     uint32_t keylen, uint32_t *val);
9549fe15d06SAlexander V. Chernikov static int ta_lookup_chash_64(struct table_info *ti, void *key, uint32_t keylen,
9559fe15d06SAlexander V. Chernikov     uint32_t *val);
9569fe15d06SAlexander V. Chernikov static int chash_parse_opts(struct chash_cfg *cfg, char *data);
9579fe15d06SAlexander V. Chernikov static void ta_print_chash_config(void *ta_state, struct table_info *ti,
9589fe15d06SAlexander V. Chernikov     char *buf, size_t bufsize);
9599e3a53fdSAlexander V. Chernikov static int ta_log2(uint32_t v);
9609fe15d06SAlexander V. Chernikov static int ta_init_chash(struct ip_fw_chain *ch, void **ta_state,
9619fe15d06SAlexander V. Chernikov     struct table_info *ti, char *data, uint8_t tflags);
9629fe15d06SAlexander V. Chernikov static void ta_destroy_chash(void *ta_state, struct table_info *ti);
9639fe15d06SAlexander V. Chernikov static void ta_dump_chash_tinfo(void *ta_state, struct table_info *ti,
9649fe15d06SAlexander V. Chernikov     ipfw_ta_tinfo *tinfo);
9659fe15d06SAlexander V. Chernikov static int ta_dump_chash_tentry(void *ta_state, struct table_info *ti,
9669fe15d06SAlexander V. Chernikov     void *e, ipfw_obj_tentry *tent);
9679fe15d06SAlexander V. Chernikov static uint32_t hash_ent(struct chashentry *ent, int af, int mlen,
9689fe15d06SAlexander V. Chernikov     uint32_t size);
9699fe15d06SAlexander V. Chernikov static int tei_to_chash_ent(struct tentry_info *tei, struct chashentry *ent);
9709fe15d06SAlexander V. Chernikov static int ta_find_chash_tentry(void *ta_state, struct table_info *ti,
9719fe15d06SAlexander V. Chernikov     ipfw_obj_tentry *tent);
9729fe15d06SAlexander V. Chernikov static void ta_foreach_chash(void *ta_state, struct table_info *ti,
9739fe15d06SAlexander V. Chernikov     ta_foreach_f *f, void *arg);
9749fe15d06SAlexander V. Chernikov static int ta_prepare_add_chash(struct ip_fw_chain *ch, struct tentry_info *tei,
9759fe15d06SAlexander V. Chernikov     void *ta_buf);
9769fe15d06SAlexander V. Chernikov static int ta_add_chash(void *ta_state, struct table_info *ti,
9779fe15d06SAlexander V. Chernikov     struct tentry_info *tei, void *ta_buf, uint32_t *pnum);
9789fe15d06SAlexander V. Chernikov static int ta_prepare_del_chash(struct ip_fw_chain *ch, struct tentry_info *tei,
9799fe15d06SAlexander V. Chernikov     void *ta_buf);
9809fe15d06SAlexander V. Chernikov static int ta_del_chash(void *ta_state, struct table_info *ti,
9819fe15d06SAlexander V. Chernikov     struct tentry_info *tei, void *ta_buf, uint32_t *pnum);
9829fe15d06SAlexander V. Chernikov static void ta_flush_chash_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
9839fe15d06SAlexander V. Chernikov     void *ta_buf);
9849fe15d06SAlexander V. Chernikov static int ta_need_modify_chash(void *ta_state, struct table_info *ti,
9859fe15d06SAlexander V. Chernikov     uint32_t count, uint64_t *pflags);
9869fe15d06SAlexander V. Chernikov static int ta_prepare_mod_chash(void *ta_buf, uint64_t *pflags);
9879fe15d06SAlexander V. Chernikov static int ta_fill_mod_chash(void *ta_state, struct table_info *ti, void *ta_buf,
9889fe15d06SAlexander V. Chernikov     uint64_t *pflags);
9899fe15d06SAlexander V. Chernikov static void ta_modify_chash(void *ta_state, struct table_info *ti, void *ta_buf,
9909fe15d06SAlexander V. Chernikov     uint64_t pflags);
9919fe15d06SAlexander V. Chernikov static void ta_flush_mod_chash(void *ta_buf);
9929fe15d06SAlexander V. Chernikov 
993d699ee2dSAlexander V. Chernikov #ifdef INET
99474b941f0SAlexander V. Chernikov static __inline uint32_t
99574b941f0SAlexander V. Chernikov hash_ip(uint32_t addr, int hsize)
99674b941f0SAlexander V. Chernikov {
99774b941f0SAlexander V. Chernikov 
99874b941f0SAlexander V. Chernikov 	return (addr % (hsize - 1));
99974b941f0SAlexander V. Chernikov }
1000d699ee2dSAlexander V. Chernikov #endif
100174b941f0SAlexander V. Chernikov 
1002d699ee2dSAlexander V. Chernikov #ifdef INET6
100374b941f0SAlexander V. Chernikov static __inline uint32_t
100474b941f0SAlexander V. Chernikov hash_ip6(struct in6_addr *addr6, int hsize)
100574b941f0SAlexander V. Chernikov {
100674b941f0SAlexander V. Chernikov 	uint32_t i;
100774b941f0SAlexander V. Chernikov 
100874b941f0SAlexander V. Chernikov 	i = addr6->s6_addr32[0] ^ addr6->s6_addr32[1] ^
100974b941f0SAlexander V. Chernikov 	    addr6->s6_addr32[2] ^ addr6->s6_addr32[3];
101074b941f0SAlexander V. Chernikov 
101174b941f0SAlexander V. Chernikov 	return (i % (hsize - 1));
101274b941f0SAlexander V. Chernikov }
101374b941f0SAlexander V. Chernikov 
101474b941f0SAlexander V. Chernikov static __inline uint16_t
101574b941f0SAlexander V. Chernikov hash_ip64(struct in6_addr *addr6, int hsize)
101674b941f0SAlexander V. Chernikov {
101774b941f0SAlexander V. Chernikov 	uint32_t i;
101874b941f0SAlexander V. Chernikov 
101974b941f0SAlexander V. Chernikov 	i = addr6->s6_addr32[0] ^ addr6->s6_addr32[1];
102074b941f0SAlexander V. Chernikov 
102174b941f0SAlexander V. Chernikov 	return (i % (hsize - 1));
102274b941f0SAlexander V. Chernikov }
102374b941f0SAlexander V. Chernikov 
102474b941f0SAlexander V. Chernikov static __inline uint32_t
102574b941f0SAlexander V. Chernikov hash_ip6_slow(struct in6_addr *addr6, void *key, int mask, int hsize)
102674b941f0SAlexander V. Chernikov {
102774b941f0SAlexander V. Chernikov 	struct in6_addr mask6;
102874b941f0SAlexander V. Chernikov 
102974b941f0SAlexander V. Chernikov 	ipv6_writemask(&mask6, mask);
103074b941f0SAlexander V. Chernikov 	memcpy(addr6, key, sizeof(struct in6_addr));
103174b941f0SAlexander V. Chernikov 	APPLY_MASK(addr6, &mask6);
103274b941f0SAlexander V. Chernikov 	return (hash_ip6(addr6, hsize));
103374b941f0SAlexander V. Chernikov }
103474b941f0SAlexander V. Chernikov 
103574b941f0SAlexander V. Chernikov static __inline uint32_t
103674b941f0SAlexander V. Chernikov hash_ip6_al(struct in6_addr *addr6, void *key, int mask, int hsize)
103774b941f0SAlexander V. Chernikov {
103874b941f0SAlexander V. Chernikov 	uint64_t *paddr;
103974b941f0SAlexander V. Chernikov 
104074b941f0SAlexander V. Chernikov 	paddr = (uint64_t *)addr6;
104174b941f0SAlexander V. Chernikov 	*paddr = 0;
104274b941f0SAlexander V. Chernikov 	*(paddr + 1) = 0;
104374b941f0SAlexander V. Chernikov 	memcpy(addr6, key, mask);
104474b941f0SAlexander V. Chernikov 	return (hash_ip6(addr6, hsize));
104574b941f0SAlexander V. Chernikov }
1046d699ee2dSAlexander V. Chernikov #endif
104774b941f0SAlexander V. Chernikov 
104874b941f0SAlexander V. Chernikov static int
104974b941f0SAlexander V. Chernikov ta_lookup_chash_slow(struct table_info *ti, void *key, uint32_t keylen,
105074b941f0SAlexander V. Chernikov     uint32_t *val)
105174b941f0SAlexander V. Chernikov {
105274b941f0SAlexander V. Chernikov 	struct chashbhead *head;
105374b941f0SAlexander V. Chernikov 	struct chashentry *ent;
105474b941f0SAlexander V. Chernikov 	uint16_t hash, hsize;
105574b941f0SAlexander V. Chernikov 	uint8_t imask;
105674b941f0SAlexander V. Chernikov 
105774b941f0SAlexander V. Chernikov 	if (keylen == sizeof(in_addr_t)) {
1058d699ee2dSAlexander V. Chernikov #ifdef INET
105974b941f0SAlexander V. Chernikov 		head = (struct chashbhead *)ti->state;
106074b941f0SAlexander V. Chernikov 		imask = ti->data >> 24;
106174b941f0SAlexander V. Chernikov 		hsize = 1 << ((ti->data & 0xFFFF) >> 8);
106274b941f0SAlexander V. Chernikov 		uint32_t a;
106374b941f0SAlexander V. Chernikov 		a = ntohl(*((in_addr_t *)key));
106474b941f0SAlexander V. Chernikov 		a = a >> imask;
106574b941f0SAlexander V. Chernikov 		hash = hash_ip(a, hsize);
106674b941f0SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
106774b941f0SAlexander V. Chernikov 			if (ent->a.a4 == a) {
106874b941f0SAlexander V. Chernikov 				*val = ent->value;
106974b941f0SAlexander V. Chernikov 				return (1);
107074b941f0SAlexander V. Chernikov 			}
107174b941f0SAlexander V. Chernikov 		}
1072d699ee2dSAlexander V. Chernikov #endif
107374b941f0SAlexander V. Chernikov 	} else {
1074d699ee2dSAlexander V. Chernikov #ifdef INET6
107574b941f0SAlexander V. Chernikov 		/* IPv6: worst scenario: non-round mask */
107674b941f0SAlexander V. Chernikov 		struct in6_addr addr6;
107774b941f0SAlexander V. Chernikov 		head = (struct chashbhead *)ti->xstate;
107874b941f0SAlexander V. Chernikov 		imask = (ti->data & 0xFF0000) >> 16;
107974b941f0SAlexander V. Chernikov 		hsize = 1 << (ti->data & 0xFF);
108074b941f0SAlexander V. Chernikov 		hash = hash_ip6_slow(&addr6, key, imask, hsize);
108174b941f0SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
108274b941f0SAlexander V. Chernikov 			if (memcmp(&ent->a.a6, &addr6, 16) == 0) {
108374b941f0SAlexander V. Chernikov 				*val = ent->value;
108474b941f0SAlexander V. Chernikov 				return (1);
108574b941f0SAlexander V. Chernikov 			}
108674b941f0SAlexander V. Chernikov 		}
1087d699ee2dSAlexander V. Chernikov #endif
108874b941f0SAlexander V. Chernikov 	}
108974b941f0SAlexander V. Chernikov 
109074b941f0SAlexander V. Chernikov 	return (0);
109174b941f0SAlexander V. Chernikov }
109274b941f0SAlexander V. Chernikov 
109374b941f0SAlexander V. Chernikov static int
109474b941f0SAlexander V. Chernikov ta_lookup_chash_aligned(struct table_info *ti, void *key, uint32_t keylen,
109574b941f0SAlexander V. Chernikov     uint32_t *val)
109674b941f0SAlexander V. Chernikov {
109774b941f0SAlexander V. Chernikov 	struct chashbhead *head;
109874b941f0SAlexander V. Chernikov 	struct chashentry *ent;
109974b941f0SAlexander V. Chernikov 	uint16_t hash, hsize;
110074b941f0SAlexander V. Chernikov 	uint8_t imask;
110174b941f0SAlexander V. Chernikov 
110274b941f0SAlexander V. Chernikov 	if (keylen == sizeof(in_addr_t)) {
1103d699ee2dSAlexander V. Chernikov #ifdef INET
110474b941f0SAlexander V. Chernikov 		head = (struct chashbhead *)ti->state;
110574b941f0SAlexander V. Chernikov 		imask = ti->data >> 24;
110674b941f0SAlexander V. Chernikov 		hsize = 1 << ((ti->data & 0xFFFF) >> 8);
110774b941f0SAlexander V. Chernikov 		uint32_t a;
110874b941f0SAlexander V. Chernikov 		a = ntohl(*((in_addr_t *)key));
110974b941f0SAlexander V. Chernikov 		a = a >> imask;
111074b941f0SAlexander V. Chernikov 		hash = hash_ip(a, hsize);
111174b941f0SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
111274b941f0SAlexander V. Chernikov 			if (ent->a.a4 == a) {
111374b941f0SAlexander V. Chernikov 				*val = ent->value;
111474b941f0SAlexander V. Chernikov 				return (1);
111574b941f0SAlexander V. Chernikov 			}
111674b941f0SAlexander V. Chernikov 		}
1117d699ee2dSAlexander V. Chernikov #endif
111874b941f0SAlexander V. Chernikov 	} else {
1119d699ee2dSAlexander V. Chernikov #ifdef INET6
112074b941f0SAlexander V. Chernikov 		/* IPv6: aligned to 8bit mask */
112174b941f0SAlexander V. Chernikov 		struct in6_addr addr6;
112274b941f0SAlexander V. Chernikov 		uint64_t *paddr, *ptmp;
112374b941f0SAlexander V. Chernikov 		head = (struct chashbhead *)ti->xstate;
112474b941f0SAlexander V. Chernikov 		imask = (ti->data & 0xFF0000) >> 16;
112574b941f0SAlexander V. Chernikov 		hsize = 1 << (ti->data & 0xFF);
112674b941f0SAlexander V. Chernikov 
112774b941f0SAlexander V. Chernikov 		hash = hash_ip6_al(&addr6, key, imask, hsize);
112874b941f0SAlexander V. Chernikov 		paddr = (uint64_t *)&addr6;
112974b941f0SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
113074b941f0SAlexander V. Chernikov 			ptmp = (uint64_t *)&ent->a.a6;
113174b941f0SAlexander V. Chernikov 			if (paddr[0] == ptmp[0] && paddr[1] == ptmp[1]) {
113274b941f0SAlexander V. Chernikov 				*val = ent->value;
113374b941f0SAlexander V. Chernikov 				return (1);
113474b941f0SAlexander V. Chernikov 			}
113574b941f0SAlexander V. Chernikov 		}
1136d699ee2dSAlexander V. Chernikov #endif
113774b941f0SAlexander V. Chernikov 	}
113874b941f0SAlexander V. Chernikov 
113974b941f0SAlexander V. Chernikov 	return (0);
114074b941f0SAlexander V. Chernikov }
114174b941f0SAlexander V. Chernikov 
114274b941f0SAlexander V. Chernikov static int
114374b941f0SAlexander V. Chernikov ta_lookup_chash_64(struct table_info *ti, void *key, uint32_t keylen,
114474b941f0SAlexander V. Chernikov     uint32_t *val)
114574b941f0SAlexander V. Chernikov {
114674b941f0SAlexander V. Chernikov 	struct chashbhead *head;
114774b941f0SAlexander V. Chernikov 	struct chashentry *ent;
114874b941f0SAlexander V. Chernikov 	uint16_t hash, hsize;
114974b941f0SAlexander V. Chernikov 	uint8_t imask;
115074b941f0SAlexander V. Chernikov 
115174b941f0SAlexander V. Chernikov 	if (keylen == sizeof(in_addr_t)) {
1152d699ee2dSAlexander V. Chernikov #ifdef INET
115374b941f0SAlexander V. Chernikov 		head = (struct chashbhead *)ti->state;
115474b941f0SAlexander V. Chernikov 		imask = ti->data >> 24;
115574b941f0SAlexander V. Chernikov 		hsize = 1 << ((ti->data & 0xFFFF) >> 8);
115674b941f0SAlexander V. Chernikov 		uint32_t a;
115774b941f0SAlexander V. Chernikov 		a = ntohl(*((in_addr_t *)key));
115874b941f0SAlexander V. Chernikov 		a = a >> imask;
115974b941f0SAlexander V. Chernikov 		hash = hash_ip(a, hsize);
116074b941f0SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
116174b941f0SAlexander V. Chernikov 			if (ent->a.a4 == a) {
116274b941f0SAlexander V. Chernikov 				*val = ent->value;
116374b941f0SAlexander V. Chernikov 				return (1);
116474b941f0SAlexander V. Chernikov 			}
116574b941f0SAlexander V. Chernikov 		}
1166d699ee2dSAlexander V. Chernikov #endif
116774b941f0SAlexander V. Chernikov 	} else {
1168d699ee2dSAlexander V. Chernikov #ifdef INET6
116974b941f0SAlexander V. Chernikov 		/* IPv6: /64 */
117074b941f0SAlexander V. Chernikov 		uint64_t a6, *paddr;
117174b941f0SAlexander V. Chernikov 		head = (struct chashbhead *)ti->xstate;
117274b941f0SAlexander V. Chernikov 		paddr = (uint64_t *)key;
117374b941f0SAlexander V. Chernikov 		hsize = 1 << (ti->data & 0xFF);
117474b941f0SAlexander V. Chernikov 		a6 = *paddr;
117574b941f0SAlexander V. Chernikov 		hash = hash_ip64((struct in6_addr *)key, hsize);
117674b941f0SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
117774b941f0SAlexander V. Chernikov 			paddr = (uint64_t *)&ent->a.a6;
117874b941f0SAlexander V. Chernikov 			if (a6 == *paddr) {
117974b941f0SAlexander V. Chernikov 				*val = ent->value;
118074b941f0SAlexander V. Chernikov 				return (1);
118174b941f0SAlexander V. Chernikov 			}
118274b941f0SAlexander V. Chernikov 		}
1183d699ee2dSAlexander V. Chernikov #endif
118474b941f0SAlexander V. Chernikov 	}
118574b941f0SAlexander V. Chernikov 
118674b941f0SAlexander V. Chernikov 	return (0);
118774b941f0SAlexander V. Chernikov }
118874b941f0SAlexander V. Chernikov 
118974b941f0SAlexander V. Chernikov static int
11900bce0c23SAlexander V. Chernikov chash_parse_opts(struct chash_cfg *cfg, char *data)
119174b941f0SAlexander V. Chernikov {
119274b941f0SAlexander V. Chernikov 	char *pdel, *pend, *s;
119374b941f0SAlexander V. Chernikov 	int mask4, mask6;
119474b941f0SAlexander V. Chernikov 
11950bce0c23SAlexander V. Chernikov 	mask4 = cfg->mask4;
11960bce0c23SAlexander V. Chernikov 	mask6 = cfg->mask6;
119774b941f0SAlexander V. Chernikov 
119874b941f0SAlexander V. Chernikov 	if (data == NULL)
119974b941f0SAlexander V. Chernikov 		return (0);
120074b941f0SAlexander V. Chernikov 	if ((pdel = strchr(data, ' ')) == NULL)
120174b941f0SAlexander V. Chernikov 		return (0);
120274b941f0SAlexander V. Chernikov 	while (*pdel == ' ')
120374b941f0SAlexander V. Chernikov 		pdel++;
120474b941f0SAlexander V. Chernikov 	if (strncmp(pdel, "masks=", 6) != 0)
120574b941f0SAlexander V. Chernikov 		return (EINVAL);
120674b941f0SAlexander V. Chernikov 	if ((s = strchr(pdel, ' ')) != NULL)
120774b941f0SAlexander V. Chernikov 		*s++ = '\0';
120874b941f0SAlexander V. Chernikov 
120974b941f0SAlexander V. Chernikov 	pdel += 6;
121074b941f0SAlexander V. Chernikov 	/* Need /XX[,/YY] */
121174b941f0SAlexander V. Chernikov 	if (*pdel++ != '/')
121274b941f0SAlexander V. Chernikov 		return (EINVAL);
121374b941f0SAlexander V. Chernikov 	mask4 = strtol(pdel, &pend, 10);
121474b941f0SAlexander V. Chernikov 	if (*pend == ',') {
121574b941f0SAlexander V. Chernikov 		/* ,/YY */
121674b941f0SAlexander V. Chernikov 		pdel = pend + 1;
121774b941f0SAlexander V. Chernikov 		if (*pdel++ != '/')
121874b941f0SAlexander V. Chernikov 			return (EINVAL);
121974b941f0SAlexander V. Chernikov 		mask6 = strtol(pdel, &pend, 10);
122074b941f0SAlexander V. Chernikov 		if (*pend != '\0')
122174b941f0SAlexander V. Chernikov 			return (EINVAL);
122274b941f0SAlexander V. Chernikov 	} else if (*pend != '\0')
122374b941f0SAlexander V. Chernikov 		return (EINVAL);
122474b941f0SAlexander V. Chernikov 
122574b941f0SAlexander V. Chernikov 	if (mask4 < 0 || mask4 > 32 || mask6 < 0 || mask6 > 128)
122674b941f0SAlexander V. Chernikov 		return (EINVAL);
122774b941f0SAlexander V. Chernikov 
12280bce0c23SAlexander V. Chernikov 	cfg->mask4 = mask4;
12290bce0c23SAlexander V. Chernikov 	cfg->mask6 = mask6;
123074b941f0SAlexander V. Chernikov 
123174b941f0SAlexander V. Chernikov 	return (0);
123274b941f0SAlexander V. Chernikov }
123374b941f0SAlexander V. Chernikov 
123474b941f0SAlexander V. Chernikov static void
123574b941f0SAlexander V. Chernikov ta_print_chash_config(void *ta_state, struct table_info *ti, char *buf,
123674b941f0SAlexander V. Chernikov     size_t bufsize)
123774b941f0SAlexander V. Chernikov {
12380bce0c23SAlexander V. Chernikov 	struct chash_cfg *cfg;
123974b941f0SAlexander V. Chernikov 
12400bce0c23SAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
124174b941f0SAlexander V. Chernikov 
12420bce0c23SAlexander V. Chernikov 	if (cfg->mask4 != 32 || cfg->mask6 != 128)
1243c21034b7SAlexander V. Chernikov 		snprintf(buf, bufsize, "%s masks=/%d,/%d", "addr:hash",
12440bce0c23SAlexander V. Chernikov 		    cfg->mask4, cfg->mask6);
124574b941f0SAlexander V. Chernikov 	else
1246c21034b7SAlexander V. Chernikov 		snprintf(buf, bufsize, "%s", "addr:hash");
124774b941f0SAlexander V. Chernikov }
124874b941f0SAlexander V. Chernikov 
1249914bffb6SAlexander V. Chernikov static int
12509e3a53fdSAlexander V. Chernikov ta_log2(uint32_t v)
1251914bffb6SAlexander V. Chernikov {
1252914bffb6SAlexander V. Chernikov 	uint32_t r;
1253914bffb6SAlexander V. Chernikov 
1254914bffb6SAlexander V. Chernikov 	r = 0;
1255914bffb6SAlexander V. Chernikov 	while (v >>= 1)
1256914bffb6SAlexander V. Chernikov 		r++;
1257914bffb6SAlexander V. Chernikov 
1258914bffb6SAlexander V. Chernikov 	return (r);
1259914bffb6SAlexander V. Chernikov }
126074b941f0SAlexander V. Chernikov 
126174b941f0SAlexander V. Chernikov /*
126274b941f0SAlexander V. Chernikov  * New table.
126374b941f0SAlexander V. Chernikov  * We assume 'data' to be either NULL or the following format:
1264c21034b7SAlexander V. Chernikov  * 'addr:hash [masks=/32[,/128]]'
126574b941f0SAlexander V. Chernikov  */
126674b941f0SAlexander V. Chernikov static int
126774b941f0SAlexander V. Chernikov ta_init_chash(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
1268914bffb6SAlexander V. Chernikov     char *data, uint8_t tflags)
126974b941f0SAlexander V. Chernikov {
127074b941f0SAlexander V. Chernikov 	int error, i;
1271914bffb6SAlexander V. Chernikov 	uint32_t hsize;
12720bce0c23SAlexander V. Chernikov 	struct chash_cfg *cfg;
127374b941f0SAlexander V. Chernikov 
12740bce0c23SAlexander V. Chernikov 	cfg = malloc(sizeof(struct chash_cfg), M_IPFW, M_WAITOK | M_ZERO);
127574b941f0SAlexander V. Chernikov 
12760bce0c23SAlexander V. Chernikov 	cfg->mask4 = 32;
12770bce0c23SAlexander V. Chernikov 	cfg->mask6 = 128;
127874b941f0SAlexander V. Chernikov 
12790bce0c23SAlexander V. Chernikov 	if ((error = chash_parse_opts(cfg, data)) != 0) {
12800bce0c23SAlexander V. Chernikov 		free(cfg, M_IPFW);
128174b941f0SAlexander V. Chernikov 		return (error);
128274b941f0SAlexander V. Chernikov 	}
128374b941f0SAlexander V. Chernikov 
12840bce0c23SAlexander V. Chernikov 	cfg->size4 = 128;
12850bce0c23SAlexander V. Chernikov 	cfg->size6 = 128;
128674b941f0SAlexander V. Chernikov 
12870bce0c23SAlexander V. Chernikov 	cfg->head4 = malloc(sizeof(struct chashbhead) * cfg->size4, M_IPFW,
128874b941f0SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
12890bce0c23SAlexander V. Chernikov 	cfg->head6 = malloc(sizeof(struct chashbhead) * cfg->size6, M_IPFW,
129074b941f0SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
12910bce0c23SAlexander V. Chernikov 	for (i = 0; i < cfg->size4; i++)
12920bce0c23SAlexander V. Chernikov 		SLIST_INIT(&cfg->head4[i]);
12930bce0c23SAlexander V. Chernikov 	for (i = 0; i < cfg->size6; i++)
12940bce0c23SAlexander V. Chernikov 		SLIST_INIT(&cfg->head6[i]);
129574b941f0SAlexander V. Chernikov 
12960bce0c23SAlexander V. Chernikov 	*ta_state = cfg;
12970bce0c23SAlexander V. Chernikov 	ti->state = cfg->head4;
12980bce0c23SAlexander V. Chernikov 	ti->xstate = cfg->head6;
129974b941f0SAlexander V. Chernikov 
130074b941f0SAlexander V. Chernikov 	/* Store data depending on v6 mask length */
13019e3a53fdSAlexander V. Chernikov 	hsize = ta_log2(cfg->size4) << 8 | ta_log2(cfg->size6);
13020bce0c23SAlexander V. Chernikov 	if (cfg->mask6 == 64) {
13030bce0c23SAlexander V. Chernikov 		ti->data = (32 - cfg->mask4) << 24 | (128 - cfg->mask6) << 16|
1304914bffb6SAlexander V. Chernikov 		    hsize;
130574b941f0SAlexander V. Chernikov 		ti->lookup = ta_lookup_chash_64;
13060bce0c23SAlexander V. Chernikov 	} else if ((cfg->mask6  % 8) == 0) {
13070bce0c23SAlexander V. Chernikov 		ti->data = (32 - cfg->mask4) << 24 |
13080bce0c23SAlexander V. Chernikov 		    cfg->mask6 << 13 | hsize;
130974b941f0SAlexander V. Chernikov 		ti->lookup = ta_lookup_chash_aligned;
131074b941f0SAlexander V. Chernikov 	} else {
131174b941f0SAlexander V. Chernikov 		/* don't do that! */
13120bce0c23SAlexander V. Chernikov 		ti->data = (32 - cfg->mask4) << 24 |
13130bce0c23SAlexander V. Chernikov 		    cfg->mask6 << 16 | hsize;
131474b941f0SAlexander V. Chernikov 		ti->lookup = ta_lookup_chash_slow;
131574b941f0SAlexander V. Chernikov 	}
131674b941f0SAlexander V. Chernikov 
131774b941f0SAlexander V. Chernikov 	return (0);
131874b941f0SAlexander V. Chernikov }
131974b941f0SAlexander V. Chernikov 
132074b941f0SAlexander V. Chernikov static void
132174b941f0SAlexander V. Chernikov ta_destroy_chash(void *ta_state, struct table_info *ti)
132274b941f0SAlexander V. Chernikov {
13230bce0c23SAlexander V. Chernikov 	struct chash_cfg *cfg;
132474b941f0SAlexander V. Chernikov 	struct chashentry *ent, *ent_next;
132574b941f0SAlexander V. Chernikov 	int i;
132674b941f0SAlexander V. Chernikov 
13270bce0c23SAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
132874b941f0SAlexander V. Chernikov 
13290bce0c23SAlexander V. Chernikov 	for (i = 0; i < cfg->size4; i++)
13300bce0c23SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &cfg->head4[i], next, ent_next)
133174b941f0SAlexander V. Chernikov 			free(ent, M_IPFW_TBL);
133274b941f0SAlexander V. Chernikov 
13330bce0c23SAlexander V. Chernikov 	for (i = 0; i < cfg->size6; i++)
13340bce0c23SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &cfg->head6[i], next, ent_next)
133574b941f0SAlexander V. Chernikov 			free(ent, M_IPFW_TBL);
133674b941f0SAlexander V. Chernikov 
13370bce0c23SAlexander V. Chernikov 	free(cfg->head4, M_IPFW);
13380bce0c23SAlexander V. Chernikov 	free(cfg->head6, M_IPFW);
1339ce2817b5SAlexander V. Chernikov 
13400bce0c23SAlexander V. Chernikov 	free(cfg, M_IPFW);
134174b941f0SAlexander V. Chernikov }
134274b941f0SAlexander V. Chernikov 
13435f379342SAlexander V. Chernikov static void
13445f379342SAlexander V. Chernikov ta_dump_chash_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo)
13455f379342SAlexander V. Chernikov {
13465f379342SAlexander V. Chernikov 	struct chash_cfg *cfg;
13475f379342SAlexander V. Chernikov 
13485f379342SAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
13495f379342SAlexander V. Chernikov 
13505f379342SAlexander V. Chernikov 	tinfo->flags = IPFW_TATFLAGS_AFDATA | IPFW_TATFLAGS_AFITEM;
13515f379342SAlexander V. Chernikov 	tinfo->taclass4 = IPFW_TACLASS_HASH;
13525f379342SAlexander V. Chernikov 	tinfo->size4 = cfg->size4;
13535f379342SAlexander V. Chernikov 	tinfo->count4 = cfg->items4;
13545f379342SAlexander V. Chernikov 	tinfo->itemsize4 = sizeof(struct chashentry);
13555f379342SAlexander V. Chernikov 	tinfo->taclass6 = IPFW_TACLASS_HASH;
13565f379342SAlexander V. Chernikov 	tinfo->size6 = cfg->size6;
13575f379342SAlexander V. Chernikov 	tinfo->count6 = cfg->items6;
13585f379342SAlexander V. Chernikov 	tinfo->itemsize6 = sizeof(struct chashentry);
13595f379342SAlexander V. Chernikov }
13605f379342SAlexander V. Chernikov 
136174b941f0SAlexander V. Chernikov static int
136274b941f0SAlexander V. Chernikov ta_dump_chash_tentry(void *ta_state, struct table_info *ti, void *e,
136374b941f0SAlexander V. Chernikov     ipfw_obj_tentry *tent)
136474b941f0SAlexander V. Chernikov {
13650bce0c23SAlexander V. Chernikov 	struct chash_cfg *cfg;
136674b941f0SAlexander V. Chernikov 	struct chashentry *ent;
136774b941f0SAlexander V. Chernikov 
13680bce0c23SAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
136974b941f0SAlexander V. Chernikov 	ent = (struct chashentry *)e;
137074b941f0SAlexander V. Chernikov 
137174b941f0SAlexander V. Chernikov 	if (ent->type == AF_INET) {
13720bce0c23SAlexander V. Chernikov 		tent->k.addr.s_addr = htonl(ent->a.a4 << (32 - cfg->mask4));
13730bce0c23SAlexander V. Chernikov 		tent->masklen = cfg->mask4;
137474b941f0SAlexander V. Chernikov 		tent->subtype = AF_INET;
13750cba2b28SAlexander V. Chernikov 		tent->v.kidx = ent->value;
137674b941f0SAlexander V. Chernikov #ifdef INET6
137774b941f0SAlexander V. Chernikov 	} else {
1378ba3e1361SAndrey V. Elsukov 		memcpy(&tent->k.addr6, &ent->a.a6, sizeof(struct in6_addr));
13790bce0c23SAlexander V. Chernikov 		tent->masklen = cfg->mask6;
138074b941f0SAlexander V. Chernikov 		tent->subtype = AF_INET6;
13810cba2b28SAlexander V. Chernikov 		tent->v.kidx = ent->value;
138274b941f0SAlexander V. Chernikov #endif
138374b941f0SAlexander V. Chernikov 	}
138474b941f0SAlexander V. Chernikov 
138574b941f0SAlexander V. Chernikov 	return (0);
138674b941f0SAlexander V. Chernikov }
138774b941f0SAlexander V. Chernikov 
1388ce2817b5SAlexander V. Chernikov static uint32_t
1389ce2817b5SAlexander V. Chernikov hash_ent(struct chashentry *ent, int af, int mlen, uint32_t size)
1390ce2817b5SAlexander V. Chernikov {
1391ce2817b5SAlexander V. Chernikov 	uint32_t hash;
1392ce2817b5SAlexander V. Chernikov 
1393d699ee2dSAlexander V. Chernikov 	hash = 0;
1394d699ee2dSAlexander V. Chernikov 
1395ce2817b5SAlexander V. Chernikov 	if (af == AF_INET) {
1396d699ee2dSAlexander V. Chernikov #ifdef INET
1397ce2817b5SAlexander V. Chernikov 		hash = hash_ip(ent->a.a4, size);
1398d699ee2dSAlexander V. Chernikov #endif
1399ce2817b5SAlexander V. Chernikov 	} else {
1400d699ee2dSAlexander V. Chernikov #ifdef INET6
1401ce2817b5SAlexander V. Chernikov 		if (mlen == 64)
1402ce2817b5SAlexander V. Chernikov 			hash = hash_ip64(&ent->a.a6, size);
1403ce2817b5SAlexander V. Chernikov 		else
1404ce2817b5SAlexander V. Chernikov 			hash = hash_ip6(&ent->a.a6, size);
1405d699ee2dSAlexander V. Chernikov #endif
1406ce2817b5SAlexander V. Chernikov 	}
1407ce2817b5SAlexander V. Chernikov 
1408ce2817b5SAlexander V. Chernikov 	return (hash);
1409ce2817b5SAlexander V. Chernikov }
1410ce2817b5SAlexander V. Chernikov 
1411ce2817b5SAlexander V. Chernikov static int
1412ce2817b5SAlexander V. Chernikov tei_to_chash_ent(struct tentry_info *tei, struct chashentry *ent)
1413ce2817b5SAlexander V. Chernikov {
1414ce2817b5SAlexander V. Chernikov 	int mlen;
1415d699ee2dSAlexander V. Chernikov #ifdef INET6
1416d699ee2dSAlexander V. Chernikov 	struct in6_addr mask6;
1417d699ee2dSAlexander V. Chernikov #endif
1418ce2817b5SAlexander V. Chernikov 
1419ce2817b5SAlexander V. Chernikov 	mlen = tei->masklen;
1420ce2817b5SAlexander V. Chernikov 
1421ce2817b5SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
1422ce2817b5SAlexander V. Chernikov #ifdef INET
1423ce2817b5SAlexander V. Chernikov 		if (mlen > 32)
1424ce2817b5SAlexander V. Chernikov 			return (EINVAL);
1425ce2817b5SAlexander V. Chernikov 		ent->type = AF_INET;
1426ce2817b5SAlexander V. Chernikov 
1427ce2817b5SAlexander V. Chernikov 		/* Calculate masked address */
1428ce2817b5SAlexander V. Chernikov 		ent->a.a4 = ntohl(*((in_addr_t *)tei->paddr)) >> (32 - mlen);
1429ce2817b5SAlexander V. Chernikov #endif
1430ce2817b5SAlexander V. Chernikov #ifdef INET6
1431ce2817b5SAlexander V. Chernikov 	} else if (tei->subtype == AF_INET6) {
1432ce2817b5SAlexander V. Chernikov 		/* IPv6 case */
1433ce2817b5SAlexander V. Chernikov 		if (mlen > 128)
1434ce2817b5SAlexander V. Chernikov 			return (EINVAL);
1435ce2817b5SAlexander V. Chernikov 		ent->type = AF_INET6;
1436ce2817b5SAlexander V. Chernikov 
1437ce2817b5SAlexander V. Chernikov 		ipv6_writemask(&mask6, mlen);
1438ce2817b5SAlexander V. Chernikov 		memcpy(&ent->a.a6, tei->paddr, sizeof(struct in6_addr));
1439ce2817b5SAlexander V. Chernikov 		APPLY_MASK(&ent->a.a6, &mask6);
1440ce2817b5SAlexander V. Chernikov #endif
1441ce2817b5SAlexander V. Chernikov 	} else {
1442ce2817b5SAlexander V. Chernikov 		/* Unknown CIDR type */
1443ce2817b5SAlexander V. Chernikov 		return (EINVAL);
1444ce2817b5SAlexander V. Chernikov 	}
1445ce2817b5SAlexander V. Chernikov 
1446ce2817b5SAlexander V. Chernikov 	return (0);
1447ce2817b5SAlexander V. Chernikov }
1448ce2817b5SAlexander V. Chernikov 
144974b941f0SAlexander V. Chernikov static int
1450914bffb6SAlexander V. Chernikov ta_find_chash_tentry(void *ta_state, struct table_info *ti,
1451914bffb6SAlexander V. Chernikov     ipfw_obj_tentry *tent)
145274b941f0SAlexander V. Chernikov {
14530bce0c23SAlexander V. Chernikov 	struct chash_cfg *cfg;
1454ce2817b5SAlexander V. Chernikov 	struct chashbhead *head;
1455ce2817b5SAlexander V. Chernikov 	struct chashentry ent, *tmp;
1456ce2817b5SAlexander V. Chernikov 	struct tentry_info tei;
1457ce2817b5SAlexander V. Chernikov 	int error;
1458ce2817b5SAlexander V. Chernikov 	uint32_t hash;
145974b941f0SAlexander V. Chernikov 
14600bce0c23SAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
1461ce2817b5SAlexander V. Chernikov 
1462ce2817b5SAlexander V. Chernikov 	memset(&ent, 0, sizeof(ent));
1463ce2817b5SAlexander V. Chernikov 	memset(&tei, 0, sizeof(tei));
1464ce2817b5SAlexander V. Chernikov 
1465914bffb6SAlexander V. Chernikov 	if (tent->subtype == AF_INET) {
1466914bffb6SAlexander V. Chernikov 		tei.paddr = &tent->k.addr;
14670bce0c23SAlexander V. Chernikov 		tei.masklen = cfg->mask4;
1468ce2817b5SAlexander V. Chernikov 		tei.subtype = AF_INET;
146974b941f0SAlexander V. Chernikov 
1470ce2817b5SAlexander V. Chernikov 		if ((error = tei_to_chash_ent(&tei, &ent)) != 0)
1471ce2817b5SAlexander V. Chernikov 			return (error);
1472ce2817b5SAlexander V. Chernikov 
14730bce0c23SAlexander V. Chernikov 		head = cfg->head4;
14740bce0c23SAlexander V. Chernikov 		hash = hash_ent(&ent, AF_INET, cfg->mask4, cfg->size4);
1475ce2817b5SAlexander V. Chernikov 		/* Check for existence */
1476ce2817b5SAlexander V. Chernikov 		SLIST_FOREACH(tmp, &head[hash], next) {
1477ce2817b5SAlexander V. Chernikov 			if (tmp->a.a4 != ent.a.a4)
1478ce2817b5SAlexander V. Chernikov 				continue;
1479ce2817b5SAlexander V. Chernikov 
1480ce2817b5SAlexander V. Chernikov 			ta_dump_chash_tentry(ta_state, ti, tmp, tent);
148174b941f0SAlexander V. Chernikov 			return (0);
148274b941f0SAlexander V. Chernikov 		}
1483ce2817b5SAlexander V. Chernikov 	} else {
1484914bffb6SAlexander V. Chernikov 		tei.paddr = &tent->k.addr6;
14850bce0c23SAlexander V. Chernikov 		tei.masklen = cfg->mask6;
1486ce2817b5SAlexander V. Chernikov 		tei.subtype = AF_INET6;
1487ce2817b5SAlexander V. Chernikov 
1488ce2817b5SAlexander V. Chernikov 		if ((error = tei_to_chash_ent(&tei, &ent)) != 0)
1489ce2817b5SAlexander V. Chernikov 			return (error);
1490ce2817b5SAlexander V. Chernikov 
14910bce0c23SAlexander V. Chernikov 		head = cfg->head6;
14920bce0c23SAlexander V. Chernikov 		hash = hash_ent(&ent, AF_INET6, cfg->mask6, cfg->size6);
1493ce2817b5SAlexander V. Chernikov 		/* Check for existence */
1494ce2817b5SAlexander V. Chernikov 		SLIST_FOREACH(tmp, &head[hash], next) {
1495ce2817b5SAlexander V. Chernikov 			if (memcmp(&tmp->a.a6, &ent.a.a6, 16) != 0)
1496ce2817b5SAlexander V. Chernikov 				continue;
1497ce2817b5SAlexander V. Chernikov 			ta_dump_chash_tentry(ta_state, ti, tmp, tent);
1498ce2817b5SAlexander V. Chernikov 			return (0);
1499ce2817b5SAlexander V. Chernikov 		}
1500ce2817b5SAlexander V. Chernikov 	}
1501ce2817b5SAlexander V. Chernikov 
150274b941f0SAlexander V. Chernikov 	return (ENOENT);
150374b941f0SAlexander V. Chernikov }
150474b941f0SAlexander V. Chernikov 
150574b941f0SAlexander V. Chernikov static void
150674b941f0SAlexander V. Chernikov ta_foreach_chash(void *ta_state, struct table_info *ti, ta_foreach_f *f,
150774b941f0SAlexander V. Chernikov     void *arg)
150874b941f0SAlexander V. Chernikov {
15090bce0c23SAlexander V. Chernikov 	struct chash_cfg *cfg;
151074b941f0SAlexander V. Chernikov 	struct chashentry *ent, *ent_next;
151174b941f0SAlexander V. Chernikov 	int i;
151274b941f0SAlexander V. Chernikov 
15130bce0c23SAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
151474b941f0SAlexander V. Chernikov 
15150bce0c23SAlexander V. Chernikov 	for (i = 0; i < cfg->size4; i++)
15160bce0c23SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &cfg->head4[i], next, ent_next)
151774b941f0SAlexander V. Chernikov 			f(ent, arg);
151874b941f0SAlexander V. Chernikov 
15190bce0c23SAlexander V. Chernikov 	for (i = 0; i < cfg->size6; i++)
15200bce0c23SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &cfg->head6[i], next, ent_next)
152174b941f0SAlexander V. Chernikov 			f(ent, arg);
152274b941f0SAlexander V. Chernikov }
152374b941f0SAlexander V. Chernikov 
152474b941f0SAlexander V. Chernikov static int
152574b941f0SAlexander V. Chernikov ta_prepare_add_chash(struct ip_fw_chain *ch, struct tentry_info *tei,
152674b941f0SAlexander V. Chernikov     void *ta_buf)
152774b941f0SAlexander V. Chernikov {
152874b941f0SAlexander V. Chernikov 	struct ta_buf_chash *tb;
152974b941f0SAlexander V. Chernikov 	struct chashentry *ent;
1530ce2817b5SAlexander V. Chernikov 	int error;
153174b941f0SAlexander V. Chernikov 
153274b941f0SAlexander V. Chernikov 	tb = (struct ta_buf_chash *)ta_buf;
153374b941f0SAlexander V. Chernikov 
153474b941f0SAlexander V. Chernikov 	ent = malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO);
153574b941f0SAlexander V. Chernikov 
1536ce2817b5SAlexander V. Chernikov 	error = tei_to_chash_ent(tei, ent);
1537ce2817b5SAlexander V. Chernikov 	if (error != 0) {
1538ce2817b5SAlexander V. Chernikov 		free(ent, M_IPFW_TBL);
1539ce2817b5SAlexander V. Chernikov 		return (error);
154074b941f0SAlexander V. Chernikov 	}
1541ce2817b5SAlexander V. Chernikov 	tb->ent_ptr = ent;
154274b941f0SAlexander V. Chernikov 
154374b941f0SAlexander V. Chernikov 	return (0);
154474b941f0SAlexander V. Chernikov }
154574b941f0SAlexander V. Chernikov 
154674b941f0SAlexander V. Chernikov static int
154774b941f0SAlexander V. Chernikov ta_add_chash(void *ta_state, struct table_info *ti, struct tentry_info *tei,
1548b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
154974b941f0SAlexander V. Chernikov {
15500bce0c23SAlexander V. Chernikov 	struct chash_cfg *cfg;
155174b941f0SAlexander V. Chernikov 	struct chashbhead *head;
155274b941f0SAlexander V. Chernikov 	struct chashentry *ent, *tmp;
155374b941f0SAlexander V. Chernikov 	struct ta_buf_chash *tb;
155474b941f0SAlexander V. Chernikov 	int exists;
1555648e8380SAlexander V. Chernikov 	uint32_t hash, value;
155674b941f0SAlexander V. Chernikov 
15570bce0c23SAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
155874b941f0SAlexander V. Chernikov 	tb = (struct ta_buf_chash *)ta_buf;
155974b941f0SAlexander V. Chernikov 	ent = (struct chashentry *)tb->ent_ptr;
156074b941f0SAlexander V. Chernikov 	hash = 0;
156174b941f0SAlexander V. Chernikov 	exists = 0;
156274b941f0SAlexander V. Chernikov 
156313263632SAlexander V. Chernikov 	/* Read current value from @tei */
156413263632SAlexander V. Chernikov 	ent->value = tei->value;
156513263632SAlexander V. Chernikov 
156613263632SAlexander V. Chernikov 	/* Read cuurrent value */
156774b941f0SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
15680bce0c23SAlexander V. Chernikov 		if (tei->masklen != cfg->mask4)
156974b941f0SAlexander V. Chernikov 			return (EINVAL);
15700bce0c23SAlexander V. Chernikov 		head = cfg->head4;
15710bce0c23SAlexander V. Chernikov 		hash = hash_ent(ent, AF_INET, cfg->mask4, cfg->size4);
1572ce2817b5SAlexander V. Chernikov 
157374b941f0SAlexander V. Chernikov 		/* Check for existence */
157474b941f0SAlexander V. Chernikov 		SLIST_FOREACH(tmp, &head[hash], next) {
157574b941f0SAlexander V. Chernikov 			if (tmp->a.a4 == ent->a.a4) {
157674b941f0SAlexander V. Chernikov 				exists = 1;
157774b941f0SAlexander V. Chernikov 				break;
157874b941f0SAlexander V. Chernikov 			}
157974b941f0SAlexander V. Chernikov 		}
158074b941f0SAlexander V. Chernikov 	} else {
15810bce0c23SAlexander V. Chernikov 		if (tei->masklen != cfg->mask6)
158274b941f0SAlexander V. Chernikov 			return (EINVAL);
15830bce0c23SAlexander V. Chernikov 		head = cfg->head6;
15840bce0c23SAlexander V. Chernikov 		hash = hash_ent(ent, AF_INET6, cfg->mask6, cfg->size6);
158574b941f0SAlexander V. Chernikov 		/* Check for existence */
158674b941f0SAlexander V. Chernikov 		SLIST_FOREACH(tmp, &head[hash], next) {
1587ce2817b5SAlexander V. Chernikov 			if (memcmp(&tmp->a.a6, &ent->a.a6, 16) == 0) {
158874b941f0SAlexander V. Chernikov 				exists = 1;
158974b941f0SAlexander V. Chernikov 				break;
159074b941f0SAlexander V. Chernikov 			}
159174b941f0SAlexander V. Chernikov 		}
159274b941f0SAlexander V. Chernikov 	}
159374b941f0SAlexander V. Chernikov 
159474b941f0SAlexander V. Chernikov 	if (exists == 1) {
159574b941f0SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
159674b941f0SAlexander V. Chernikov 			return (EEXIST);
159774b941f0SAlexander V. Chernikov 		/* Record already exists. Update value if we're asked to */
1598648e8380SAlexander V. Chernikov 		value = tmp->value;
159974b941f0SAlexander V. Chernikov 		tmp->value = tei->value;
1600648e8380SAlexander V. Chernikov 		tei->value = value;
160174b941f0SAlexander V. Chernikov 		/* Indicate that update has happened instead of addition */
160274b941f0SAlexander V. Chernikov 		tei->flags |= TEI_FLAGS_UPDATED;
160374b941f0SAlexander V. Chernikov 		*pnum = 0;
160474b941f0SAlexander V. Chernikov 	} else {
16054c0c07a5SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_DONTADD) != 0)
16064c0c07a5SAlexander V. Chernikov 			return (EFBIG);
160774b941f0SAlexander V. Chernikov 		SLIST_INSERT_HEAD(&head[hash], ent, next);
160874b941f0SAlexander V. Chernikov 		tb->ent_ptr = NULL;
160974b941f0SAlexander V. Chernikov 		*pnum = 1;
1610ce2817b5SAlexander V. Chernikov 
1611b6ee846eSAlexander V. Chernikov 		/* Update counters */
1612b6ee846eSAlexander V. Chernikov 		if (tei->subtype == AF_INET)
16130bce0c23SAlexander V. Chernikov 			cfg->items4++;
1614b6ee846eSAlexander V. Chernikov 		else
16150bce0c23SAlexander V. Chernikov 			cfg->items6++;
161674b941f0SAlexander V. Chernikov 	}
161774b941f0SAlexander V. Chernikov 
161874b941f0SAlexander V. Chernikov 	return (0);
161974b941f0SAlexander V. Chernikov }
162074b941f0SAlexander V. Chernikov 
162174b941f0SAlexander V. Chernikov static int
162274b941f0SAlexander V. Chernikov ta_prepare_del_chash(struct ip_fw_chain *ch, struct tentry_info *tei,
162374b941f0SAlexander V. Chernikov     void *ta_buf)
162474b941f0SAlexander V. Chernikov {
162574b941f0SAlexander V. Chernikov 	struct ta_buf_chash *tb;
162674b941f0SAlexander V. Chernikov 
162774b941f0SAlexander V. Chernikov 	tb = (struct ta_buf_chash *)ta_buf;
162874b941f0SAlexander V. Chernikov 
1629ce2817b5SAlexander V. Chernikov 	return (tei_to_chash_ent(tei, &tb->ent));
163074b941f0SAlexander V. Chernikov }
163174b941f0SAlexander V. Chernikov 
163274b941f0SAlexander V. Chernikov static int
163374b941f0SAlexander V. Chernikov ta_del_chash(void *ta_state, struct table_info *ti, struct tentry_info *tei,
1634b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
163574b941f0SAlexander V. Chernikov {
16360bce0c23SAlexander V. Chernikov 	struct chash_cfg *cfg;
163774b941f0SAlexander V. Chernikov 	struct chashbhead *head;
16380bce0c23SAlexander V. Chernikov 	struct chashentry *tmp, *tmp_next, *ent;
163974b941f0SAlexander V. Chernikov 	struct ta_buf_chash *tb;
164074b941f0SAlexander V. Chernikov 	uint32_t hash;
164174b941f0SAlexander V. Chernikov 
16420bce0c23SAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
164374b941f0SAlexander V. Chernikov 	tb = (struct ta_buf_chash *)ta_buf;
16440bce0c23SAlexander V. Chernikov 	ent = &tb->ent;
164574b941f0SAlexander V. Chernikov 
164674b941f0SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
16470bce0c23SAlexander V. Chernikov 		if (tei->masklen != cfg->mask4)
164874b941f0SAlexander V. Chernikov 			return (EINVAL);
16490bce0c23SAlexander V. Chernikov 		head = cfg->head4;
16500bce0c23SAlexander V. Chernikov 		hash = hash_ent(ent, AF_INET, cfg->mask4, cfg->size4);
165174b941f0SAlexander V. Chernikov 
16520bce0c23SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(tmp, &head[hash], next, tmp_next) {
16530bce0c23SAlexander V. Chernikov 			if (tmp->a.a4 != ent->a.a4)
1654648e8380SAlexander V. Chernikov 				continue;
1655648e8380SAlexander V. Chernikov 
16560bce0c23SAlexander V. Chernikov 			SLIST_REMOVE(&head[hash], tmp, chashentry, next);
16570bce0c23SAlexander V. Chernikov 			cfg->items4--;
16580bce0c23SAlexander V. Chernikov 			tb->ent_ptr = tmp;
16590bce0c23SAlexander V. Chernikov 			tei->value = tmp->value;
1660648e8380SAlexander V. Chernikov 			*pnum = 1;
166174b941f0SAlexander V. Chernikov 			return (0);
166274b941f0SAlexander V. Chernikov 		}
166374b941f0SAlexander V. Chernikov 	} else {
16640bce0c23SAlexander V. Chernikov 		if (tei->masklen != cfg->mask6)
166574b941f0SAlexander V. Chernikov 			return (EINVAL);
16660bce0c23SAlexander V. Chernikov 		head = cfg->head6;
16670bce0c23SAlexander V. Chernikov 		hash = hash_ent(ent, AF_INET6, cfg->mask6, cfg->size6);
16680bce0c23SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(tmp, &head[hash], next, tmp_next) {
16690bce0c23SAlexander V. Chernikov 			if (memcmp(&tmp->a.a6, &ent->a.a6, 16) != 0)
1670648e8380SAlexander V. Chernikov 				continue;
1671648e8380SAlexander V. Chernikov 
16720bce0c23SAlexander V. Chernikov 			SLIST_REMOVE(&head[hash], tmp, chashentry, next);
16730bce0c23SAlexander V. Chernikov 			cfg->items6--;
16740bce0c23SAlexander V. Chernikov 			tb->ent_ptr = tmp;
16750bce0c23SAlexander V. Chernikov 			tei->value = tmp->value;
167674b941f0SAlexander V. Chernikov 			*pnum = 1;
167774b941f0SAlexander V. Chernikov 			return (0);
167874b941f0SAlexander V. Chernikov 		}
167974b941f0SAlexander V. Chernikov 	}
168074b941f0SAlexander V. Chernikov 
168174b941f0SAlexander V. Chernikov 	return (ENOENT);
168274b941f0SAlexander V. Chernikov }
168374b941f0SAlexander V. Chernikov 
168474b941f0SAlexander V. Chernikov static void
168574b941f0SAlexander V. Chernikov ta_flush_chash_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
168674b941f0SAlexander V. Chernikov     void *ta_buf)
168774b941f0SAlexander V. Chernikov {
168874b941f0SAlexander V. Chernikov 	struct ta_buf_chash *tb;
168974b941f0SAlexander V. Chernikov 
169074b941f0SAlexander V. Chernikov 	tb = (struct ta_buf_chash *)ta_buf;
169174b941f0SAlexander V. Chernikov 
169274b941f0SAlexander V. Chernikov 	if (tb->ent_ptr != NULL)
169374b941f0SAlexander V. Chernikov 		free(tb->ent_ptr, M_IPFW_TBL);
169474b941f0SAlexander V. Chernikov }
169574b941f0SAlexander V. Chernikov 
1696ce2817b5SAlexander V. Chernikov /*
1697ce2817b5SAlexander V. Chernikov  * Hash growing callbacks.
1698ce2817b5SAlexander V. Chernikov  */
1699ce2817b5SAlexander V. Chernikov 
1700b6ee846eSAlexander V. Chernikov static int
1701301290bcSAlexander V. Chernikov ta_need_modify_chash(void *ta_state, struct table_info *ti, uint32_t count,
1702b6ee846eSAlexander V. Chernikov     uint64_t *pflags)
1703b6ee846eSAlexander V. Chernikov {
1704b6ee846eSAlexander V. Chernikov 	struct chash_cfg *cfg;
1705b6ee846eSAlexander V. Chernikov 	uint64_t data;
1706b6ee846eSAlexander V. Chernikov 
1707b6ee846eSAlexander V. Chernikov 	/*
1708b6ee846eSAlexander V. Chernikov 	 * Since we don't know exact number of IPv4/IPv6 records in @count,
1709b6ee846eSAlexander V. Chernikov 	 * ignore non-zero @count value at all. Check current hash sizes
1710b6ee846eSAlexander V. Chernikov 	 * and return appropriate data.
1711b6ee846eSAlexander V. Chernikov 	 */
1712b6ee846eSAlexander V. Chernikov 
1713b6ee846eSAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
1714b6ee846eSAlexander V. Chernikov 
1715b6ee846eSAlexander V. Chernikov 	data = 0;
1716b6ee846eSAlexander V. Chernikov 	if (cfg->items4 > cfg->size4 && cfg->size4 < 65536)
1717b6ee846eSAlexander V. Chernikov 		data |= (cfg->size4 * 2) << 16;
1718b6ee846eSAlexander V. Chernikov 	if (cfg->items6 > cfg->size6 && cfg->size6 < 65536)
1719b6ee846eSAlexander V. Chernikov 		data |= cfg->size6 * 2;
1720b6ee846eSAlexander V. Chernikov 
1721b6ee846eSAlexander V. Chernikov 	if (data != 0) {
1722b6ee846eSAlexander V. Chernikov 		*pflags = data;
1723301290bcSAlexander V. Chernikov 		return (1);
1724b6ee846eSAlexander V. Chernikov 	}
1725b6ee846eSAlexander V. Chernikov 
1726301290bcSAlexander V. Chernikov 	return (0);
1727b6ee846eSAlexander V. Chernikov }
1728b6ee846eSAlexander V. Chernikov 
1729ce2817b5SAlexander V. Chernikov /*
1730ce2817b5SAlexander V. Chernikov  * Allocate new, larger chash.
1731ce2817b5SAlexander V. Chernikov  */
1732ce2817b5SAlexander V. Chernikov static int
1733ce2817b5SAlexander V. Chernikov ta_prepare_mod_chash(void *ta_buf, uint64_t *pflags)
1734ce2817b5SAlexander V. Chernikov {
1735ce2817b5SAlexander V. Chernikov 	struct mod_item *mi;
1736ce2817b5SAlexander V. Chernikov 	struct chashbhead *head;
1737ce2817b5SAlexander V. Chernikov 	int i;
1738ce2817b5SAlexander V. Chernikov 
1739ce2817b5SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
1740ce2817b5SAlexander V. Chernikov 
1741ce2817b5SAlexander V. Chernikov 	memset(mi, 0, sizeof(struct mod_item));
1742b6ee846eSAlexander V. Chernikov 	mi->size = (*pflags >> 16) & 0xFFFF;
1743b6ee846eSAlexander V. Chernikov 	mi->size6 = *pflags & 0xFFFF;
1744b6ee846eSAlexander V. Chernikov 	if (mi->size > 0) {
1745b6ee846eSAlexander V. Chernikov 		head = malloc(sizeof(struct chashbhead) * mi->size,
1746b6ee846eSAlexander V. Chernikov 		    M_IPFW, M_WAITOK | M_ZERO);
1747ce2817b5SAlexander V. Chernikov 		for (i = 0; i < mi->size; i++)
1748ce2817b5SAlexander V. Chernikov 			SLIST_INIT(&head[i]);
1749ce2817b5SAlexander V. Chernikov 		mi->main_ptr = head;
1750b6ee846eSAlexander V. Chernikov 	}
1751b6ee846eSAlexander V. Chernikov 
1752b6ee846eSAlexander V. Chernikov 	if (mi->size6 > 0) {
1753b6ee846eSAlexander V. Chernikov 		head = malloc(sizeof(struct chashbhead) * mi->size6,
1754b6ee846eSAlexander V. Chernikov 		    M_IPFW, M_WAITOK | M_ZERO);
1755b6ee846eSAlexander V. Chernikov 		for (i = 0; i < mi->size6; i++)
1756b6ee846eSAlexander V. Chernikov 			SLIST_INIT(&head[i]);
1757b6ee846eSAlexander V. Chernikov 		mi->main_ptr6 = head;
1758b6ee846eSAlexander V. Chernikov 	}
1759ce2817b5SAlexander V. Chernikov 
1760ce2817b5SAlexander V. Chernikov 	return (0);
1761ce2817b5SAlexander V. Chernikov }
1762ce2817b5SAlexander V. Chernikov 
1763ce2817b5SAlexander V. Chernikov /*
1764ce2817b5SAlexander V. Chernikov  * Copy data from old runtime array to new one.
1765ce2817b5SAlexander V. Chernikov  */
1766ce2817b5SAlexander V. Chernikov static int
1767ce2817b5SAlexander V. Chernikov ta_fill_mod_chash(void *ta_state, struct table_info *ti, void *ta_buf,
1768ce2817b5SAlexander V. Chernikov     uint64_t *pflags)
1769ce2817b5SAlexander V. Chernikov {
1770ce2817b5SAlexander V. Chernikov 
1771ce2817b5SAlexander V. Chernikov 	/* In is not possible to do rehash if we're not holidng WLOCK. */
1772ce2817b5SAlexander V. Chernikov 	return (0);
1773ce2817b5SAlexander V. Chernikov }
1774ce2817b5SAlexander V. Chernikov 
1775ce2817b5SAlexander V. Chernikov /*
1776ce2817b5SAlexander V. Chernikov  * Switch old & new arrays.
1777ce2817b5SAlexander V. Chernikov  */
1778301290bcSAlexander V. Chernikov static void
1779ce2817b5SAlexander V. Chernikov ta_modify_chash(void *ta_state, struct table_info *ti, void *ta_buf,
1780ce2817b5SAlexander V. Chernikov     uint64_t pflags)
1781ce2817b5SAlexander V. Chernikov {
1782ce2817b5SAlexander V. Chernikov 	struct mod_item *mi;
1783b6ee846eSAlexander V. Chernikov 	struct chash_cfg *cfg;
1784ce2817b5SAlexander V. Chernikov 	struct chashbhead *old_head, *new_head;
1785ce2817b5SAlexander V. Chernikov 	struct chashentry *ent, *ent_next;
1786ce2817b5SAlexander V. Chernikov 	int af, i, mlen;
1787ce2817b5SAlexander V. Chernikov 	uint32_t nhash;
1788b6ee846eSAlexander V. Chernikov 	size_t old_size, new_size;
1789ce2817b5SAlexander V. Chernikov 
1790ce2817b5SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
1791b6ee846eSAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
1792ce2817b5SAlexander V. Chernikov 
1793ce2817b5SAlexander V. Chernikov 	/* Check which hash we need to grow and do we still need that */
1794b6ee846eSAlexander V. Chernikov 	if (mi->size > 0 && cfg->size4 < mi->size) {
1795ce2817b5SAlexander V. Chernikov 		new_head = (struct chashbhead *)mi->main_ptr;
1796b6ee846eSAlexander V. Chernikov 		new_size = mi->size;
1797b6ee846eSAlexander V. Chernikov 		old_size = cfg->size4;
1798b6ee846eSAlexander V. Chernikov 		old_head = ti->state;
1799b6ee846eSAlexander V. Chernikov 		mlen = cfg->mask4;
1800b6ee846eSAlexander V. Chernikov 		af = AF_INET;
1801b6ee846eSAlexander V. Chernikov 
1802ce2817b5SAlexander V. Chernikov 		for (i = 0; i < old_size; i++) {
1803ce2817b5SAlexander V. Chernikov 			SLIST_FOREACH_SAFE(ent, &old_head[i], next, ent_next) {
1804b6ee846eSAlexander V. Chernikov 				nhash = hash_ent(ent, af, mlen, new_size);
1805ce2817b5SAlexander V. Chernikov 				SLIST_INSERT_HEAD(&new_head[nhash], ent, next);
1806ce2817b5SAlexander V. Chernikov 			}
1807ce2817b5SAlexander V. Chernikov 		}
1808ce2817b5SAlexander V. Chernikov 
1809ce2817b5SAlexander V. Chernikov 		ti->state = new_head;
1810b6ee846eSAlexander V. Chernikov 		cfg->head4 = new_head;
1811b6ee846eSAlexander V. Chernikov 		cfg->size4 = mi->size;
1812b6ee846eSAlexander V. Chernikov 		mi->main_ptr = old_head;
1813ce2817b5SAlexander V. Chernikov 	}
1814ce2817b5SAlexander V. Chernikov 
1815b6ee846eSAlexander V. Chernikov 	if (mi->size6 > 0 && cfg->size6 < mi->size6) {
1816b6ee846eSAlexander V. Chernikov 		new_head = (struct chashbhead *)mi->main_ptr6;
1817b6ee846eSAlexander V. Chernikov 		new_size = mi->size6;
1818b6ee846eSAlexander V. Chernikov 		old_size = cfg->size6;
1819b6ee846eSAlexander V. Chernikov 		old_head = ti->xstate;
1820b6ee846eSAlexander V. Chernikov 		mlen = cfg->mask6;
1821b6ee846eSAlexander V. Chernikov 		af = AF_INET6;
1822914bffb6SAlexander V. Chernikov 
1823b6ee846eSAlexander V. Chernikov 		for (i = 0; i < old_size; i++) {
1824b6ee846eSAlexander V. Chernikov 			SLIST_FOREACH_SAFE(ent, &old_head[i], next, ent_next) {
1825b6ee846eSAlexander V. Chernikov 				nhash = hash_ent(ent, af, mlen, new_size);
1826b6ee846eSAlexander V. Chernikov 				SLIST_INSERT_HEAD(&new_head[nhash], ent, next);
1827b6ee846eSAlexander V. Chernikov 			}
1828b6ee846eSAlexander V. Chernikov 		}
1829b6ee846eSAlexander V. Chernikov 
1830b6ee846eSAlexander V. Chernikov 		ti->xstate = new_head;
1831b6ee846eSAlexander V. Chernikov 		cfg->head6 = new_head;
1832b6ee846eSAlexander V. Chernikov 		cfg->size6 = mi->size6;
1833b6ee846eSAlexander V. Chernikov 		mi->main_ptr6 = old_head;
1834b6ee846eSAlexander V. Chernikov 	}
1835b6ee846eSAlexander V. Chernikov 
1836b6ee846eSAlexander V. Chernikov 	/* Update lower 32 bits with new values */
1837b6ee846eSAlexander V. Chernikov 	ti->data &= 0xFFFFFFFF00000000;
18389e3a53fdSAlexander V. Chernikov 	ti->data |= ta_log2(cfg->size4) << 8 | ta_log2(cfg->size6);
1839ce2817b5SAlexander V. Chernikov }
1840ce2817b5SAlexander V. Chernikov 
1841ce2817b5SAlexander V. Chernikov /*
1842ce2817b5SAlexander V. Chernikov  * Free unneded array.
1843ce2817b5SAlexander V. Chernikov  */
1844ce2817b5SAlexander V. Chernikov static void
1845ce2817b5SAlexander V. Chernikov ta_flush_mod_chash(void *ta_buf)
1846ce2817b5SAlexander V. Chernikov {
1847ce2817b5SAlexander V. Chernikov 	struct mod_item *mi;
1848ce2817b5SAlexander V. Chernikov 
1849ce2817b5SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
1850ce2817b5SAlexander V. Chernikov 	if (mi->main_ptr != NULL)
1851ce2817b5SAlexander V. Chernikov 		free(mi->main_ptr, M_IPFW);
1852b6ee846eSAlexander V. Chernikov 	if (mi->main_ptr6 != NULL)
1853b6ee846eSAlexander V. Chernikov 		free(mi->main_ptr6, M_IPFW);
1854ce2817b5SAlexander V. Chernikov }
1855ce2817b5SAlexander V. Chernikov 
1856c21034b7SAlexander V. Chernikov struct table_algo addr_hash = {
1857c21034b7SAlexander V. Chernikov 	.name		= "addr:hash",
1858c21034b7SAlexander V. Chernikov 	.type		= IPFW_TABLE_ADDR,
185957a1cf95SAlexander V. Chernikov 	.ta_buf_size	= sizeof(struct ta_buf_chash),
186074b941f0SAlexander V. Chernikov 	.init		= ta_init_chash,
186174b941f0SAlexander V. Chernikov 	.destroy	= ta_destroy_chash,
186274b941f0SAlexander V. Chernikov 	.prepare_add	= ta_prepare_add_chash,
186374b941f0SAlexander V. Chernikov 	.prepare_del	= ta_prepare_del_chash,
186474b941f0SAlexander V. Chernikov 	.add		= ta_add_chash,
186574b941f0SAlexander V. Chernikov 	.del		= ta_del_chash,
186674b941f0SAlexander V. Chernikov 	.flush_entry	= ta_flush_chash_entry,
186774b941f0SAlexander V. Chernikov 	.foreach	= ta_foreach_chash,
186874b941f0SAlexander V. Chernikov 	.dump_tentry	= ta_dump_chash_tentry,
186974b941f0SAlexander V. Chernikov 	.find_tentry	= ta_find_chash_tentry,
187074b941f0SAlexander V. Chernikov 	.print_config	= ta_print_chash_config,
18715f379342SAlexander V. Chernikov 	.dump_tinfo	= ta_dump_chash_tinfo,
1872301290bcSAlexander V. Chernikov 	.need_modify	= ta_need_modify_chash,
1873ce2817b5SAlexander V. Chernikov 	.prepare_mod	= ta_prepare_mod_chash,
1874ce2817b5SAlexander V. Chernikov 	.fill_mod	= ta_fill_mod_chash,
1875ce2817b5SAlexander V. Chernikov 	.modify		= ta_modify_chash,
1876ce2817b5SAlexander V. Chernikov 	.flush_mod	= ta_flush_mod_chash,
187774b941f0SAlexander V. Chernikov };
187874b941f0SAlexander V. Chernikov 
187974b941f0SAlexander V. Chernikov /*
188068394ec8SAlexander V. Chernikov  * Iface table cmds.
188168394ec8SAlexander V. Chernikov  *
188268394ec8SAlexander V. Chernikov  * Implementation:
188368394ec8SAlexander V. Chernikov  *
188468394ec8SAlexander V. Chernikov  * Runtime part:
188568394ec8SAlexander V. Chernikov  * - sorted array of "struct ifidx" pointed by ti->state.
1886b23d5de9SAlexander V. Chernikov  *   Array is allocated with rounding up to IFIDX_CHUNK. Only existing
188768394ec8SAlexander V. Chernikov  *   interfaces are stored in array, however its allocated size is
188868394ec8SAlexander V. Chernikov  *   sufficient to hold all table records if needed.
188968394ec8SAlexander V. Chernikov  * - current array size is stored in ti->data
189068394ec8SAlexander V. Chernikov  *
189168394ec8SAlexander V. Chernikov  * Table data:
189268394ec8SAlexander V. Chernikov  * - "struct iftable_cfg" is allocated to store table state (ta_state).
189368394ec8SAlexander V. Chernikov  * - All table records are stored inside namedobj instance.
18949f7d47b0SAlexander V. Chernikov  *
18959f7d47b0SAlexander V. Chernikov  */
18969f7d47b0SAlexander V. Chernikov 
189768394ec8SAlexander V. Chernikov struct ifidx {
189868394ec8SAlexander V. Chernikov 	uint16_t	kidx;
189968394ec8SAlexander V. Chernikov 	uint16_t	spare;
190068394ec8SAlexander V. Chernikov 	uint32_t	value;
190168394ec8SAlexander V. Chernikov };
19020cba2b28SAlexander V. Chernikov #define	DEFAULT_IFIDX_SIZE	64
190368394ec8SAlexander V. Chernikov 
190468394ec8SAlexander V. Chernikov struct iftable_cfg;
190568394ec8SAlexander V. Chernikov 
190668394ec8SAlexander V. Chernikov struct ifentry {
190768394ec8SAlexander V. Chernikov 	struct named_object	no;
190868394ec8SAlexander V. Chernikov 	struct ipfw_ifc		ic;
190968394ec8SAlexander V. Chernikov 	struct iftable_cfg	*icfg;
191068394ec8SAlexander V. Chernikov 	uint32_t		value;
191168394ec8SAlexander V. Chernikov 	int			linked;
191268394ec8SAlexander V. Chernikov };
191368394ec8SAlexander V. Chernikov 
191468394ec8SAlexander V. Chernikov struct iftable_cfg {
191568394ec8SAlexander V. Chernikov 	struct namedobj_instance	*ii;
191668394ec8SAlexander V. Chernikov 	struct ip_fw_chain	*ch;
191768394ec8SAlexander V. Chernikov 	struct table_info	*ti;
191868394ec8SAlexander V. Chernikov 	void	*main_ptr;
191968394ec8SAlexander V. Chernikov 	size_t	size;	/* Number of items allocated in array */
192068394ec8SAlexander V. Chernikov 	size_t	count;	/* Number of all items */
192168394ec8SAlexander V. Chernikov 	size_t	used;	/* Number of items _active_ now */
192268394ec8SAlexander V. Chernikov };
192368394ec8SAlexander V. Chernikov 
19240bce0c23SAlexander V. Chernikov struct ta_buf_ifidx
19250bce0c23SAlexander V. Chernikov {
19260bce0c23SAlexander V. Chernikov 	struct ifentry *ife;
19270bce0c23SAlexander V. Chernikov 	uint32_t value;
19280bce0c23SAlexander V. Chernikov };
192968394ec8SAlexander V. Chernikov 
193068394ec8SAlexander V. Chernikov int compare_ifidx(const void *k, const void *v);
19319fe15d06SAlexander V. Chernikov static struct ifidx * ifidx_find(struct table_info *ti, void *key);
19329fe15d06SAlexander V. Chernikov static int ta_lookup_ifidx(struct table_info *ti, void *key, uint32_t keylen,
19339fe15d06SAlexander V. Chernikov     uint32_t *val);
19349fe15d06SAlexander V. Chernikov static int ta_init_ifidx(struct ip_fw_chain *ch, void **ta_state,
19359fe15d06SAlexander V. Chernikov     struct table_info *ti, char *data, uint8_t tflags);
19369fe15d06SAlexander V. Chernikov static void ta_change_ti_ifidx(void *ta_state, struct table_info *ti);
1937b309f085SAndrey V. Elsukov static int destroy_ifidx_locked(struct namedobj_instance *ii,
19389fe15d06SAlexander V. Chernikov     struct named_object *no, void *arg);
19399fe15d06SAlexander V. Chernikov static void ta_destroy_ifidx(void *ta_state, struct table_info *ti);
19409fe15d06SAlexander V. Chernikov static void ta_dump_ifidx_tinfo(void *ta_state, struct table_info *ti,
19419fe15d06SAlexander V. Chernikov     ipfw_ta_tinfo *tinfo);
19429fe15d06SAlexander V. Chernikov static int ta_prepare_add_ifidx(struct ip_fw_chain *ch, struct tentry_info *tei,
19439fe15d06SAlexander V. Chernikov     void *ta_buf);
19449fe15d06SAlexander V. Chernikov static int ta_add_ifidx(void *ta_state, struct table_info *ti,
19459fe15d06SAlexander V. Chernikov     struct tentry_info *tei, void *ta_buf, uint32_t *pnum);
19469fe15d06SAlexander V. Chernikov static int ta_prepare_del_ifidx(struct ip_fw_chain *ch, struct tentry_info *tei,
19479fe15d06SAlexander V. Chernikov     void *ta_buf);
19489fe15d06SAlexander V. Chernikov static int ta_del_ifidx(void *ta_state, struct table_info *ti,
19499fe15d06SAlexander V. Chernikov     struct tentry_info *tei, void *ta_buf, uint32_t *pnum);
19509fe15d06SAlexander V. Chernikov static void ta_flush_ifidx_entry(struct ip_fw_chain *ch,
19519fe15d06SAlexander V. Chernikov     struct tentry_info *tei, void *ta_buf);
19529fe15d06SAlexander V. Chernikov static void if_notifier(struct ip_fw_chain *ch, void *cbdata, uint16_t ifindex);
19539fe15d06SAlexander V. Chernikov static int ta_need_modify_ifidx(void *ta_state, struct table_info *ti,
19549fe15d06SAlexander V. Chernikov     uint32_t count, uint64_t *pflags);
19559fe15d06SAlexander V. Chernikov static int ta_prepare_mod_ifidx(void *ta_buf, uint64_t *pflags);
19569fe15d06SAlexander V. Chernikov static int ta_fill_mod_ifidx(void *ta_state, struct table_info *ti,
19579fe15d06SAlexander V. Chernikov     void *ta_buf, uint64_t *pflags);
19589fe15d06SAlexander V. Chernikov static void ta_modify_ifidx(void *ta_state, struct table_info *ti, void *ta_buf,
19599fe15d06SAlexander V. Chernikov     uint64_t pflags);
19609fe15d06SAlexander V. Chernikov static void ta_flush_mod_ifidx(void *ta_buf);
19619fe15d06SAlexander V. Chernikov static int ta_dump_ifidx_tentry(void *ta_state, struct table_info *ti, void *e,
19629fe15d06SAlexander V. Chernikov     ipfw_obj_tentry *tent);
19639fe15d06SAlexander V. Chernikov static int ta_find_ifidx_tentry(void *ta_state, struct table_info *ti,
19649fe15d06SAlexander V. Chernikov     ipfw_obj_tentry *tent);
1965b309f085SAndrey V. Elsukov static int foreach_ifidx(struct namedobj_instance *ii, struct named_object *no,
19669fe15d06SAlexander V. Chernikov     void *arg);
19679fe15d06SAlexander V. Chernikov static void ta_foreach_ifidx(void *ta_state, struct table_info *ti,
19689fe15d06SAlexander V. Chernikov     ta_foreach_f *f, void *arg);
196968394ec8SAlexander V. Chernikov 
197068394ec8SAlexander V. Chernikov int
197168394ec8SAlexander V. Chernikov compare_ifidx(const void *k, const void *v)
197268394ec8SAlexander V. Chernikov {
1973d4e1b515SAlexander V. Chernikov 	const struct ifidx *ifidx;
197468394ec8SAlexander V. Chernikov 	uint16_t key;
197568394ec8SAlexander V. Chernikov 
1976d4e1b515SAlexander V. Chernikov 	key = *((const uint16_t *)k);
1977d4e1b515SAlexander V. Chernikov 	ifidx = (const struct ifidx *)v;
197868394ec8SAlexander V. Chernikov 
197968394ec8SAlexander V. Chernikov 	if (key < ifidx->kidx)
198068394ec8SAlexander V. Chernikov 		return (-1);
198168394ec8SAlexander V. Chernikov 	else if (key > ifidx->kidx)
198268394ec8SAlexander V. Chernikov 		return (1);
198368394ec8SAlexander V. Chernikov 
198468394ec8SAlexander V. Chernikov 	return (0);
198568394ec8SAlexander V. Chernikov }
198668394ec8SAlexander V. Chernikov 
198768394ec8SAlexander V. Chernikov /*
198868394ec8SAlexander V. Chernikov  * Adds item @item with key @key into ascending-sorted array @base.
198968394ec8SAlexander V. Chernikov  * Assumes @base has enough additional storage.
199068394ec8SAlexander V. Chernikov  *
199168394ec8SAlexander V. Chernikov  * Returns 1 on success, 0 on duplicate key.
199268394ec8SAlexander V. Chernikov  */
19939f7d47b0SAlexander V. Chernikov static int
199468394ec8SAlexander V. Chernikov badd(const void *key, void *item, void *base, size_t nmemb,
199568394ec8SAlexander V. Chernikov     size_t size, int (*compar) (const void *, const void *))
199668394ec8SAlexander V. Chernikov {
199768394ec8SAlexander V. Chernikov 	int min, max, mid, shift, res;
199868394ec8SAlexander V. Chernikov 	caddr_t paddr;
199968394ec8SAlexander V. Chernikov 
200068394ec8SAlexander V. Chernikov 	if (nmemb == 0) {
200168394ec8SAlexander V. Chernikov 		memcpy(base, item, size);
200268394ec8SAlexander V. Chernikov 		return (1);
200368394ec8SAlexander V. Chernikov 	}
200468394ec8SAlexander V. Chernikov 
200568394ec8SAlexander V. Chernikov 	/* Binary search */
200668394ec8SAlexander V. Chernikov 	min = 0;
200768394ec8SAlexander V. Chernikov 	max = nmemb - 1;
200868394ec8SAlexander V. Chernikov 	mid = 0;
200968394ec8SAlexander V. Chernikov 	while (min <= max) {
201068394ec8SAlexander V. Chernikov 		mid = (min + max) / 2;
201168394ec8SAlexander V. Chernikov 		res = compar(key, (const void *)((caddr_t)base + mid * size));
201268394ec8SAlexander V. Chernikov 		if (res == 0)
201368394ec8SAlexander V. Chernikov 			return (0);
201468394ec8SAlexander V. Chernikov 
201568394ec8SAlexander V. Chernikov 		if (res > 0)
201668394ec8SAlexander V. Chernikov 			min = mid + 1;
201768394ec8SAlexander V. Chernikov 		else
201868394ec8SAlexander V. Chernikov 			max = mid - 1;
201968394ec8SAlexander V. Chernikov 	}
202068394ec8SAlexander V. Chernikov 
202168394ec8SAlexander V. Chernikov 	/* Item not found. */
202268394ec8SAlexander V. Chernikov 	res = compar(key, (const void *)((caddr_t)base + mid * size));
202368394ec8SAlexander V. Chernikov 	if (res > 0)
202468394ec8SAlexander V. Chernikov 		shift = mid + 1;
202568394ec8SAlexander V. Chernikov 	else
202668394ec8SAlexander V. Chernikov 		shift = mid;
202768394ec8SAlexander V. Chernikov 
202868394ec8SAlexander V. Chernikov 	paddr = (caddr_t)base + shift * size;
202968394ec8SAlexander V. Chernikov 	if (nmemb > shift)
203068394ec8SAlexander V. Chernikov 		memmove(paddr + size, paddr, (nmemb - shift) * size);
203168394ec8SAlexander V. Chernikov 
203268394ec8SAlexander V. Chernikov 	memcpy(paddr, item, size);
203368394ec8SAlexander V. Chernikov 
203468394ec8SAlexander V. Chernikov 	return (1);
203568394ec8SAlexander V. Chernikov }
203668394ec8SAlexander V. Chernikov 
203768394ec8SAlexander V. Chernikov /*
203868394ec8SAlexander V. Chernikov  * Deletes item with key @key from ascending-sorted array @base.
203968394ec8SAlexander V. Chernikov  *
204068394ec8SAlexander V. Chernikov  * Returns 1 on success, 0 for non-existent key.
204168394ec8SAlexander V. Chernikov  */
204268394ec8SAlexander V. Chernikov static int
204368394ec8SAlexander V. Chernikov bdel(const void *key, void *base, size_t nmemb, size_t size,
204468394ec8SAlexander V. Chernikov     int (*compar) (const void *, const void *))
204568394ec8SAlexander V. Chernikov {
204668394ec8SAlexander V. Chernikov 	caddr_t item;
204768394ec8SAlexander V. Chernikov 	size_t sz;
204868394ec8SAlexander V. Chernikov 
204968394ec8SAlexander V. Chernikov 	item = (caddr_t)bsearch(key, base, nmemb, size, compar);
205068394ec8SAlexander V. Chernikov 
205168394ec8SAlexander V. Chernikov 	if (item == NULL)
205268394ec8SAlexander V. Chernikov 		return (0);
205368394ec8SAlexander V. Chernikov 
205468394ec8SAlexander V. Chernikov 	sz = (caddr_t)base + nmemb * size - item;
205568394ec8SAlexander V. Chernikov 
205668394ec8SAlexander V. Chernikov 	if (sz > 0)
205768394ec8SAlexander V. Chernikov 		memmove(item, item + size, sz);
205868394ec8SAlexander V. Chernikov 
205968394ec8SAlexander V. Chernikov 	return (1);
206068394ec8SAlexander V. Chernikov }
206168394ec8SAlexander V. Chernikov 
206268394ec8SAlexander V. Chernikov static struct ifidx *
206368394ec8SAlexander V. Chernikov ifidx_find(struct table_info *ti, void *key)
206468394ec8SAlexander V. Chernikov {
206568394ec8SAlexander V. Chernikov 	struct ifidx *ifi;
206668394ec8SAlexander V. Chernikov 
206768394ec8SAlexander V. Chernikov 	ifi = bsearch(key, ti->state, ti->data, sizeof(struct ifidx),
206868394ec8SAlexander V. Chernikov 	    compare_ifidx);
206968394ec8SAlexander V. Chernikov 
207068394ec8SAlexander V. Chernikov 	return (ifi);
207168394ec8SAlexander V. Chernikov }
207268394ec8SAlexander V. Chernikov 
207368394ec8SAlexander V. Chernikov static int
207468394ec8SAlexander V. Chernikov ta_lookup_ifidx(struct table_info *ti, void *key, uint32_t keylen,
20759f7d47b0SAlexander V. Chernikov     uint32_t *val)
20769f7d47b0SAlexander V. Chernikov {
207768394ec8SAlexander V. Chernikov 	struct ifidx *ifi;
20789f7d47b0SAlexander V. Chernikov 
207968394ec8SAlexander V. Chernikov 	ifi = ifidx_find(ti, key);
20809f7d47b0SAlexander V. Chernikov 
208168394ec8SAlexander V. Chernikov 	if (ifi != NULL) {
208268394ec8SAlexander V. Chernikov 		*val = ifi->value;
20839f7d47b0SAlexander V. Chernikov 		return (1);
20849f7d47b0SAlexander V. Chernikov 	}
20859f7d47b0SAlexander V. Chernikov 
20869f7d47b0SAlexander V. Chernikov 	return (0);
20879f7d47b0SAlexander V. Chernikov }
20889f7d47b0SAlexander V. Chernikov 
20899f7d47b0SAlexander V. Chernikov static int
209068394ec8SAlexander V. Chernikov ta_init_ifidx(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
2091914bffb6SAlexander V. Chernikov     char *data, uint8_t tflags)
20929f7d47b0SAlexander V. Chernikov {
209368394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
20949f7d47b0SAlexander V. Chernikov 
209568394ec8SAlexander V. Chernikov 	icfg = malloc(sizeof(struct iftable_cfg), M_IPFW, M_WAITOK | M_ZERO);
20969f7d47b0SAlexander V. Chernikov 
20970cba2b28SAlexander V. Chernikov 	icfg->ii = ipfw_objhash_create(DEFAULT_IFIDX_SIZE);
20980cba2b28SAlexander V. Chernikov 	icfg->size = DEFAULT_IFIDX_SIZE;
20990bce0c23SAlexander V. Chernikov 	icfg->main_ptr = malloc(sizeof(struct ifidx) * icfg->size, M_IPFW,
210068394ec8SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
210168394ec8SAlexander V. Chernikov 	icfg->ch = ch;
21029f7d47b0SAlexander V. Chernikov 
210368394ec8SAlexander V. Chernikov 	*ta_state = icfg;
210468394ec8SAlexander V. Chernikov 	ti->state = icfg->main_ptr;
210568394ec8SAlexander V. Chernikov 	ti->lookup = ta_lookup_ifidx;
21069f7d47b0SAlexander V. Chernikov 
21079f7d47b0SAlexander V. Chernikov 	return (0);
21089f7d47b0SAlexander V. Chernikov }
21099f7d47b0SAlexander V. Chernikov 
211068394ec8SAlexander V. Chernikov /*
211168394ec8SAlexander V. Chernikov  * Handle tableinfo @ti pointer change (on table array resize).
211268394ec8SAlexander V. Chernikov  */
211368394ec8SAlexander V. Chernikov static void
211468394ec8SAlexander V. Chernikov ta_change_ti_ifidx(void *ta_state, struct table_info *ti)
211568394ec8SAlexander V. Chernikov {
211668394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
211768394ec8SAlexander V. Chernikov 
211868394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
211968394ec8SAlexander V. Chernikov 	icfg->ti = ti;
212068394ec8SAlexander V. Chernikov }
21219f7d47b0SAlexander V. Chernikov 
2122b309f085SAndrey V. Elsukov static int
212368394ec8SAlexander V. Chernikov destroy_ifidx_locked(struct namedobj_instance *ii, struct named_object *no,
212468394ec8SAlexander V. Chernikov     void *arg)
21259f7d47b0SAlexander V. Chernikov {
212668394ec8SAlexander V. Chernikov 	struct ifentry *ife;
212768394ec8SAlexander V. Chernikov 	struct ip_fw_chain *ch;
21289f7d47b0SAlexander V. Chernikov 
212968394ec8SAlexander V. Chernikov 	ch = (struct ip_fw_chain *)arg;
213068394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)no;
213168394ec8SAlexander V. Chernikov 
213268394ec8SAlexander V. Chernikov 	ipfw_iface_del_notify(ch, &ife->ic);
21330caab009SAlexander V. Chernikov 	ipfw_iface_unref(ch, &ife->ic);
213468394ec8SAlexander V. Chernikov 	free(ife, M_IPFW_TBL);
2135b309f085SAndrey V. Elsukov 	return (0);
21369f7d47b0SAlexander V. Chernikov }
21379f7d47b0SAlexander V. Chernikov 
213868394ec8SAlexander V. Chernikov /*
213968394ec8SAlexander V. Chernikov  * Destroys table @ti
214068394ec8SAlexander V. Chernikov  */
214168394ec8SAlexander V. Chernikov static void
214268394ec8SAlexander V. Chernikov ta_destroy_ifidx(void *ta_state, struct table_info *ti)
21439f7d47b0SAlexander V. Chernikov {
214468394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
214568394ec8SAlexander V. Chernikov 	struct ip_fw_chain *ch;
214668394ec8SAlexander V. Chernikov 
214768394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
214868394ec8SAlexander V. Chernikov 	ch = icfg->ch;
214968394ec8SAlexander V. Chernikov 
215068394ec8SAlexander V. Chernikov 	if (icfg->main_ptr != NULL)
215168394ec8SAlexander V. Chernikov 		free(icfg->main_ptr, M_IPFW);
215268394ec8SAlexander V. Chernikov 
21530caab009SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
215468394ec8SAlexander V. Chernikov 	ipfw_objhash_foreach(icfg->ii, destroy_ifidx_locked, ch);
21550caab009SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
215668394ec8SAlexander V. Chernikov 
215768394ec8SAlexander V. Chernikov 	ipfw_objhash_destroy(icfg->ii);
215868394ec8SAlexander V. Chernikov 
215968394ec8SAlexander V. Chernikov 	free(icfg, M_IPFW);
216068394ec8SAlexander V. Chernikov }
216168394ec8SAlexander V. Chernikov 
216268394ec8SAlexander V. Chernikov /*
21635f379342SAlexander V. Chernikov  * Provide algo-specific table info
21645f379342SAlexander V. Chernikov  */
21655f379342SAlexander V. Chernikov static void
21665f379342SAlexander V. Chernikov ta_dump_ifidx_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo)
21675f379342SAlexander V. Chernikov {
21685f379342SAlexander V. Chernikov 	struct iftable_cfg *cfg;
21695f379342SAlexander V. Chernikov 
21705f379342SAlexander V. Chernikov 	cfg = (struct iftable_cfg *)ta_state;
21715f379342SAlexander V. Chernikov 
21725f379342SAlexander V. Chernikov 	tinfo->taclass4 = IPFW_TACLASS_ARRAY;
21735f379342SAlexander V. Chernikov 	tinfo->size4 = cfg->size;
21745f379342SAlexander V. Chernikov 	tinfo->count4 = cfg->used;
21755f379342SAlexander V. Chernikov 	tinfo->itemsize4 = sizeof(struct ifidx);
21765f379342SAlexander V. Chernikov }
21775f379342SAlexander V. Chernikov 
21785f379342SAlexander V. Chernikov /*
217968394ec8SAlexander V. Chernikov  * Prepare state to add to the table:
218068394ec8SAlexander V. Chernikov  * allocate ifentry and reference needed interface.
218168394ec8SAlexander V. Chernikov  */
21829f7d47b0SAlexander V. Chernikov static int
218368394ec8SAlexander V. Chernikov ta_prepare_add_ifidx(struct ip_fw_chain *ch, struct tentry_info *tei,
218468394ec8SAlexander V. Chernikov     void *ta_buf)
218568394ec8SAlexander V. Chernikov {
218668394ec8SAlexander V. Chernikov 	struct ta_buf_ifidx *tb;
218768394ec8SAlexander V. Chernikov 	char *ifname;
218868394ec8SAlexander V. Chernikov 	struct ifentry *ife;
218968394ec8SAlexander V. Chernikov 
219068394ec8SAlexander V. Chernikov 	tb = (struct ta_buf_ifidx *)ta_buf;
219168394ec8SAlexander V. Chernikov 
219268394ec8SAlexander V. Chernikov 	/* Check if string is terminated */
219368394ec8SAlexander V. Chernikov 	ifname = (char *)tei->paddr;
219468394ec8SAlexander V. Chernikov 	if (strnlen(ifname, IF_NAMESIZE) == IF_NAMESIZE)
219568394ec8SAlexander V. Chernikov 		return (EINVAL);
219668394ec8SAlexander V. Chernikov 
219768394ec8SAlexander V. Chernikov 	ife = malloc(sizeof(struct ifentry), M_IPFW_TBL, M_WAITOK | M_ZERO);
219868394ec8SAlexander V. Chernikov 	ife->ic.cb = if_notifier;
219968394ec8SAlexander V. Chernikov 	ife->ic.cbdata = ife;
220068394ec8SAlexander V. Chernikov 
22018ebca97fSAlexander V. Chernikov 	if (ipfw_iface_ref(ch, ifname, &ife->ic) != 0) {
22028ebca97fSAlexander V. Chernikov 		free(ife, M_IPFW_TBL);
220368394ec8SAlexander V. Chernikov 		return (EINVAL);
22048ebca97fSAlexander V. Chernikov 	}
220568394ec8SAlexander V. Chernikov 
220668394ec8SAlexander V. Chernikov 	/* Use ipfw_iface 'ifname' field as stable storage */
220768394ec8SAlexander V. Chernikov 	ife->no.name = ife->ic.iface->ifname;
220868394ec8SAlexander V. Chernikov 
220968394ec8SAlexander V. Chernikov 	tb->ife = ife;
221068394ec8SAlexander V. Chernikov 
221168394ec8SAlexander V. Chernikov 	return (0);
221268394ec8SAlexander V. Chernikov }
221368394ec8SAlexander V. Chernikov 
221468394ec8SAlexander V. Chernikov static int
2215adea6201SAlexander V. Chernikov ta_add_ifidx(void *ta_state, struct table_info *ti, struct tentry_info *tei,
2216b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
221768394ec8SAlexander V. Chernikov {
221868394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
221968394ec8SAlexander V. Chernikov 	struct ifentry *ife, *tmp;
222068394ec8SAlexander V. Chernikov 	struct ta_buf_ifidx *tb;
222168394ec8SAlexander V. Chernikov 	struct ipfw_iface *iif;
222268394ec8SAlexander V. Chernikov 	struct ifidx *ifi;
222368394ec8SAlexander V. Chernikov 	char *ifname;
2224648e8380SAlexander V. Chernikov 	uint32_t value;
222568394ec8SAlexander V. Chernikov 
222668394ec8SAlexander V. Chernikov 	tb = (struct ta_buf_ifidx *)ta_buf;
222768394ec8SAlexander V. Chernikov 	ifname = (char *)tei->paddr;
222868394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
222968394ec8SAlexander V. Chernikov 	ife = tb->ife;
223068394ec8SAlexander V. Chernikov 
223168394ec8SAlexander V. Chernikov 	ife->icfg = icfg;
223213263632SAlexander V. Chernikov 	ife->value = tei->value;
223368394ec8SAlexander V. Chernikov 
223468394ec8SAlexander V. Chernikov 	tmp = (struct ifentry *)ipfw_objhash_lookup_name(icfg->ii, 0, ifname);
223568394ec8SAlexander V. Chernikov 
223668394ec8SAlexander V. Chernikov 	if (tmp != NULL) {
223768394ec8SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
223868394ec8SAlexander V. Chernikov 			return (EEXIST);
223968394ec8SAlexander V. Chernikov 
2240648e8380SAlexander V. Chernikov 		/* Exchange values in @tmp and @tei */
2241648e8380SAlexander V. Chernikov 		value = tmp->value;
2242648e8380SAlexander V. Chernikov 		tmp->value = tei->value;
2243648e8380SAlexander V. Chernikov 		tei->value = value;
224468394ec8SAlexander V. Chernikov 
2245648e8380SAlexander V. Chernikov 		iif = tmp->ic.iface;
224668394ec8SAlexander V. Chernikov 		if (iif->resolved != 0) {
2247648e8380SAlexander V. Chernikov 			/* We have to update runtime value, too */
224868394ec8SAlexander V. Chernikov 			ifi = ifidx_find(ti, &iif->ifindex);
224968394ec8SAlexander V. Chernikov 			ifi->value = ife->value;
225068394ec8SAlexander V. Chernikov 		}
225168394ec8SAlexander V. Chernikov 
225268394ec8SAlexander V. Chernikov 		/* Indicate that update has happened instead of addition */
225368394ec8SAlexander V. Chernikov 		tei->flags |= TEI_FLAGS_UPDATED;
2254adea6201SAlexander V. Chernikov 		*pnum = 0;
225568394ec8SAlexander V. Chernikov 		return (0);
225668394ec8SAlexander V. Chernikov 	}
225768394ec8SAlexander V. Chernikov 
22584c0c07a5SAlexander V. Chernikov 	if ((tei->flags & TEI_FLAGS_DONTADD) != 0)
22594c0c07a5SAlexander V. Chernikov 		return (EFBIG);
22604c0c07a5SAlexander V. Chernikov 
226168394ec8SAlexander V. Chernikov 	/* Link to internal list */
226268394ec8SAlexander V. Chernikov 	ipfw_objhash_add(icfg->ii, &ife->no);
226368394ec8SAlexander V. Chernikov 
226468394ec8SAlexander V. Chernikov 	/* Link notifier (possible running its callback) */
226568394ec8SAlexander V. Chernikov 	ipfw_iface_add_notify(icfg->ch, &ife->ic);
226668394ec8SAlexander V. Chernikov 	icfg->count++;
226768394ec8SAlexander V. Chernikov 
226868394ec8SAlexander V. Chernikov 	tb->ife = NULL;
2269adea6201SAlexander V. Chernikov 	*pnum = 1;
227068394ec8SAlexander V. Chernikov 
227168394ec8SAlexander V. Chernikov 	return (0);
227268394ec8SAlexander V. Chernikov }
227368394ec8SAlexander V. Chernikov 
227468394ec8SAlexander V. Chernikov /*
227568394ec8SAlexander V. Chernikov  * Prepare to delete key from table.
227668394ec8SAlexander V. Chernikov  * Do basic interface name checks.
227768394ec8SAlexander V. Chernikov  */
227868394ec8SAlexander V. Chernikov static int
227968394ec8SAlexander V. Chernikov ta_prepare_del_ifidx(struct ip_fw_chain *ch, struct tentry_info *tei,
228068394ec8SAlexander V. Chernikov     void *ta_buf)
22819f7d47b0SAlexander V. Chernikov {
2282e0a8b9eeSAlexander V. Chernikov 	char *ifname;
22839f7d47b0SAlexander V. Chernikov 
22849f7d47b0SAlexander V. Chernikov 	/* Check if string is terminated */
2285e0a8b9eeSAlexander V. Chernikov 	ifname = (char *)tei->paddr;
2286e0a8b9eeSAlexander V. Chernikov 	if (strnlen(ifname, IF_NAMESIZE) == IF_NAMESIZE)
22879f7d47b0SAlexander V. Chernikov 		return (EINVAL);
22889f7d47b0SAlexander V. Chernikov 
22899f7d47b0SAlexander V. Chernikov 	return (0);
22909f7d47b0SAlexander V. Chernikov }
22919f7d47b0SAlexander V. Chernikov 
229268394ec8SAlexander V. Chernikov /*
229368394ec8SAlexander V. Chernikov  * Remove key from both configuration list and
229468394ec8SAlexander V. Chernikov  * runtime array. Removed interface notification.
229568394ec8SAlexander V. Chernikov  */
22969f7d47b0SAlexander V. Chernikov static int
2297adea6201SAlexander V. Chernikov ta_del_ifidx(void *ta_state, struct table_info *ti, struct tentry_info *tei,
2298b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
22999f7d47b0SAlexander V. Chernikov {
230068394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
230168394ec8SAlexander V. Chernikov 	struct ifentry *ife;
230268394ec8SAlexander V. Chernikov 	struct ta_buf_ifidx *tb;
230368394ec8SAlexander V. Chernikov 	char *ifname;
230468394ec8SAlexander V. Chernikov 	uint16_t ifindex;
2305*60a28b09SMateusz Guzik 	int res __diagused;
23069f7d47b0SAlexander V. Chernikov 
230768394ec8SAlexander V. Chernikov 	tb = (struct ta_buf_ifidx *)ta_buf;
230868394ec8SAlexander V. Chernikov 	ifname = (char *)tei->paddr;
230968394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
23109f7d47b0SAlexander V. Chernikov 
231168394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)ipfw_objhash_lookup_name(icfg->ii, 0, ifname);
23129f7d47b0SAlexander V. Chernikov 
231368394ec8SAlexander V. Chernikov 	if (ife == NULL)
231481d3153dSAlexander V. Chernikov 		return (ENOENT);
23159f7d47b0SAlexander V. Chernikov 
231668394ec8SAlexander V. Chernikov 	if (ife->linked != 0) {
231768394ec8SAlexander V. Chernikov 		/* We have to remove item from runtime */
231868394ec8SAlexander V. Chernikov 		ifindex = ife->ic.iface->ifindex;
231968394ec8SAlexander V. Chernikov 
232068394ec8SAlexander V. Chernikov 		res = bdel(&ifindex, icfg->main_ptr, icfg->used,
232168394ec8SAlexander V. Chernikov 		    sizeof(struct ifidx), compare_ifidx);
232268394ec8SAlexander V. Chernikov 
232368394ec8SAlexander V. Chernikov 		KASSERT(res == 1, ("index %d does not exist", ifindex));
232468394ec8SAlexander V. Chernikov 		icfg->used--;
232568394ec8SAlexander V. Chernikov 		ti->data = icfg->used;
232668394ec8SAlexander V. Chernikov 		ife->linked = 0;
232768394ec8SAlexander V. Chernikov 	}
232868394ec8SAlexander V. Chernikov 
232968394ec8SAlexander V. Chernikov 	/* Unlink from local list */
233068394ec8SAlexander V. Chernikov 	ipfw_objhash_del(icfg->ii, &ife->no);
23310caab009SAlexander V. Chernikov 	/* Unlink notifier and deref */
233268394ec8SAlexander V. Chernikov 	ipfw_iface_del_notify(icfg->ch, &ife->ic);
23330caab009SAlexander V. Chernikov 	ipfw_iface_unref(icfg->ch, &ife->ic);
233468394ec8SAlexander V. Chernikov 
233568394ec8SAlexander V. Chernikov 	icfg->count--;
2336648e8380SAlexander V. Chernikov 	tei->value = ife->value;
233768394ec8SAlexander V. Chernikov 
233868394ec8SAlexander V. Chernikov 	tb->ife = ife;
2339adea6201SAlexander V. Chernikov 	*pnum = 1;
234068394ec8SAlexander V. Chernikov 
23419f7d47b0SAlexander V. Chernikov 	return (0);
23429f7d47b0SAlexander V. Chernikov }
23439f7d47b0SAlexander V. Chernikov 
234468394ec8SAlexander V. Chernikov /*
234568394ec8SAlexander V. Chernikov  * Flush deleted entry.
234668394ec8SAlexander V. Chernikov  * Drops interface reference and frees entry.
234768394ec8SAlexander V. Chernikov  */
23489f7d47b0SAlexander V. Chernikov static void
234968394ec8SAlexander V. Chernikov ta_flush_ifidx_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
235068394ec8SAlexander V. Chernikov     void *ta_buf)
23519f7d47b0SAlexander V. Chernikov {
235268394ec8SAlexander V. Chernikov 	struct ta_buf_ifidx *tb;
23539f7d47b0SAlexander V. Chernikov 
235468394ec8SAlexander V. Chernikov 	tb = (struct ta_buf_ifidx *)ta_buf;
23559f7d47b0SAlexander V. Chernikov 
23560caab009SAlexander V. Chernikov 	if (tb->ife != NULL)
235768394ec8SAlexander V. Chernikov 		free(tb->ife, M_IPFW_TBL);
235868394ec8SAlexander V. Chernikov }
235968394ec8SAlexander V. Chernikov 
236068394ec8SAlexander V. Chernikov /*
236168394ec8SAlexander V. Chernikov  * Handle interface announce/withdrawal for particular table.
236268394ec8SAlexander V. Chernikov  * Every real runtime array modification happens here.
236368394ec8SAlexander V. Chernikov  */
236468394ec8SAlexander V. Chernikov static void
236568394ec8SAlexander V. Chernikov if_notifier(struct ip_fw_chain *ch, void *cbdata, uint16_t ifindex)
236668394ec8SAlexander V. Chernikov {
236768394ec8SAlexander V. Chernikov 	struct ifentry *ife;
236868394ec8SAlexander V. Chernikov 	struct ifidx ifi;
236968394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
237068394ec8SAlexander V. Chernikov 	struct table_info *ti;
2371*60a28b09SMateusz Guzik 	int res __diagused;
237268394ec8SAlexander V. Chernikov 
237368394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)cbdata;
237468394ec8SAlexander V. Chernikov 	icfg = ife->icfg;
237568394ec8SAlexander V. Chernikov 	ti = icfg->ti;
237668394ec8SAlexander V. Chernikov 
237768394ec8SAlexander V. Chernikov 	KASSERT(ti != NULL, ("ti=NULL, check change_ti handler"));
237868394ec8SAlexander V. Chernikov 
237968394ec8SAlexander V. Chernikov 	if (ife->linked == 0 && ifindex != 0) {
238068394ec8SAlexander V. Chernikov 		/* Interface announce */
238168394ec8SAlexander V. Chernikov 		ifi.kidx = ifindex;
238268394ec8SAlexander V. Chernikov 		ifi.spare = 0;
238368394ec8SAlexander V. Chernikov 		ifi.value = ife->value;
238468394ec8SAlexander V. Chernikov 		res = badd(&ifindex, &ifi, icfg->main_ptr, icfg->used,
238568394ec8SAlexander V. Chernikov 		    sizeof(struct ifidx), compare_ifidx);
238668394ec8SAlexander V. Chernikov 		KASSERT(res == 1, ("index %d already exists", ifindex));
238768394ec8SAlexander V. Chernikov 		icfg->used++;
238868394ec8SAlexander V. Chernikov 		ti->data = icfg->used;
238968394ec8SAlexander V. Chernikov 		ife->linked = 1;
239068394ec8SAlexander V. Chernikov 	} else if (ife->linked != 0 && ifindex == 0) {
239168394ec8SAlexander V. Chernikov 		/* Interface withdrawal */
239268394ec8SAlexander V. Chernikov 		ifindex = ife->ic.iface->ifindex;
239368394ec8SAlexander V. Chernikov 
239468394ec8SAlexander V. Chernikov 		res = bdel(&ifindex, icfg->main_ptr, icfg->used,
239568394ec8SAlexander V. Chernikov 		    sizeof(struct ifidx), compare_ifidx);
239668394ec8SAlexander V. Chernikov 
239768394ec8SAlexander V. Chernikov 		KASSERT(res == 1, ("index %d does not exist", ifindex));
239868394ec8SAlexander V. Chernikov 		icfg->used--;
239968394ec8SAlexander V. Chernikov 		ti->data = icfg->used;
240068394ec8SAlexander V. Chernikov 		ife->linked = 0;
240168394ec8SAlexander V. Chernikov 	}
240268394ec8SAlexander V. Chernikov }
240368394ec8SAlexander V. Chernikov 
240468394ec8SAlexander V. Chernikov /*
240568394ec8SAlexander V. Chernikov  * Table growing callbacks.
240668394ec8SAlexander V. Chernikov  */
240768394ec8SAlexander V. Chernikov 
2408b6ee846eSAlexander V. Chernikov static int
2409301290bcSAlexander V. Chernikov ta_need_modify_ifidx(void *ta_state, struct table_info *ti, uint32_t count,
2410b6ee846eSAlexander V. Chernikov     uint64_t *pflags)
2411b6ee846eSAlexander V. Chernikov {
2412b6ee846eSAlexander V. Chernikov 	struct iftable_cfg *cfg;
24130bce0c23SAlexander V. Chernikov 	uint32_t size;
2414b6ee846eSAlexander V. Chernikov 
2415b6ee846eSAlexander V. Chernikov 	cfg = (struct iftable_cfg *)ta_state;
2416b6ee846eSAlexander V. Chernikov 
24170bce0c23SAlexander V. Chernikov 	size = cfg->size;
24180bce0c23SAlexander V. Chernikov 	while (size < cfg->count + count)
24190bce0c23SAlexander V. Chernikov 		size *= 2;
24200bce0c23SAlexander V. Chernikov 
24210bce0c23SAlexander V. Chernikov 	if (size != cfg->size) {
24220bce0c23SAlexander V. Chernikov 		*pflags = size;
2423301290bcSAlexander V. Chernikov 		return (1);
2424b6ee846eSAlexander V. Chernikov 	}
2425b6ee846eSAlexander V. Chernikov 
2426301290bcSAlexander V. Chernikov 	return (0);
2427b6ee846eSAlexander V. Chernikov }
2428b6ee846eSAlexander V. Chernikov 
242968394ec8SAlexander V. Chernikov /*
243068394ec8SAlexander V. Chernikov  * Allocate ned, larger runtime ifidx array.
243168394ec8SAlexander V. Chernikov  */
243268394ec8SAlexander V. Chernikov static int
243368394ec8SAlexander V. Chernikov ta_prepare_mod_ifidx(void *ta_buf, uint64_t *pflags)
243468394ec8SAlexander V. Chernikov {
24350bce0c23SAlexander V. Chernikov 	struct mod_item *mi;
243668394ec8SAlexander V. Chernikov 
24370bce0c23SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
243868394ec8SAlexander V. Chernikov 
24390bce0c23SAlexander V. Chernikov 	memset(mi, 0, sizeof(struct mod_item));
244068394ec8SAlexander V. Chernikov 	mi->size = *pflags;
244168394ec8SAlexander V. Chernikov 	mi->main_ptr = malloc(sizeof(struct ifidx) * mi->size, M_IPFW,
244268394ec8SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
244368394ec8SAlexander V. Chernikov 
244468394ec8SAlexander V. Chernikov 	return (0);
244568394ec8SAlexander V. Chernikov }
244668394ec8SAlexander V. Chernikov 
244768394ec8SAlexander V. Chernikov /*
244868394ec8SAlexander V. Chernikov  * Copy data from old runtime array to new one.
244968394ec8SAlexander V. Chernikov  */
245068394ec8SAlexander V. Chernikov static int
245168394ec8SAlexander V. Chernikov ta_fill_mod_ifidx(void *ta_state, struct table_info *ti, void *ta_buf,
245268394ec8SAlexander V. Chernikov     uint64_t *pflags)
245368394ec8SAlexander V. Chernikov {
24540bce0c23SAlexander V. Chernikov 	struct mod_item *mi;
245568394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
245668394ec8SAlexander V. Chernikov 
24570bce0c23SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
245868394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
245968394ec8SAlexander V. Chernikov 
246068394ec8SAlexander V. Chernikov 	/* Check if we still need to grow array */
246168394ec8SAlexander V. Chernikov 	if (icfg->size >= mi->size) {
246268394ec8SAlexander V. Chernikov 		*pflags = 0;
246368394ec8SAlexander V. Chernikov 		return (0);
246468394ec8SAlexander V. Chernikov 	}
246568394ec8SAlexander V. Chernikov 
246668394ec8SAlexander V. Chernikov 	memcpy(mi->main_ptr, icfg->main_ptr, icfg->used * sizeof(struct ifidx));
246768394ec8SAlexander V. Chernikov 
246868394ec8SAlexander V. Chernikov 	return (0);
246968394ec8SAlexander V. Chernikov }
247068394ec8SAlexander V. Chernikov 
247168394ec8SAlexander V. Chernikov /*
247268394ec8SAlexander V. Chernikov  * Switch old & new arrays.
247368394ec8SAlexander V. Chernikov  */
2474301290bcSAlexander V. Chernikov static void
247568394ec8SAlexander V. Chernikov ta_modify_ifidx(void *ta_state, struct table_info *ti, void *ta_buf,
247668394ec8SAlexander V. Chernikov     uint64_t pflags)
247768394ec8SAlexander V. Chernikov {
24780bce0c23SAlexander V. Chernikov 	struct mod_item *mi;
247968394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
248068394ec8SAlexander V. Chernikov 	void *old_ptr;
248168394ec8SAlexander V. Chernikov 
24820bce0c23SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
248368394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
248468394ec8SAlexander V. Chernikov 
248568394ec8SAlexander V. Chernikov 	old_ptr = icfg->main_ptr;
248668394ec8SAlexander V. Chernikov 	icfg->main_ptr = mi->main_ptr;
248768394ec8SAlexander V. Chernikov 	icfg->size = mi->size;
248868394ec8SAlexander V. Chernikov 	ti->state = icfg->main_ptr;
248968394ec8SAlexander V. Chernikov 
249068394ec8SAlexander V. Chernikov 	mi->main_ptr = old_ptr;
249168394ec8SAlexander V. Chernikov }
249268394ec8SAlexander V. Chernikov 
249368394ec8SAlexander V. Chernikov /*
249468394ec8SAlexander V. Chernikov  * Free unneded array.
249568394ec8SAlexander V. Chernikov  */
249668394ec8SAlexander V. Chernikov static void
249768394ec8SAlexander V. Chernikov ta_flush_mod_ifidx(void *ta_buf)
249868394ec8SAlexander V. Chernikov {
24990bce0c23SAlexander V. Chernikov 	struct mod_item *mi;
250068394ec8SAlexander V. Chernikov 
25010bce0c23SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
250268394ec8SAlexander V. Chernikov 	if (mi->main_ptr != NULL)
250368394ec8SAlexander V. Chernikov 		free(mi->main_ptr, M_IPFW);
25049f7d47b0SAlexander V. Chernikov }
25059f7d47b0SAlexander V. Chernikov 
25069f7d47b0SAlexander V. Chernikov static int
250768394ec8SAlexander V. Chernikov ta_dump_ifidx_tentry(void *ta_state, struct table_info *ti, void *e,
250881d3153dSAlexander V. Chernikov     ipfw_obj_tentry *tent)
25099f7d47b0SAlexander V. Chernikov {
251068394ec8SAlexander V. Chernikov 	struct ifentry *ife;
25119f7d47b0SAlexander V. Chernikov 
251268394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)e;
251368394ec8SAlexander V. Chernikov 
251481d3153dSAlexander V. Chernikov 	tent->masklen = 8 * IF_NAMESIZE;
251568394ec8SAlexander V. Chernikov 	memcpy(&tent->k, ife->no.name, IF_NAMESIZE);
25160cba2b28SAlexander V. Chernikov 	tent->v.kidx = ife->value;
25179f7d47b0SAlexander V. Chernikov 
25189f7d47b0SAlexander V. Chernikov 	return (0);
25199f7d47b0SAlexander V. Chernikov }
25209f7d47b0SAlexander V. Chernikov 
252181d3153dSAlexander V. Chernikov static int
2522914bffb6SAlexander V. Chernikov ta_find_ifidx_tentry(void *ta_state, struct table_info *ti,
2523914bffb6SAlexander V. Chernikov     ipfw_obj_tentry *tent)
252481d3153dSAlexander V. Chernikov {
252568394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
252668394ec8SAlexander V. Chernikov 	struct ifentry *ife;
252768394ec8SAlexander V. Chernikov 	char *ifname;
252881d3153dSAlexander V. Chernikov 
252968394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
2530914bffb6SAlexander V. Chernikov 	ifname = tent->k.iface;
253181d3153dSAlexander V. Chernikov 
253268394ec8SAlexander V. Chernikov 	if (strnlen(ifname, IF_NAMESIZE) == IF_NAMESIZE)
253368394ec8SAlexander V. Chernikov 		return (EINVAL);
253481d3153dSAlexander V. Chernikov 
253568394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)ipfw_objhash_lookup_name(icfg->ii, 0, ifname);
253668394ec8SAlexander V. Chernikov 
253768394ec8SAlexander V. Chernikov 	if (ife != NULL) {
253868394ec8SAlexander V. Chernikov 		ta_dump_ifidx_tentry(ta_state, ti, ife, tent);
253981d3153dSAlexander V. Chernikov 		return (0);
254081d3153dSAlexander V. Chernikov 	}
254181d3153dSAlexander V. Chernikov 
254281d3153dSAlexander V. Chernikov 	return (ENOENT);
254381d3153dSAlexander V. Chernikov }
254481d3153dSAlexander V. Chernikov 
254568394ec8SAlexander V. Chernikov struct wa_ifidx {
254668394ec8SAlexander V. Chernikov 	ta_foreach_f	*f;
254768394ec8SAlexander V. Chernikov 	void		*arg;
254868394ec8SAlexander V. Chernikov };
254968394ec8SAlexander V. Chernikov 
2550b309f085SAndrey V. Elsukov static int
255168394ec8SAlexander V. Chernikov foreach_ifidx(struct namedobj_instance *ii, struct named_object *no,
25529f7d47b0SAlexander V. Chernikov     void *arg)
25539f7d47b0SAlexander V. Chernikov {
255468394ec8SAlexander V. Chernikov 	struct ifentry *ife;
255568394ec8SAlexander V. Chernikov 	struct wa_ifidx *wa;
25569f7d47b0SAlexander V. Chernikov 
255768394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)no;
255868394ec8SAlexander V. Chernikov 	wa = (struct wa_ifidx *)arg;
255968394ec8SAlexander V. Chernikov 
256068394ec8SAlexander V. Chernikov 	wa->f(ife, wa->arg);
2561b309f085SAndrey V. Elsukov 	return (0);
25629f7d47b0SAlexander V. Chernikov }
25639f7d47b0SAlexander V. Chernikov 
256468394ec8SAlexander V. Chernikov static void
256568394ec8SAlexander V. Chernikov ta_foreach_ifidx(void *ta_state, struct table_info *ti, ta_foreach_f *f,
256668394ec8SAlexander V. Chernikov     void *arg)
256768394ec8SAlexander V. Chernikov {
256868394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
256968394ec8SAlexander V. Chernikov 	struct wa_ifidx wa;
257068394ec8SAlexander V. Chernikov 
257168394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
257268394ec8SAlexander V. Chernikov 
257368394ec8SAlexander V. Chernikov 	wa.f = f;
257468394ec8SAlexander V. Chernikov 	wa.arg = arg;
257568394ec8SAlexander V. Chernikov 
257668394ec8SAlexander V. Chernikov 	ipfw_objhash_foreach(icfg->ii, foreach_ifidx, &wa);
257768394ec8SAlexander V. Chernikov }
257868394ec8SAlexander V. Chernikov 
257974b941f0SAlexander V. Chernikov struct table_algo iface_idx = {
2580adea6201SAlexander V. Chernikov 	.name		= "iface:array",
25819d099b4fSAlexander V. Chernikov 	.type		= IPFW_TABLE_INTERFACE,
258257a1cf95SAlexander V. Chernikov 	.flags		= TA_FLAG_DEFAULT,
258357a1cf95SAlexander V. Chernikov 	.ta_buf_size	= sizeof(struct ta_buf_ifidx),
258468394ec8SAlexander V. Chernikov 	.init		= ta_init_ifidx,
258568394ec8SAlexander V. Chernikov 	.destroy	= ta_destroy_ifidx,
258668394ec8SAlexander V. Chernikov 	.prepare_add	= ta_prepare_add_ifidx,
258768394ec8SAlexander V. Chernikov 	.prepare_del	= ta_prepare_del_ifidx,
258868394ec8SAlexander V. Chernikov 	.add		= ta_add_ifidx,
258968394ec8SAlexander V. Chernikov 	.del		= ta_del_ifidx,
259068394ec8SAlexander V. Chernikov 	.flush_entry	= ta_flush_ifidx_entry,
259168394ec8SAlexander V. Chernikov 	.foreach	= ta_foreach_ifidx,
259268394ec8SAlexander V. Chernikov 	.dump_tentry	= ta_dump_ifidx_tentry,
259368394ec8SAlexander V. Chernikov 	.find_tentry	= ta_find_ifidx_tentry,
25945f379342SAlexander V. Chernikov 	.dump_tinfo	= ta_dump_ifidx_tinfo,
2595301290bcSAlexander V. Chernikov 	.need_modify	= ta_need_modify_ifidx,
259668394ec8SAlexander V. Chernikov 	.prepare_mod	= ta_prepare_mod_ifidx,
259768394ec8SAlexander V. Chernikov 	.fill_mod	= ta_fill_mod_ifidx,
259868394ec8SAlexander V. Chernikov 	.modify		= ta_modify_ifidx,
259968394ec8SAlexander V. Chernikov 	.flush_mod	= ta_flush_mod_ifidx,
260068394ec8SAlexander V. Chernikov 	.change_ti	= ta_change_ti_ifidx,
26019f7d47b0SAlexander V. Chernikov };
26029f7d47b0SAlexander V. Chernikov 
2603b23d5de9SAlexander V. Chernikov /*
2604b23d5de9SAlexander V. Chernikov  * Number array cmds.
2605b23d5de9SAlexander V. Chernikov  *
2606b23d5de9SAlexander V. Chernikov  * Implementation:
2607b23d5de9SAlexander V. Chernikov  *
2608b23d5de9SAlexander V. Chernikov  * Runtime part:
2609b23d5de9SAlexander V. Chernikov  * - sorted array of "struct numarray" pointed by ti->state.
2610b23d5de9SAlexander V. Chernikov  *   Array is allocated with rounding up to NUMARRAY_CHUNK.
2611b23d5de9SAlexander V. Chernikov  * - current array size is stored in ti->data
2612b23d5de9SAlexander V. Chernikov  *
2613b23d5de9SAlexander V. Chernikov  */
2614b23d5de9SAlexander V. Chernikov 
2615b23d5de9SAlexander V. Chernikov struct numarray {
2616b23d5de9SAlexander V. Chernikov 	uint32_t	number;
2617b23d5de9SAlexander V. Chernikov 	uint32_t	value;
2618b23d5de9SAlexander V. Chernikov };
2619b23d5de9SAlexander V. Chernikov 
2620b23d5de9SAlexander V. Chernikov struct numarray_cfg {
2621b23d5de9SAlexander V. Chernikov 	void	*main_ptr;
2622b23d5de9SAlexander V. Chernikov 	size_t	size;	/* Number of items allocated in array */
2623b23d5de9SAlexander V. Chernikov 	size_t	used;	/* Number of items _active_ now */
2624b23d5de9SAlexander V. Chernikov };
2625b23d5de9SAlexander V. Chernikov 
26260bce0c23SAlexander V. Chernikov struct ta_buf_numarray
26270bce0c23SAlexander V. Chernikov {
26280bce0c23SAlexander V. Chernikov 	struct numarray na;
26290bce0c23SAlexander V. Chernikov };
2630b23d5de9SAlexander V. Chernikov 
2631b23d5de9SAlexander V. Chernikov int compare_numarray(const void *k, const void *v);
26329fe15d06SAlexander V. Chernikov static struct numarray *numarray_find(struct table_info *ti, void *key);
26339fe15d06SAlexander V. Chernikov static int ta_lookup_numarray(struct table_info *ti, void *key,
26349fe15d06SAlexander V. Chernikov     uint32_t keylen, uint32_t *val);
26359fe15d06SAlexander V. Chernikov static int ta_init_numarray(struct ip_fw_chain *ch, void **ta_state,
26369fe15d06SAlexander V. Chernikov     struct table_info *ti, char *data, uint8_t tflags);
26379fe15d06SAlexander V. Chernikov static void ta_destroy_numarray(void *ta_state, struct table_info *ti);
26389fe15d06SAlexander V. Chernikov static void ta_dump_numarray_tinfo(void *ta_state, struct table_info *ti,
26399fe15d06SAlexander V. Chernikov     ipfw_ta_tinfo *tinfo);
26409fe15d06SAlexander V. Chernikov static int ta_prepare_add_numarray(struct ip_fw_chain *ch,
26419fe15d06SAlexander V. Chernikov     struct tentry_info *tei, void *ta_buf);
26429fe15d06SAlexander V. Chernikov static int ta_add_numarray(void *ta_state, struct table_info *ti,
26439fe15d06SAlexander V. Chernikov     struct tentry_info *tei, void *ta_buf, uint32_t *pnum);
26449fe15d06SAlexander V. Chernikov static int ta_del_numarray(void *ta_state, struct table_info *ti,
26459fe15d06SAlexander V. Chernikov     struct tentry_info *tei, void *ta_buf, uint32_t *pnum);
26469fe15d06SAlexander V. Chernikov static void ta_flush_numarray_entry(struct ip_fw_chain *ch,
26479fe15d06SAlexander V. Chernikov     struct tentry_info *tei, void *ta_buf);
26489fe15d06SAlexander V. Chernikov static int ta_need_modify_numarray(void *ta_state, struct table_info *ti,
26499fe15d06SAlexander V. Chernikov     uint32_t count, uint64_t *pflags);
26509fe15d06SAlexander V. Chernikov static int ta_prepare_mod_numarray(void *ta_buf, uint64_t *pflags);
26519fe15d06SAlexander V. Chernikov static int ta_fill_mod_numarray(void *ta_state, struct table_info *ti,
26529fe15d06SAlexander V. Chernikov     void *ta_buf, uint64_t *pflags);
26539fe15d06SAlexander V. Chernikov static void ta_modify_numarray(void *ta_state, struct table_info *ti,
26549fe15d06SAlexander V. Chernikov     void *ta_buf, uint64_t pflags);
26559fe15d06SAlexander V. Chernikov static void ta_flush_mod_numarray(void *ta_buf);
26569fe15d06SAlexander V. Chernikov static int ta_dump_numarray_tentry(void *ta_state, struct table_info *ti,
26579fe15d06SAlexander V. Chernikov     void *e, ipfw_obj_tentry *tent);
26589fe15d06SAlexander V. Chernikov static int ta_find_numarray_tentry(void *ta_state, struct table_info *ti,
26599fe15d06SAlexander V. Chernikov     ipfw_obj_tentry *tent);
26609fe15d06SAlexander V. Chernikov static void ta_foreach_numarray(void *ta_state, struct table_info *ti,
26619fe15d06SAlexander V. Chernikov     ta_foreach_f *f, void *arg);
2662b23d5de9SAlexander V. Chernikov 
2663b23d5de9SAlexander V. Chernikov int
2664b23d5de9SAlexander V. Chernikov compare_numarray(const void *k, const void *v)
2665b23d5de9SAlexander V. Chernikov {
2666d4e1b515SAlexander V. Chernikov 	const struct numarray *na;
2667b23d5de9SAlexander V. Chernikov 	uint32_t key;
2668b23d5de9SAlexander V. Chernikov 
2669d4e1b515SAlexander V. Chernikov 	key = *((const uint32_t *)k);
2670d4e1b515SAlexander V. Chernikov 	na = (const struct numarray *)v;
2671b23d5de9SAlexander V. Chernikov 
2672b23d5de9SAlexander V. Chernikov 	if (key < na->number)
2673b23d5de9SAlexander V. Chernikov 		return (-1);
2674b23d5de9SAlexander V. Chernikov 	else if (key > na->number)
2675b23d5de9SAlexander V. Chernikov 		return (1);
2676b23d5de9SAlexander V. Chernikov 
2677b23d5de9SAlexander V. Chernikov 	return (0);
2678b23d5de9SAlexander V. Chernikov }
2679b23d5de9SAlexander V. Chernikov 
2680b23d5de9SAlexander V. Chernikov static struct numarray *
2681b23d5de9SAlexander V. Chernikov numarray_find(struct table_info *ti, void *key)
2682b23d5de9SAlexander V. Chernikov {
2683b23d5de9SAlexander V. Chernikov 	struct numarray *ri;
2684b23d5de9SAlexander V. Chernikov 
2685b23d5de9SAlexander V. Chernikov 	ri = bsearch(key, ti->state, ti->data, sizeof(struct numarray),
2686b23d5de9SAlexander V. Chernikov 	    compare_ifidx);
2687b23d5de9SAlexander V. Chernikov 
2688b23d5de9SAlexander V. Chernikov 	return (ri);
2689b23d5de9SAlexander V. Chernikov }
2690b23d5de9SAlexander V. Chernikov 
2691b23d5de9SAlexander V. Chernikov static int
2692b23d5de9SAlexander V. Chernikov ta_lookup_numarray(struct table_info *ti, void *key, uint32_t keylen,
2693b23d5de9SAlexander V. Chernikov     uint32_t *val)
2694b23d5de9SAlexander V. Chernikov {
2695b23d5de9SAlexander V. Chernikov 	struct numarray *ri;
2696b23d5de9SAlexander V. Chernikov 
2697b23d5de9SAlexander V. Chernikov 	ri = numarray_find(ti, key);
2698b23d5de9SAlexander V. Chernikov 
2699b23d5de9SAlexander V. Chernikov 	if (ri != NULL) {
2700b23d5de9SAlexander V. Chernikov 		*val = ri->value;
2701b23d5de9SAlexander V. Chernikov 		return (1);
2702b23d5de9SAlexander V. Chernikov 	}
2703b23d5de9SAlexander V. Chernikov 
2704b23d5de9SAlexander V. Chernikov 	return (0);
2705b23d5de9SAlexander V. Chernikov }
2706b23d5de9SAlexander V. Chernikov 
2707b23d5de9SAlexander V. Chernikov static int
2708b23d5de9SAlexander V. Chernikov ta_init_numarray(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
2709914bffb6SAlexander V. Chernikov     char *data, uint8_t tflags)
2710b23d5de9SAlexander V. Chernikov {
2711b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2712b23d5de9SAlexander V. Chernikov 
2713b23d5de9SAlexander V. Chernikov 	cfg = malloc(sizeof(*cfg), M_IPFW, M_WAITOK | M_ZERO);
2714b23d5de9SAlexander V. Chernikov 
27150bce0c23SAlexander V. Chernikov 	cfg->size = 16;
2716b23d5de9SAlexander V. Chernikov 	cfg->main_ptr = malloc(sizeof(struct numarray) * cfg->size, M_IPFW,
2717b23d5de9SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
2718b23d5de9SAlexander V. Chernikov 
2719b23d5de9SAlexander V. Chernikov 	*ta_state = cfg;
2720b23d5de9SAlexander V. Chernikov 	ti->state = cfg->main_ptr;
2721b23d5de9SAlexander V. Chernikov 	ti->lookup = ta_lookup_numarray;
2722b23d5de9SAlexander V. Chernikov 
2723b23d5de9SAlexander V. Chernikov 	return (0);
2724b23d5de9SAlexander V. Chernikov }
2725b23d5de9SAlexander V. Chernikov 
2726b23d5de9SAlexander V. Chernikov /*
2727b23d5de9SAlexander V. Chernikov  * Destroys table @ti
2728b23d5de9SAlexander V. Chernikov  */
2729b23d5de9SAlexander V. Chernikov static void
2730b23d5de9SAlexander V. Chernikov ta_destroy_numarray(void *ta_state, struct table_info *ti)
2731b23d5de9SAlexander V. Chernikov {
2732b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2733b23d5de9SAlexander V. Chernikov 
2734b23d5de9SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2735b23d5de9SAlexander V. Chernikov 
2736b23d5de9SAlexander V. Chernikov 	if (cfg->main_ptr != NULL)
2737b23d5de9SAlexander V. Chernikov 		free(cfg->main_ptr, M_IPFW);
2738b23d5de9SAlexander V. Chernikov 
2739b23d5de9SAlexander V. Chernikov 	free(cfg, M_IPFW);
2740b23d5de9SAlexander V. Chernikov }
2741b23d5de9SAlexander V. Chernikov 
2742b23d5de9SAlexander V. Chernikov /*
27435f379342SAlexander V. Chernikov  * Provide algo-specific table info
27445f379342SAlexander V. Chernikov  */
27455f379342SAlexander V. Chernikov static void
27465f379342SAlexander V. Chernikov ta_dump_numarray_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo)
27475f379342SAlexander V. Chernikov {
27485f379342SAlexander V. Chernikov 	struct numarray_cfg *cfg;
27495f379342SAlexander V. Chernikov 
27505f379342SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
27515f379342SAlexander V. Chernikov 
27525f379342SAlexander V. Chernikov 	tinfo->taclass4 = IPFW_TACLASS_ARRAY;
27535f379342SAlexander V. Chernikov 	tinfo->size4 = cfg->size;
27545f379342SAlexander V. Chernikov 	tinfo->count4 = cfg->used;
27555f379342SAlexander V. Chernikov 	tinfo->itemsize4 = sizeof(struct numarray);
27565f379342SAlexander V. Chernikov }
27575f379342SAlexander V. Chernikov 
27585f379342SAlexander V. Chernikov /*
2759b23d5de9SAlexander V. Chernikov  * Prepare for addition/deletion to an array.
2760b23d5de9SAlexander V. Chernikov  */
2761b23d5de9SAlexander V. Chernikov static int
2762b23d5de9SAlexander V. Chernikov ta_prepare_add_numarray(struct ip_fw_chain *ch, struct tentry_info *tei,
2763b23d5de9SAlexander V. Chernikov     void *ta_buf)
2764b23d5de9SAlexander V. Chernikov {
2765b23d5de9SAlexander V. Chernikov 	struct ta_buf_numarray *tb;
2766b23d5de9SAlexander V. Chernikov 
2767b23d5de9SAlexander V. Chernikov 	tb = (struct ta_buf_numarray *)ta_buf;
2768b23d5de9SAlexander V. Chernikov 
2769b23d5de9SAlexander V. Chernikov 	tb->na.number = *((uint32_t *)tei->paddr);
2770b23d5de9SAlexander V. Chernikov 
2771b23d5de9SAlexander V. Chernikov 	return (0);
2772b23d5de9SAlexander V. Chernikov }
2773b23d5de9SAlexander V. Chernikov 
2774b23d5de9SAlexander V. Chernikov static int
2775b23d5de9SAlexander V. Chernikov ta_add_numarray(void *ta_state, struct table_info *ti, struct tentry_info *tei,
2776b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
2777b23d5de9SAlexander V. Chernikov {
2778b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2779b23d5de9SAlexander V. Chernikov 	struct ta_buf_numarray *tb;
2780b23d5de9SAlexander V. Chernikov 	struct numarray *ri;
2781*60a28b09SMateusz Guzik 	int res __diagused;
2782648e8380SAlexander V. Chernikov 	uint32_t value;
2783b23d5de9SAlexander V. Chernikov 
2784b23d5de9SAlexander V. Chernikov 	tb = (struct ta_buf_numarray *)ta_buf;
2785b23d5de9SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2786b23d5de9SAlexander V. Chernikov 
278713263632SAlexander V. Chernikov 	/* Read current value from @tei */
278813263632SAlexander V. Chernikov 	tb->na.value = tei->value;
278913263632SAlexander V. Chernikov 
2790b23d5de9SAlexander V. Chernikov 	ri = numarray_find(ti, &tb->na.number);
2791b23d5de9SAlexander V. Chernikov 
2792b23d5de9SAlexander V. Chernikov 	if (ri != NULL) {
2793b23d5de9SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
2794b23d5de9SAlexander V. Chernikov 			return (EEXIST);
2795b23d5de9SAlexander V. Chernikov 
2796648e8380SAlexander V. Chernikov 		/* Exchange values between ri and @tei */
2797648e8380SAlexander V. Chernikov 		value = ri->value;
2798648e8380SAlexander V. Chernikov 		ri->value = tei->value;
2799648e8380SAlexander V. Chernikov 		tei->value = value;
2800b23d5de9SAlexander V. Chernikov 		/* Indicate that update has happened instead of addition */
2801b23d5de9SAlexander V. Chernikov 		tei->flags |= TEI_FLAGS_UPDATED;
2802b23d5de9SAlexander V. Chernikov 		*pnum = 0;
2803b23d5de9SAlexander V. Chernikov 		return (0);
2804b23d5de9SAlexander V. Chernikov 	}
2805b23d5de9SAlexander V. Chernikov 
28064c0c07a5SAlexander V. Chernikov 	if ((tei->flags & TEI_FLAGS_DONTADD) != 0)
28074c0c07a5SAlexander V. Chernikov 		return (EFBIG);
28084c0c07a5SAlexander V. Chernikov 
2809b23d5de9SAlexander V. Chernikov 	res = badd(&tb->na.number, &tb->na, cfg->main_ptr, cfg->used,
2810b23d5de9SAlexander V. Chernikov 	    sizeof(struct numarray), compare_numarray);
2811b23d5de9SAlexander V. Chernikov 
2812b23d5de9SAlexander V. Chernikov 	KASSERT(res == 1, ("number %d already exists", tb->na.number));
2813b23d5de9SAlexander V. Chernikov 	cfg->used++;
2814b23d5de9SAlexander V. Chernikov 	ti->data = cfg->used;
2815b23d5de9SAlexander V. Chernikov 	*pnum = 1;
2816b23d5de9SAlexander V. Chernikov 
2817b23d5de9SAlexander V. Chernikov 	return (0);
2818b23d5de9SAlexander V. Chernikov }
2819b23d5de9SAlexander V. Chernikov 
2820b23d5de9SAlexander V. Chernikov /*
2821b23d5de9SAlexander V. Chernikov  * Remove key from both configuration list and
2822b23d5de9SAlexander V. Chernikov  * runtime array. Removed interface notification.
2823b23d5de9SAlexander V. Chernikov  */
2824b23d5de9SAlexander V. Chernikov static int
2825b23d5de9SAlexander V. Chernikov ta_del_numarray(void *ta_state, struct table_info *ti, struct tentry_info *tei,
2826b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
2827b23d5de9SAlexander V. Chernikov {
2828b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2829b23d5de9SAlexander V. Chernikov 	struct ta_buf_numarray *tb;
2830b23d5de9SAlexander V. Chernikov 	struct numarray *ri;
2831*60a28b09SMateusz Guzik 	int res __diagused;
2832b23d5de9SAlexander V. Chernikov 
2833b23d5de9SAlexander V. Chernikov 	tb = (struct ta_buf_numarray *)ta_buf;
2834b23d5de9SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2835b23d5de9SAlexander V. Chernikov 
2836b23d5de9SAlexander V. Chernikov 	ri = numarray_find(ti, &tb->na.number);
2837b23d5de9SAlexander V. Chernikov 	if (ri == NULL)
2838b23d5de9SAlexander V. Chernikov 		return (ENOENT);
2839b23d5de9SAlexander V. Chernikov 
2840648e8380SAlexander V. Chernikov 	tei->value = ri->value;
2841648e8380SAlexander V. Chernikov 
2842b23d5de9SAlexander V. Chernikov 	res = bdel(&tb->na.number, cfg->main_ptr, cfg->used,
2843b23d5de9SAlexander V. Chernikov 	    sizeof(struct numarray), compare_numarray);
2844b23d5de9SAlexander V. Chernikov 
2845b23d5de9SAlexander V. Chernikov 	KASSERT(res == 1, ("number %u does not exist", tb->na.number));
2846b23d5de9SAlexander V. Chernikov 	cfg->used--;
2847b23d5de9SAlexander V. Chernikov 	ti->data = cfg->used;
2848b23d5de9SAlexander V. Chernikov 	*pnum = 1;
2849b23d5de9SAlexander V. Chernikov 
2850b23d5de9SAlexander V. Chernikov 	return (0);
2851b23d5de9SAlexander V. Chernikov }
2852b23d5de9SAlexander V. Chernikov 
2853b23d5de9SAlexander V. Chernikov static void
2854b23d5de9SAlexander V. Chernikov ta_flush_numarray_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
2855b23d5de9SAlexander V. Chernikov     void *ta_buf)
2856b23d5de9SAlexander V. Chernikov {
2857b23d5de9SAlexander V. Chernikov 
28580bce0c23SAlexander V. Chernikov 	/* We don't have any state, do nothing */
2859b23d5de9SAlexander V. Chernikov }
2860b23d5de9SAlexander V. Chernikov 
2861b23d5de9SAlexander V. Chernikov /*
2862b23d5de9SAlexander V. Chernikov  * Table growing callbacks.
2863b23d5de9SAlexander V. Chernikov  */
2864b23d5de9SAlexander V. Chernikov 
2865b6ee846eSAlexander V. Chernikov static int
2866301290bcSAlexander V. Chernikov ta_need_modify_numarray(void *ta_state, struct table_info *ti, uint32_t count,
2867b6ee846eSAlexander V. Chernikov     uint64_t *pflags)
2868b6ee846eSAlexander V. Chernikov {
2869b6ee846eSAlexander V. Chernikov 	struct numarray_cfg *cfg;
28700bce0c23SAlexander V. Chernikov 	size_t size;
2871b6ee846eSAlexander V. Chernikov 
2872b6ee846eSAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2873b6ee846eSAlexander V. Chernikov 
28740bce0c23SAlexander V. Chernikov 	size = cfg->size;
28750bce0c23SAlexander V. Chernikov 	while (size < cfg->used + count)
28760bce0c23SAlexander V. Chernikov 		size *= 2;
28770bce0c23SAlexander V. Chernikov 
28780bce0c23SAlexander V. Chernikov 	if (size != cfg->size) {
28790bce0c23SAlexander V. Chernikov 		*pflags = size;
2880301290bcSAlexander V. Chernikov 		return (1);
2881b6ee846eSAlexander V. Chernikov 	}
2882b6ee846eSAlexander V. Chernikov 
2883301290bcSAlexander V. Chernikov 	return (0);
2884b6ee846eSAlexander V. Chernikov }
2885b6ee846eSAlexander V. Chernikov 
2886b23d5de9SAlexander V. Chernikov /*
2887b6ee846eSAlexander V. Chernikov  * Allocate new, larger runtime array.
2888b23d5de9SAlexander V. Chernikov  */
2889b23d5de9SAlexander V. Chernikov static int
2890b23d5de9SAlexander V. Chernikov ta_prepare_mod_numarray(void *ta_buf, uint64_t *pflags)
2891b23d5de9SAlexander V. Chernikov {
2892b23d5de9SAlexander V. Chernikov 	struct mod_item *mi;
2893b23d5de9SAlexander V. Chernikov 
2894b23d5de9SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
2895b23d5de9SAlexander V. Chernikov 
2896b23d5de9SAlexander V. Chernikov 	memset(mi, 0, sizeof(struct mod_item));
2897b23d5de9SAlexander V. Chernikov 	mi->size = *pflags;
2898b23d5de9SAlexander V. Chernikov 	mi->main_ptr = malloc(sizeof(struct numarray) * mi->size, M_IPFW,
2899b23d5de9SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
2900b23d5de9SAlexander V. Chernikov 
2901b23d5de9SAlexander V. Chernikov 	return (0);
2902b23d5de9SAlexander V. Chernikov }
2903b23d5de9SAlexander V. Chernikov 
2904b23d5de9SAlexander V. Chernikov /*
2905b23d5de9SAlexander V. Chernikov  * Copy data from old runtime array to new one.
2906b23d5de9SAlexander V. Chernikov  */
2907b23d5de9SAlexander V. Chernikov static int
2908b23d5de9SAlexander V. Chernikov ta_fill_mod_numarray(void *ta_state, struct table_info *ti, void *ta_buf,
2909b23d5de9SAlexander V. Chernikov     uint64_t *pflags)
2910b23d5de9SAlexander V. Chernikov {
2911b23d5de9SAlexander V. Chernikov 	struct mod_item *mi;
2912b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2913b23d5de9SAlexander V. Chernikov 
2914b23d5de9SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
2915b23d5de9SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2916b23d5de9SAlexander V. Chernikov 
2917b23d5de9SAlexander V. Chernikov 	/* Check if we still need to grow array */
2918b23d5de9SAlexander V. Chernikov 	if (cfg->size >= mi->size) {
2919b23d5de9SAlexander V. Chernikov 		*pflags = 0;
2920b23d5de9SAlexander V. Chernikov 		return (0);
2921b23d5de9SAlexander V. Chernikov 	}
2922b23d5de9SAlexander V. Chernikov 
2923b23d5de9SAlexander V. Chernikov 	memcpy(mi->main_ptr, cfg->main_ptr, cfg->used * sizeof(struct numarray));
2924b23d5de9SAlexander V. Chernikov 
2925b23d5de9SAlexander V. Chernikov 	return (0);
2926b23d5de9SAlexander V. Chernikov }
2927b23d5de9SAlexander V. Chernikov 
2928b23d5de9SAlexander V. Chernikov /*
2929b23d5de9SAlexander V. Chernikov  * Switch old & new arrays.
2930b23d5de9SAlexander V. Chernikov  */
2931301290bcSAlexander V. Chernikov static void
2932b23d5de9SAlexander V. Chernikov ta_modify_numarray(void *ta_state, struct table_info *ti, void *ta_buf,
2933b23d5de9SAlexander V. Chernikov     uint64_t pflags)
2934b23d5de9SAlexander V. Chernikov {
2935b23d5de9SAlexander V. Chernikov 	struct mod_item *mi;
2936b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2937b23d5de9SAlexander V. Chernikov 	void *old_ptr;
2938b23d5de9SAlexander V. Chernikov 
2939b23d5de9SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
2940b23d5de9SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2941b23d5de9SAlexander V. Chernikov 
2942b23d5de9SAlexander V. Chernikov 	old_ptr = cfg->main_ptr;
2943b23d5de9SAlexander V. Chernikov 	cfg->main_ptr = mi->main_ptr;
2944b23d5de9SAlexander V. Chernikov 	cfg->size = mi->size;
2945b23d5de9SAlexander V. Chernikov 	ti->state = cfg->main_ptr;
2946b23d5de9SAlexander V. Chernikov 
2947b23d5de9SAlexander V. Chernikov 	mi->main_ptr = old_ptr;
2948b23d5de9SAlexander V. Chernikov }
2949b23d5de9SAlexander V. Chernikov 
2950b23d5de9SAlexander V. Chernikov /*
2951b23d5de9SAlexander V. Chernikov  * Free unneded array.
2952b23d5de9SAlexander V. Chernikov  */
2953b23d5de9SAlexander V. Chernikov static void
2954b23d5de9SAlexander V. Chernikov ta_flush_mod_numarray(void *ta_buf)
2955b23d5de9SAlexander V. Chernikov {
2956b23d5de9SAlexander V. Chernikov 	struct mod_item *mi;
2957b23d5de9SAlexander V. Chernikov 
2958b23d5de9SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
2959b23d5de9SAlexander V. Chernikov 	if (mi->main_ptr != NULL)
2960b23d5de9SAlexander V. Chernikov 		free(mi->main_ptr, M_IPFW);
2961b23d5de9SAlexander V. Chernikov }
2962b23d5de9SAlexander V. Chernikov 
2963b23d5de9SAlexander V. Chernikov static int
2964b23d5de9SAlexander V. Chernikov ta_dump_numarray_tentry(void *ta_state, struct table_info *ti, void *e,
2965b23d5de9SAlexander V. Chernikov     ipfw_obj_tentry *tent)
2966b23d5de9SAlexander V. Chernikov {
2967b23d5de9SAlexander V. Chernikov 	struct numarray *na;
2968b23d5de9SAlexander V. Chernikov 
2969b23d5de9SAlexander V. Chernikov 	na = (struct numarray *)e;
2970b23d5de9SAlexander V. Chernikov 
2971b23d5de9SAlexander V. Chernikov 	tent->k.key = na->number;
29720cba2b28SAlexander V. Chernikov 	tent->v.kidx = na->value;
2973b23d5de9SAlexander V. Chernikov 
2974b23d5de9SAlexander V. Chernikov 	return (0);
2975b23d5de9SAlexander V. Chernikov }
2976b23d5de9SAlexander V. Chernikov 
2977b23d5de9SAlexander V. Chernikov static int
2978914bffb6SAlexander V. Chernikov ta_find_numarray_tentry(void *ta_state, struct table_info *ti,
2979914bffb6SAlexander V. Chernikov     ipfw_obj_tentry *tent)
2980b23d5de9SAlexander V. Chernikov {
2981b23d5de9SAlexander V. Chernikov 	struct numarray *ri;
2982b23d5de9SAlexander V. Chernikov 
2983914bffb6SAlexander V. Chernikov 	ri = numarray_find(ti, &tent->k.key);
2984b23d5de9SAlexander V. Chernikov 
2985b23d5de9SAlexander V. Chernikov 	if (ri != NULL) {
2986b23d5de9SAlexander V. Chernikov 		ta_dump_numarray_tentry(ta_state, ti, ri, tent);
2987b23d5de9SAlexander V. Chernikov 		return (0);
2988b23d5de9SAlexander V. Chernikov 	}
2989b23d5de9SAlexander V. Chernikov 
2990b23d5de9SAlexander V. Chernikov 	return (ENOENT);
2991b23d5de9SAlexander V. Chernikov }
2992b23d5de9SAlexander V. Chernikov 
2993b23d5de9SAlexander V. Chernikov static void
2994b23d5de9SAlexander V. Chernikov ta_foreach_numarray(void *ta_state, struct table_info *ti, ta_foreach_f *f,
2995b23d5de9SAlexander V. Chernikov     void *arg)
2996b23d5de9SAlexander V. Chernikov {
2997b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2998b23d5de9SAlexander V. Chernikov 	struct numarray *array;
2999b23d5de9SAlexander V. Chernikov 	int i;
3000b23d5de9SAlexander V. Chernikov 
3001b23d5de9SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
3002b23d5de9SAlexander V. Chernikov 	array = cfg->main_ptr;
3003b23d5de9SAlexander V. Chernikov 
3004b23d5de9SAlexander V. Chernikov 	for (i = 0; i < cfg->used; i++)
3005b23d5de9SAlexander V. Chernikov 		f(&array[i], arg);
3006b23d5de9SAlexander V. Chernikov }
3007b23d5de9SAlexander V. Chernikov 
3008b23d5de9SAlexander V. Chernikov struct table_algo number_array = {
3009b23d5de9SAlexander V. Chernikov 	.name		= "number:array",
3010b23d5de9SAlexander V. Chernikov 	.type		= IPFW_TABLE_NUMBER,
301157a1cf95SAlexander V. Chernikov 	.ta_buf_size	= sizeof(struct ta_buf_numarray),
3012b23d5de9SAlexander V. Chernikov 	.init		= ta_init_numarray,
3013b23d5de9SAlexander V. Chernikov 	.destroy	= ta_destroy_numarray,
3014b23d5de9SAlexander V. Chernikov 	.prepare_add	= ta_prepare_add_numarray,
3015b23d5de9SAlexander V. Chernikov 	.prepare_del	= ta_prepare_add_numarray,
3016b23d5de9SAlexander V. Chernikov 	.add		= ta_add_numarray,
3017b23d5de9SAlexander V. Chernikov 	.del		= ta_del_numarray,
3018b23d5de9SAlexander V. Chernikov 	.flush_entry	= ta_flush_numarray_entry,
3019b23d5de9SAlexander V. Chernikov 	.foreach	= ta_foreach_numarray,
3020b23d5de9SAlexander V. Chernikov 	.dump_tentry	= ta_dump_numarray_tentry,
3021b23d5de9SAlexander V. Chernikov 	.find_tentry	= ta_find_numarray_tentry,
30225f379342SAlexander V. Chernikov 	.dump_tinfo	= ta_dump_numarray_tinfo,
3023301290bcSAlexander V. Chernikov 	.need_modify	= ta_need_modify_numarray,
3024b23d5de9SAlexander V. Chernikov 	.prepare_mod	= ta_prepare_mod_numarray,
3025b23d5de9SAlexander V. Chernikov 	.fill_mod	= ta_fill_mod_numarray,
3026b23d5de9SAlexander V. Chernikov 	.modify		= ta_modify_numarray,
3027b23d5de9SAlexander V. Chernikov 	.flush_mod	= ta_flush_mod_numarray,
3028b23d5de9SAlexander V. Chernikov };
3029b23d5de9SAlexander V. Chernikov 
3030914bffb6SAlexander V. Chernikov /*
3031914bffb6SAlexander V. Chernikov  * flow:hash cmds
3032914bffb6SAlexander V. Chernikov  *
3033914bffb6SAlexander V. Chernikov  *
3034914bffb6SAlexander V. Chernikov  * ti->data:
3035914bffb6SAlexander V. Chernikov  * [inv.mask4][inv.mask6][log2hsize4][log2hsize6]
3036914bffb6SAlexander V. Chernikov  * [        8][        8[          8][         8]
3037914bffb6SAlexander V. Chernikov  *
3038914bffb6SAlexander V. Chernikov  * inv.mask4: 32 - mask
3039914bffb6SAlexander V. Chernikov  * inv.mask6:
3040914bffb6SAlexander V. Chernikov  * 1) _slow lookup: mask
3041914bffb6SAlexander V. Chernikov  * 2) _aligned: (128 - mask) / 8
3042914bffb6SAlexander V. Chernikov  * 3) _64: 8
3043914bffb6SAlexander V. Chernikov  *
3044914bffb6SAlexander V. Chernikov  *
3045914bffb6SAlexander V. Chernikov  * pflags:
3046b6ee846eSAlexander V. Chernikov  * [hsize4][hsize6]
3047b6ee846eSAlexander V. Chernikov  * [    16][    16]
3048914bffb6SAlexander V. Chernikov  */
3049914bffb6SAlexander V. Chernikov 
3050914bffb6SAlexander V. Chernikov struct fhashentry;
3051914bffb6SAlexander V. Chernikov 
3052914bffb6SAlexander V. Chernikov SLIST_HEAD(fhashbhead, fhashentry);
3053914bffb6SAlexander V. Chernikov 
3054914bffb6SAlexander V. Chernikov struct fhashentry {
3055914bffb6SAlexander V. Chernikov 	SLIST_ENTRY(fhashentry)	next;
3056914bffb6SAlexander V. Chernikov 	uint8_t		af;
3057914bffb6SAlexander V. Chernikov 	uint8_t		proto;
3058914bffb6SAlexander V. Chernikov 	uint16_t	spare0;
3059914bffb6SAlexander V. Chernikov 	uint16_t	dport;
3060914bffb6SAlexander V. Chernikov 	uint16_t	sport;
3061914bffb6SAlexander V. Chernikov 	uint32_t	value;
3062914bffb6SAlexander V. Chernikov 	uint32_t	spare1;
3063914bffb6SAlexander V. Chernikov };
3064914bffb6SAlexander V. Chernikov 
3065914bffb6SAlexander V. Chernikov struct fhashentry4 {
3066914bffb6SAlexander V. Chernikov 	struct fhashentry	e;
3067914bffb6SAlexander V. Chernikov 	struct in_addr		dip;
3068914bffb6SAlexander V. Chernikov 	struct in_addr		sip;
3069914bffb6SAlexander V. Chernikov };
3070914bffb6SAlexander V. Chernikov 
3071914bffb6SAlexander V. Chernikov struct fhashentry6 {
3072914bffb6SAlexander V. Chernikov 	struct fhashentry	e;
3073914bffb6SAlexander V. Chernikov 	struct in6_addr		dip6;
3074914bffb6SAlexander V. Chernikov 	struct in6_addr		sip6;
3075914bffb6SAlexander V. Chernikov };
3076914bffb6SAlexander V. Chernikov 
3077914bffb6SAlexander V. Chernikov struct fhash_cfg {
3078914bffb6SAlexander V. Chernikov 	struct fhashbhead	*head;
3079914bffb6SAlexander V. Chernikov 	size_t			size;
3080914bffb6SAlexander V. Chernikov 	size_t			items;
3081914bffb6SAlexander V. Chernikov 	struct fhashentry4	fe4;
3082914bffb6SAlexander V. Chernikov 	struct fhashentry6	fe6;
3083914bffb6SAlexander V. Chernikov };
3084914bffb6SAlexander V. Chernikov 
30853fe2ef91SAlexander V. Chernikov struct ta_buf_fhash {
30860bce0c23SAlexander V. Chernikov 	void	*ent_ptr;
30870bce0c23SAlexander V. Chernikov 	struct fhashentry6 fe6;
30880bce0c23SAlexander V. Chernikov };
30890bce0c23SAlexander V. Chernikov 
30909fe15d06SAlexander V. Chernikov static __inline int cmp_flow_ent(struct fhashentry *a,
30919fe15d06SAlexander V. Chernikov     struct fhashentry *b, size_t sz);
30929fe15d06SAlexander V. Chernikov static __inline uint32_t hash_flow4(struct fhashentry4 *f, int hsize);
30939fe15d06SAlexander V. Chernikov static __inline uint32_t hash_flow6(struct fhashentry6 *f, int hsize);
30949fe15d06SAlexander V. Chernikov static uint32_t hash_flow_ent(struct fhashentry *ent, uint32_t size);
30959fe15d06SAlexander V. Chernikov static int ta_lookup_fhash(struct table_info *ti, void *key, uint32_t keylen,
30969fe15d06SAlexander V. Chernikov     uint32_t *val);
30979fe15d06SAlexander V. Chernikov static int ta_init_fhash(struct ip_fw_chain *ch, void **ta_state,
30989fe15d06SAlexander V. Chernikov struct table_info *ti, char *data, uint8_t tflags);
30999fe15d06SAlexander V. Chernikov static void ta_destroy_fhash(void *ta_state, struct table_info *ti);
31009fe15d06SAlexander V. Chernikov static void ta_dump_fhash_tinfo(void *ta_state, struct table_info *ti,
31019fe15d06SAlexander V. Chernikov     ipfw_ta_tinfo *tinfo);
31029fe15d06SAlexander V. Chernikov static int ta_dump_fhash_tentry(void *ta_state, struct table_info *ti,
31039fe15d06SAlexander V. Chernikov     void *e, ipfw_obj_tentry *tent);
31049fe15d06SAlexander V. Chernikov static int tei_to_fhash_ent(struct tentry_info *tei, struct fhashentry *ent);
31059fe15d06SAlexander V. Chernikov static int ta_find_fhash_tentry(void *ta_state, struct table_info *ti,
31069fe15d06SAlexander V. Chernikov     ipfw_obj_tentry *tent);
31079fe15d06SAlexander V. Chernikov static void ta_foreach_fhash(void *ta_state, struct table_info *ti,
31089fe15d06SAlexander V. Chernikov     ta_foreach_f *f, void *arg);
31099fe15d06SAlexander V. Chernikov static int ta_prepare_add_fhash(struct ip_fw_chain *ch,
31109fe15d06SAlexander V. Chernikov     struct tentry_info *tei, void *ta_buf);
31119fe15d06SAlexander V. Chernikov static int ta_add_fhash(void *ta_state, struct table_info *ti,
31129fe15d06SAlexander V. Chernikov     struct tentry_info *tei, void *ta_buf, uint32_t *pnum);
31139fe15d06SAlexander V. Chernikov static int ta_prepare_del_fhash(struct ip_fw_chain *ch, struct tentry_info *tei,
31149fe15d06SAlexander V. Chernikov     void *ta_buf);
31159fe15d06SAlexander V. Chernikov static int ta_del_fhash(void *ta_state, struct table_info *ti,
31169fe15d06SAlexander V. Chernikov     struct tentry_info *tei, void *ta_buf, uint32_t *pnum);
31179fe15d06SAlexander V. Chernikov static void ta_flush_fhash_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
31189fe15d06SAlexander V. Chernikov     void *ta_buf);
31199fe15d06SAlexander V. Chernikov static int ta_need_modify_fhash(void *ta_state, struct table_info *ti,
31209fe15d06SAlexander V. Chernikov     uint32_t count, uint64_t *pflags);
31219fe15d06SAlexander V. Chernikov static int ta_prepare_mod_fhash(void *ta_buf, uint64_t *pflags);
31229fe15d06SAlexander V. Chernikov static int ta_fill_mod_fhash(void *ta_state, struct table_info *ti,
31239fe15d06SAlexander V. Chernikov     void *ta_buf, uint64_t *pflags);
31249fe15d06SAlexander V. Chernikov static void ta_modify_fhash(void *ta_state, struct table_info *ti, void *ta_buf,
31259fe15d06SAlexander V. Chernikov     uint64_t pflags);
31269fe15d06SAlexander V. Chernikov static void ta_flush_mod_fhash(void *ta_buf);
31279fe15d06SAlexander V. Chernikov 
3128914bffb6SAlexander V. Chernikov static __inline int
3129914bffb6SAlexander V. Chernikov cmp_flow_ent(struct fhashentry *a, struct fhashentry *b, size_t sz)
3130914bffb6SAlexander V. Chernikov {
3131914bffb6SAlexander V. Chernikov 	uint64_t *ka, *kb;
3132914bffb6SAlexander V. Chernikov 
3133914bffb6SAlexander V. Chernikov 	ka = (uint64_t *)(&a->next + 1);
3134914bffb6SAlexander V. Chernikov 	kb = (uint64_t *)(&b->next + 1);
3135914bffb6SAlexander V. Chernikov 
3136914bffb6SAlexander V. Chernikov 	if (*ka == *kb && (memcmp(a + 1, b + 1, sz) == 0))
3137914bffb6SAlexander V. Chernikov 		return (1);
3138914bffb6SAlexander V. Chernikov 
3139914bffb6SAlexander V. Chernikov 	return (0);
3140914bffb6SAlexander V. Chernikov }
3141914bffb6SAlexander V. Chernikov 
3142914bffb6SAlexander V. Chernikov static __inline uint32_t
3143914bffb6SAlexander V. Chernikov hash_flow4(struct fhashentry4 *f, int hsize)
3144914bffb6SAlexander V. Chernikov {
3145914bffb6SAlexander V. Chernikov 	uint32_t i;
3146914bffb6SAlexander V. Chernikov 
3147914bffb6SAlexander V. Chernikov 	i = (f->dip.s_addr) ^ (f->sip.s_addr) ^ (f->e.dport) ^ (f->e.sport);
3148914bffb6SAlexander V. Chernikov 
3149914bffb6SAlexander V. Chernikov 	return (i % (hsize - 1));
3150914bffb6SAlexander V. Chernikov }
3151914bffb6SAlexander V. Chernikov 
3152914bffb6SAlexander V. Chernikov static __inline uint32_t
3153914bffb6SAlexander V. Chernikov hash_flow6(struct fhashentry6 *f, int hsize)
3154914bffb6SAlexander V. Chernikov {
3155914bffb6SAlexander V. Chernikov 	uint32_t i;
3156914bffb6SAlexander V. Chernikov 
3157914bffb6SAlexander V. Chernikov 	i = (f->dip6.__u6_addr.__u6_addr32[2]) ^
3158914bffb6SAlexander V. Chernikov 	    (f->dip6.__u6_addr.__u6_addr32[3]) ^
3159914bffb6SAlexander V. Chernikov 	    (f->sip6.__u6_addr.__u6_addr32[2]) ^
3160914bffb6SAlexander V. Chernikov 	    (f->sip6.__u6_addr.__u6_addr32[3]) ^
3161914bffb6SAlexander V. Chernikov 	    (f->e.dport) ^ (f->e.sport);
3162914bffb6SAlexander V. Chernikov 
3163914bffb6SAlexander V. Chernikov 	return (i % (hsize - 1));
3164914bffb6SAlexander V. Chernikov }
3165914bffb6SAlexander V. Chernikov 
3166914bffb6SAlexander V. Chernikov static uint32_t
3167914bffb6SAlexander V. Chernikov hash_flow_ent(struct fhashentry *ent, uint32_t size)
3168914bffb6SAlexander V. Chernikov {
3169914bffb6SAlexander V. Chernikov 	uint32_t hash;
3170914bffb6SAlexander V. Chernikov 
3171914bffb6SAlexander V. Chernikov 	if (ent->af == AF_INET) {
3172914bffb6SAlexander V. Chernikov 		hash = hash_flow4((struct fhashentry4 *)ent, size);
3173914bffb6SAlexander V. Chernikov 	} else {
3174914bffb6SAlexander V. Chernikov 		hash = hash_flow6((struct fhashentry6 *)ent, size);
3175914bffb6SAlexander V. Chernikov 	}
3176914bffb6SAlexander V. Chernikov 
3177914bffb6SAlexander V. Chernikov 	return (hash);
3178914bffb6SAlexander V. Chernikov }
3179914bffb6SAlexander V. Chernikov 
3180914bffb6SAlexander V. Chernikov static int
3181914bffb6SAlexander V. Chernikov ta_lookup_fhash(struct table_info *ti, void *key, uint32_t keylen,
3182914bffb6SAlexander V. Chernikov     uint32_t *val)
3183914bffb6SAlexander V. Chernikov {
3184914bffb6SAlexander V. Chernikov 	struct fhashbhead *head;
3185914bffb6SAlexander V. Chernikov 	struct fhashentry *ent;
3186914bffb6SAlexander V. Chernikov 	struct fhashentry4 *m4;
3187914bffb6SAlexander V. Chernikov 	struct ipfw_flow_id *id;
318847cb0632SEugene Grosbein 	uint32_t hsize;
318947cb0632SEugene Grosbein 	uint16_t hash;
3190914bffb6SAlexander V. Chernikov 
3191914bffb6SAlexander V. Chernikov 	id = (struct ipfw_flow_id *)key;
3192914bffb6SAlexander V. Chernikov 	head = (struct fhashbhead *)ti->state;
3193914bffb6SAlexander V. Chernikov 	hsize = ti->data;
3194914bffb6SAlexander V. Chernikov 	m4 = (struct fhashentry4 *)ti->xstate;
3195914bffb6SAlexander V. Chernikov 
3196914bffb6SAlexander V. Chernikov 	if (id->addr_type == 4) {
3197914bffb6SAlexander V. Chernikov 		struct fhashentry4 f;
3198914bffb6SAlexander V. Chernikov 
3199914bffb6SAlexander V. Chernikov 		/* Copy hash mask */
3200914bffb6SAlexander V. Chernikov 		f = *m4;
3201914bffb6SAlexander V. Chernikov 
3202914bffb6SAlexander V. Chernikov 		f.dip.s_addr &= id->dst_ip;
3203914bffb6SAlexander V. Chernikov 		f.sip.s_addr &= id->src_ip;
3204914bffb6SAlexander V. Chernikov 		f.e.dport &= id->dst_port;
3205914bffb6SAlexander V. Chernikov 		f.e.sport &= id->src_port;
3206914bffb6SAlexander V. Chernikov 		f.e.proto &= id->proto;
3207914bffb6SAlexander V. Chernikov 		hash = hash_flow4(&f, hsize);
3208914bffb6SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
3209914bffb6SAlexander V. Chernikov 			if (cmp_flow_ent(ent, &f.e, 2 * 4) != 0) {
3210914bffb6SAlexander V. Chernikov 				*val = ent->value;
3211914bffb6SAlexander V. Chernikov 				return (1);
3212914bffb6SAlexander V. Chernikov 			}
3213914bffb6SAlexander V. Chernikov 		}
3214914bffb6SAlexander V. Chernikov 	} else if (id->addr_type == 6) {
3215914bffb6SAlexander V. Chernikov 		struct fhashentry6 f;
3216914bffb6SAlexander V. Chernikov 		uint64_t *fp, *idp;
3217914bffb6SAlexander V. Chernikov 
3218914bffb6SAlexander V. Chernikov 		/* Copy hash mask */
3219914bffb6SAlexander V. Chernikov 		f = *((struct fhashentry6 *)(m4 + 1));
3220914bffb6SAlexander V. Chernikov 
3221914bffb6SAlexander V. Chernikov 		/* Handle lack of __u6_addr.__u6_addr64 */
3222914bffb6SAlexander V. Chernikov 		fp = (uint64_t *)&f.dip6;
3223914bffb6SAlexander V. Chernikov 		idp = (uint64_t *)&id->dst_ip6;
3224914bffb6SAlexander V. Chernikov 		/* src IPv6 is stored after dst IPv6 */
3225914bffb6SAlexander V. Chernikov 		*fp++ &= *idp++;
3226914bffb6SAlexander V. Chernikov 		*fp++ &= *idp++;
3227914bffb6SAlexander V. Chernikov 		*fp++ &= *idp++;
3228914bffb6SAlexander V. Chernikov 		*fp &= *idp;
3229914bffb6SAlexander V. Chernikov 		f.e.dport &= id->dst_port;
3230914bffb6SAlexander V. Chernikov 		f.e.sport &= id->src_port;
3231914bffb6SAlexander V. Chernikov 		f.e.proto &= id->proto;
3232914bffb6SAlexander V. Chernikov 		hash = hash_flow6(&f, hsize);
3233914bffb6SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
3234914bffb6SAlexander V. Chernikov 			if (cmp_flow_ent(ent, &f.e, 2 * 16) != 0) {
3235914bffb6SAlexander V. Chernikov 				*val = ent->value;
3236914bffb6SAlexander V. Chernikov 				return (1);
3237914bffb6SAlexander V. Chernikov 			}
3238914bffb6SAlexander V. Chernikov 		}
3239914bffb6SAlexander V. Chernikov 	}
3240914bffb6SAlexander V. Chernikov 
3241914bffb6SAlexander V. Chernikov 	return (0);
3242914bffb6SAlexander V. Chernikov }
3243914bffb6SAlexander V. Chernikov 
3244914bffb6SAlexander V. Chernikov /*
3245914bffb6SAlexander V. Chernikov  * New table.
3246914bffb6SAlexander V. Chernikov  */
3247914bffb6SAlexander V. Chernikov static int
3248914bffb6SAlexander V. Chernikov ta_init_fhash(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
3249914bffb6SAlexander V. Chernikov     char *data, uint8_t tflags)
3250914bffb6SAlexander V. Chernikov {
3251914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
3252914bffb6SAlexander V. Chernikov 	struct fhashentry4 *fe4;
3253914bffb6SAlexander V. Chernikov 	struct fhashentry6 *fe6;
3254d821d364SPedro F. Giffuni 	u_int i;
3255914bffb6SAlexander V. Chernikov 
3256914bffb6SAlexander V. Chernikov 	cfg = malloc(sizeof(struct fhash_cfg), M_IPFW, M_WAITOK | M_ZERO);
3257914bffb6SAlexander V. Chernikov 
3258914bffb6SAlexander V. Chernikov 	cfg->size = 512;
3259914bffb6SAlexander V. Chernikov 
3260914bffb6SAlexander V. Chernikov 	cfg->head = malloc(sizeof(struct fhashbhead) * cfg->size, M_IPFW,
3261914bffb6SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
3262914bffb6SAlexander V. Chernikov 	for (i = 0; i < cfg->size; i++)
3263914bffb6SAlexander V. Chernikov 		SLIST_INIT(&cfg->head[i]);
3264914bffb6SAlexander V. Chernikov 
3265914bffb6SAlexander V. Chernikov 	/* Fill in fe masks based on @tflags */
3266914bffb6SAlexander V. Chernikov 	fe4 = &cfg->fe4;
3267914bffb6SAlexander V. Chernikov 	fe6 = &cfg->fe6;
3268914bffb6SAlexander V. Chernikov 	if (tflags & IPFW_TFFLAG_SRCIP) {
3269914bffb6SAlexander V. Chernikov 		memset(&fe4->sip, 0xFF, sizeof(fe4->sip));
3270914bffb6SAlexander V. Chernikov 		memset(&fe6->sip6, 0xFF, sizeof(fe6->sip6));
3271914bffb6SAlexander V. Chernikov 	}
3272914bffb6SAlexander V. Chernikov 	if (tflags & IPFW_TFFLAG_DSTIP) {
3273914bffb6SAlexander V. Chernikov 		memset(&fe4->dip, 0xFF, sizeof(fe4->dip));
3274914bffb6SAlexander V. Chernikov 		memset(&fe6->dip6, 0xFF, sizeof(fe6->dip6));
3275914bffb6SAlexander V. Chernikov 	}
3276914bffb6SAlexander V. Chernikov 	if (tflags & IPFW_TFFLAG_SRCPORT) {
3277914bffb6SAlexander V. Chernikov 		memset(&fe4->e.sport, 0xFF, sizeof(fe4->e.sport));
3278914bffb6SAlexander V. Chernikov 		memset(&fe6->e.sport, 0xFF, sizeof(fe6->e.sport));
3279914bffb6SAlexander V. Chernikov 	}
3280914bffb6SAlexander V. Chernikov 	if (tflags & IPFW_TFFLAG_DSTPORT) {
3281914bffb6SAlexander V. Chernikov 		memset(&fe4->e.dport, 0xFF, sizeof(fe4->e.dport));
3282914bffb6SAlexander V. Chernikov 		memset(&fe6->e.dport, 0xFF, sizeof(fe6->e.dport));
3283914bffb6SAlexander V. Chernikov 	}
3284914bffb6SAlexander V. Chernikov 	if (tflags & IPFW_TFFLAG_PROTO) {
3285914bffb6SAlexander V. Chernikov 		memset(&fe4->e.proto, 0xFF, sizeof(fe4->e.proto));
3286914bffb6SAlexander V. Chernikov 		memset(&fe6->e.proto, 0xFF, sizeof(fe6->e.proto));
3287914bffb6SAlexander V. Chernikov 	}
3288914bffb6SAlexander V. Chernikov 
3289914bffb6SAlexander V. Chernikov 	fe4->e.af = AF_INET;
3290914bffb6SAlexander V. Chernikov 	fe6->e.af = AF_INET6;
3291914bffb6SAlexander V. Chernikov 
3292914bffb6SAlexander V. Chernikov 	*ta_state = cfg;
3293914bffb6SAlexander V. Chernikov 	ti->state = cfg->head;
3294914bffb6SAlexander V. Chernikov 	ti->xstate = &cfg->fe4;
3295914bffb6SAlexander V. Chernikov 	ti->data = cfg->size;
3296914bffb6SAlexander V. Chernikov 	ti->lookup = ta_lookup_fhash;
3297914bffb6SAlexander V. Chernikov 
3298914bffb6SAlexander V. Chernikov 	return (0);
3299914bffb6SAlexander V. Chernikov }
3300914bffb6SAlexander V. Chernikov 
3301914bffb6SAlexander V. Chernikov static void
3302914bffb6SAlexander V. Chernikov ta_destroy_fhash(void *ta_state, struct table_info *ti)
3303914bffb6SAlexander V. Chernikov {
3304914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
3305914bffb6SAlexander V. Chernikov 	struct fhashentry *ent, *ent_next;
3306914bffb6SAlexander V. Chernikov 	int i;
3307914bffb6SAlexander V. Chernikov 
3308914bffb6SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
3309914bffb6SAlexander V. Chernikov 
3310914bffb6SAlexander V. Chernikov 	for (i = 0; i < cfg->size; i++)
3311914bffb6SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &cfg->head[i], next, ent_next)
3312914bffb6SAlexander V. Chernikov 			free(ent, M_IPFW_TBL);
3313914bffb6SAlexander V. Chernikov 
3314914bffb6SAlexander V. Chernikov 	free(cfg->head, M_IPFW);
3315914bffb6SAlexander V. Chernikov 	free(cfg, M_IPFW);
3316914bffb6SAlexander V. Chernikov }
3317914bffb6SAlexander V. Chernikov 
33185f379342SAlexander V. Chernikov /*
33195f379342SAlexander V. Chernikov  * Provide algo-specific table info
33205f379342SAlexander V. Chernikov  */
33215f379342SAlexander V. Chernikov static void
33225f379342SAlexander V. Chernikov ta_dump_fhash_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo)
33235f379342SAlexander V. Chernikov {
33245f379342SAlexander V. Chernikov 	struct fhash_cfg *cfg;
33255f379342SAlexander V. Chernikov 
33265f379342SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
33275f379342SAlexander V. Chernikov 
33285f379342SAlexander V. Chernikov 	tinfo->flags = IPFW_TATFLAGS_AFITEM;
33295f379342SAlexander V. Chernikov 	tinfo->taclass4 = IPFW_TACLASS_HASH;
33305f379342SAlexander V. Chernikov 	tinfo->size4 = cfg->size;
33315f379342SAlexander V. Chernikov 	tinfo->count4 = cfg->items;
33325f379342SAlexander V. Chernikov 	tinfo->itemsize4 = sizeof(struct fhashentry4);
33335f379342SAlexander V. Chernikov 	tinfo->itemsize6 = sizeof(struct fhashentry6);
33345f379342SAlexander V. Chernikov }
33355f379342SAlexander V. Chernikov 
3336914bffb6SAlexander V. Chernikov static int
3337914bffb6SAlexander V. Chernikov ta_dump_fhash_tentry(void *ta_state, struct table_info *ti, void *e,
3338914bffb6SAlexander V. Chernikov     ipfw_obj_tentry *tent)
3339914bffb6SAlexander V. Chernikov {
3340914bffb6SAlexander V. Chernikov 	struct fhashentry *ent;
3341914bffb6SAlexander V. Chernikov 	struct fhashentry4 *fe4;
33429fe15d06SAlexander V. Chernikov #ifdef INET6
3343914bffb6SAlexander V. Chernikov 	struct fhashentry6 *fe6;
33449fe15d06SAlexander V. Chernikov #endif
3345914bffb6SAlexander V. Chernikov 	struct tflow_entry *tfe;
3346914bffb6SAlexander V. Chernikov 
3347914bffb6SAlexander V. Chernikov 	ent = (struct fhashentry *)e;
3348914bffb6SAlexander V. Chernikov 	tfe = &tent->k.flow;
3349914bffb6SAlexander V. Chernikov 
3350914bffb6SAlexander V. Chernikov 	tfe->af = ent->af;
3351914bffb6SAlexander V. Chernikov 	tfe->proto = ent->proto;
3352914bffb6SAlexander V. Chernikov 	tfe->dport = htons(ent->dport);
3353914bffb6SAlexander V. Chernikov 	tfe->sport = htons(ent->sport);
33540cba2b28SAlexander V. Chernikov 	tent->v.kidx = ent->value;
3355914bffb6SAlexander V. Chernikov 	tent->subtype = ent->af;
3356914bffb6SAlexander V. Chernikov 
3357914bffb6SAlexander V. Chernikov 	if (ent->af == AF_INET) {
3358914bffb6SAlexander V. Chernikov 		fe4 = (struct fhashentry4 *)ent;
3359914bffb6SAlexander V. Chernikov 		tfe->a.a4.sip.s_addr = htonl(fe4->sip.s_addr);
3360914bffb6SAlexander V. Chernikov 		tfe->a.a4.dip.s_addr = htonl(fe4->dip.s_addr);
3361914bffb6SAlexander V. Chernikov 		tent->masklen = 32;
3362914bffb6SAlexander V. Chernikov #ifdef INET6
3363914bffb6SAlexander V. Chernikov 	} else {
3364914bffb6SAlexander V. Chernikov 		fe6 = (struct fhashentry6 *)ent;
3365914bffb6SAlexander V. Chernikov 		tfe->a.a6.sip6 = fe6->sip6;
3366914bffb6SAlexander V. Chernikov 		tfe->a.a6.dip6 = fe6->dip6;
3367914bffb6SAlexander V. Chernikov 		tent->masklen = 128;
3368914bffb6SAlexander V. Chernikov #endif
3369914bffb6SAlexander V. Chernikov 	}
3370914bffb6SAlexander V. Chernikov 
3371914bffb6SAlexander V. Chernikov 	return (0);
3372914bffb6SAlexander V. Chernikov }
3373914bffb6SAlexander V. Chernikov 
3374914bffb6SAlexander V. Chernikov static int
3375914bffb6SAlexander V. Chernikov tei_to_fhash_ent(struct tentry_info *tei, struct fhashentry *ent)
3376914bffb6SAlexander V. Chernikov {
3377d699ee2dSAlexander V. Chernikov #ifdef INET
3378914bffb6SAlexander V. Chernikov 	struct fhashentry4 *fe4;
3379d699ee2dSAlexander V. Chernikov #endif
3380d699ee2dSAlexander V. Chernikov #ifdef INET6
3381914bffb6SAlexander V. Chernikov 	struct fhashentry6 *fe6;
3382d699ee2dSAlexander V. Chernikov #endif
3383914bffb6SAlexander V. Chernikov 	struct tflow_entry *tfe;
3384914bffb6SAlexander V. Chernikov 
3385914bffb6SAlexander V. Chernikov 	tfe = (struct tflow_entry *)tei->paddr;
3386914bffb6SAlexander V. Chernikov 
3387914bffb6SAlexander V. Chernikov 	ent->af = tei->subtype;
3388914bffb6SAlexander V. Chernikov 	ent->proto = tfe->proto;
3389914bffb6SAlexander V. Chernikov 	ent->dport = ntohs(tfe->dport);
3390914bffb6SAlexander V. Chernikov 	ent->sport = ntohs(tfe->sport);
3391914bffb6SAlexander V. Chernikov 
3392914bffb6SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
3393914bffb6SAlexander V. Chernikov #ifdef INET
3394914bffb6SAlexander V. Chernikov 		fe4 = (struct fhashentry4 *)ent;
3395914bffb6SAlexander V. Chernikov 		fe4->sip.s_addr = ntohl(tfe->a.a4.sip.s_addr);
3396914bffb6SAlexander V. Chernikov 		fe4->dip.s_addr = ntohl(tfe->a.a4.dip.s_addr);
3397914bffb6SAlexander V. Chernikov #endif
3398914bffb6SAlexander V. Chernikov #ifdef INET6
3399914bffb6SAlexander V. Chernikov 	} else if (tei->subtype == AF_INET6) {
3400914bffb6SAlexander V. Chernikov 		fe6 = (struct fhashentry6 *)ent;
3401914bffb6SAlexander V. Chernikov 		fe6->sip6 = tfe->a.a6.sip6;
3402914bffb6SAlexander V. Chernikov 		fe6->dip6 = tfe->a.a6.dip6;
3403914bffb6SAlexander V. Chernikov #endif
3404914bffb6SAlexander V. Chernikov 	} else {
3405914bffb6SAlexander V. Chernikov 		/* Unknown CIDR type */
3406914bffb6SAlexander V. Chernikov 		return (EINVAL);
3407914bffb6SAlexander V. Chernikov 	}
3408914bffb6SAlexander V. Chernikov 
3409914bffb6SAlexander V. Chernikov 	return (0);
3410914bffb6SAlexander V. Chernikov }
3411914bffb6SAlexander V. Chernikov 
3412914bffb6SAlexander V. Chernikov static int
3413914bffb6SAlexander V. Chernikov ta_find_fhash_tentry(void *ta_state, struct table_info *ti,
3414914bffb6SAlexander V. Chernikov     ipfw_obj_tentry *tent)
3415914bffb6SAlexander V. Chernikov {
3416914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
3417914bffb6SAlexander V. Chernikov 	struct fhashbhead *head;
3418914bffb6SAlexander V. Chernikov 	struct fhashentry *ent, *tmp;
3419914bffb6SAlexander V. Chernikov 	struct fhashentry6 fe6;
3420914bffb6SAlexander V. Chernikov 	struct tentry_info tei;
3421914bffb6SAlexander V. Chernikov 	int error;
3422914bffb6SAlexander V. Chernikov 	uint32_t hash;
3423914bffb6SAlexander V. Chernikov 	size_t sz;
3424914bffb6SAlexander V. Chernikov 
3425914bffb6SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
3426914bffb6SAlexander V. Chernikov 
3427914bffb6SAlexander V. Chernikov 	ent = &fe6.e;
3428914bffb6SAlexander V. Chernikov 
3429914bffb6SAlexander V. Chernikov 	memset(&fe6, 0, sizeof(fe6));
3430914bffb6SAlexander V. Chernikov 	memset(&tei, 0, sizeof(tei));
3431914bffb6SAlexander V. Chernikov 
3432914bffb6SAlexander V. Chernikov 	tei.paddr = &tent->k.flow;
3433914bffb6SAlexander V. Chernikov 	tei.subtype = tent->subtype;
3434914bffb6SAlexander V. Chernikov 
3435914bffb6SAlexander V. Chernikov 	if ((error = tei_to_fhash_ent(&tei, ent)) != 0)
3436914bffb6SAlexander V. Chernikov 		return (error);
3437914bffb6SAlexander V. Chernikov 
3438914bffb6SAlexander V. Chernikov 	head = cfg->head;
3439914bffb6SAlexander V. Chernikov 	hash = hash_flow_ent(ent, cfg->size);
3440914bffb6SAlexander V. Chernikov 
3441914bffb6SAlexander V. Chernikov 	if (tei.subtype == AF_INET)
3442914bffb6SAlexander V. Chernikov 		sz = 2 * sizeof(struct in_addr);
3443914bffb6SAlexander V. Chernikov 	else
3444914bffb6SAlexander V. Chernikov 		sz = 2 * sizeof(struct in6_addr);
3445914bffb6SAlexander V. Chernikov 
3446914bffb6SAlexander V. Chernikov 	/* Check for existence */
3447914bffb6SAlexander V. Chernikov 	SLIST_FOREACH(tmp, &head[hash], next) {
3448914bffb6SAlexander V. Chernikov 		if (cmp_flow_ent(tmp, ent, sz) != 0) {
3449914bffb6SAlexander V. Chernikov 			ta_dump_fhash_tentry(ta_state, ti, tmp, tent);
3450914bffb6SAlexander V. Chernikov 			return (0);
3451914bffb6SAlexander V. Chernikov 		}
3452914bffb6SAlexander V. Chernikov 	}
3453914bffb6SAlexander V. Chernikov 
3454914bffb6SAlexander V. Chernikov 	return (ENOENT);
3455914bffb6SAlexander V. Chernikov }
3456914bffb6SAlexander V. Chernikov 
3457914bffb6SAlexander V. Chernikov static void
3458914bffb6SAlexander V. Chernikov ta_foreach_fhash(void *ta_state, struct table_info *ti, ta_foreach_f *f,
3459914bffb6SAlexander V. Chernikov     void *arg)
3460914bffb6SAlexander V. Chernikov {
3461914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
3462914bffb6SAlexander V. Chernikov 	struct fhashentry *ent, *ent_next;
3463914bffb6SAlexander V. Chernikov 	int i;
3464914bffb6SAlexander V. Chernikov 
3465914bffb6SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
3466914bffb6SAlexander V. Chernikov 
3467914bffb6SAlexander V. Chernikov 	for (i = 0; i < cfg->size; i++)
3468914bffb6SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &cfg->head[i], next, ent_next)
3469914bffb6SAlexander V. Chernikov 			f(ent, arg);
3470914bffb6SAlexander V. Chernikov }
3471914bffb6SAlexander V. Chernikov 
3472914bffb6SAlexander V. Chernikov static int
3473914bffb6SAlexander V. Chernikov ta_prepare_add_fhash(struct ip_fw_chain *ch, struct tentry_info *tei,
3474914bffb6SAlexander V. Chernikov     void *ta_buf)
3475914bffb6SAlexander V. Chernikov {
3476914bffb6SAlexander V. Chernikov 	struct ta_buf_fhash *tb;
3477914bffb6SAlexander V. Chernikov 	struct fhashentry *ent;
3478914bffb6SAlexander V. Chernikov 	size_t sz;
3479914bffb6SAlexander V. Chernikov 	int error;
3480914bffb6SAlexander V. Chernikov 
3481914bffb6SAlexander V. Chernikov 	tb = (struct ta_buf_fhash *)ta_buf;
3482914bffb6SAlexander V. Chernikov 
3483914bffb6SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
3484914bffb6SAlexander V. Chernikov 		sz = sizeof(struct fhashentry4);
3485914bffb6SAlexander V. Chernikov 	else if (tei->subtype == AF_INET6)
3486914bffb6SAlexander V. Chernikov 		sz = sizeof(struct fhashentry6);
3487914bffb6SAlexander V. Chernikov 	else
3488914bffb6SAlexander V. Chernikov 		return (EINVAL);
3489914bffb6SAlexander V. Chernikov 
3490914bffb6SAlexander V. Chernikov 	ent = malloc(sz, M_IPFW_TBL, M_WAITOK | M_ZERO);
3491914bffb6SAlexander V. Chernikov 
3492914bffb6SAlexander V. Chernikov 	error = tei_to_fhash_ent(tei, ent);
3493914bffb6SAlexander V. Chernikov 	if (error != 0) {
3494914bffb6SAlexander V. Chernikov 		free(ent, M_IPFW_TBL);
3495914bffb6SAlexander V. Chernikov 		return (error);
3496914bffb6SAlexander V. Chernikov 	}
3497914bffb6SAlexander V. Chernikov 	tb->ent_ptr = ent;
3498914bffb6SAlexander V. Chernikov 
3499914bffb6SAlexander V. Chernikov 	return (0);
3500914bffb6SAlexander V. Chernikov }
3501914bffb6SAlexander V. Chernikov 
3502914bffb6SAlexander V. Chernikov static int
3503914bffb6SAlexander V. Chernikov ta_add_fhash(void *ta_state, struct table_info *ti, struct tentry_info *tei,
3504b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
3505914bffb6SAlexander V. Chernikov {
3506914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
3507914bffb6SAlexander V. Chernikov 	struct fhashbhead *head;
3508914bffb6SAlexander V. Chernikov 	struct fhashentry *ent, *tmp;
3509914bffb6SAlexander V. Chernikov 	struct ta_buf_fhash *tb;
3510914bffb6SAlexander V. Chernikov 	int exists;
3511648e8380SAlexander V. Chernikov 	uint32_t hash, value;
3512914bffb6SAlexander V. Chernikov 	size_t sz;
3513914bffb6SAlexander V. Chernikov 
3514914bffb6SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
3515914bffb6SAlexander V. Chernikov 	tb = (struct ta_buf_fhash *)ta_buf;
3516914bffb6SAlexander V. Chernikov 	ent = (struct fhashentry *)tb->ent_ptr;
3517914bffb6SAlexander V. Chernikov 	exists = 0;
3518914bffb6SAlexander V. Chernikov 
351913263632SAlexander V. Chernikov 	/* Read current value from @tei */
352013263632SAlexander V. Chernikov 	ent->value = tei->value;
352113263632SAlexander V. Chernikov 
3522914bffb6SAlexander V. Chernikov 	head = cfg->head;
3523914bffb6SAlexander V. Chernikov 	hash = hash_flow_ent(ent, cfg->size);
3524914bffb6SAlexander V. Chernikov 
3525914bffb6SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
3526914bffb6SAlexander V. Chernikov 		sz = 2 * sizeof(struct in_addr);
3527914bffb6SAlexander V. Chernikov 	else
3528914bffb6SAlexander V. Chernikov 		sz = 2 * sizeof(struct in6_addr);
3529914bffb6SAlexander V. Chernikov 
3530914bffb6SAlexander V. Chernikov 	/* Check for existence */
3531914bffb6SAlexander V. Chernikov 	SLIST_FOREACH(tmp, &head[hash], next) {
3532914bffb6SAlexander V. Chernikov 		if (cmp_flow_ent(tmp, ent, sz) != 0) {
3533914bffb6SAlexander V. Chernikov 			exists = 1;
3534914bffb6SAlexander V. Chernikov 			break;
3535914bffb6SAlexander V. Chernikov 		}
3536914bffb6SAlexander V. Chernikov 	}
3537914bffb6SAlexander V. Chernikov 
3538914bffb6SAlexander V. Chernikov 	if (exists == 1) {
3539914bffb6SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
3540914bffb6SAlexander V. Chernikov 			return (EEXIST);
3541914bffb6SAlexander V. Chernikov 		/* Record already exists. Update value if we're asked to */
3542648e8380SAlexander V. Chernikov 		/* Exchange values between tmp and @tei */
3543648e8380SAlexander V. Chernikov 		value = tmp->value;
3544914bffb6SAlexander V. Chernikov 		tmp->value = tei->value;
3545648e8380SAlexander V. Chernikov 		tei->value = value;
3546914bffb6SAlexander V. Chernikov 		/* Indicate that update has happened instead of addition */
3547914bffb6SAlexander V. Chernikov 		tei->flags |= TEI_FLAGS_UPDATED;
3548914bffb6SAlexander V. Chernikov 		*pnum = 0;
3549914bffb6SAlexander V. Chernikov 	} else {
35504c0c07a5SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_DONTADD) != 0)
35514c0c07a5SAlexander V. Chernikov 			return (EFBIG);
35524c0c07a5SAlexander V. Chernikov 
3553914bffb6SAlexander V. Chernikov 		SLIST_INSERT_HEAD(&head[hash], ent, next);
3554914bffb6SAlexander V. Chernikov 		tb->ent_ptr = NULL;
3555914bffb6SAlexander V. Chernikov 		*pnum = 1;
3556914bffb6SAlexander V. Chernikov 
3557914bffb6SAlexander V. Chernikov 		/* Update counters and check if we need to grow hash */
3558914bffb6SAlexander V. Chernikov 		cfg->items++;
3559914bffb6SAlexander V. Chernikov 	}
3560914bffb6SAlexander V. Chernikov 
3561914bffb6SAlexander V. Chernikov 	return (0);
3562914bffb6SAlexander V. Chernikov }
3563914bffb6SAlexander V. Chernikov 
3564914bffb6SAlexander V. Chernikov static int
3565914bffb6SAlexander V. Chernikov ta_prepare_del_fhash(struct ip_fw_chain *ch, struct tentry_info *tei,
3566914bffb6SAlexander V. Chernikov     void *ta_buf)
3567914bffb6SAlexander V. Chernikov {
3568914bffb6SAlexander V. Chernikov 	struct ta_buf_fhash *tb;
3569914bffb6SAlexander V. Chernikov 
3570914bffb6SAlexander V. Chernikov 	tb = (struct ta_buf_fhash *)ta_buf;
3571914bffb6SAlexander V. Chernikov 
3572914bffb6SAlexander V. Chernikov 	return (tei_to_fhash_ent(tei, &tb->fe6.e));
3573914bffb6SAlexander V. Chernikov }
3574914bffb6SAlexander V. Chernikov 
3575914bffb6SAlexander V. Chernikov static int
3576914bffb6SAlexander V. Chernikov ta_del_fhash(void *ta_state, struct table_info *ti, struct tentry_info *tei,
3577b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
3578914bffb6SAlexander V. Chernikov {
3579914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
3580914bffb6SAlexander V. Chernikov 	struct fhashbhead *head;
3581914bffb6SAlexander V. Chernikov 	struct fhashentry *ent, *tmp;
3582914bffb6SAlexander V. Chernikov 	struct ta_buf_fhash *tb;
3583914bffb6SAlexander V. Chernikov 	uint32_t hash;
3584914bffb6SAlexander V. Chernikov 	size_t sz;
3585914bffb6SAlexander V. Chernikov 
3586914bffb6SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
3587914bffb6SAlexander V. Chernikov 	tb = (struct ta_buf_fhash *)ta_buf;
3588914bffb6SAlexander V. Chernikov 	ent = &tb->fe6.e;
3589914bffb6SAlexander V. Chernikov 
3590914bffb6SAlexander V. Chernikov 	head = cfg->head;
3591914bffb6SAlexander V. Chernikov 	hash = hash_flow_ent(ent, cfg->size);
3592914bffb6SAlexander V. Chernikov 
3593914bffb6SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
3594914bffb6SAlexander V. Chernikov 		sz = 2 * sizeof(struct in_addr);
3595914bffb6SAlexander V. Chernikov 	else
3596914bffb6SAlexander V. Chernikov 		sz = 2 * sizeof(struct in6_addr);
3597914bffb6SAlexander V. Chernikov 
3598914bffb6SAlexander V. Chernikov 	/* Check for existence */
3599914bffb6SAlexander V. Chernikov 	SLIST_FOREACH(tmp, &head[hash], next) {
3600648e8380SAlexander V. Chernikov 		if (cmp_flow_ent(tmp, ent, sz) == 0)
3601648e8380SAlexander V. Chernikov 			continue;
3602648e8380SAlexander V. Chernikov 
3603914bffb6SAlexander V. Chernikov 		SLIST_REMOVE(&head[hash], tmp, fhashentry, next);
3604648e8380SAlexander V. Chernikov 		tei->value = tmp->value;
3605914bffb6SAlexander V. Chernikov 		*pnum = 1;
3606914bffb6SAlexander V. Chernikov 		cfg->items--;
3607648e8380SAlexander V. Chernikov 		tb->ent_ptr = tmp;
3608914bffb6SAlexander V. Chernikov 		return (0);
3609914bffb6SAlexander V. Chernikov 	}
3610914bffb6SAlexander V. Chernikov 
3611914bffb6SAlexander V. Chernikov 	return (ENOENT);
3612914bffb6SAlexander V. Chernikov }
3613914bffb6SAlexander V. Chernikov 
3614914bffb6SAlexander V. Chernikov static void
3615914bffb6SAlexander V. Chernikov ta_flush_fhash_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
3616914bffb6SAlexander V. Chernikov     void *ta_buf)
3617914bffb6SAlexander V. Chernikov {
3618914bffb6SAlexander V. Chernikov 	struct ta_buf_fhash *tb;
3619914bffb6SAlexander V. Chernikov 
3620914bffb6SAlexander V. Chernikov 	tb = (struct ta_buf_fhash *)ta_buf;
3621914bffb6SAlexander V. Chernikov 
3622914bffb6SAlexander V. Chernikov 	if (tb->ent_ptr != NULL)
3623914bffb6SAlexander V. Chernikov 		free(tb->ent_ptr, M_IPFW_TBL);
3624914bffb6SAlexander V. Chernikov }
3625914bffb6SAlexander V. Chernikov 
3626914bffb6SAlexander V. Chernikov /*
3627914bffb6SAlexander V. Chernikov  * Hash growing callbacks.
3628914bffb6SAlexander V. Chernikov  */
3629914bffb6SAlexander V. Chernikov 
3630b6ee846eSAlexander V. Chernikov static int
3631301290bcSAlexander V. Chernikov ta_need_modify_fhash(void *ta_state, struct table_info *ti, uint32_t count,
3632b6ee846eSAlexander V. Chernikov     uint64_t *pflags)
3633b6ee846eSAlexander V. Chernikov {
3634b6ee846eSAlexander V. Chernikov 	struct fhash_cfg *cfg;
3635b6ee846eSAlexander V. Chernikov 
3636b6ee846eSAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
3637b6ee846eSAlexander V. Chernikov 
3638b6ee846eSAlexander V. Chernikov 	if (cfg->items > cfg->size && cfg->size < 65536) {
3639b6ee846eSAlexander V. Chernikov 		*pflags = cfg->size * 2;
3640301290bcSAlexander V. Chernikov 		return (1);
3641b6ee846eSAlexander V. Chernikov 	}
3642b6ee846eSAlexander V. Chernikov 
3643301290bcSAlexander V. Chernikov 	return (0);
3644b6ee846eSAlexander V. Chernikov }
3645b6ee846eSAlexander V. Chernikov 
3646914bffb6SAlexander V. Chernikov /*
3647914bffb6SAlexander V. Chernikov  * Allocate new, larger fhash.
3648914bffb6SAlexander V. Chernikov  */
3649914bffb6SAlexander V. Chernikov static int
3650914bffb6SAlexander V. Chernikov ta_prepare_mod_fhash(void *ta_buf, uint64_t *pflags)
3651914bffb6SAlexander V. Chernikov {
3652914bffb6SAlexander V. Chernikov 	struct mod_item *mi;
3653914bffb6SAlexander V. Chernikov 	struct fhashbhead *head;
3654d821d364SPedro F. Giffuni 	u_int i;
3655914bffb6SAlexander V. Chernikov 
3656914bffb6SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
3657914bffb6SAlexander V. Chernikov 
3658914bffb6SAlexander V. Chernikov 	memset(mi, 0, sizeof(struct mod_item));
3659914bffb6SAlexander V. Chernikov 	mi->size = *pflags;
3660914bffb6SAlexander V. Chernikov 	head = malloc(sizeof(struct fhashbhead) * mi->size, M_IPFW,
3661914bffb6SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
3662914bffb6SAlexander V. Chernikov 	for (i = 0; i < mi->size; i++)
3663914bffb6SAlexander V. Chernikov 		SLIST_INIT(&head[i]);
3664914bffb6SAlexander V. Chernikov 
3665914bffb6SAlexander V. Chernikov 	mi->main_ptr = head;
3666914bffb6SAlexander V. Chernikov 
3667914bffb6SAlexander V. Chernikov 	return (0);
3668914bffb6SAlexander V. Chernikov }
3669914bffb6SAlexander V. Chernikov 
3670914bffb6SAlexander V. Chernikov /*
3671914bffb6SAlexander V. Chernikov  * Copy data from old runtime array to new one.
3672914bffb6SAlexander V. Chernikov  */
3673914bffb6SAlexander V. Chernikov static int
3674914bffb6SAlexander V. Chernikov ta_fill_mod_fhash(void *ta_state, struct table_info *ti, void *ta_buf,
3675914bffb6SAlexander V. Chernikov     uint64_t *pflags)
3676914bffb6SAlexander V. Chernikov {
3677914bffb6SAlexander V. Chernikov 
3678914bffb6SAlexander V. Chernikov 	/* In is not possible to do rehash if we're not holidng WLOCK. */
3679914bffb6SAlexander V. Chernikov 	return (0);
3680914bffb6SAlexander V. Chernikov }
3681914bffb6SAlexander V. Chernikov 
3682914bffb6SAlexander V. Chernikov /*
3683914bffb6SAlexander V. Chernikov  * Switch old & new arrays.
3684914bffb6SAlexander V. Chernikov  */
3685301290bcSAlexander V. Chernikov static void
3686914bffb6SAlexander V. Chernikov ta_modify_fhash(void *ta_state, struct table_info *ti, void *ta_buf,
3687914bffb6SAlexander V. Chernikov     uint64_t pflags)
3688914bffb6SAlexander V. Chernikov {
3689914bffb6SAlexander V. Chernikov 	struct mod_item *mi;
3690914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
3691914bffb6SAlexander V. Chernikov 	struct fhashbhead *old_head, *new_head;
3692914bffb6SAlexander V. Chernikov 	struct fhashentry *ent, *ent_next;
3693914bffb6SAlexander V. Chernikov 	int i;
3694914bffb6SAlexander V. Chernikov 	uint32_t nhash;
3695914bffb6SAlexander V. Chernikov 	size_t old_size;
3696914bffb6SAlexander V. Chernikov 
3697914bffb6SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
3698914bffb6SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
3699914bffb6SAlexander V. Chernikov 
3700914bffb6SAlexander V. Chernikov 	old_size = cfg->size;
3701914bffb6SAlexander V. Chernikov 	old_head = ti->state;
3702914bffb6SAlexander V. Chernikov 
3703914bffb6SAlexander V. Chernikov 	new_head = (struct fhashbhead *)mi->main_ptr;
3704914bffb6SAlexander V. Chernikov 	for (i = 0; i < old_size; i++) {
3705914bffb6SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &old_head[i], next, ent_next) {
3706914bffb6SAlexander V. Chernikov 			nhash = hash_flow_ent(ent, mi->size);
3707914bffb6SAlexander V. Chernikov 			SLIST_INSERT_HEAD(&new_head[nhash], ent, next);
3708914bffb6SAlexander V. Chernikov 		}
3709914bffb6SAlexander V. Chernikov 	}
3710914bffb6SAlexander V. Chernikov 
3711914bffb6SAlexander V. Chernikov 	ti->state = new_head;
3712914bffb6SAlexander V. Chernikov 	ti->data = mi->size;
3713914bffb6SAlexander V. Chernikov 	cfg->head = new_head;
3714914bffb6SAlexander V. Chernikov 	cfg->size = mi->size;
3715914bffb6SAlexander V. Chernikov 
3716914bffb6SAlexander V. Chernikov 	mi->main_ptr = old_head;
3717914bffb6SAlexander V. Chernikov }
3718914bffb6SAlexander V. Chernikov 
3719914bffb6SAlexander V. Chernikov /*
3720914bffb6SAlexander V. Chernikov  * Free unneded array.
3721914bffb6SAlexander V. Chernikov  */
3722914bffb6SAlexander V. Chernikov static void
3723914bffb6SAlexander V. Chernikov ta_flush_mod_fhash(void *ta_buf)
3724914bffb6SAlexander V. Chernikov {
3725914bffb6SAlexander V. Chernikov 	struct mod_item *mi;
3726914bffb6SAlexander V. Chernikov 
3727914bffb6SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
3728914bffb6SAlexander V. Chernikov 	if (mi->main_ptr != NULL)
3729914bffb6SAlexander V. Chernikov 		free(mi->main_ptr, M_IPFW);
3730914bffb6SAlexander V. Chernikov }
3731914bffb6SAlexander V. Chernikov 
3732914bffb6SAlexander V. Chernikov struct table_algo flow_hash = {
3733914bffb6SAlexander V. Chernikov 	.name		= "flow:hash",
3734914bffb6SAlexander V. Chernikov 	.type		= IPFW_TABLE_FLOW,
373557a1cf95SAlexander V. Chernikov 	.flags		= TA_FLAG_DEFAULT,
373657a1cf95SAlexander V. Chernikov 	.ta_buf_size	= sizeof(struct ta_buf_fhash),
3737914bffb6SAlexander V. Chernikov 	.init		= ta_init_fhash,
3738914bffb6SAlexander V. Chernikov 	.destroy	= ta_destroy_fhash,
3739914bffb6SAlexander V. Chernikov 	.prepare_add	= ta_prepare_add_fhash,
3740914bffb6SAlexander V. Chernikov 	.prepare_del	= ta_prepare_del_fhash,
3741914bffb6SAlexander V. Chernikov 	.add		= ta_add_fhash,
3742914bffb6SAlexander V. Chernikov 	.del		= ta_del_fhash,
3743914bffb6SAlexander V. Chernikov 	.flush_entry	= ta_flush_fhash_entry,
3744914bffb6SAlexander V. Chernikov 	.foreach	= ta_foreach_fhash,
3745914bffb6SAlexander V. Chernikov 	.dump_tentry	= ta_dump_fhash_tentry,
3746914bffb6SAlexander V. Chernikov 	.find_tentry	= ta_find_fhash_tentry,
37475f379342SAlexander V. Chernikov 	.dump_tinfo	= ta_dump_fhash_tinfo,
3748301290bcSAlexander V. Chernikov 	.need_modify	= ta_need_modify_fhash,
3749914bffb6SAlexander V. Chernikov 	.prepare_mod	= ta_prepare_mod_fhash,
3750914bffb6SAlexander V. Chernikov 	.fill_mod	= ta_fill_mod_fhash,
3751914bffb6SAlexander V. Chernikov 	.modify		= ta_modify_fhash,
3752914bffb6SAlexander V. Chernikov 	.flush_mod	= ta_flush_mod_fhash,
3753914bffb6SAlexander V. Chernikov };
37543fe2ef91SAlexander V. Chernikov 
3755d3b00c08SAlexander V. Chernikov /*
3756d3b00c08SAlexander V. Chernikov  * Kernel fibs bindings.
3757d3b00c08SAlexander V. Chernikov  *
3758d3b00c08SAlexander V. Chernikov  * Implementation:
3759d3b00c08SAlexander V. Chernikov  *
3760d3b00c08SAlexander V. Chernikov  * Runtime part:
3761d3b00c08SAlexander V. Chernikov  * - fully relies on route API
3762d3b00c08SAlexander V. Chernikov  * - fib number is stored in ti->data
3763d3b00c08SAlexander V. Chernikov  *
3764d3b00c08SAlexander V. Chernikov  */
3765d3b00c08SAlexander V. Chernikov 
37669fe15d06SAlexander V. Chernikov static int ta_lookup_kfib(struct table_info *ti, void *key, uint32_t keylen,
37679fe15d06SAlexander V. Chernikov     uint32_t *val);
37689fe15d06SAlexander V. Chernikov static int kfib_parse_opts(int *pfib, char *data);
37699fe15d06SAlexander V. Chernikov static void ta_print_kfib_config(void *ta_state, struct table_info *ti,
37709fe15d06SAlexander V. Chernikov     char *buf, size_t bufsize);
37719fe15d06SAlexander V. Chernikov static int ta_init_kfib(struct ip_fw_chain *ch, void **ta_state,
37729fe15d06SAlexander V. Chernikov     struct table_info *ti, char *data, uint8_t tflags);
37739fe15d06SAlexander V. Chernikov static void ta_destroy_kfib(void *ta_state, struct table_info *ti);
37749fe15d06SAlexander V. Chernikov static void ta_dump_kfib_tinfo(void *ta_state, struct table_info *ti,
37759fe15d06SAlexander V. Chernikov     ipfw_ta_tinfo *tinfo);
37769fe15d06SAlexander V. Chernikov static int ta_dump_kfib_tentry(void *ta_state, struct table_info *ti, void *e,
37779fe15d06SAlexander V. Chernikov     ipfw_obj_tentry *tent);
37784451d893SAlexander V. Chernikov static int ta_dump_kfib_tentry_int(int familt, const struct rtentry *rt,
37794451d893SAlexander V. Chernikov     ipfw_obj_tentry *tent);
37809fe15d06SAlexander V. Chernikov static int ta_find_kfib_tentry(void *ta_state, struct table_info *ti,
37819fe15d06SAlexander V. Chernikov     ipfw_obj_tentry *tent);
37829fe15d06SAlexander V. Chernikov static void ta_foreach_kfib(void *ta_state, struct table_info *ti,
37839fe15d06SAlexander V. Chernikov     ta_foreach_f *f, void *arg);
37849fe15d06SAlexander V. Chernikov 
3785d3b00c08SAlexander V. Chernikov static int
3786d3b00c08SAlexander V. Chernikov ta_lookup_kfib(struct table_info *ti, void *key, uint32_t keylen,
3787d3b00c08SAlexander V. Chernikov     uint32_t *val)
3788d3b00c08SAlexander V. Chernikov {
3789004d3e30SAlexander V. Chernikov #ifdef INET
3790004d3e30SAlexander V. Chernikov 	struct in_addr in;
3791004d3e30SAlexander V. Chernikov #endif
3792004d3e30SAlexander V. Chernikov 	int error;
3793d3b00c08SAlexander V. Chernikov 
379489fc126aSAlexander V. Chernikov 	error = ENOENT;
3795004d3e30SAlexander V. Chernikov #ifdef INET
3796004d3e30SAlexander V. Chernikov 	if (keylen == 4) {
3797004d3e30SAlexander V. Chernikov 		in.s_addr = *(in_addr_t *)key;
37986ad7446cSAlexander V. Chernikov 		NET_EPOCH_ASSERT();
37996ad7446cSAlexander V. Chernikov 		error = fib4_lookup(ti->data, in, 0, NHR_NONE, 0) != NULL;
3800004d3e30SAlexander V. Chernikov 	}
3801004d3e30SAlexander V. Chernikov #endif
3802004d3e30SAlexander V. Chernikov #ifdef INET6
3803004d3e30SAlexander V. Chernikov 	if (keylen == 6)
38046ad7446cSAlexander V. Chernikov 		error = fib6_lookup(ti->data, (struct in6_addr *)key,
38056ad7446cSAlexander V. Chernikov 		    0, NHR_NONE, 0) != NULL;
3806004d3e30SAlexander V. Chernikov #endif
3807004d3e30SAlexander V. Chernikov 
3808004d3e30SAlexander V. Chernikov 	if (error != 0)
3809d3b00c08SAlexander V. Chernikov 		return (0);
3810d3b00c08SAlexander V. Chernikov 
3811d3b00c08SAlexander V. Chernikov 	*val = 0;
3812d3b00c08SAlexander V. Chernikov 
3813d3b00c08SAlexander V. Chernikov 	return (1);
3814d3b00c08SAlexander V. Chernikov }
3815d3b00c08SAlexander V. Chernikov 
3816d3b00c08SAlexander V. Chernikov /* Parse 'fib=%d' */
3817d3b00c08SAlexander V. Chernikov static int
3818d3b00c08SAlexander V. Chernikov kfib_parse_opts(int *pfib, char *data)
3819d3b00c08SAlexander V. Chernikov {
3820d3b00c08SAlexander V. Chernikov 	char *pdel, *pend, *s;
3821d3b00c08SAlexander V. Chernikov 	int fibnum;
3822d3b00c08SAlexander V. Chernikov 
3823d3b00c08SAlexander V. Chernikov 	if (data == NULL)
3824d3b00c08SAlexander V. Chernikov 		return (0);
3825d3b00c08SAlexander V. Chernikov 	if ((pdel = strchr(data, ' ')) == NULL)
3826d3b00c08SAlexander V. Chernikov 		return (0);
3827d3b00c08SAlexander V. Chernikov 	while (*pdel == ' ')
3828d3b00c08SAlexander V. Chernikov 		pdel++;
3829d3b00c08SAlexander V. Chernikov 	if (strncmp(pdel, "fib=", 4) != 0)
3830d3b00c08SAlexander V. Chernikov 		return (EINVAL);
3831d3b00c08SAlexander V. Chernikov 	if ((s = strchr(pdel, ' ')) != NULL)
3832d3b00c08SAlexander V. Chernikov 		*s++ = '\0';
3833d3b00c08SAlexander V. Chernikov 
3834d3b00c08SAlexander V. Chernikov 	pdel += 4;
3835d3b00c08SAlexander V. Chernikov 	/* Need \d+ */
3836d3b00c08SAlexander V. Chernikov 	fibnum = strtol(pdel, &pend, 10);
3837d3b00c08SAlexander V. Chernikov 	if (*pend != '\0')
3838d3b00c08SAlexander V. Chernikov 		return (EINVAL);
3839d3b00c08SAlexander V. Chernikov 
3840d3b00c08SAlexander V. Chernikov 	*pfib = fibnum;
3841d3b00c08SAlexander V. Chernikov 
3842d3b00c08SAlexander V. Chernikov 	return (0);
3843d3b00c08SAlexander V. Chernikov }
3844d3b00c08SAlexander V. Chernikov 
3845d3b00c08SAlexander V. Chernikov static void
3846d3b00c08SAlexander V. Chernikov ta_print_kfib_config(void *ta_state, struct table_info *ti, char *buf,
3847d3b00c08SAlexander V. Chernikov     size_t bufsize)
3848d3b00c08SAlexander V. Chernikov {
3849d3b00c08SAlexander V. Chernikov 
3850d3b00c08SAlexander V. Chernikov 	if (ti->data != 0)
3851c21034b7SAlexander V. Chernikov 		snprintf(buf, bufsize, "%s fib=%lu", "addr:kfib", ti->data);
3852d3b00c08SAlexander V. Chernikov 	else
3853c21034b7SAlexander V. Chernikov 		snprintf(buf, bufsize, "%s", "addr:kfib");
3854d3b00c08SAlexander V. Chernikov }
3855d3b00c08SAlexander V. Chernikov 
3856d3b00c08SAlexander V. Chernikov static int
3857d3b00c08SAlexander V. Chernikov ta_init_kfib(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
3858d3b00c08SAlexander V. Chernikov     char *data, uint8_t tflags)
3859d3b00c08SAlexander V. Chernikov {
3860d3b00c08SAlexander V. Chernikov 	int error, fibnum;
3861d3b00c08SAlexander V. Chernikov 
3862d3b00c08SAlexander V. Chernikov 	fibnum = 0;
3863d3b00c08SAlexander V. Chernikov 	if ((error = kfib_parse_opts(&fibnum, data)) != 0)
3864d3b00c08SAlexander V. Chernikov 		return (error);
3865d3b00c08SAlexander V. Chernikov 
3866d3b00c08SAlexander V. Chernikov 	if (fibnum >= rt_numfibs)
3867d3b00c08SAlexander V. Chernikov 		return (E2BIG);
3868d3b00c08SAlexander V. Chernikov 
3869d3b00c08SAlexander V. Chernikov 	ti->data = fibnum;
3870d3b00c08SAlexander V. Chernikov 	ti->lookup = ta_lookup_kfib;
3871d3b00c08SAlexander V. Chernikov 
3872d3b00c08SAlexander V. Chernikov 	return (0);
3873d3b00c08SAlexander V. Chernikov }
3874d3b00c08SAlexander V. Chernikov 
3875d3b00c08SAlexander V. Chernikov /*
3876d3b00c08SAlexander V. Chernikov  * Destroys table @ti
3877d3b00c08SAlexander V. Chernikov  */
3878d3b00c08SAlexander V. Chernikov static void
3879d3b00c08SAlexander V. Chernikov ta_destroy_kfib(void *ta_state, struct table_info *ti)
3880d3b00c08SAlexander V. Chernikov {
3881d3b00c08SAlexander V. Chernikov 
3882d3b00c08SAlexander V. Chernikov }
3883d3b00c08SAlexander V. Chernikov 
3884d3b00c08SAlexander V. Chernikov /*
3885d3b00c08SAlexander V. Chernikov  * Provide algo-specific table info
3886d3b00c08SAlexander V. Chernikov  */
3887d3b00c08SAlexander V. Chernikov static void
3888d3b00c08SAlexander V. Chernikov ta_dump_kfib_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo)
3889d3b00c08SAlexander V. Chernikov {
3890d3b00c08SAlexander V. Chernikov 
3891d3b00c08SAlexander V. Chernikov 	tinfo->flags = IPFW_TATFLAGS_AFDATA;
3892d3b00c08SAlexander V. Chernikov 	tinfo->taclass4 = IPFW_TACLASS_RADIX;
3893d3b00c08SAlexander V. Chernikov 	tinfo->count4 = 0;
38944451d893SAlexander V. Chernikov 	tinfo->itemsize4 = 128; /* table is readonly, value does not matter */
3895d3b00c08SAlexander V. Chernikov 	tinfo->taclass6 = IPFW_TACLASS_RADIX;
3896d3b00c08SAlexander V. Chernikov 	tinfo->count6 = 0;
38974451d893SAlexander V. Chernikov 	tinfo->itemsize6 = 128;
3898d3b00c08SAlexander V. Chernikov }
3899d3b00c08SAlexander V. Chernikov 
3900d3b00c08SAlexander V. Chernikov static int
39014451d893SAlexander V. Chernikov ta_dump_kfib_tentry_int(int family, const struct rtentry *rt,
3902d3b00c08SAlexander V. Chernikov     ipfw_obj_tentry *tent)
3903d3b00c08SAlexander V. Chernikov {
39044451d893SAlexander V. Chernikov 	uint32_t scopeid;
39054451d893SAlexander V. Chernikov 	int plen;
3906004d3e30SAlexander V. Chernikov 
3907d699ee2dSAlexander V. Chernikov #ifdef INET
39084451d893SAlexander V. Chernikov 	if (family == AF_INET) {
39094451d893SAlexander V. Chernikov 		rt_get_inet_prefix_plen(rt, &tent->k.addr, &plen, &scopeid);
39104451d893SAlexander V. Chernikov 		tent->masklen = plen;
3911d3b00c08SAlexander V. Chernikov 		tent->subtype = AF_INET;
39124451d893SAlexander V. Chernikov 		tent->v.kidx = 0;
3913d699ee2dSAlexander V. Chernikov 	}
3914d699ee2dSAlexander V. Chernikov #endif
39152616eaa3SAlexander V. Chernikov #ifdef INET6
39164451d893SAlexander V. Chernikov 	if (family == AF_INET6) {
39174451d893SAlexander V. Chernikov 		rt_get_inet6_prefix_plen(rt, &tent->k.addr6, &plen, &scopeid);
39184451d893SAlexander V. Chernikov 		tent->masklen = plen;
3919d3b00c08SAlexander V. Chernikov 		tent->subtype = AF_INET6;
39200cba2b28SAlexander V. Chernikov 		tent->v.kidx = 0;
3921d3b00c08SAlexander V. Chernikov 	}
3922d699ee2dSAlexander V. Chernikov #endif
3923d3b00c08SAlexander V. Chernikov 	return (0);
3924d3b00c08SAlexander V. Chernikov }
3925d3b00c08SAlexander V. Chernikov 
3926d3b00c08SAlexander V. Chernikov static int
3927d3b00c08SAlexander V. Chernikov ta_find_kfib_tentry(void *ta_state, struct table_info *ti,
3928d3b00c08SAlexander V. Chernikov     ipfw_obj_tentry *tent)
3929d3b00c08SAlexander V. Chernikov {
39303ad80c65SAlexander V. Chernikov 	struct rtentry *rt = NULL;
39314451d893SAlexander V. Chernikov 	struct route_nhop_data rnd;
39324451d893SAlexander V. Chernikov 	struct epoch_tracker et;
39334451d893SAlexander V. Chernikov 	int error;
3934004d3e30SAlexander V. Chernikov 
39354451d893SAlexander V. Chernikov 	NET_EPOCH_ENTER(et);
39363ad80c65SAlexander V. Chernikov 
39373ad80c65SAlexander V. Chernikov 	switch (tent->subtype) {
39383ad80c65SAlexander V. Chernikov #ifdef INET
39393ad80c65SAlexander V. Chernikov 	case AF_INET:
39404451d893SAlexander V. Chernikov 		rt = fib4_lookup_rt(ti->data, tent->k.addr, 0, 0, &rnd);
39413ad80c65SAlexander V. Chernikov 		break;
39423ad80c65SAlexander V. Chernikov #endif
39433ad80c65SAlexander V. Chernikov #ifdef INET6
39443ad80c65SAlexander V. Chernikov 	case AF_INET6:
39454451d893SAlexander V. Chernikov 		rt = fib6_lookup_rt(ti->data, &tent->k.addr6, 0, 0, &rnd);
39463ad80c65SAlexander V. Chernikov 		break;
39473ad80c65SAlexander V. Chernikov #endif
39484451d893SAlexander V. Chernikov 	}
39494451d893SAlexander V. Chernikov 	if (rt != NULL)
39504451d893SAlexander V. Chernikov 		error = ta_dump_kfib_tentry_int(tent->subtype, rt, tent);
39514451d893SAlexander V. Chernikov 	else
39524451d893SAlexander V. Chernikov 		error = ENOENT;
39534451d893SAlexander V. Chernikov 	NET_EPOCH_EXIT(et);
39544451d893SAlexander V. Chernikov 
39554451d893SAlexander V. Chernikov 	return (error);
3956d3b00c08SAlexander V. Chernikov }
3957d3b00c08SAlexander V. Chernikov 
39584451d893SAlexander V. Chernikov struct kfib_dump_arg {
39594451d893SAlexander V. Chernikov 	struct rtentry *rt;
39604451d893SAlexander V. Chernikov 	int		family;
39614451d893SAlexander V. Chernikov 	ta_foreach_f	*f;
39624451d893SAlexander V. Chernikov 	void		*arg;
39634451d893SAlexander V. Chernikov };
3964004d3e30SAlexander V. Chernikov 
39654451d893SAlexander V. Chernikov static int
39664451d893SAlexander V. Chernikov ta_dump_kfib_tentry(void *ta_state, struct table_info *ti, void *e,
39674451d893SAlexander V. Chernikov     ipfw_obj_tentry *tent)
39684451d893SAlexander V. Chernikov {
39694451d893SAlexander V. Chernikov 	struct kfib_dump_arg *karg = (struct kfib_dump_arg *)e;
3970004d3e30SAlexander V. Chernikov 
39714451d893SAlexander V. Chernikov 	return (ta_dump_kfib_tentry_int(karg->family, karg->rt, tent));
39724451d893SAlexander V. Chernikov }
39734451d893SAlexander V. Chernikov 
39744451d893SAlexander V. Chernikov static int
39754451d893SAlexander V. Chernikov walk_wrapper_f(struct rtentry *rt, void *arg)
39764451d893SAlexander V. Chernikov {
39774451d893SAlexander V. Chernikov 	struct kfib_dump_arg *karg = (struct kfib_dump_arg *)arg;
39784451d893SAlexander V. Chernikov 
39794451d893SAlexander V. Chernikov 	karg->rt = rt;
39804451d893SAlexander V. Chernikov 	return (karg->f(karg, karg->arg));
3981d3b00c08SAlexander V. Chernikov }
3982d3b00c08SAlexander V. Chernikov 
3983d3b00c08SAlexander V. Chernikov static void
3984d3b00c08SAlexander V. Chernikov ta_foreach_kfib(void *ta_state, struct table_info *ti, ta_foreach_f *f,
3985d3b00c08SAlexander V. Chernikov     void *arg)
3986d3b00c08SAlexander V. Chernikov {
39874451d893SAlexander V. Chernikov 	struct kfib_dump_arg karg = { .f = f, .arg = arg };
3988d3b00c08SAlexander V. Chernikov 
39894451d893SAlexander V. Chernikov 	karg.family = AF_INET;
39904451d893SAlexander V. Chernikov 	rib_walk(ti->data, AF_INET, false, walk_wrapper_f, &karg);
39914451d893SAlexander V. Chernikov 	karg.family = AF_INET6;
39924451d893SAlexander V. Chernikov 	rib_walk(ti->data, AF_INET6, false, walk_wrapper_f, &karg);
3993d3b00c08SAlexander V. Chernikov }
3994d3b00c08SAlexander V. Chernikov 
3995c21034b7SAlexander V. Chernikov struct table_algo addr_kfib = {
3996c21034b7SAlexander V. Chernikov 	.name		= "addr:kfib",
3997c21034b7SAlexander V. Chernikov 	.type		= IPFW_TABLE_ADDR,
3998d3b00c08SAlexander V. Chernikov 	.flags		= TA_FLAG_READONLY,
3999d3b00c08SAlexander V. Chernikov 	.ta_buf_size	= 0,
4000d3b00c08SAlexander V. Chernikov 	.init		= ta_init_kfib,
4001d3b00c08SAlexander V. Chernikov 	.destroy	= ta_destroy_kfib,
4002d3b00c08SAlexander V. Chernikov 	.foreach	= ta_foreach_kfib,
4003d3b00c08SAlexander V. Chernikov 	.dump_tentry	= ta_dump_kfib_tentry,
4004d3b00c08SAlexander V. Chernikov 	.find_tentry	= ta_find_kfib_tentry,
4005d3b00c08SAlexander V. Chernikov 	.dump_tinfo	= ta_dump_kfib_tinfo,
4006d3b00c08SAlexander V. Chernikov 	.print_config	= ta_print_kfib_config,
4007d3b00c08SAlexander V. Chernikov };
4008d3b00c08SAlexander V. Chernikov 
40099f7d47b0SAlexander V. Chernikov void
40100b565ac0SAlexander V. Chernikov ipfw_table_algo_init(struct ip_fw_chain *ch)
40119f7d47b0SAlexander V. Chernikov {
40120b565ac0SAlexander V. Chernikov 	size_t sz;
40130b565ac0SAlexander V. Chernikov 
40149f7d47b0SAlexander V. Chernikov 	/*
40159f7d47b0SAlexander V. Chernikov 	 * Register all algorithms presented here.
40169f7d47b0SAlexander V. Chernikov 	 */
40170b565ac0SAlexander V. Chernikov 	sz = sizeof(struct table_algo);
4018c21034b7SAlexander V. Chernikov 	ipfw_add_table_algo(ch, &addr_radix, sz, &addr_radix.idx);
4019c21034b7SAlexander V. Chernikov 	ipfw_add_table_algo(ch, &addr_hash, sz, &addr_hash.idx);
40200b565ac0SAlexander V. Chernikov 	ipfw_add_table_algo(ch, &iface_idx, sz, &iface_idx.idx);
4021b23d5de9SAlexander V. Chernikov 	ipfw_add_table_algo(ch, &number_array, sz, &number_array.idx);
4022914bffb6SAlexander V. Chernikov 	ipfw_add_table_algo(ch, &flow_hash, sz, &flow_hash.idx);
4023c21034b7SAlexander V. Chernikov 	ipfw_add_table_algo(ch, &addr_kfib, sz, &addr_kfib.idx);
40249f7d47b0SAlexander V. Chernikov }
40259f7d47b0SAlexander V. Chernikov 
40269f7d47b0SAlexander V. Chernikov void
40270b565ac0SAlexander V. Chernikov ipfw_table_algo_destroy(struct ip_fw_chain *ch)
40289f7d47b0SAlexander V. Chernikov {
40290b565ac0SAlexander V. Chernikov 
4030c21034b7SAlexander V. Chernikov 	ipfw_del_table_algo(ch, addr_radix.idx);
4031c21034b7SAlexander V. Chernikov 	ipfw_del_table_algo(ch, addr_hash.idx);
40320b565ac0SAlexander V. Chernikov 	ipfw_del_table_algo(ch, iface_idx.idx);
4033b23d5de9SAlexander V. Chernikov 	ipfw_del_table_algo(ch, number_array.idx);
4034914bffb6SAlexander V. Chernikov 	ipfw_del_table_algo(ch, flow_hash.idx);
4035c21034b7SAlexander V. Chernikov 	ipfw_del_table_algo(ch, addr_kfib.idx);
40369f7d47b0SAlexander V. Chernikov }
4037