xref: /freebsd/sys/netpfil/ipfw/ip_fw_table_algo.c (revision 9fe15d06124fb36157e496363d74b1c306bfc8a0)
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>
549f7d47b0SAlexander V. Chernikov 
559f7d47b0SAlexander V. Chernikov #include <netinet/in.h>
569f7d47b0SAlexander V. Chernikov #include <netinet/ip_var.h>	/* struct ipfw_rule_ref */
579f7d47b0SAlexander V. Chernikov #include <netinet/ip_fw.h>
589f7d47b0SAlexander V. Chernikov 
599f7d47b0SAlexander V. Chernikov #include <netpfil/ipfw/ip_fw_private.h>
60ea761a5dSAlexander V. Chernikov #include <netpfil/ipfw/ip_fw_table.h>
619f7d47b0SAlexander V. Chernikov 
62301290bcSAlexander V. Chernikov 
63301290bcSAlexander V. Chernikov /*
64301290bcSAlexander V. Chernikov  * IPFW table lookup algorithms.
65301290bcSAlexander V. Chernikov  *
66301290bcSAlexander V. Chernikov  * What is needed to add another table algo?
67301290bcSAlexander V. Chernikov  *
68301290bcSAlexander V. Chernikov  * Algo init:
69301290bcSAlexander V. Chernikov  * * struct table_algo has to be filled with:
70c21034b7SAlexander V. Chernikov  *   name: "type:algoname" format, e.g. "addr:radix". Currently
71c21034b7SAlexander V. Chernikov  *     there are the following types: "addr", "iface", "number" and "flow".
72301290bcSAlexander V. Chernikov  *   type: one of IPFW_TABLE_* types
73301290bcSAlexander V. Chernikov  *   flags: one or more TA_FLAGS_*
74301290bcSAlexander V. Chernikov  *   ta_buf_size: size of structure used to store add/del item state.
75301290bcSAlexander V. Chernikov  *     Needs to be less than TA_BUF_SZ.
76301290bcSAlexander V. Chernikov  *   callbacks: see below for description.
77301290bcSAlexander V. Chernikov  * * ipfw_add_table_algo / ipfw_del_table_algo has to be called
78301290bcSAlexander V. Chernikov  *
79301290bcSAlexander V. Chernikov  * Callbacks description:
80301290bcSAlexander V. Chernikov  *
81301290bcSAlexander V. Chernikov  * -init: request to initialize new table instance.
82301290bcSAlexander V. Chernikov  * typedef int (ta_init)(struct ip_fw_chain *ch, void **ta_state,
83301290bcSAlexander V. Chernikov  *     struct table_info *ti, char *data, uint8_t tflags);
84301290bcSAlexander V. Chernikov  * MANDATORY, unlocked. (M_WAITOK). Returns 0 on success.
85301290bcSAlexander V. Chernikov  *
86301290bcSAlexander V. Chernikov  *  Allocate all structures needed for normal operations.
87301290bcSAlexander V. Chernikov  *  * Caller may want to parse @data for some algo-specific
88301290bcSAlexander V. Chernikov  *    options provided by userland.
89301290bcSAlexander V. Chernikov  *  * Caller may want to save configuration state pointer to @ta_state
90301290bcSAlexander V. Chernikov  *  * Caller needs to save desired runtime structure pointer(s)
91301290bcSAlexander V. Chernikov  *    inside @ti fields. Note that it is not correct to save
92301290bcSAlexander V. Chernikov  *    @ti pointer at this moment. Use -change_ti hook for that.
93301290bcSAlexander V. Chernikov  *  * Caller has to fill in ti->lookup to appropriate function
94301290bcSAlexander V. Chernikov  *    pointer.
95301290bcSAlexander V. Chernikov  *
96301290bcSAlexander V. Chernikov  *
97301290bcSAlexander V. Chernikov  *
98301290bcSAlexander V. Chernikov  * -destroy: request to destroy table instance.
99301290bcSAlexander V. Chernikov  * typedef void (ta_destroy)(void *ta_state, struct table_info *ti);
100301290bcSAlexander V. Chernikov  * MANDATORY, may be locked (UH+WLOCK). (M_NOWAIT).
101301290bcSAlexander V. Chernikov  *
102301290bcSAlexander V. Chernikov  * Frees all table entries and all tables structures allocated by -init.
103301290bcSAlexander V. Chernikov  *
104301290bcSAlexander V. Chernikov  *
105301290bcSAlexander V. Chernikov  *
106301290bcSAlexander V. Chernikov  * -prepare_add: request to allocate state for adding new entry.
107301290bcSAlexander V. Chernikov  * typedef int (ta_prepare_add)(struct ip_fw_chain *ch, struct tentry_info *tei,
108301290bcSAlexander V. Chernikov  *     void *ta_buf);
109301290bcSAlexander V. Chernikov  * MANDATORY, unlocked. (M_WAITOK). Returns 0 on success.
110301290bcSAlexander V. Chernikov  *
11113263632SAlexander V. Chernikov  * Allocates state and fills it in with all necessary data (EXCEPT value)
11213263632SAlexander V. Chernikov  * from @tei to minimize operations needed to be done under WLOCK.
11313263632SAlexander V. Chernikov  * "value" field has to be copied to new entry in @add callback.
114301290bcSAlexander V. Chernikov  * Buffer ta_buf of size ta->ta_buf_sz may be used to store
115301290bcSAlexander V. Chernikov  * allocated state.
116301290bcSAlexander V. Chernikov  *
117301290bcSAlexander V. Chernikov  *
118301290bcSAlexander V. Chernikov  *
119301290bcSAlexander V. Chernikov  * -prepare_del: request to set state for deleting existing entry.
120301290bcSAlexander V. Chernikov  * typedef int (ta_prepare_del)(struct ip_fw_chain *ch, struct tentry_info *tei,
121301290bcSAlexander V. Chernikov  *     void *ta_buf);
122301290bcSAlexander V. Chernikov  * MANDATORY, locked, UH. (M_NOWAIT). Returns 0 on success.
123301290bcSAlexander V. Chernikov  *
124301290bcSAlexander V. Chernikov  * Buffer ta_buf of size ta->ta_buf_sz may be used to store
125301290bcSAlexander V. Chernikov  * allocated state. Caller should use on-stack ta_buf allocation
126301290bcSAlexander V. Chernikov  * instead of doing malloc().
127301290bcSAlexander V. Chernikov  *
128301290bcSAlexander V. Chernikov  *
129301290bcSAlexander V. Chernikov  *
130301290bcSAlexander V. Chernikov  * -add: request to insert new entry into runtime/config structures.
131301290bcSAlexander V. Chernikov  *  typedef int (ta_add)(void *ta_state, struct table_info *ti,
132301290bcSAlexander V. Chernikov  *     struct tentry_info *tei, void *ta_buf, uint32_t *pnum);
133301290bcSAlexander V. Chernikov  * MANDATORY, UH+WLOCK. (M_NOWAIT). Returns 0 on success.
134301290bcSAlexander V. Chernikov  *
135301290bcSAlexander V. Chernikov  * Insert new entry using previously-allocated state in @ta_buf.
136301290bcSAlexander V. Chernikov  * * @tei may have the following flags:
137301290bcSAlexander V. Chernikov  *   TEI_FLAGS_UPDATE: request to add or update entry.
138301290bcSAlexander V. Chernikov  *   TEI_FLAGS_DONTADD: request to update (but not add) entry.
139301290bcSAlexander V. Chernikov  * * Caller is required to do the following:
14013263632SAlexander V. Chernikov  *   copy real entry value from @tei
141301290bcSAlexander V. Chernikov  *   entry added: return 0, set 1 to @pnum
142301290bcSAlexander V. Chernikov  *   entry updated: return 0, store 0 to @pnum, store old value in @tei,
143301290bcSAlexander V. Chernikov  *     add TEI_FLAGS_UPDATED flag to @tei.
144301290bcSAlexander V. Chernikov  *   entry exists: return EEXIST
145301290bcSAlexander V. Chernikov  *   entry not found: return ENOENT
146301290bcSAlexander V. Chernikov  *   other error: return non-zero error code.
147301290bcSAlexander V. Chernikov  *
148301290bcSAlexander V. Chernikov  *
149301290bcSAlexander V. Chernikov  *
150301290bcSAlexander V. Chernikov  * -del: request to delete existing entry from runtime/config structures.
151301290bcSAlexander V. Chernikov  *  typedef int (ta_del)(void *ta_state, struct table_info *ti,
152301290bcSAlexander V. Chernikov  *     struct tentry_info *tei, void *ta_buf, uint32_t *pnum);
153301290bcSAlexander V. Chernikov  *  MANDATORY, UH+WLOCK. (M_NOWAIT). Returns 0 on success.
154301290bcSAlexander V. Chernikov  *
155301290bcSAlexander V. Chernikov  *  Delete entry using previously set up in @ta_buf.
156301290bcSAlexander V. Chernikov  * * Caller is required to do the following:
15713263632SAlexander V. Chernikov  *   entry deleted: return 0, set 1 to @pnum, store old value in @tei.
158301290bcSAlexander V. Chernikov  *   entry not found: return ENOENT
159301290bcSAlexander V. Chernikov  *   other error: return non-zero error code.
160301290bcSAlexander V. Chernikov  *
161301290bcSAlexander V. Chernikov  *
162301290bcSAlexander V. Chernikov  *
163301290bcSAlexander V. Chernikov  * -flush_entry: flush entry state created by -prepare_add / -del / others
164301290bcSAlexander V. Chernikov  *  typedef void (ta_flush_entry)(struct ip_fw_chain *ch,
165301290bcSAlexander V. Chernikov  *      struct tentry_info *tei, void *ta_buf);
166301290bcSAlexander V. Chernikov  *  MANDATORY, may be locked. (M_NOWAIT).
167301290bcSAlexander V. Chernikov  *
168301290bcSAlexander V. Chernikov  *  Delete state allocated by:
169301290bcSAlexander V. Chernikov  *  -prepare_add (-add returned EEXIST|UPDATED)
170301290bcSAlexander V. Chernikov  *  -prepare_del (if any)
171301290bcSAlexander V. Chernikov  *  -del
172301290bcSAlexander V. Chernikov  *  * Caller is required to handle empty @ta_buf correctly.
173301290bcSAlexander V. Chernikov  *
174301290bcSAlexander V. Chernikov  *
175301290bcSAlexander V. Chernikov  * -find_tentry: finds entry specified by key @tei
176301290bcSAlexander V. Chernikov  *  typedef int ta_find_tentry(void *ta_state, struct table_info *ti,
177301290bcSAlexander V. Chernikov  *      ipfw_obj_tentry *tent);
178301290bcSAlexander V. Chernikov  *  OPTIONAL, locked (UH). (M_NOWAIT). Returns 0 on success.
179301290bcSAlexander V. Chernikov  *
180301290bcSAlexander V. Chernikov  *  Finds entry specified by given key.
181301290bcSAlexander V. Chernikov  *  * Caller is requred to do the following:
182301290bcSAlexander V. Chernikov  *    entry found: returns 0, export entry to @tent
183301290bcSAlexander V. Chernikov  *    entry not found: returns ENOENT
184301290bcSAlexander V. Chernikov  *
185301290bcSAlexander V. Chernikov  *
186301290bcSAlexander V. Chernikov  * -need_modify: checks if @ti has enough space to hold another @count items.
187301290bcSAlexander V. Chernikov  *  typedef int (ta_need_modify)(void *ta_state, struct table_info *ti,
188301290bcSAlexander V. Chernikov  *      uint32_t count, uint64_t *pflags);
189fd0869d5SAlexander V. Chernikov  *  OPTIONAL, locked (UH). (M_NOWAIT). Returns 0 if has.
190301290bcSAlexander V. Chernikov  *
191301290bcSAlexander V. Chernikov  *  Checks if given table has enough space to add @count items without
192301290bcSAlexander V. Chernikov  *  resize. Caller may use @pflags to store desired modification data.
193301290bcSAlexander V. Chernikov  *
194301290bcSAlexander V. Chernikov  *
195301290bcSAlexander V. Chernikov  *
196301290bcSAlexander V. Chernikov  * -prepare_mod: allocate structures for table modification.
197301290bcSAlexander V. Chernikov  *  typedef int (ta_prepare_mod)(void *ta_buf, uint64_t *pflags);
198fd0869d5SAlexander V. Chernikov  * OPTIONAL(need_modify), unlocked. (M_WAITOK). Returns 0 on success.
199301290bcSAlexander V. Chernikov  *
200301290bcSAlexander V. Chernikov  * Allocate all needed state for table modification. Caller
201301290bcSAlexander V. Chernikov  * should use `struct mod_item` to store new state in @ta_buf.
202301290bcSAlexander V. Chernikov  * Up to TA_BUF_SZ (128 bytes) can be stored in @ta_buf.
203301290bcSAlexander V. Chernikov  *
204301290bcSAlexander V. Chernikov  *
205301290bcSAlexander V. Chernikov  *
206301290bcSAlexander V. Chernikov  * -fill_mod: copy some data to new state/
207301290bcSAlexander V. Chernikov  *  typedef int (ta_fill_mod)(void *ta_state, struct table_info *ti,
208301290bcSAlexander V. Chernikov  *      void *ta_buf, uint64_t *pflags);
209fd0869d5SAlexander V. Chernikov  * OPTIONAL(need_modify), locked (UH). (M_NOWAIT). Returns 0 on success.
210301290bcSAlexander V. Chernikov  *
211301290bcSAlexander V. Chernikov  * Copy as much data as we can to minimize changes under WLOCK.
212301290bcSAlexander V. Chernikov  * For example, array can be merged inside this callback.
213301290bcSAlexander V. Chernikov  *
214301290bcSAlexander V. Chernikov  *
215301290bcSAlexander V. Chernikov  *
216301290bcSAlexander V. Chernikov  * -modify: perform final modification.
217301290bcSAlexander V. Chernikov  *  typedef void (ta_modify)(void *ta_state, struct table_info *ti,
218301290bcSAlexander V. Chernikov  *      void *ta_buf, uint64_t pflags);
219fd0869d5SAlexander V. Chernikov  * OPTIONAL(need_modify), locked (UH+WLOCK). (M_NOWAIT).
220301290bcSAlexander V. Chernikov  *
221301290bcSAlexander V. Chernikov  * Performs all changes necessary to switch to new structures.
222301290bcSAlexander V. Chernikov  * * Caller should save old pointers to @ta_buf storage.
223301290bcSAlexander V. Chernikov  *
224301290bcSAlexander V. Chernikov  *
225301290bcSAlexander V. Chernikov  *
226301290bcSAlexander V. Chernikov  * -flush_mod: flush table modification state.
227301290bcSAlexander V. Chernikov  *  typedef void (ta_flush_mod)(void *ta_buf);
228fd0869d5SAlexander V. Chernikov  * OPTIONAL(need_modify), unlocked. (M_WAITOK).
229301290bcSAlexander V. Chernikov  *
230301290bcSAlexander V. Chernikov  * Performs flush for the following:
231301290bcSAlexander V. Chernikov  *   - prepare_mod (modification was not necessary)
232301290bcSAlexander V. Chernikov  *   - modify (for the old state)
233301290bcSAlexander V. Chernikov  *
234301290bcSAlexander V. Chernikov  *
235301290bcSAlexander V. Chernikov  *
236301290bcSAlexander V. Chernikov  * -change_gi: monitor table info pointer changes
237301290bcSAlexander V. Chernikov  * typedef void (ta_change_ti)(void *ta_state, struct table_info *ti);
238301290bcSAlexander V. Chernikov  * OPTIONAL, locked (UH). (M_NOWAIT).
239301290bcSAlexander V. Chernikov  *
240301290bcSAlexander V. Chernikov  * Called on @ti pointer changed. Called immediately after -init
241301290bcSAlexander V. Chernikov  * to set initial state.
242301290bcSAlexander V. Chernikov  *
243301290bcSAlexander V. Chernikov  *
244301290bcSAlexander V. Chernikov  *
245301290bcSAlexander V. Chernikov  * -foreach: calls @f for each table entry
246301290bcSAlexander V. Chernikov  *  typedef void ta_foreach(void *ta_state, struct table_info *ti,
247301290bcSAlexander V. Chernikov  *      ta_foreach_f *f, void *arg);
248301290bcSAlexander V. Chernikov  * MANDATORY, locked(UH). (M_NOWAIT).
249301290bcSAlexander V. Chernikov  *
250301290bcSAlexander V. Chernikov  * Runs callback with specified argument for each table entry,
251301290bcSAlexander V. Chernikov  * Typically used for dumping table entries.
252301290bcSAlexander V. Chernikov  *
253301290bcSAlexander V. Chernikov  *
254301290bcSAlexander V. Chernikov  *
255301290bcSAlexander V. Chernikov  * -dump_tentry: dump table entry in current @tentry format.
256301290bcSAlexander V. Chernikov  *  typedef int ta_dump_tentry(void *ta_state, struct table_info *ti, void *e,
257301290bcSAlexander V. Chernikov  *      ipfw_obj_tentry *tent);
258301290bcSAlexander V. Chernikov  * MANDATORY, locked(UH). (M_NOWAIT). Returns 0 on success.
259301290bcSAlexander V. Chernikov  *
260301290bcSAlexander V. Chernikov  * Dumps entry @e to @tent.
261301290bcSAlexander V. Chernikov  *
262301290bcSAlexander V. Chernikov  *
263301290bcSAlexander V. Chernikov  * -print_config: prints custom algoritm options into buffer.
264301290bcSAlexander V. Chernikov  *  typedef void (ta_print_config)(void *ta_state, struct table_info *ti,
265301290bcSAlexander V. Chernikov  *      char *buf, size_t bufsize);
266301290bcSAlexander V. Chernikov  * OPTIONAL. locked(UH). (M_NOWAIT).
267301290bcSAlexander V. Chernikov  *
268301290bcSAlexander V. Chernikov  * Prints custom algorithm options in the format suitable to pass
269301290bcSAlexander V. Chernikov  * back to -init callback.
270301290bcSAlexander V. Chernikov  *
271301290bcSAlexander V. Chernikov  *
272301290bcSAlexander V. Chernikov  *
273301290bcSAlexander V. Chernikov  * -dump_tinfo: dumps algo-specific info.
274301290bcSAlexander V. Chernikov  *  typedef void ta_dump_tinfo(void *ta_state, struct table_info *ti,
275301290bcSAlexander V. Chernikov  *      ipfw_ta_tinfo *tinfo);
276301290bcSAlexander V. Chernikov  * OPTIONAL. locked(UH). (M_NOWAIT).
277301290bcSAlexander V. Chernikov  *
278301290bcSAlexander V. Chernikov  * Dumps options like items size/hash size, etc.
279301290bcSAlexander V. Chernikov  */
280301290bcSAlexander V. Chernikov 
281b1d105bcSAlexander V. Chernikov MALLOC_DEFINE(M_IPFW_TBL, "ipfw_tbl", "IpFw tables");
2829f7d47b0SAlexander V. Chernikov 
2830bce0c23SAlexander V. Chernikov /*
2840bce0c23SAlexander V. Chernikov  * Utility structures/functions common to more than one algo
2850bce0c23SAlexander V. Chernikov  */
2860bce0c23SAlexander V. Chernikov 
2870bce0c23SAlexander V. Chernikov struct mod_item {
2880bce0c23SAlexander V. Chernikov 	void	*main_ptr;
2890bce0c23SAlexander V. Chernikov 	size_t	size;
2900bce0c23SAlexander V. Chernikov 	void	*main_ptr6;
2910bce0c23SAlexander V. Chernikov 	size_t	size6;
2920bce0c23SAlexander V. Chernikov };
2930bce0c23SAlexander V. Chernikov 
29468394ec8SAlexander V. Chernikov static int badd(const void *key, void *item, void *base, size_t nmemb,
29568394ec8SAlexander V. Chernikov     size_t size, int (*compar) (const void *, const void *));
29668394ec8SAlexander V. Chernikov static int bdel(const void *key, void *base, size_t nmemb, size_t size,
29768394ec8SAlexander V. Chernikov     int (*compar) (const void *, const void *));
29868394ec8SAlexander V. Chernikov 
29968394ec8SAlexander V. Chernikov 
30068394ec8SAlexander V. Chernikov /*
301c21034b7SAlexander V. Chernikov  * ADDR implementation using radix
30268394ec8SAlexander V. Chernikov  *
30368394ec8SAlexander V. Chernikov  */
30468394ec8SAlexander V. Chernikov 
3059f7d47b0SAlexander V. Chernikov /*
3069f7d47b0SAlexander V. Chernikov  * The radix code expects addr and mask to be array of bytes,
3079f7d47b0SAlexander V. Chernikov  * with the first byte being the length of the array. rn_inithead
3089f7d47b0SAlexander V. Chernikov  * is called with the offset in bits of the lookup key within the
3099f7d47b0SAlexander V. Chernikov  * array. If we use a sockaddr_in as the underlying type,
3109f7d47b0SAlexander V. Chernikov  * sin_len is conveniently located at offset 0, sin_addr is at
3119f7d47b0SAlexander V. Chernikov  * offset 4 and normally aligned.
3129f7d47b0SAlexander V. Chernikov  * But for portability, let's avoid assumption and make the code explicit
3139f7d47b0SAlexander V. Chernikov  */
3149f7d47b0SAlexander V. Chernikov #define KEY_LEN(v)	*((uint8_t *)&(v))
3159f7d47b0SAlexander V. Chernikov /*
3169f7d47b0SAlexander V. Chernikov  * Do not require radix to compare more than actual IPv4/IPv6 address
3179f7d47b0SAlexander V. Chernikov  */
3189f7d47b0SAlexander V. Chernikov #define KEY_LEN_INET	(offsetof(struct sockaddr_in, sin_addr) + sizeof(in_addr_t))
319e0a8b9eeSAlexander V. Chernikov #define KEY_LEN_INET6	(offsetof(struct sa_in6, sin6_addr) + sizeof(struct in6_addr))
3209f7d47b0SAlexander V. Chernikov 
3219f7d47b0SAlexander V. Chernikov #define OFF_LEN_INET	(8 * offsetof(struct sockaddr_in, sin_addr))
322e0a8b9eeSAlexander V. Chernikov #define OFF_LEN_INET6	(8 * offsetof(struct sa_in6, sin6_addr))
3239f7d47b0SAlexander V. Chernikov 
324c21034b7SAlexander V. Chernikov struct radix_addr_entry {
3259f7d47b0SAlexander V. Chernikov 	struct radix_node	rn[2];
326e0a8b9eeSAlexander V. Chernikov 	struct sockaddr_in	addr;
327e0a8b9eeSAlexander V. Chernikov 	uint32_t		value;
328e0a8b9eeSAlexander V. Chernikov 	uint8_t			masklen;
329e0a8b9eeSAlexander V. Chernikov };
330e0a8b9eeSAlexander V. Chernikov 
331e0a8b9eeSAlexander V. Chernikov struct sa_in6 {
332e0a8b9eeSAlexander V. Chernikov 	uint8_t			sin6_len;
333e0a8b9eeSAlexander V. Chernikov 	uint8_t			sin6_family;
334e0a8b9eeSAlexander V. Chernikov 	uint8_t			pad[2];
335e0a8b9eeSAlexander V. Chernikov 	struct in6_addr		sin6_addr;
336e0a8b9eeSAlexander V. Chernikov };
337e0a8b9eeSAlexander V. Chernikov 
338c21034b7SAlexander V. Chernikov struct radix_addr_xentry {
339e0a8b9eeSAlexander V. Chernikov 	struct radix_node	rn[2];
340e0a8b9eeSAlexander V. Chernikov 	struct sa_in6		addr6;
341e0a8b9eeSAlexander V. Chernikov 	uint32_t		value;
342e0a8b9eeSAlexander V. Chernikov 	uint8_t			masklen;
3439f7d47b0SAlexander V. Chernikov };
3449f7d47b0SAlexander V. Chernikov 
3455f379342SAlexander V. Chernikov struct radix_cfg {
3465f379342SAlexander V. Chernikov 	struct radix_node_head	*head4;
3475f379342SAlexander V. Chernikov 	struct radix_node_head	*head6;
3485f379342SAlexander V. Chernikov 	size_t			count4;
3495f379342SAlexander V. Chernikov 	size_t			count6;
3505f379342SAlexander V. Chernikov };
3515f379342SAlexander V. Chernikov 
352c21034b7SAlexander V. Chernikov struct ta_buf_radix
3530bce0c23SAlexander V. Chernikov {
3540bce0c23SAlexander V. Chernikov 	void *ent_ptr;
3550bce0c23SAlexander V. Chernikov 	struct sockaddr	*addr_ptr;
3560bce0c23SAlexander V. Chernikov 	struct sockaddr	*mask_ptr;
3570bce0c23SAlexander V. Chernikov 	union {
3580bce0c23SAlexander V. Chernikov 		struct {
3590bce0c23SAlexander V. Chernikov 			struct sockaddr_in sa;
3600bce0c23SAlexander V. Chernikov 			struct sockaddr_in ma;
3610bce0c23SAlexander V. Chernikov 		} a4;
3620bce0c23SAlexander V. Chernikov 		struct {
3630bce0c23SAlexander V. Chernikov 			struct sa_in6 sa;
3640bce0c23SAlexander V. Chernikov 			struct sa_in6 ma;
3650bce0c23SAlexander V. Chernikov 		} a6;
3660bce0c23SAlexander V. Chernikov 	} addr;
3670bce0c23SAlexander V. Chernikov };
3680bce0c23SAlexander V. Chernikov 
369*9fe15d06SAlexander V. Chernikov static int ta_lookup_radix(struct table_info *ti, void *key, uint32_t keylen,
370*9fe15d06SAlexander V. Chernikov     uint32_t *val);
371*9fe15d06SAlexander V. Chernikov static int ta_init_radix(struct ip_fw_chain *ch, void **ta_state,
372*9fe15d06SAlexander V. Chernikov     struct table_info *ti, char *data, uint8_t tflags);
373*9fe15d06SAlexander V. Chernikov static int flush_radix_entry(struct radix_node *rn, void *arg);
374*9fe15d06SAlexander V. Chernikov static void ta_destroy_radix(void *ta_state, struct table_info *ti);
375*9fe15d06SAlexander V. Chernikov static void ta_dump_radix_tinfo(void *ta_state, struct table_info *ti,
376*9fe15d06SAlexander V. Chernikov     ipfw_ta_tinfo *tinfo);
377*9fe15d06SAlexander V. Chernikov static int ta_dump_radix_tentry(void *ta_state, struct table_info *ti,
378*9fe15d06SAlexander V. Chernikov     void *e, ipfw_obj_tentry *tent);
379*9fe15d06SAlexander V. Chernikov static int ta_find_radix_tentry(void *ta_state, struct table_info *ti,
380*9fe15d06SAlexander V. Chernikov     ipfw_obj_tentry *tent);
381*9fe15d06SAlexander V. Chernikov static void ta_foreach_radix(void *ta_state, struct table_info *ti,
382*9fe15d06SAlexander V. Chernikov     ta_foreach_f *f, void *arg);
383*9fe15d06SAlexander V. Chernikov static inline void ipv6_writemask(struct in6_addr *addr6, uint8_t mask);
384*9fe15d06SAlexander V. Chernikov static void tei_to_sockaddr_ent(struct tentry_info *tei, struct sockaddr *sa,
385*9fe15d06SAlexander V. Chernikov     struct sockaddr *ma, int *set_mask);
386*9fe15d06SAlexander V. Chernikov static int ta_prepare_add_radix(struct ip_fw_chain *ch, struct tentry_info *tei,
387*9fe15d06SAlexander V. Chernikov     void *ta_buf);
388*9fe15d06SAlexander V. Chernikov static int ta_add_radix(void *ta_state, struct table_info *ti,
389*9fe15d06SAlexander V. Chernikov     struct tentry_info *tei, void *ta_buf, uint32_t *pnum);
390*9fe15d06SAlexander V. Chernikov static int ta_prepare_del_radix(struct ip_fw_chain *ch, struct tentry_info *tei,
391*9fe15d06SAlexander V. Chernikov     void *ta_buf);
392*9fe15d06SAlexander V. Chernikov static int ta_del_radix(void *ta_state, struct table_info *ti,
393*9fe15d06SAlexander V. Chernikov     struct tentry_info *tei, void *ta_buf, uint32_t *pnum);
394*9fe15d06SAlexander V. Chernikov static void ta_flush_radix_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
395*9fe15d06SAlexander V. Chernikov     void *ta_buf);
396*9fe15d06SAlexander V. Chernikov static int ta_need_modify_radix(void *ta_state, struct table_info *ti,
397*9fe15d06SAlexander V. Chernikov     uint32_t count, uint64_t *pflags);
398*9fe15d06SAlexander V. Chernikov 
3999f7d47b0SAlexander V. Chernikov static int
4009f7d47b0SAlexander V. Chernikov ta_lookup_radix(struct table_info *ti, void *key, uint32_t keylen,
4019f7d47b0SAlexander V. Chernikov     uint32_t *val)
4029f7d47b0SAlexander V. Chernikov {
4039f7d47b0SAlexander V. Chernikov 	struct radix_node_head *rnh;
4049f7d47b0SAlexander V. Chernikov 
4059f7d47b0SAlexander V. Chernikov 	if (keylen == sizeof(in_addr_t)) {
406c21034b7SAlexander V. Chernikov 		struct radix_addr_entry *ent;
4079f7d47b0SAlexander V. Chernikov 		struct sockaddr_in sa;
4089f7d47b0SAlexander V. Chernikov 		KEY_LEN(sa) = KEY_LEN_INET;
4099f7d47b0SAlexander V. Chernikov 		sa.sin_addr.s_addr = *((in_addr_t *)key);
4109f7d47b0SAlexander V. Chernikov 		rnh = (struct radix_node_head *)ti->state;
411c21034b7SAlexander V. Chernikov 		ent = (struct radix_addr_entry *)(rnh->rnh_matchaddr(&sa, rnh));
4129f7d47b0SAlexander V. Chernikov 		if (ent != NULL) {
4139f7d47b0SAlexander V. Chernikov 			*val = ent->value;
4149f7d47b0SAlexander V. Chernikov 			return (1);
4159f7d47b0SAlexander V. Chernikov 		}
4169f7d47b0SAlexander V. Chernikov 	} else {
417c21034b7SAlexander V. Chernikov 		struct radix_addr_xentry *xent;
418e0a8b9eeSAlexander V. Chernikov 		struct sa_in6 sa6;
4199f7d47b0SAlexander V. Chernikov 		KEY_LEN(sa6) = KEY_LEN_INET6;
4209f7d47b0SAlexander V. Chernikov 		memcpy(&sa6.sin6_addr, key, sizeof(struct in6_addr));
4219f7d47b0SAlexander V. Chernikov 		rnh = (struct radix_node_head *)ti->xstate;
422c21034b7SAlexander V. Chernikov 		xent = (struct radix_addr_xentry *)(rnh->rnh_matchaddr(&sa6, rnh));
4239f7d47b0SAlexander V. Chernikov 		if (xent != NULL) {
4249f7d47b0SAlexander V. Chernikov 			*val = xent->value;
4259f7d47b0SAlexander V. Chernikov 			return (1);
4269f7d47b0SAlexander V. Chernikov 		}
4279f7d47b0SAlexander V. Chernikov 	}
4289f7d47b0SAlexander V. Chernikov 
4299f7d47b0SAlexander V. Chernikov 	return (0);
4309f7d47b0SAlexander V. Chernikov }
4319f7d47b0SAlexander V. Chernikov 
4329f7d47b0SAlexander V. Chernikov /*
4339f7d47b0SAlexander V. Chernikov  * New table
4349f7d47b0SAlexander V. Chernikov  */
4359f7d47b0SAlexander V. Chernikov static int
43668394ec8SAlexander V. Chernikov ta_init_radix(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
437914bffb6SAlexander V. Chernikov     char *data, uint8_t tflags)
4389f7d47b0SAlexander V. Chernikov {
4395f379342SAlexander V. Chernikov 	struct radix_cfg *cfg;
4409f7d47b0SAlexander V. Chernikov 
4419f7d47b0SAlexander V. Chernikov 	if (!rn_inithead(&ti->state, OFF_LEN_INET))
4429f7d47b0SAlexander V. Chernikov 		return (ENOMEM);
4439f7d47b0SAlexander V. Chernikov 	if (!rn_inithead(&ti->xstate, OFF_LEN_INET6)) {
4449f7d47b0SAlexander V. Chernikov 		rn_detachhead(&ti->state);
4459f7d47b0SAlexander V. Chernikov 		return (ENOMEM);
4469f7d47b0SAlexander V. Chernikov 	}
4479f7d47b0SAlexander V. Chernikov 
4485f379342SAlexander V. Chernikov 	cfg = malloc(sizeof(struct radix_cfg), M_IPFW, M_WAITOK | M_ZERO);
4495f379342SAlexander V. Chernikov 
4505f379342SAlexander V. Chernikov 	*ta_state = cfg;
4519f7d47b0SAlexander V. Chernikov 	ti->lookup = ta_lookup_radix;
4529f7d47b0SAlexander V. Chernikov 
4539f7d47b0SAlexander V. Chernikov 	return (0);
4549f7d47b0SAlexander V. Chernikov }
4559f7d47b0SAlexander V. Chernikov 
4569f7d47b0SAlexander V. Chernikov static int
457a399f8beSAlexander V. Chernikov flush_radix_entry(struct radix_node *rn, void *arg)
4589f7d47b0SAlexander V. Chernikov {
4599f7d47b0SAlexander V. Chernikov 	struct radix_node_head * const rnh = arg;
460c21034b7SAlexander V. Chernikov 	struct radix_addr_entry *ent;
4619f7d47b0SAlexander V. Chernikov 
462c21034b7SAlexander V. Chernikov 	ent = (struct radix_addr_entry *)
4639f7d47b0SAlexander V. Chernikov 	    rnh->rnh_deladdr(rn->rn_key, rn->rn_mask, rnh);
4649f7d47b0SAlexander V. Chernikov 	if (ent != NULL)
4659f7d47b0SAlexander V. Chernikov 		free(ent, M_IPFW_TBL);
4669f7d47b0SAlexander V. Chernikov 	return (0);
4679f7d47b0SAlexander V. Chernikov }
4689f7d47b0SAlexander V. Chernikov 
4699f7d47b0SAlexander V. Chernikov static void
4709f7d47b0SAlexander V. Chernikov ta_destroy_radix(void *ta_state, struct table_info *ti)
4719f7d47b0SAlexander V. Chernikov {
4725f379342SAlexander V. Chernikov 	struct radix_cfg *cfg;
4739f7d47b0SAlexander V. Chernikov 	struct radix_node_head *rnh;
4749f7d47b0SAlexander V. Chernikov 
4755f379342SAlexander V. Chernikov 	cfg = (struct radix_cfg *)ta_state;
4765f379342SAlexander V. Chernikov 
4779f7d47b0SAlexander V. Chernikov 	rnh = (struct radix_node_head *)(ti->state);
478a399f8beSAlexander V. Chernikov 	rnh->rnh_walktree(rnh, flush_radix_entry, rnh);
4799f7d47b0SAlexander V. Chernikov 	rn_detachhead(&ti->state);
4809f7d47b0SAlexander V. Chernikov 
4819f7d47b0SAlexander V. Chernikov 	rnh = (struct radix_node_head *)(ti->xstate);
482a399f8beSAlexander V. Chernikov 	rnh->rnh_walktree(rnh, flush_radix_entry, rnh);
4839f7d47b0SAlexander V. Chernikov 	rn_detachhead(&ti->xstate);
4845f379342SAlexander V. Chernikov 
4855f379342SAlexander V. Chernikov 	free(cfg, M_IPFW);
4865f379342SAlexander V. Chernikov }
4875f379342SAlexander V. Chernikov 
4885f379342SAlexander V. Chernikov /*
4895f379342SAlexander V. Chernikov  * Provide algo-specific table info
4905f379342SAlexander V. Chernikov  */
4915f379342SAlexander V. Chernikov static void
4925f379342SAlexander V. Chernikov ta_dump_radix_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo)
4935f379342SAlexander V. Chernikov {
4945f379342SAlexander V. Chernikov 	struct radix_cfg *cfg;
4955f379342SAlexander V. Chernikov 
4965f379342SAlexander V. Chernikov 	cfg = (struct radix_cfg *)ta_state;
4975f379342SAlexander V. Chernikov 
4985f379342SAlexander V. Chernikov 	tinfo->flags = IPFW_TATFLAGS_AFDATA | IPFW_TATFLAGS_AFITEM;
4995f379342SAlexander V. Chernikov 	tinfo->taclass4 = IPFW_TACLASS_RADIX;
5005f379342SAlexander V. Chernikov 	tinfo->count4 = cfg->count4;
501c21034b7SAlexander V. Chernikov 	tinfo->itemsize4 = sizeof(struct radix_addr_entry);
5025f379342SAlexander V. Chernikov 	tinfo->taclass6 = IPFW_TACLASS_RADIX;
5035f379342SAlexander V. Chernikov 	tinfo->count6 = cfg->count6;
504c21034b7SAlexander V. Chernikov 	tinfo->itemsize6 = sizeof(struct radix_addr_xentry);
5059f7d47b0SAlexander V. Chernikov }
5069f7d47b0SAlexander V. Chernikov 
5079f7d47b0SAlexander V. Chernikov static int
50881d3153dSAlexander V. Chernikov ta_dump_radix_tentry(void *ta_state, struct table_info *ti, void *e,
50981d3153dSAlexander V. Chernikov     ipfw_obj_tentry *tent)
5109f7d47b0SAlexander V. Chernikov {
511c21034b7SAlexander V. Chernikov 	struct radix_addr_entry *n;
512*9fe15d06SAlexander V. Chernikov #ifdef INET6
513c21034b7SAlexander V. Chernikov 	struct radix_addr_xentry *xn;
514*9fe15d06SAlexander V. Chernikov #endif
5159f7d47b0SAlexander V. Chernikov 
516c21034b7SAlexander V. Chernikov 	n = (struct radix_addr_entry *)e;
5179f7d47b0SAlexander V. Chernikov 
5189f7d47b0SAlexander V. Chernikov 	/* Guess IPv4/IPv6 radix by sockaddr family */
5199f7d47b0SAlexander V. Chernikov 	if (n->addr.sin_family == AF_INET) {
52081d3153dSAlexander V. Chernikov 		tent->k.addr.s_addr = n->addr.sin_addr.s_addr;
521e0a8b9eeSAlexander V. Chernikov 		tent->masklen = n->masklen;
52281d3153dSAlexander V. Chernikov 		tent->subtype = AF_INET;
5230cba2b28SAlexander V. Chernikov 		tent->v.kidx = n->value;
5249f7d47b0SAlexander V. Chernikov #ifdef INET6
5259f7d47b0SAlexander V. Chernikov 	} else {
526c21034b7SAlexander V. Chernikov 		xn = (struct radix_addr_xentry *)e;
527e0a8b9eeSAlexander V. Chernikov 		memcpy(&tent->k, &xn->addr6.sin6_addr, sizeof(struct in6_addr));
528e0a8b9eeSAlexander V. Chernikov 		tent->masklen = xn->masklen;
52981d3153dSAlexander V. Chernikov 		tent->subtype = AF_INET6;
5300cba2b28SAlexander V. Chernikov 		tent->v.kidx = xn->value;
5319f7d47b0SAlexander V. Chernikov #endif
5329f7d47b0SAlexander V. Chernikov 	}
5339f7d47b0SAlexander V. Chernikov 
5349f7d47b0SAlexander V. Chernikov 	return (0);
5359f7d47b0SAlexander V. Chernikov }
5369f7d47b0SAlexander V. Chernikov 
53781d3153dSAlexander V. Chernikov static int
538914bffb6SAlexander V. Chernikov ta_find_radix_tentry(void *ta_state, struct table_info *ti,
539914bffb6SAlexander V. Chernikov     ipfw_obj_tentry *tent)
54081d3153dSAlexander V. Chernikov {
54181d3153dSAlexander V. Chernikov 	struct radix_node_head *rnh;
54281d3153dSAlexander V. Chernikov 	void *e;
54381d3153dSAlexander V. Chernikov 
54481d3153dSAlexander V. Chernikov 	e = NULL;
545914bffb6SAlexander V. Chernikov 	if (tent->subtype == AF_INET) {
54681d3153dSAlexander V. Chernikov 		struct sockaddr_in sa;
54781d3153dSAlexander V. Chernikov 		KEY_LEN(sa) = KEY_LEN_INET;
548914bffb6SAlexander V. Chernikov 		sa.sin_addr.s_addr = tent->k.addr.s_addr;
54981d3153dSAlexander V. Chernikov 		rnh = (struct radix_node_head *)ti->state;
55081d3153dSAlexander V. Chernikov 		e = rnh->rnh_matchaddr(&sa, rnh);
55181d3153dSAlexander V. Chernikov 	} else {
552e0a8b9eeSAlexander V. Chernikov 		struct sa_in6 sa6;
55381d3153dSAlexander V. Chernikov 		KEY_LEN(sa6) = KEY_LEN_INET6;
554914bffb6SAlexander V. Chernikov 		memcpy(&sa6.sin6_addr, &tent->k.addr6, sizeof(struct in6_addr));
55581d3153dSAlexander V. Chernikov 		rnh = (struct radix_node_head *)ti->xstate;
55681d3153dSAlexander V. Chernikov 		e = rnh->rnh_matchaddr(&sa6, rnh);
55781d3153dSAlexander V. Chernikov 	}
55881d3153dSAlexander V. Chernikov 
55981d3153dSAlexander V. Chernikov 	if (e != NULL) {
56081d3153dSAlexander V. Chernikov 		ta_dump_radix_tentry(ta_state, ti, e, tent);
56181d3153dSAlexander V. Chernikov 		return (0);
56281d3153dSAlexander V. Chernikov 	}
56381d3153dSAlexander V. Chernikov 
56481d3153dSAlexander V. Chernikov 	return (ENOENT);
56581d3153dSAlexander V. Chernikov }
56681d3153dSAlexander V. Chernikov 
5679f7d47b0SAlexander V. Chernikov static void
5689f7d47b0SAlexander V. Chernikov ta_foreach_radix(void *ta_state, struct table_info *ti, ta_foreach_f *f,
5699f7d47b0SAlexander V. Chernikov     void *arg)
5709f7d47b0SAlexander V. Chernikov {
5719f7d47b0SAlexander V. Chernikov 	struct radix_node_head *rnh;
5729f7d47b0SAlexander V. Chernikov 
5739f7d47b0SAlexander V. Chernikov 	rnh = (struct radix_node_head *)(ti->state);
5749f7d47b0SAlexander V. Chernikov 	rnh->rnh_walktree(rnh, (walktree_f_t *)f, arg);
5759f7d47b0SAlexander V. Chernikov 
5769f7d47b0SAlexander V. Chernikov 	rnh = (struct radix_node_head *)(ti->xstate);
5779f7d47b0SAlexander V. Chernikov 	rnh->rnh_walktree(rnh, (walktree_f_t *)f, arg);
5789f7d47b0SAlexander V. Chernikov }
5799f7d47b0SAlexander V. Chernikov 
5809f7d47b0SAlexander V. Chernikov 
5819f7d47b0SAlexander V. Chernikov #ifdef INET6
5829f7d47b0SAlexander V. Chernikov static inline void
5839f7d47b0SAlexander V. Chernikov ipv6_writemask(struct in6_addr *addr6, uint8_t mask)
5849f7d47b0SAlexander V. Chernikov {
5859f7d47b0SAlexander V. Chernikov 	uint32_t *cp;
5869f7d47b0SAlexander V. Chernikov 
5879f7d47b0SAlexander V. Chernikov 	for (cp = (uint32_t *)addr6; mask >= 32; mask -= 32)
5889f7d47b0SAlexander V. Chernikov 		*cp++ = 0xFFFFFFFF;
5899f7d47b0SAlexander V. Chernikov 	*cp = htonl(mask ? ~((1 << (32 - mask)) - 1) : 0);
5909f7d47b0SAlexander V. Chernikov }
5919f7d47b0SAlexander V. Chernikov #endif
5929f7d47b0SAlexander V. Chernikov 
5932e324d29SAlexander V. Chernikov static void
5942e324d29SAlexander V. Chernikov tei_to_sockaddr_ent(struct tentry_info *tei, struct sockaddr *sa,
5952e324d29SAlexander V. Chernikov     struct sockaddr *ma, int *set_mask)
5962e324d29SAlexander V. Chernikov {
5972e324d29SAlexander V. Chernikov 	int mlen;
598*9fe15d06SAlexander V. Chernikov #ifdef INET
5992e324d29SAlexander V. Chernikov 	struct sockaddr_in *addr, *mask;
600*9fe15d06SAlexander V. Chernikov #endif
601*9fe15d06SAlexander V. Chernikov #ifdef INET6
602720ee730SAlexander V. Chernikov 	struct sa_in6 *addr6, *mask6;
603*9fe15d06SAlexander V. Chernikov #endif
6042e324d29SAlexander V. Chernikov 	in_addr_t a4;
6052e324d29SAlexander V. Chernikov 
6062e324d29SAlexander V. Chernikov 	mlen = tei->masklen;
6072e324d29SAlexander V. Chernikov 
6082e324d29SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
6092e324d29SAlexander V. Chernikov #ifdef INET
6102e324d29SAlexander V. Chernikov 		addr = (struct sockaddr_in *)sa;
6112e324d29SAlexander V. Chernikov 		mask = (struct sockaddr_in *)ma;
6122e324d29SAlexander V. Chernikov 		/* Set 'total' structure length */
6132e324d29SAlexander V. Chernikov 		KEY_LEN(*addr) = KEY_LEN_INET;
6142e324d29SAlexander V. Chernikov 		KEY_LEN(*mask) = KEY_LEN_INET;
6152e324d29SAlexander V. Chernikov 		addr->sin_family = AF_INET;
6162e324d29SAlexander V. Chernikov 		mask->sin_addr.s_addr =
6172e324d29SAlexander V. Chernikov 		    htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);
6182e324d29SAlexander V. Chernikov 		a4 = *((in_addr_t *)tei->paddr);
6192e324d29SAlexander V. Chernikov 		addr->sin_addr.s_addr = a4 & mask->sin_addr.s_addr;
6202e324d29SAlexander V. Chernikov 		if (mlen != 32)
6212e324d29SAlexander V. Chernikov 			*set_mask = 1;
6222e324d29SAlexander V. Chernikov 		else
6232e324d29SAlexander V. Chernikov 			*set_mask = 0;
6242e324d29SAlexander V. Chernikov #endif
6252e324d29SAlexander V. Chernikov #ifdef INET6
6262e324d29SAlexander V. Chernikov 	} else if (tei->subtype == AF_INET6) {
6272e324d29SAlexander V. Chernikov 		/* IPv6 case */
628720ee730SAlexander V. Chernikov 		addr6 = (struct sa_in6 *)sa;
629720ee730SAlexander V. Chernikov 		mask6 = (struct sa_in6 *)ma;
6302e324d29SAlexander V. Chernikov 		/* Set 'total' structure length */
6312e324d29SAlexander V. Chernikov 		KEY_LEN(*addr6) = KEY_LEN_INET6;
6322e324d29SAlexander V. Chernikov 		KEY_LEN(*mask6) = KEY_LEN_INET6;
6332e324d29SAlexander V. Chernikov 		addr6->sin6_family = AF_INET6;
6342e324d29SAlexander V. Chernikov 		ipv6_writemask(&mask6->sin6_addr, mlen);
6352e324d29SAlexander V. Chernikov 		memcpy(&addr6->sin6_addr, tei->paddr, sizeof(struct in6_addr));
6362e324d29SAlexander V. Chernikov 		APPLY_MASK(&addr6->sin6_addr, &mask6->sin6_addr);
6372e324d29SAlexander V. Chernikov 		if (mlen != 128)
6382e324d29SAlexander V. Chernikov 			*set_mask = 1;
6392e324d29SAlexander V. Chernikov 		else
6402e324d29SAlexander V. Chernikov 			*set_mask = 0;
6412e324d29SAlexander V. Chernikov 	}
6422e324d29SAlexander V. Chernikov #endif
6432e324d29SAlexander V. Chernikov }
6449f7d47b0SAlexander V. Chernikov 
6459f7d47b0SAlexander V. Chernikov static int
646a399f8beSAlexander V. Chernikov ta_prepare_add_radix(struct ip_fw_chain *ch, struct tentry_info *tei,
64768394ec8SAlexander V. Chernikov     void *ta_buf)
6489f7d47b0SAlexander V. Chernikov {
649c21034b7SAlexander V. Chernikov 	struct ta_buf_radix *tb;
650c21034b7SAlexander V. Chernikov 	struct radix_addr_entry *ent;
651c21034b7SAlexander V. Chernikov 	struct radix_addr_xentry *xent;
6522e324d29SAlexander V. Chernikov 	struct sockaddr *addr, *mask;
6532e324d29SAlexander V. Chernikov 	int mlen, set_mask;
6549f7d47b0SAlexander V. Chernikov 
655c21034b7SAlexander V. Chernikov 	tb = (struct ta_buf_radix *)ta_buf;
6569f7d47b0SAlexander V. Chernikov 
6579f7d47b0SAlexander V. Chernikov 	mlen = tei->masklen;
6582e324d29SAlexander V. Chernikov 	set_mask = 0;
6599f7d47b0SAlexander V. Chernikov 
660ac35ff17SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
6619f7d47b0SAlexander V. Chernikov #ifdef INET
6629f7d47b0SAlexander V. Chernikov 		if (mlen > 32)
6639f7d47b0SAlexander V. Chernikov 			return (EINVAL);
6649f7d47b0SAlexander V. Chernikov 		ent = malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO);
665e0a8b9eeSAlexander V. Chernikov 		ent->masklen = mlen;
6662e324d29SAlexander V. Chernikov 
6672e324d29SAlexander V. Chernikov 		addr = (struct sockaddr *)&ent->addr;
6682e324d29SAlexander V. Chernikov 		mask = (struct sockaddr *)&tb->addr.a4.ma;
6699f7d47b0SAlexander V. Chernikov 		tb->ent_ptr = ent;
6709f7d47b0SAlexander V. Chernikov #endif
6719f7d47b0SAlexander V. Chernikov #ifdef INET6
672ac35ff17SAlexander V. Chernikov 	} else if (tei->subtype == AF_INET6) {
6739f7d47b0SAlexander V. Chernikov 		/* IPv6 case */
6749f7d47b0SAlexander V. Chernikov 		if (mlen > 128)
6759f7d47b0SAlexander V. Chernikov 			return (EINVAL);
6769f7d47b0SAlexander V. Chernikov 		xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO);
677e0a8b9eeSAlexander V. Chernikov 		xent->masklen = mlen;
6782e324d29SAlexander V. Chernikov 
6792e324d29SAlexander V. Chernikov 		addr = (struct sockaddr *)&xent->addr6;
6802e324d29SAlexander V. Chernikov 		mask = (struct sockaddr *)&tb->addr.a6.ma;
6819f7d47b0SAlexander V. Chernikov 		tb->ent_ptr = xent;
6829f7d47b0SAlexander V. Chernikov #endif
6839f7d47b0SAlexander V. Chernikov 	} else {
6849f7d47b0SAlexander V. Chernikov 		/* Unknown CIDR type */
6859f7d47b0SAlexander V. Chernikov 		return (EINVAL);
6869f7d47b0SAlexander V. Chernikov 	}
6879f7d47b0SAlexander V. Chernikov 
6882e324d29SAlexander V. Chernikov 	tei_to_sockaddr_ent(tei, addr, mask, &set_mask);
6892e324d29SAlexander V. Chernikov 	/* Set pointers */
6902e324d29SAlexander V. Chernikov 	tb->addr_ptr = addr;
6912e324d29SAlexander V. Chernikov 	if (set_mask != 0)
6922e324d29SAlexander V. Chernikov 		tb->mask_ptr = mask;
6932e324d29SAlexander V. Chernikov 
6949f7d47b0SAlexander V. Chernikov 	return (0);
6959f7d47b0SAlexander V. Chernikov }
6969f7d47b0SAlexander V. Chernikov 
6979f7d47b0SAlexander V. Chernikov static int
698a399f8beSAlexander V. Chernikov ta_add_radix(void *ta_state, struct table_info *ti, struct tentry_info *tei,
699b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
7009f7d47b0SAlexander V. Chernikov {
7015f379342SAlexander V. Chernikov 	struct radix_cfg *cfg;
7029f7d47b0SAlexander V. Chernikov 	struct radix_node_head *rnh;
7039f7d47b0SAlexander V. Chernikov 	struct radix_node *rn;
704c21034b7SAlexander V. Chernikov 	struct ta_buf_radix *tb;
705648e8380SAlexander V. Chernikov 	uint32_t *old_value, value;
7069f7d47b0SAlexander V. Chernikov 
7075f379342SAlexander V. Chernikov 	cfg = (struct radix_cfg *)ta_state;
708c21034b7SAlexander V. Chernikov 	tb = (struct ta_buf_radix *)ta_buf;
7099f7d47b0SAlexander V. Chernikov 
71013263632SAlexander V. Chernikov 	/* Save current entry value from @tei */
71113263632SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
7129f7d47b0SAlexander V. Chernikov 		rnh = ti->state;
71313263632SAlexander V. Chernikov 		((struct radix_addr_entry *)tb->ent_ptr)->value = tei->value;
71413263632SAlexander V. Chernikov 	} else {
7159f7d47b0SAlexander V. Chernikov 		rnh = ti->xstate;
71613263632SAlexander V. Chernikov 		((struct radix_addr_xentry *)tb->ent_ptr)->value = tei->value;
71713263632SAlexander V. Chernikov 	}
7189f7d47b0SAlexander V. Chernikov 
7194c0c07a5SAlexander V. Chernikov 	/* Search for an entry first */
7204c0c07a5SAlexander V. Chernikov 	rn = rnh->rnh_lookup(tb->addr_ptr, tb->mask_ptr, rnh);
7214c0c07a5SAlexander V. Chernikov 	if (rn != NULL) {
722ac35ff17SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
7239f7d47b0SAlexander V. Chernikov 			return (EEXIST);
724ac35ff17SAlexander V. Chernikov 		/* Record already exists. Update value if we're asked to */
725648e8380SAlexander V. Chernikov 		if (tei->subtype == AF_INET)
726c21034b7SAlexander V. Chernikov 			old_value = &((struct radix_addr_entry *)rn)->value;
727648e8380SAlexander V. Chernikov 		else
728c21034b7SAlexander V. Chernikov 			old_value = &((struct radix_addr_xentry *)rn)->value;
729648e8380SAlexander V. Chernikov 
730648e8380SAlexander V. Chernikov 		value = *old_value;
731648e8380SAlexander V. Chernikov 		*old_value = tei->value;
732648e8380SAlexander V. Chernikov 		tei->value = value;
733ac35ff17SAlexander V. Chernikov 
734ac35ff17SAlexander V. Chernikov 		/* Indicate that update has happened instead of addition */
735ac35ff17SAlexander V. Chernikov 		tei->flags |= TEI_FLAGS_UPDATED;
736adea6201SAlexander V. Chernikov 		*pnum = 0;
737e0a8b9eeSAlexander V. Chernikov 
738e0a8b9eeSAlexander V. Chernikov 		return (0);
739ac35ff17SAlexander V. Chernikov 	}
740ac35ff17SAlexander V. Chernikov 
7414c0c07a5SAlexander V. Chernikov 	if ((tei->flags & TEI_FLAGS_DONTADD) != 0)
7424c0c07a5SAlexander V. Chernikov 		return (EFBIG);
7434c0c07a5SAlexander V. Chernikov 
7444c0c07a5SAlexander V. Chernikov 	rn = rnh->rnh_addaddr(tb->addr_ptr, tb->mask_ptr, rnh, tb->ent_ptr);
7454c0c07a5SAlexander V. Chernikov 	if (rn == NULL) {
7464c0c07a5SAlexander V. Chernikov 		/* Unknown error */
7474c0c07a5SAlexander V. Chernikov 		return (EINVAL);
7484c0c07a5SAlexander V. Chernikov 	}
7494c0c07a5SAlexander V. Chernikov 
7505f379342SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
7515f379342SAlexander V. Chernikov 		cfg->count4++;
7525f379342SAlexander V. Chernikov 	else
7535f379342SAlexander V. Chernikov 		cfg->count6++;
754ac35ff17SAlexander V. Chernikov 	tb->ent_ptr = NULL;
755adea6201SAlexander V. Chernikov 	*pnum = 1;
7569f7d47b0SAlexander V. Chernikov 
7579f7d47b0SAlexander V. Chernikov 	return (0);
7589f7d47b0SAlexander V. Chernikov }
7599f7d47b0SAlexander V. Chernikov 
7609f7d47b0SAlexander V. Chernikov static int
761a399f8beSAlexander V. Chernikov ta_prepare_del_radix(struct ip_fw_chain *ch, struct tentry_info *tei,
76268394ec8SAlexander V. Chernikov     void *ta_buf)
7639f7d47b0SAlexander V. Chernikov {
764c21034b7SAlexander V. Chernikov 	struct ta_buf_radix *tb;
7652e324d29SAlexander V. Chernikov 	struct sockaddr *addr, *mask;
7662e324d29SAlexander V. Chernikov 	int mlen, set_mask;
7679f7d47b0SAlexander V. Chernikov 
768c21034b7SAlexander V. Chernikov 	tb = (struct ta_buf_radix *)ta_buf;
7699f7d47b0SAlexander V. Chernikov 
7709f7d47b0SAlexander V. Chernikov 	mlen = tei->masklen;
7712e324d29SAlexander V. Chernikov 	set_mask = 0;
7729f7d47b0SAlexander V. Chernikov 
773ac35ff17SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
774e0a8b9eeSAlexander V. Chernikov 		if (mlen > 32)
775e0a8b9eeSAlexander V. Chernikov 			return (EINVAL);
7762e324d29SAlexander V. Chernikov 
7772e324d29SAlexander V. Chernikov 		addr = (struct sockaddr *)&tb->addr.a4.sa;
7782e324d29SAlexander V. Chernikov 		mask = (struct sockaddr *)&tb->addr.a4.ma;
7799f7d47b0SAlexander V. Chernikov #ifdef INET6
780ac35ff17SAlexander V. Chernikov 	} else if (tei->subtype == AF_INET6) {
7819f7d47b0SAlexander V. Chernikov 		if (mlen > 128)
7829f7d47b0SAlexander V. Chernikov 			return (EINVAL);
7832e324d29SAlexander V. Chernikov 
7842e324d29SAlexander V. Chernikov 		addr = (struct sockaddr *)&tb->addr.a6.sa;
7852e324d29SAlexander V. Chernikov 		mask = (struct sockaddr *)&tb->addr.a6.ma;
7869f7d47b0SAlexander V. Chernikov #endif
7879f7d47b0SAlexander V. Chernikov 	} else
7889f7d47b0SAlexander V. Chernikov 		return (EINVAL);
7899f7d47b0SAlexander V. Chernikov 
7902e324d29SAlexander V. Chernikov 	tei_to_sockaddr_ent(tei, addr, mask, &set_mask);
7912e324d29SAlexander V. Chernikov 	tb->addr_ptr = addr;
7922e324d29SAlexander V. Chernikov 	if (set_mask != 0)
7932e324d29SAlexander V. Chernikov 		tb->mask_ptr = mask;
7942e324d29SAlexander V. Chernikov 
7959f7d47b0SAlexander V. Chernikov 	return (0);
7969f7d47b0SAlexander V. Chernikov }
7979f7d47b0SAlexander V. Chernikov 
7989f7d47b0SAlexander V. Chernikov static int
799a399f8beSAlexander V. Chernikov ta_del_radix(void *ta_state, struct table_info *ti, struct tentry_info *tei,
800b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
8019f7d47b0SAlexander V. Chernikov {
8025f379342SAlexander V. Chernikov 	struct radix_cfg *cfg;
8039f7d47b0SAlexander V. Chernikov 	struct radix_node_head *rnh;
8049f7d47b0SAlexander V. Chernikov 	struct radix_node *rn;
805c21034b7SAlexander V. Chernikov 	struct ta_buf_radix *tb;
8069f7d47b0SAlexander V. Chernikov 
8075f379342SAlexander V. Chernikov 	cfg = (struct radix_cfg *)ta_state;
808c21034b7SAlexander V. Chernikov 	tb = (struct ta_buf_radix *)ta_buf;
8099f7d47b0SAlexander V. Chernikov 
810ac35ff17SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
8119f7d47b0SAlexander V. Chernikov 		rnh = ti->state;
8129f7d47b0SAlexander V. Chernikov 	else
8139f7d47b0SAlexander V. Chernikov 		rnh = ti->xstate;
8149f7d47b0SAlexander V. Chernikov 
8159f7d47b0SAlexander V. Chernikov 	rn = rnh->rnh_deladdr(tb->addr_ptr, tb->mask_ptr, rnh);
8169f7d47b0SAlexander V. Chernikov 
8173a845e10SAlexander V. Chernikov 	if (rn == NULL)
8183a845e10SAlexander V. Chernikov 		return (ENOENT);
8193a845e10SAlexander V. Chernikov 
820648e8380SAlexander V. Chernikov 	/* Save entry value to @tei */
821648e8380SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
822c21034b7SAlexander V. Chernikov 		tei->value = ((struct radix_addr_entry *)rn)->value;
823648e8380SAlexander V. Chernikov 	else
824c21034b7SAlexander V. Chernikov 		tei->value = ((struct radix_addr_xentry *)rn)->value;
825648e8380SAlexander V. Chernikov 
8269f7d47b0SAlexander V. Chernikov 	tb->ent_ptr = rn;
8279f7d47b0SAlexander V. Chernikov 
8285f379342SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
8295f379342SAlexander V. Chernikov 		cfg->count4--;
8305f379342SAlexander V. Chernikov 	else
8315f379342SAlexander V. Chernikov 		cfg->count6--;
832adea6201SAlexander V. Chernikov 	*pnum = 1;
833adea6201SAlexander V. Chernikov 
8349f7d47b0SAlexander V. Chernikov 	return (0);
8359f7d47b0SAlexander V. Chernikov }
8369f7d47b0SAlexander V. Chernikov 
8379f7d47b0SAlexander V. Chernikov static void
838a399f8beSAlexander V. Chernikov ta_flush_radix_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
83968394ec8SAlexander V. Chernikov     void *ta_buf)
8409f7d47b0SAlexander V. Chernikov {
841c21034b7SAlexander V. Chernikov 	struct ta_buf_radix *tb;
8429f7d47b0SAlexander V. Chernikov 
843c21034b7SAlexander V. Chernikov 	tb = (struct ta_buf_radix *)ta_buf;
8449f7d47b0SAlexander V. Chernikov 
845ac35ff17SAlexander V. Chernikov 	if (tb->ent_ptr != NULL)
8469f7d47b0SAlexander V. Chernikov 		free(tb->ent_ptr, M_IPFW_TBL);
8479f7d47b0SAlexander V. Chernikov }
8489f7d47b0SAlexander V. Chernikov 
849b6ee846eSAlexander V. Chernikov static int
850301290bcSAlexander V. Chernikov ta_need_modify_radix(void *ta_state, struct table_info *ti, uint32_t count,
851b6ee846eSAlexander V. Chernikov     uint64_t *pflags)
852b6ee846eSAlexander V. Chernikov {
853b6ee846eSAlexander V. Chernikov 
854b6ee846eSAlexander V. Chernikov 	/*
8550bce0c23SAlexander V. Chernikov 	 * radix does not require additional memory allocations
856b6ee846eSAlexander V. Chernikov 	 * other than nodes itself. Adding new masks to the tree do
857b6ee846eSAlexander V. Chernikov 	 * but we don't have any API to call (and we don't known which
858b6ee846eSAlexander V. Chernikov 	 * sizes do we need).
859b6ee846eSAlexander V. Chernikov 	 */
860301290bcSAlexander V. Chernikov 	return (0);
861b6ee846eSAlexander V. Chernikov }
862b6ee846eSAlexander V. Chernikov 
863c21034b7SAlexander V. Chernikov struct table_algo addr_radix = {
864c21034b7SAlexander V. Chernikov 	.name		= "addr:radix",
865c21034b7SAlexander V. Chernikov 	.type		= IPFW_TABLE_ADDR,
86657a1cf95SAlexander V. Chernikov 	.flags		= TA_FLAG_DEFAULT,
867c21034b7SAlexander V. Chernikov 	.ta_buf_size	= sizeof(struct ta_buf_radix),
8689f7d47b0SAlexander V. Chernikov 	.init		= ta_init_radix,
8699f7d47b0SAlexander V. Chernikov 	.destroy	= ta_destroy_radix,
870a399f8beSAlexander V. Chernikov 	.prepare_add	= ta_prepare_add_radix,
871a399f8beSAlexander V. Chernikov 	.prepare_del	= ta_prepare_del_radix,
872a399f8beSAlexander V. Chernikov 	.add		= ta_add_radix,
873a399f8beSAlexander V. Chernikov 	.del		= ta_del_radix,
874a399f8beSAlexander V. Chernikov 	.flush_entry	= ta_flush_radix_entry,
8759f7d47b0SAlexander V. Chernikov 	.foreach	= ta_foreach_radix,
87681d3153dSAlexander V. Chernikov 	.dump_tentry	= ta_dump_radix_tentry,
87781d3153dSAlexander V. Chernikov 	.find_tentry	= ta_find_radix_tentry,
8785f379342SAlexander V. Chernikov 	.dump_tinfo	= ta_dump_radix_tinfo,
879301290bcSAlexander V. Chernikov 	.need_modify	= ta_need_modify_radix,
8809f7d47b0SAlexander V. Chernikov };
8819f7d47b0SAlexander V. Chernikov 
8829f7d47b0SAlexander V. Chernikov 
8839f7d47b0SAlexander V. Chernikov /*
884c21034b7SAlexander V. Chernikov  * addr:hash cmds
88574b941f0SAlexander V. Chernikov  *
88674b941f0SAlexander V. Chernikov  *
88774b941f0SAlexander V. Chernikov  * ti->data:
88874b941f0SAlexander V. Chernikov  * [inv.mask4][inv.mask6][log2hsize4][log2hsize6]
88974b941f0SAlexander V. Chernikov  * [        8][        8[          8][         8]
89074b941f0SAlexander V. Chernikov  *
89174b941f0SAlexander V. Chernikov  * inv.mask4: 32 - mask
89274b941f0SAlexander V. Chernikov  * inv.mask6:
89374b941f0SAlexander V. Chernikov  * 1) _slow lookup: mask
89474b941f0SAlexander V. Chernikov  * 2) _aligned: (128 - mask) / 8
89574b941f0SAlexander V. Chernikov  * 3) _64: 8
896ce2817b5SAlexander V. Chernikov  *
897ce2817b5SAlexander V. Chernikov  *
898ce2817b5SAlexander V. Chernikov  * pflags:
899ce2817b5SAlexander V. Chernikov  * [v4=1/v6=0][hsize]
900ce2817b5SAlexander V. Chernikov  * [       32][   32]
90174b941f0SAlexander V. Chernikov  */
90274b941f0SAlexander V. Chernikov 
90374b941f0SAlexander V. Chernikov struct chashentry;
90474b941f0SAlexander V. Chernikov 
90574b941f0SAlexander V. Chernikov SLIST_HEAD(chashbhead, chashentry);
90674b941f0SAlexander V. Chernikov 
90774b941f0SAlexander V. Chernikov struct chash_cfg {
90874b941f0SAlexander V. Chernikov 	struct chashbhead *head4;
90974b941f0SAlexander V. Chernikov 	struct chashbhead *head6;
91074b941f0SAlexander V. Chernikov 	size_t	size4;
91174b941f0SAlexander V. Chernikov 	size_t	size6;
912ce2817b5SAlexander V. Chernikov 	size_t	items4;
913ce2817b5SAlexander V. Chernikov 	size_t	items6;
91474b941f0SAlexander V. Chernikov 	uint8_t	mask4;
91574b941f0SAlexander V. Chernikov 	uint8_t	mask6;
91674b941f0SAlexander V. Chernikov };
91774b941f0SAlexander V. Chernikov 
91874b941f0SAlexander V. Chernikov struct chashentry {
91974b941f0SAlexander V. Chernikov 	SLIST_ENTRY(chashentry)	next;
92074b941f0SAlexander V. Chernikov 	uint32_t	value;
92174b941f0SAlexander V. Chernikov 	uint32_t	type;
92274b941f0SAlexander V. Chernikov 	union {
92374b941f0SAlexander V. Chernikov 		uint32_t	a4;	/* Host format */
92474b941f0SAlexander V. Chernikov 		struct in6_addr	a6;	/* Network format */
92574b941f0SAlexander V. Chernikov 	} a;
92674b941f0SAlexander V. Chernikov };
92774b941f0SAlexander V. Chernikov 
9280bce0c23SAlexander V. Chernikov struct ta_buf_chash
9290bce0c23SAlexander V. Chernikov {
9300bce0c23SAlexander V. Chernikov 	void *ent_ptr;
9310bce0c23SAlexander V. Chernikov 	struct chashentry ent;
9320bce0c23SAlexander V. Chernikov };
9330bce0c23SAlexander V. Chernikov 
934*9fe15d06SAlexander V. Chernikov static __inline uint32_t hash_ip(uint32_t addr, int hsize);
935*9fe15d06SAlexander V. Chernikov static __inline uint32_t hash_ip6(struct in6_addr *addr6, int hsize);
936*9fe15d06SAlexander V. Chernikov static __inline uint16_t hash_ip64(struct in6_addr *addr6, int hsize);
937*9fe15d06SAlexander V. Chernikov static __inline uint32_t hash_ip6_slow(struct in6_addr *addr6, void *key,
938*9fe15d06SAlexander V. Chernikov     int mask, int hsize);
939*9fe15d06SAlexander V. Chernikov static __inline uint32_t hash_ip6_al(struct in6_addr *addr6, void *key, int mask,
940*9fe15d06SAlexander V. Chernikov     int hsize);
941*9fe15d06SAlexander V. Chernikov static int ta_lookup_chash_slow(struct table_info *ti, void *key, uint32_t keylen,
942*9fe15d06SAlexander V. Chernikov     uint32_t *val);
943*9fe15d06SAlexander V. Chernikov static int ta_lookup_chash_aligned(struct table_info *ti, void *key,
944*9fe15d06SAlexander V. Chernikov     uint32_t keylen, uint32_t *val);
945*9fe15d06SAlexander V. Chernikov static int ta_lookup_chash_64(struct table_info *ti, void *key, uint32_t keylen,
946*9fe15d06SAlexander V. Chernikov     uint32_t *val);
947*9fe15d06SAlexander V. Chernikov static int chash_parse_opts(struct chash_cfg *cfg, char *data);
948*9fe15d06SAlexander V. Chernikov static void ta_print_chash_config(void *ta_state, struct table_info *ti,
949*9fe15d06SAlexander V. Chernikov     char *buf, size_t bufsize);
950*9fe15d06SAlexander V. Chernikov static int log2(uint32_t v);
951*9fe15d06SAlexander V. Chernikov static int ta_init_chash(struct ip_fw_chain *ch, void **ta_state,
952*9fe15d06SAlexander V. Chernikov     struct table_info *ti, char *data, uint8_t tflags);
953*9fe15d06SAlexander V. Chernikov static void ta_destroy_chash(void *ta_state, struct table_info *ti);
954*9fe15d06SAlexander V. Chernikov static void ta_dump_chash_tinfo(void *ta_state, struct table_info *ti,
955*9fe15d06SAlexander V. Chernikov     ipfw_ta_tinfo *tinfo);
956*9fe15d06SAlexander V. Chernikov static int ta_dump_chash_tentry(void *ta_state, struct table_info *ti,
957*9fe15d06SAlexander V. Chernikov     void *e, ipfw_obj_tentry *tent);
958*9fe15d06SAlexander V. Chernikov static uint32_t hash_ent(struct chashentry *ent, int af, int mlen,
959*9fe15d06SAlexander V. Chernikov     uint32_t size);
960*9fe15d06SAlexander V. Chernikov static int tei_to_chash_ent(struct tentry_info *tei, struct chashentry *ent);
961*9fe15d06SAlexander V. Chernikov static int ta_find_chash_tentry(void *ta_state, struct table_info *ti,
962*9fe15d06SAlexander V. Chernikov     ipfw_obj_tentry *tent);
963*9fe15d06SAlexander V. Chernikov static void ta_foreach_chash(void *ta_state, struct table_info *ti,
964*9fe15d06SAlexander V. Chernikov     ta_foreach_f *f, void *arg);
965*9fe15d06SAlexander V. Chernikov static int ta_prepare_add_chash(struct ip_fw_chain *ch, struct tentry_info *tei,
966*9fe15d06SAlexander V. Chernikov     void *ta_buf);
967*9fe15d06SAlexander V. Chernikov static int ta_add_chash(void *ta_state, struct table_info *ti,
968*9fe15d06SAlexander V. Chernikov     struct tentry_info *tei, void *ta_buf, uint32_t *pnum);
969*9fe15d06SAlexander V. Chernikov static int ta_prepare_del_chash(struct ip_fw_chain *ch, struct tentry_info *tei,
970*9fe15d06SAlexander V. Chernikov     void *ta_buf);
971*9fe15d06SAlexander V. Chernikov static int ta_del_chash(void *ta_state, struct table_info *ti,
972*9fe15d06SAlexander V. Chernikov     struct tentry_info *tei, void *ta_buf, uint32_t *pnum);
973*9fe15d06SAlexander V. Chernikov static void ta_flush_chash_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
974*9fe15d06SAlexander V. Chernikov     void *ta_buf);
975*9fe15d06SAlexander V. Chernikov static int ta_need_modify_chash(void *ta_state, struct table_info *ti,
976*9fe15d06SAlexander V. Chernikov     uint32_t count, uint64_t *pflags);
977*9fe15d06SAlexander V. Chernikov static int ta_prepare_mod_chash(void *ta_buf, uint64_t *pflags);
978*9fe15d06SAlexander V. Chernikov static int ta_fill_mod_chash(void *ta_state, struct table_info *ti, void *ta_buf,
979*9fe15d06SAlexander V. Chernikov     uint64_t *pflags);
980*9fe15d06SAlexander V. Chernikov static void ta_modify_chash(void *ta_state, struct table_info *ti, void *ta_buf,
981*9fe15d06SAlexander V. Chernikov     uint64_t pflags);
982*9fe15d06SAlexander V. Chernikov static void ta_flush_mod_chash(void *ta_buf);
983*9fe15d06SAlexander V. Chernikov 
9840bce0c23SAlexander V. Chernikov 
98574b941f0SAlexander V. Chernikov static __inline uint32_t
98674b941f0SAlexander V. Chernikov hash_ip(uint32_t addr, int hsize)
98774b941f0SAlexander V. Chernikov {
98874b941f0SAlexander V. Chernikov 
98974b941f0SAlexander V. Chernikov 	return (addr % (hsize - 1));
99074b941f0SAlexander V. Chernikov }
99174b941f0SAlexander V. Chernikov 
99274b941f0SAlexander V. Chernikov static __inline uint32_t
99374b941f0SAlexander V. Chernikov hash_ip6(struct in6_addr *addr6, int hsize)
99474b941f0SAlexander V. Chernikov {
99574b941f0SAlexander V. Chernikov 	uint32_t i;
99674b941f0SAlexander V. Chernikov 
99774b941f0SAlexander V. Chernikov 	i = addr6->s6_addr32[0] ^ addr6->s6_addr32[1] ^
99874b941f0SAlexander V. Chernikov 	    addr6->s6_addr32[2] ^ addr6->s6_addr32[3];
99974b941f0SAlexander V. Chernikov 
100074b941f0SAlexander V. Chernikov 	return (i % (hsize - 1));
100174b941f0SAlexander V. Chernikov }
100274b941f0SAlexander V. Chernikov 
100374b941f0SAlexander V. Chernikov 
100474b941f0SAlexander V. Chernikov static __inline uint16_t
100574b941f0SAlexander V. Chernikov hash_ip64(struct in6_addr *addr6, int hsize)
100674b941f0SAlexander V. Chernikov {
100774b941f0SAlexander V. Chernikov 	uint32_t i;
100874b941f0SAlexander V. Chernikov 
100974b941f0SAlexander V. Chernikov 	i = addr6->s6_addr32[0] ^ addr6->s6_addr32[1];
101074b941f0SAlexander V. Chernikov 
101174b941f0SAlexander V. Chernikov 	return (i % (hsize - 1));
101274b941f0SAlexander V. Chernikov }
101374b941f0SAlexander V. Chernikov 
101474b941f0SAlexander V. Chernikov 
101574b941f0SAlexander V. Chernikov static __inline uint32_t
101674b941f0SAlexander V. Chernikov hash_ip6_slow(struct in6_addr *addr6, void *key, int mask, int hsize)
101774b941f0SAlexander V. Chernikov {
101874b941f0SAlexander V. Chernikov 	struct in6_addr mask6;
101974b941f0SAlexander V. Chernikov 
102074b941f0SAlexander V. Chernikov 	ipv6_writemask(&mask6, mask);
102174b941f0SAlexander V. Chernikov 	memcpy(addr6, key, sizeof(struct in6_addr));
102274b941f0SAlexander V. Chernikov 	APPLY_MASK(addr6, &mask6);
102374b941f0SAlexander V. Chernikov 	return (hash_ip6(addr6, hsize));
102474b941f0SAlexander V. Chernikov }
102574b941f0SAlexander V. Chernikov 
102674b941f0SAlexander V. Chernikov static __inline uint32_t
102774b941f0SAlexander V. Chernikov hash_ip6_al(struct in6_addr *addr6, void *key, int mask, int hsize)
102874b941f0SAlexander V. Chernikov {
102974b941f0SAlexander V. Chernikov 	uint64_t *paddr;
103074b941f0SAlexander V. Chernikov 
103174b941f0SAlexander V. Chernikov 	paddr = (uint64_t *)addr6;
103274b941f0SAlexander V. Chernikov 	*paddr = 0;
103374b941f0SAlexander V. Chernikov 	*(paddr + 1) = 0;
103474b941f0SAlexander V. Chernikov 	memcpy(addr6, key, mask);
103574b941f0SAlexander V. Chernikov 	return (hash_ip6(addr6, hsize));
103674b941f0SAlexander V. Chernikov }
103774b941f0SAlexander V. Chernikov 
103874b941f0SAlexander V. Chernikov static int
103974b941f0SAlexander V. Chernikov ta_lookup_chash_slow(struct table_info *ti, void *key, uint32_t keylen,
104074b941f0SAlexander V. Chernikov     uint32_t *val)
104174b941f0SAlexander V. Chernikov {
104274b941f0SAlexander V. Chernikov 	struct chashbhead *head;
104374b941f0SAlexander V. Chernikov 	struct chashentry *ent;
104474b941f0SAlexander V. Chernikov 	uint16_t hash, hsize;
104574b941f0SAlexander V. Chernikov 	uint8_t imask;
104674b941f0SAlexander V. Chernikov 
104774b941f0SAlexander V. Chernikov 	if (keylen == sizeof(in_addr_t)) {
104874b941f0SAlexander V. Chernikov 		head = (struct chashbhead *)ti->state;
104974b941f0SAlexander V. Chernikov 		imask = ti->data >> 24;
105074b941f0SAlexander V. Chernikov 		hsize = 1 << ((ti->data & 0xFFFF) >> 8);
105174b941f0SAlexander V. Chernikov 		uint32_t a;
105274b941f0SAlexander V. Chernikov 		a = ntohl(*((in_addr_t *)key));
105374b941f0SAlexander V. Chernikov 		a = a >> imask;
105474b941f0SAlexander V. Chernikov 		hash = hash_ip(a, hsize);
105574b941f0SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
105674b941f0SAlexander V. Chernikov 			if (ent->a.a4 == a) {
105774b941f0SAlexander V. Chernikov 				*val = ent->value;
105874b941f0SAlexander V. Chernikov 				return (1);
105974b941f0SAlexander V. Chernikov 			}
106074b941f0SAlexander V. Chernikov 		}
106174b941f0SAlexander V. Chernikov 	} else {
106274b941f0SAlexander V. Chernikov 		/* IPv6: worst scenario: non-round mask */
106374b941f0SAlexander V. Chernikov 		struct in6_addr addr6;
106474b941f0SAlexander V. Chernikov 		head = (struct chashbhead *)ti->xstate;
106574b941f0SAlexander V. Chernikov 		imask = (ti->data & 0xFF0000) >> 16;
106674b941f0SAlexander V. Chernikov 		hsize = 1 << (ti->data & 0xFF);
106774b941f0SAlexander V. Chernikov 		hash = hash_ip6_slow(&addr6, key, imask, hsize);
106874b941f0SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
106974b941f0SAlexander V. Chernikov 			if (memcmp(&ent->a.a6, &addr6, 16) == 0) {
107074b941f0SAlexander V. Chernikov 				*val = ent->value;
107174b941f0SAlexander V. Chernikov 				return (1);
107274b941f0SAlexander V. Chernikov 			}
107374b941f0SAlexander V. Chernikov 		}
107474b941f0SAlexander V. Chernikov 	}
107574b941f0SAlexander V. Chernikov 
107674b941f0SAlexander V. Chernikov 	return (0);
107774b941f0SAlexander V. Chernikov }
107874b941f0SAlexander V. Chernikov 
107974b941f0SAlexander V. Chernikov static int
108074b941f0SAlexander V. Chernikov ta_lookup_chash_aligned(struct table_info *ti, void *key, uint32_t keylen,
108174b941f0SAlexander V. Chernikov     uint32_t *val)
108274b941f0SAlexander V. Chernikov {
108374b941f0SAlexander V. Chernikov 	struct chashbhead *head;
108474b941f0SAlexander V. Chernikov 	struct chashentry *ent;
108574b941f0SAlexander V. Chernikov 	uint16_t hash, hsize;
108674b941f0SAlexander V. Chernikov 	uint8_t imask;
108774b941f0SAlexander V. Chernikov 
108874b941f0SAlexander V. Chernikov 	if (keylen == sizeof(in_addr_t)) {
108974b941f0SAlexander V. Chernikov 		head = (struct chashbhead *)ti->state;
109074b941f0SAlexander V. Chernikov 		imask = ti->data >> 24;
109174b941f0SAlexander V. Chernikov 		hsize = 1 << ((ti->data & 0xFFFF) >> 8);
109274b941f0SAlexander V. Chernikov 		uint32_t a;
109374b941f0SAlexander V. Chernikov 		a = ntohl(*((in_addr_t *)key));
109474b941f0SAlexander V. Chernikov 		a = a >> imask;
109574b941f0SAlexander V. Chernikov 		hash = hash_ip(a, hsize);
109674b941f0SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
109774b941f0SAlexander V. Chernikov 			if (ent->a.a4 == a) {
109874b941f0SAlexander V. Chernikov 				*val = ent->value;
109974b941f0SAlexander V. Chernikov 				return (1);
110074b941f0SAlexander V. Chernikov 			}
110174b941f0SAlexander V. Chernikov 		}
110274b941f0SAlexander V. Chernikov 	} else {
110374b941f0SAlexander V. Chernikov 		/* IPv6: aligned to 8bit mask */
110474b941f0SAlexander V. Chernikov 		struct in6_addr addr6;
110574b941f0SAlexander V. Chernikov 		uint64_t *paddr, *ptmp;
110674b941f0SAlexander V. Chernikov 		head = (struct chashbhead *)ti->xstate;
110774b941f0SAlexander V. Chernikov 		imask = (ti->data & 0xFF0000) >> 16;
110874b941f0SAlexander V. Chernikov 		hsize = 1 << (ti->data & 0xFF);
110974b941f0SAlexander V. Chernikov 
111074b941f0SAlexander V. Chernikov 		hash = hash_ip6_al(&addr6, key, imask, hsize);
111174b941f0SAlexander V. Chernikov 		paddr = (uint64_t *)&addr6;
111274b941f0SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
111374b941f0SAlexander V. Chernikov 			ptmp = (uint64_t *)&ent->a.a6;
111474b941f0SAlexander V. Chernikov 			if (paddr[0] == ptmp[0] && paddr[1] == ptmp[1]) {
111574b941f0SAlexander V. Chernikov 				*val = ent->value;
111674b941f0SAlexander V. Chernikov 				return (1);
111774b941f0SAlexander V. Chernikov 			}
111874b941f0SAlexander V. Chernikov 		}
111974b941f0SAlexander V. Chernikov 	}
112074b941f0SAlexander V. Chernikov 
112174b941f0SAlexander V. Chernikov 	return (0);
112274b941f0SAlexander V. Chernikov }
112374b941f0SAlexander V. Chernikov 
112474b941f0SAlexander V. Chernikov static int
112574b941f0SAlexander V. Chernikov ta_lookup_chash_64(struct table_info *ti, void *key, uint32_t keylen,
112674b941f0SAlexander V. Chernikov     uint32_t *val)
112774b941f0SAlexander V. Chernikov {
112874b941f0SAlexander V. Chernikov 	struct chashbhead *head;
112974b941f0SAlexander V. Chernikov 	struct chashentry *ent;
113074b941f0SAlexander V. Chernikov 	uint16_t hash, hsize;
113174b941f0SAlexander V. Chernikov 	uint8_t imask;
113274b941f0SAlexander V. Chernikov 
113374b941f0SAlexander V. Chernikov 	if (keylen == sizeof(in_addr_t)) {
113474b941f0SAlexander V. Chernikov 		head = (struct chashbhead *)ti->state;
113574b941f0SAlexander V. Chernikov 		imask = ti->data >> 24;
113674b941f0SAlexander V. Chernikov 		hsize = 1 << ((ti->data & 0xFFFF) >> 8);
113774b941f0SAlexander V. Chernikov 		uint32_t a;
113874b941f0SAlexander V. Chernikov 		a = ntohl(*((in_addr_t *)key));
113974b941f0SAlexander V. Chernikov 		a = a >> imask;
114074b941f0SAlexander V. Chernikov 		hash = hash_ip(a, hsize);
114174b941f0SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
114274b941f0SAlexander V. Chernikov 			if (ent->a.a4 == a) {
114374b941f0SAlexander V. Chernikov 				*val = ent->value;
114474b941f0SAlexander V. Chernikov 				return (1);
114574b941f0SAlexander V. Chernikov 			}
114674b941f0SAlexander V. Chernikov 		}
114774b941f0SAlexander V. Chernikov 	} else {
114874b941f0SAlexander V. Chernikov 		/* IPv6: /64 */
114974b941f0SAlexander V. Chernikov 		uint64_t a6, *paddr;
115074b941f0SAlexander V. Chernikov 		head = (struct chashbhead *)ti->xstate;
115174b941f0SAlexander V. Chernikov 		paddr = (uint64_t *)key;
115274b941f0SAlexander V. Chernikov 		hsize = 1 << (ti->data & 0xFF);
115374b941f0SAlexander V. Chernikov 		a6 = *paddr;
115474b941f0SAlexander V. Chernikov 		hash = hash_ip64((struct in6_addr *)key, hsize);
115574b941f0SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
115674b941f0SAlexander V. Chernikov 			paddr = (uint64_t *)&ent->a.a6;
115774b941f0SAlexander V. Chernikov 			if (a6 == *paddr) {
115874b941f0SAlexander V. Chernikov 				*val = ent->value;
115974b941f0SAlexander V. Chernikov 				return (1);
116074b941f0SAlexander V. Chernikov 			}
116174b941f0SAlexander V. Chernikov 		}
116274b941f0SAlexander V. Chernikov 	}
116374b941f0SAlexander V. Chernikov 
116474b941f0SAlexander V. Chernikov 	return (0);
116574b941f0SAlexander V. Chernikov }
116674b941f0SAlexander V. Chernikov 
116774b941f0SAlexander V. Chernikov static int
11680bce0c23SAlexander V. Chernikov chash_parse_opts(struct chash_cfg *cfg, char *data)
116974b941f0SAlexander V. Chernikov {
117074b941f0SAlexander V. Chernikov 	char *pdel, *pend, *s;
117174b941f0SAlexander V. Chernikov 	int mask4, mask6;
117274b941f0SAlexander V. Chernikov 
11730bce0c23SAlexander V. Chernikov 	mask4 = cfg->mask4;
11740bce0c23SAlexander V. Chernikov 	mask6 = cfg->mask6;
117574b941f0SAlexander V. Chernikov 
117674b941f0SAlexander V. Chernikov 	if (data == NULL)
117774b941f0SAlexander V. Chernikov 		return (0);
117874b941f0SAlexander V. Chernikov 	if ((pdel = strchr(data, ' ')) == NULL)
117974b941f0SAlexander V. Chernikov 		return (0);
118074b941f0SAlexander V. Chernikov 	while (*pdel == ' ')
118174b941f0SAlexander V. Chernikov 		pdel++;
118274b941f0SAlexander V. Chernikov 	if (strncmp(pdel, "masks=", 6) != 0)
118374b941f0SAlexander V. Chernikov 		return (EINVAL);
118474b941f0SAlexander V. Chernikov 	if ((s = strchr(pdel, ' ')) != NULL)
118574b941f0SAlexander V. Chernikov 		*s++ = '\0';
118674b941f0SAlexander V. Chernikov 
118774b941f0SAlexander V. Chernikov 	pdel += 6;
118874b941f0SAlexander V. Chernikov 	/* Need /XX[,/YY] */
118974b941f0SAlexander V. Chernikov 	if (*pdel++ != '/')
119074b941f0SAlexander V. Chernikov 		return (EINVAL);
119174b941f0SAlexander V. Chernikov 	mask4 = strtol(pdel, &pend, 10);
119274b941f0SAlexander V. Chernikov 	if (*pend == ',') {
119374b941f0SAlexander V. Chernikov 		/* ,/YY */
119474b941f0SAlexander V. Chernikov 		pdel = pend + 1;
119574b941f0SAlexander V. Chernikov 		if (*pdel++ != '/')
119674b941f0SAlexander V. Chernikov 			return (EINVAL);
119774b941f0SAlexander V. Chernikov 		mask6 = strtol(pdel, &pend, 10);
119874b941f0SAlexander V. Chernikov 		if (*pend != '\0')
119974b941f0SAlexander V. Chernikov 			return (EINVAL);
120074b941f0SAlexander V. Chernikov 	} else if (*pend != '\0')
120174b941f0SAlexander V. Chernikov 		return (EINVAL);
120274b941f0SAlexander V. Chernikov 
120374b941f0SAlexander V. Chernikov 	if (mask4 < 0 || mask4 > 32 || mask6 < 0 || mask6 > 128)
120474b941f0SAlexander V. Chernikov 		return (EINVAL);
120574b941f0SAlexander V. Chernikov 
12060bce0c23SAlexander V. Chernikov 	cfg->mask4 = mask4;
12070bce0c23SAlexander V. Chernikov 	cfg->mask6 = mask6;
120874b941f0SAlexander V. Chernikov 
120974b941f0SAlexander V. Chernikov 	return (0);
121074b941f0SAlexander V. Chernikov }
121174b941f0SAlexander V. Chernikov 
121274b941f0SAlexander V. Chernikov static void
121374b941f0SAlexander V. Chernikov ta_print_chash_config(void *ta_state, struct table_info *ti, char *buf,
121474b941f0SAlexander V. Chernikov     size_t bufsize)
121574b941f0SAlexander V. Chernikov {
12160bce0c23SAlexander V. Chernikov 	struct chash_cfg *cfg;
121774b941f0SAlexander V. Chernikov 
12180bce0c23SAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
121974b941f0SAlexander V. Chernikov 
12200bce0c23SAlexander V. Chernikov 	if (cfg->mask4 != 32 || cfg->mask6 != 128)
1221c21034b7SAlexander V. Chernikov 		snprintf(buf, bufsize, "%s masks=/%d,/%d", "addr:hash",
12220bce0c23SAlexander V. Chernikov 		    cfg->mask4, cfg->mask6);
122374b941f0SAlexander V. Chernikov 	else
1224c21034b7SAlexander V. Chernikov 		snprintf(buf, bufsize, "%s", "addr:hash");
122574b941f0SAlexander V. Chernikov }
122674b941f0SAlexander V. Chernikov 
1227914bffb6SAlexander V. Chernikov static int
1228914bffb6SAlexander V. Chernikov log2(uint32_t v)
1229914bffb6SAlexander V. Chernikov {
1230914bffb6SAlexander V. Chernikov 	uint32_t r;
1231914bffb6SAlexander V. Chernikov 
1232914bffb6SAlexander V. Chernikov 	r = 0;
1233914bffb6SAlexander V. Chernikov 	while (v >>= 1)
1234914bffb6SAlexander V. Chernikov 		r++;
1235914bffb6SAlexander V. Chernikov 
1236914bffb6SAlexander V. Chernikov 	return (r);
1237914bffb6SAlexander V. Chernikov }
123874b941f0SAlexander V. Chernikov 
123974b941f0SAlexander V. Chernikov /*
124074b941f0SAlexander V. Chernikov  * New table.
124174b941f0SAlexander V. Chernikov  * We assume 'data' to be either NULL or the following format:
1242c21034b7SAlexander V. Chernikov  * 'addr:hash [masks=/32[,/128]]'
124374b941f0SAlexander V. Chernikov  */
124474b941f0SAlexander V. Chernikov static int
124574b941f0SAlexander V. Chernikov ta_init_chash(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
1246914bffb6SAlexander V. Chernikov     char *data, uint8_t tflags)
124774b941f0SAlexander V. Chernikov {
124874b941f0SAlexander V. Chernikov 	int error, i;
1249914bffb6SAlexander V. Chernikov 	uint32_t hsize;
12500bce0c23SAlexander V. Chernikov 	struct chash_cfg *cfg;
125174b941f0SAlexander V. Chernikov 
12520bce0c23SAlexander V. Chernikov 	cfg = malloc(sizeof(struct chash_cfg), M_IPFW, M_WAITOK | M_ZERO);
125374b941f0SAlexander V. Chernikov 
12540bce0c23SAlexander V. Chernikov 	cfg->mask4 = 32;
12550bce0c23SAlexander V. Chernikov 	cfg->mask6 = 128;
125674b941f0SAlexander V. Chernikov 
12570bce0c23SAlexander V. Chernikov 	if ((error = chash_parse_opts(cfg, data)) != 0) {
12580bce0c23SAlexander V. Chernikov 		free(cfg, M_IPFW);
125974b941f0SAlexander V. Chernikov 		return (error);
126074b941f0SAlexander V. Chernikov 	}
126174b941f0SAlexander V. Chernikov 
12620bce0c23SAlexander V. Chernikov 	cfg->size4 = 128;
12630bce0c23SAlexander V. Chernikov 	cfg->size6 = 128;
126474b941f0SAlexander V. Chernikov 
12650bce0c23SAlexander V. Chernikov 	cfg->head4 = malloc(sizeof(struct chashbhead) * cfg->size4, M_IPFW,
126674b941f0SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
12670bce0c23SAlexander V. Chernikov 	cfg->head6 = malloc(sizeof(struct chashbhead) * cfg->size6, M_IPFW,
126874b941f0SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
12690bce0c23SAlexander V. Chernikov 	for (i = 0; i < cfg->size4; i++)
12700bce0c23SAlexander V. Chernikov 		SLIST_INIT(&cfg->head4[i]);
12710bce0c23SAlexander V. Chernikov 	for (i = 0; i < cfg->size6; i++)
12720bce0c23SAlexander V. Chernikov 		SLIST_INIT(&cfg->head6[i]);
127374b941f0SAlexander V. Chernikov 
127474b941f0SAlexander V. Chernikov 
12750bce0c23SAlexander V. Chernikov 	*ta_state = cfg;
12760bce0c23SAlexander V. Chernikov 	ti->state = cfg->head4;
12770bce0c23SAlexander V. Chernikov 	ti->xstate = cfg->head6;
127874b941f0SAlexander V. Chernikov 
127974b941f0SAlexander V. Chernikov 	/* Store data depending on v6 mask length */
12800bce0c23SAlexander V. Chernikov 	hsize = log2(cfg->size4) << 8 | log2(cfg->size6);
12810bce0c23SAlexander V. Chernikov 	if (cfg->mask6 == 64) {
12820bce0c23SAlexander V. Chernikov 		ti->data = (32 - cfg->mask4) << 24 | (128 - cfg->mask6) << 16|
1283914bffb6SAlexander V. Chernikov 		    hsize;
128474b941f0SAlexander V. Chernikov 		ti->lookup = ta_lookup_chash_64;
12850bce0c23SAlexander V. Chernikov 	} else if ((cfg->mask6  % 8) == 0) {
12860bce0c23SAlexander V. Chernikov 		ti->data = (32 - cfg->mask4) << 24 |
12870bce0c23SAlexander V. Chernikov 		    cfg->mask6 << 13 | hsize;
128874b941f0SAlexander V. Chernikov 		ti->lookup = ta_lookup_chash_aligned;
128974b941f0SAlexander V. Chernikov 	} else {
129074b941f0SAlexander V. Chernikov 		/* don't do that! */
12910bce0c23SAlexander V. Chernikov 		ti->data = (32 - cfg->mask4) << 24 |
12920bce0c23SAlexander V. Chernikov 		    cfg->mask6 << 16 | hsize;
129374b941f0SAlexander V. Chernikov 		ti->lookup = ta_lookup_chash_slow;
129474b941f0SAlexander V. Chernikov 	}
129574b941f0SAlexander V. Chernikov 
129674b941f0SAlexander V. Chernikov 	return (0);
129774b941f0SAlexander V. Chernikov }
129874b941f0SAlexander V. Chernikov 
129974b941f0SAlexander V. Chernikov static void
130074b941f0SAlexander V. Chernikov ta_destroy_chash(void *ta_state, struct table_info *ti)
130174b941f0SAlexander V. Chernikov {
13020bce0c23SAlexander V. Chernikov 	struct chash_cfg *cfg;
130374b941f0SAlexander V. Chernikov 	struct chashentry *ent, *ent_next;
130474b941f0SAlexander V. Chernikov 	int i;
130574b941f0SAlexander V. Chernikov 
13060bce0c23SAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
130774b941f0SAlexander V. Chernikov 
13080bce0c23SAlexander V. Chernikov 	for (i = 0; i < cfg->size4; i++)
13090bce0c23SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &cfg->head4[i], next, ent_next)
131074b941f0SAlexander V. Chernikov 			free(ent, M_IPFW_TBL);
131174b941f0SAlexander V. Chernikov 
13120bce0c23SAlexander V. Chernikov 	for (i = 0; i < cfg->size6; i++)
13130bce0c23SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &cfg->head6[i], next, ent_next)
131474b941f0SAlexander V. Chernikov 			free(ent, M_IPFW_TBL);
131574b941f0SAlexander V. Chernikov 
13160bce0c23SAlexander V. Chernikov 	free(cfg->head4, M_IPFW);
13170bce0c23SAlexander V. Chernikov 	free(cfg->head6, M_IPFW);
1318ce2817b5SAlexander V. Chernikov 
13190bce0c23SAlexander V. Chernikov 	free(cfg, M_IPFW);
132074b941f0SAlexander V. Chernikov }
132174b941f0SAlexander V. Chernikov 
13225f379342SAlexander V. Chernikov static void
13235f379342SAlexander V. Chernikov ta_dump_chash_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo)
13245f379342SAlexander V. Chernikov {
13255f379342SAlexander V. Chernikov 	struct chash_cfg *cfg;
13265f379342SAlexander V. Chernikov 
13275f379342SAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
13285f379342SAlexander V. Chernikov 
13295f379342SAlexander V. Chernikov 	tinfo->flags = IPFW_TATFLAGS_AFDATA | IPFW_TATFLAGS_AFITEM;
13305f379342SAlexander V. Chernikov 	tinfo->taclass4 = IPFW_TACLASS_HASH;
13315f379342SAlexander V. Chernikov 	tinfo->size4 = cfg->size4;
13325f379342SAlexander V. Chernikov 	tinfo->count4 = cfg->items4;
13335f379342SAlexander V. Chernikov 	tinfo->itemsize4 = sizeof(struct chashentry);
13345f379342SAlexander V. Chernikov 	tinfo->taclass6 = IPFW_TACLASS_HASH;
13355f379342SAlexander V. Chernikov 	tinfo->size6 = cfg->size6;
13365f379342SAlexander V. Chernikov 	tinfo->count6 = cfg->items6;
13375f379342SAlexander V. Chernikov 	tinfo->itemsize6 = sizeof(struct chashentry);
13385f379342SAlexander V. Chernikov }
13395f379342SAlexander V. Chernikov 
134074b941f0SAlexander V. Chernikov static int
134174b941f0SAlexander V. Chernikov ta_dump_chash_tentry(void *ta_state, struct table_info *ti, void *e,
134274b941f0SAlexander V. Chernikov     ipfw_obj_tentry *tent)
134374b941f0SAlexander V. Chernikov {
13440bce0c23SAlexander V. Chernikov 	struct chash_cfg *cfg;
134574b941f0SAlexander V. Chernikov 	struct chashentry *ent;
134674b941f0SAlexander V. Chernikov 
13470bce0c23SAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
134874b941f0SAlexander V. Chernikov 	ent = (struct chashentry *)e;
134974b941f0SAlexander V. Chernikov 
135074b941f0SAlexander V. Chernikov 	if (ent->type == AF_INET) {
13510bce0c23SAlexander V. Chernikov 		tent->k.addr.s_addr = htonl(ent->a.a4 << (32 - cfg->mask4));
13520bce0c23SAlexander V. Chernikov 		tent->masklen = cfg->mask4;
135374b941f0SAlexander V. Chernikov 		tent->subtype = AF_INET;
13540cba2b28SAlexander V. Chernikov 		tent->v.kidx = ent->value;
135574b941f0SAlexander V. Chernikov #ifdef INET6
135674b941f0SAlexander V. Chernikov 	} else {
135774b941f0SAlexander V. Chernikov 		memcpy(&tent->k, &ent->a.a6, sizeof(struct in6_addr));
13580bce0c23SAlexander V. Chernikov 		tent->masklen = cfg->mask6;
135974b941f0SAlexander V. Chernikov 		tent->subtype = AF_INET6;
13600cba2b28SAlexander V. Chernikov 		tent->v.kidx = ent->value;
136174b941f0SAlexander V. Chernikov #endif
136274b941f0SAlexander V. Chernikov 	}
136374b941f0SAlexander V. Chernikov 
136474b941f0SAlexander V. Chernikov 	return (0);
136574b941f0SAlexander V. Chernikov }
136674b941f0SAlexander V. Chernikov 
1367ce2817b5SAlexander V. Chernikov static uint32_t
1368ce2817b5SAlexander V. Chernikov hash_ent(struct chashentry *ent, int af, int mlen, uint32_t size)
1369ce2817b5SAlexander V. Chernikov {
1370ce2817b5SAlexander V. Chernikov 	uint32_t hash;
1371ce2817b5SAlexander V. Chernikov 
1372ce2817b5SAlexander V. Chernikov 	if (af == AF_INET) {
1373ce2817b5SAlexander V. Chernikov 		hash = hash_ip(ent->a.a4, size);
1374ce2817b5SAlexander V. Chernikov 	} else {
1375ce2817b5SAlexander V. Chernikov 		if (mlen == 64)
1376ce2817b5SAlexander V. Chernikov 			hash = hash_ip64(&ent->a.a6, size);
1377ce2817b5SAlexander V. Chernikov 		else
1378ce2817b5SAlexander V. Chernikov 			hash = hash_ip6(&ent->a.a6, size);
1379ce2817b5SAlexander V. Chernikov 	}
1380ce2817b5SAlexander V. Chernikov 
1381ce2817b5SAlexander V. Chernikov 	return (hash);
1382ce2817b5SAlexander V. Chernikov }
1383ce2817b5SAlexander V. Chernikov 
1384ce2817b5SAlexander V. Chernikov static int
1385ce2817b5SAlexander V. Chernikov tei_to_chash_ent(struct tentry_info *tei, struct chashentry *ent)
1386ce2817b5SAlexander V. Chernikov {
1387ce2817b5SAlexander V. Chernikov 	struct in6_addr mask6;
1388ce2817b5SAlexander V. Chernikov 	int mlen;
1389ce2817b5SAlexander V. Chernikov 
1390ce2817b5SAlexander V. Chernikov 
1391ce2817b5SAlexander V. Chernikov 	mlen = tei->masklen;
1392ce2817b5SAlexander V. Chernikov 
1393ce2817b5SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
1394ce2817b5SAlexander V. Chernikov #ifdef INET
1395ce2817b5SAlexander V. Chernikov 		if (mlen > 32)
1396ce2817b5SAlexander V. Chernikov 			return (EINVAL);
1397ce2817b5SAlexander V. Chernikov 		ent->type = AF_INET;
1398ce2817b5SAlexander V. Chernikov 
1399ce2817b5SAlexander V. Chernikov 		/* Calculate masked address */
1400ce2817b5SAlexander V. Chernikov 		ent->a.a4 = ntohl(*((in_addr_t *)tei->paddr)) >> (32 - mlen);
1401ce2817b5SAlexander V. Chernikov #endif
1402ce2817b5SAlexander V. Chernikov #ifdef INET6
1403ce2817b5SAlexander V. Chernikov 	} else if (tei->subtype == AF_INET6) {
1404ce2817b5SAlexander V. Chernikov 		/* IPv6 case */
1405ce2817b5SAlexander V. Chernikov 		if (mlen > 128)
1406ce2817b5SAlexander V. Chernikov 			return (EINVAL);
1407ce2817b5SAlexander V. Chernikov 		ent->type = AF_INET6;
1408ce2817b5SAlexander V. Chernikov 
1409ce2817b5SAlexander V. Chernikov 		ipv6_writemask(&mask6, mlen);
1410ce2817b5SAlexander V. Chernikov 		memcpy(&ent->a.a6, tei->paddr, sizeof(struct in6_addr));
1411ce2817b5SAlexander V. Chernikov 		APPLY_MASK(&ent->a.a6, &mask6);
1412ce2817b5SAlexander V. Chernikov #endif
1413ce2817b5SAlexander V. Chernikov 	} else {
1414ce2817b5SAlexander V. Chernikov 		/* Unknown CIDR type */
1415ce2817b5SAlexander V. Chernikov 		return (EINVAL);
1416ce2817b5SAlexander V. Chernikov 	}
1417ce2817b5SAlexander V. Chernikov 
1418ce2817b5SAlexander V. Chernikov 	return (0);
1419ce2817b5SAlexander V. Chernikov }
1420ce2817b5SAlexander V. Chernikov 
142174b941f0SAlexander V. Chernikov static int
1422914bffb6SAlexander V. Chernikov ta_find_chash_tentry(void *ta_state, struct table_info *ti,
1423914bffb6SAlexander V. Chernikov     ipfw_obj_tentry *tent)
142474b941f0SAlexander V. Chernikov {
14250bce0c23SAlexander V. Chernikov 	struct chash_cfg *cfg;
1426ce2817b5SAlexander V. Chernikov 	struct chashbhead *head;
1427ce2817b5SAlexander V. Chernikov 	struct chashentry ent, *tmp;
1428ce2817b5SAlexander V. Chernikov 	struct tentry_info tei;
1429ce2817b5SAlexander V. Chernikov 	int error;
1430ce2817b5SAlexander V. Chernikov 	uint32_t hash;
143174b941f0SAlexander V. Chernikov 
14320bce0c23SAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
1433ce2817b5SAlexander V. Chernikov 
1434ce2817b5SAlexander V. Chernikov 	memset(&ent, 0, sizeof(ent));
1435ce2817b5SAlexander V. Chernikov 	memset(&tei, 0, sizeof(tei));
1436ce2817b5SAlexander V. Chernikov 
1437914bffb6SAlexander V. Chernikov 	if (tent->subtype == AF_INET) {
1438914bffb6SAlexander V. Chernikov 		tei.paddr = &tent->k.addr;
14390bce0c23SAlexander V. Chernikov 		tei.masklen = cfg->mask4;
1440ce2817b5SAlexander V. Chernikov 		tei.subtype = AF_INET;
144174b941f0SAlexander V. Chernikov 
1442ce2817b5SAlexander V. Chernikov 		if ((error = tei_to_chash_ent(&tei, &ent)) != 0)
1443ce2817b5SAlexander V. Chernikov 			return (error);
1444ce2817b5SAlexander V. Chernikov 
14450bce0c23SAlexander V. Chernikov 		head = cfg->head4;
14460bce0c23SAlexander V. Chernikov 		hash = hash_ent(&ent, AF_INET, cfg->mask4, cfg->size4);
1447ce2817b5SAlexander V. Chernikov 		/* Check for existence */
1448ce2817b5SAlexander V. Chernikov 		SLIST_FOREACH(tmp, &head[hash], next) {
1449ce2817b5SAlexander V. Chernikov 			if (tmp->a.a4 != ent.a.a4)
1450ce2817b5SAlexander V. Chernikov 				continue;
1451ce2817b5SAlexander V. Chernikov 
1452ce2817b5SAlexander V. Chernikov 			ta_dump_chash_tentry(ta_state, ti, tmp, tent);
145374b941f0SAlexander V. Chernikov 			return (0);
145474b941f0SAlexander V. Chernikov 		}
1455ce2817b5SAlexander V. Chernikov 	} else {
1456914bffb6SAlexander V. Chernikov 		tei.paddr = &tent->k.addr6;
14570bce0c23SAlexander V. Chernikov 		tei.masklen = cfg->mask6;
1458ce2817b5SAlexander V. Chernikov 		tei.subtype = AF_INET6;
1459ce2817b5SAlexander V. Chernikov 
1460ce2817b5SAlexander V. Chernikov 		if ((error = tei_to_chash_ent(&tei, &ent)) != 0)
1461ce2817b5SAlexander V. Chernikov 			return (error);
1462ce2817b5SAlexander V. Chernikov 
14630bce0c23SAlexander V. Chernikov 		head = cfg->head6;
14640bce0c23SAlexander V. Chernikov 		hash = hash_ent(&ent, AF_INET6, cfg->mask6, cfg->size6);
1465ce2817b5SAlexander V. Chernikov 		/* Check for existence */
1466ce2817b5SAlexander V. Chernikov 		SLIST_FOREACH(tmp, &head[hash], next) {
1467ce2817b5SAlexander V. Chernikov 			if (memcmp(&tmp->a.a6, &ent.a.a6, 16) != 0)
1468ce2817b5SAlexander V. Chernikov 				continue;
1469ce2817b5SAlexander V. Chernikov 			ta_dump_chash_tentry(ta_state, ti, tmp, tent);
1470ce2817b5SAlexander V. Chernikov 			return (0);
1471ce2817b5SAlexander V. Chernikov 		}
1472ce2817b5SAlexander V. Chernikov 	}
1473ce2817b5SAlexander V. Chernikov 
147474b941f0SAlexander V. Chernikov 	return (ENOENT);
147574b941f0SAlexander V. Chernikov }
147674b941f0SAlexander V. Chernikov 
147774b941f0SAlexander V. Chernikov static void
147874b941f0SAlexander V. Chernikov ta_foreach_chash(void *ta_state, struct table_info *ti, ta_foreach_f *f,
147974b941f0SAlexander V. Chernikov     void *arg)
148074b941f0SAlexander V. Chernikov {
14810bce0c23SAlexander V. Chernikov 	struct chash_cfg *cfg;
148274b941f0SAlexander V. Chernikov 	struct chashentry *ent, *ent_next;
148374b941f0SAlexander V. Chernikov 	int i;
148474b941f0SAlexander V. Chernikov 
14850bce0c23SAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
148674b941f0SAlexander V. Chernikov 
14870bce0c23SAlexander V. Chernikov 	for (i = 0; i < cfg->size4; i++)
14880bce0c23SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &cfg->head4[i], next, ent_next)
148974b941f0SAlexander V. Chernikov 			f(ent, arg);
149074b941f0SAlexander V. Chernikov 
14910bce0c23SAlexander V. Chernikov 	for (i = 0; i < cfg->size6; i++)
14920bce0c23SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &cfg->head6[i], next, ent_next)
149374b941f0SAlexander V. Chernikov 			f(ent, arg);
149474b941f0SAlexander V. Chernikov }
149574b941f0SAlexander V. Chernikov 
149674b941f0SAlexander V. Chernikov static int
149774b941f0SAlexander V. Chernikov ta_prepare_add_chash(struct ip_fw_chain *ch, struct tentry_info *tei,
149874b941f0SAlexander V. Chernikov     void *ta_buf)
149974b941f0SAlexander V. Chernikov {
150074b941f0SAlexander V. Chernikov 	struct ta_buf_chash *tb;
150174b941f0SAlexander V. Chernikov 	struct chashentry *ent;
1502ce2817b5SAlexander V. Chernikov 	int error;
150374b941f0SAlexander V. Chernikov 
150474b941f0SAlexander V. Chernikov 	tb = (struct ta_buf_chash *)ta_buf;
150574b941f0SAlexander V. Chernikov 
150674b941f0SAlexander V. Chernikov 	ent = malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO);
150774b941f0SAlexander V. Chernikov 
1508ce2817b5SAlexander V. Chernikov 	error = tei_to_chash_ent(tei, ent);
1509ce2817b5SAlexander V. Chernikov 	if (error != 0) {
1510ce2817b5SAlexander V. Chernikov 		free(ent, M_IPFW_TBL);
1511ce2817b5SAlexander V. Chernikov 		return (error);
151274b941f0SAlexander V. Chernikov 	}
1513ce2817b5SAlexander V. Chernikov 	tb->ent_ptr = ent;
151474b941f0SAlexander V. Chernikov 
151574b941f0SAlexander V. Chernikov 	return (0);
151674b941f0SAlexander V. Chernikov }
151774b941f0SAlexander V. Chernikov 
151874b941f0SAlexander V. Chernikov static int
151974b941f0SAlexander V. Chernikov ta_add_chash(void *ta_state, struct table_info *ti, struct tentry_info *tei,
1520b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
152174b941f0SAlexander V. Chernikov {
15220bce0c23SAlexander V. Chernikov 	struct chash_cfg *cfg;
152374b941f0SAlexander V. Chernikov 	struct chashbhead *head;
152474b941f0SAlexander V. Chernikov 	struct chashentry *ent, *tmp;
152574b941f0SAlexander V. Chernikov 	struct ta_buf_chash *tb;
152674b941f0SAlexander V. Chernikov 	int exists;
1527648e8380SAlexander V. Chernikov 	uint32_t hash, value;
152874b941f0SAlexander V. Chernikov 
15290bce0c23SAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
153074b941f0SAlexander V. Chernikov 	tb = (struct ta_buf_chash *)ta_buf;
153174b941f0SAlexander V. Chernikov 	ent = (struct chashentry *)tb->ent_ptr;
153274b941f0SAlexander V. Chernikov 	hash = 0;
153374b941f0SAlexander V. Chernikov 	exists = 0;
153474b941f0SAlexander V. Chernikov 
153513263632SAlexander V. Chernikov 	/* Read current value from @tei */
153613263632SAlexander V. Chernikov 	ent->value = tei->value;
153713263632SAlexander V. Chernikov 
153813263632SAlexander V. Chernikov 	/* Read cuurrent value */
153974b941f0SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
15400bce0c23SAlexander V. Chernikov 		if (tei->masklen != cfg->mask4)
154174b941f0SAlexander V. Chernikov 			return (EINVAL);
15420bce0c23SAlexander V. Chernikov 		head = cfg->head4;
15430bce0c23SAlexander V. Chernikov 		hash = hash_ent(ent, AF_INET, cfg->mask4, cfg->size4);
1544ce2817b5SAlexander V. Chernikov 
154574b941f0SAlexander V. Chernikov 		/* Check for existence */
154674b941f0SAlexander V. Chernikov 		SLIST_FOREACH(tmp, &head[hash], next) {
154774b941f0SAlexander V. Chernikov 			if (tmp->a.a4 == ent->a.a4) {
154874b941f0SAlexander V. Chernikov 				exists = 1;
154974b941f0SAlexander V. Chernikov 				break;
155074b941f0SAlexander V. Chernikov 			}
155174b941f0SAlexander V. Chernikov 		}
155274b941f0SAlexander V. Chernikov 	} else {
15530bce0c23SAlexander V. Chernikov 		if (tei->masklen != cfg->mask6)
155474b941f0SAlexander V. Chernikov 			return (EINVAL);
15550bce0c23SAlexander V. Chernikov 		head = cfg->head6;
15560bce0c23SAlexander V. Chernikov 		hash = hash_ent(ent, AF_INET6, cfg->mask6, cfg->size6);
155774b941f0SAlexander V. Chernikov 		/* Check for existence */
155874b941f0SAlexander V. Chernikov 		SLIST_FOREACH(tmp, &head[hash], next) {
1559ce2817b5SAlexander V. Chernikov 			if (memcmp(&tmp->a.a6, &ent->a.a6, 16) == 0) {
156074b941f0SAlexander V. Chernikov 				exists = 1;
156174b941f0SAlexander V. Chernikov 				break;
156274b941f0SAlexander V. Chernikov 			}
156374b941f0SAlexander V. Chernikov 		}
156474b941f0SAlexander V. Chernikov 	}
156574b941f0SAlexander V. Chernikov 
156674b941f0SAlexander V. Chernikov 	if (exists == 1) {
156774b941f0SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
156874b941f0SAlexander V. Chernikov 			return (EEXIST);
156974b941f0SAlexander V. Chernikov 		/* Record already exists. Update value if we're asked to */
1570648e8380SAlexander V. Chernikov 		value = tmp->value;
157174b941f0SAlexander V. Chernikov 		tmp->value = tei->value;
1572648e8380SAlexander V. Chernikov 		tei->value = value;
157374b941f0SAlexander V. Chernikov 		/* Indicate that update has happened instead of addition */
157474b941f0SAlexander V. Chernikov 		tei->flags |= TEI_FLAGS_UPDATED;
157574b941f0SAlexander V. Chernikov 		*pnum = 0;
157674b941f0SAlexander V. Chernikov 	} else {
15774c0c07a5SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_DONTADD) != 0)
15784c0c07a5SAlexander V. Chernikov 			return (EFBIG);
157974b941f0SAlexander V. Chernikov 		SLIST_INSERT_HEAD(&head[hash], ent, next);
158074b941f0SAlexander V. Chernikov 		tb->ent_ptr = NULL;
158174b941f0SAlexander V. Chernikov 		*pnum = 1;
1582ce2817b5SAlexander V. Chernikov 
1583b6ee846eSAlexander V. Chernikov 		/* Update counters */
1584b6ee846eSAlexander V. Chernikov 		if (tei->subtype == AF_INET)
15850bce0c23SAlexander V. Chernikov 			cfg->items4++;
1586b6ee846eSAlexander V. Chernikov 		else
15870bce0c23SAlexander V. Chernikov 			cfg->items6++;
158874b941f0SAlexander V. Chernikov 	}
158974b941f0SAlexander V. Chernikov 
159074b941f0SAlexander V. Chernikov 	return (0);
159174b941f0SAlexander V. Chernikov }
159274b941f0SAlexander V. Chernikov 
159374b941f0SAlexander V. Chernikov static int
159474b941f0SAlexander V. Chernikov ta_prepare_del_chash(struct ip_fw_chain *ch, struct tentry_info *tei,
159574b941f0SAlexander V. Chernikov     void *ta_buf)
159674b941f0SAlexander V. Chernikov {
159774b941f0SAlexander V. Chernikov 	struct ta_buf_chash *tb;
159874b941f0SAlexander V. Chernikov 
159974b941f0SAlexander V. Chernikov 	tb = (struct ta_buf_chash *)ta_buf;
160074b941f0SAlexander V. Chernikov 
1601ce2817b5SAlexander V. Chernikov 	return (tei_to_chash_ent(tei, &tb->ent));
160274b941f0SAlexander V. Chernikov }
160374b941f0SAlexander V. Chernikov 
160474b941f0SAlexander V. Chernikov static int
160574b941f0SAlexander V. Chernikov ta_del_chash(void *ta_state, struct table_info *ti, struct tentry_info *tei,
1606b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
160774b941f0SAlexander V. Chernikov {
16080bce0c23SAlexander V. Chernikov 	struct chash_cfg *cfg;
160974b941f0SAlexander V. Chernikov 	struct chashbhead *head;
16100bce0c23SAlexander V. Chernikov 	struct chashentry *tmp, *tmp_next, *ent;
161174b941f0SAlexander V. Chernikov 	struct ta_buf_chash *tb;
161274b941f0SAlexander V. Chernikov 	uint32_t hash;
161374b941f0SAlexander V. Chernikov 
16140bce0c23SAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
161574b941f0SAlexander V. Chernikov 	tb = (struct ta_buf_chash *)ta_buf;
16160bce0c23SAlexander V. Chernikov 	ent = &tb->ent;
161774b941f0SAlexander V. Chernikov 
161874b941f0SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
16190bce0c23SAlexander V. Chernikov 		if (tei->masklen != cfg->mask4)
162074b941f0SAlexander V. Chernikov 			return (EINVAL);
16210bce0c23SAlexander V. Chernikov 		head = cfg->head4;
16220bce0c23SAlexander V. Chernikov 		hash = hash_ent(ent, AF_INET, cfg->mask4, cfg->size4);
162374b941f0SAlexander V. Chernikov 
16240bce0c23SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(tmp, &head[hash], next, tmp_next) {
16250bce0c23SAlexander V. Chernikov 			if (tmp->a.a4 != ent->a.a4)
1626648e8380SAlexander V. Chernikov 				continue;
1627648e8380SAlexander V. Chernikov 
16280bce0c23SAlexander V. Chernikov 			SLIST_REMOVE(&head[hash], tmp, chashentry, next);
16290bce0c23SAlexander V. Chernikov 			cfg->items4--;
16300bce0c23SAlexander V. Chernikov 			tb->ent_ptr = tmp;
16310bce0c23SAlexander V. Chernikov 			tei->value = tmp->value;
1632648e8380SAlexander V. Chernikov 			*pnum = 1;
163374b941f0SAlexander V. Chernikov 			return (0);
163474b941f0SAlexander V. Chernikov 		}
163574b941f0SAlexander V. Chernikov 	} else {
16360bce0c23SAlexander V. Chernikov 		if (tei->masklen != cfg->mask6)
163774b941f0SAlexander V. Chernikov 			return (EINVAL);
16380bce0c23SAlexander V. Chernikov 		head = cfg->head6;
16390bce0c23SAlexander V. Chernikov 		hash = hash_ent(ent, AF_INET6, cfg->mask6, cfg->size6);
16400bce0c23SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(tmp, &head[hash], next, tmp_next) {
16410bce0c23SAlexander V. Chernikov 			if (memcmp(&tmp->a.a6, &ent->a.a6, 16) != 0)
1642648e8380SAlexander V. Chernikov 				continue;
1643648e8380SAlexander V. Chernikov 
16440bce0c23SAlexander V. Chernikov 			SLIST_REMOVE(&head[hash], tmp, chashentry, next);
16450bce0c23SAlexander V. Chernikov 			cfg->items6--;
16460bce0c23SAlexander V. Chernikov 			tb->ent_ptr = tmp;
16470bce0c23SAlexander V. Chernikov 			tei->value = tmp->value;
164874b941f0SAlexander V. Chernikov 			*pnum = 1;
164974b941f0SAlexander V. Chernikov 			return (0);
165074b941f0SAlexander V. Chernikov 		}
165174b941f0SAlexander V. Chernikov 	}
165274b941f0SAlexander V. Chernikov 
165374b941f0SAlexander V. Chernikov 	return (ENOENT);
165474b941f0SAlexander V. Chernikov }
165574b941f0SAlexander V. Chernikov 
165674b941f0SAlexander V. Chernikov static void
165774b941f0SAlexander V. Chernikov ta_flush_chash_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
165874b941f0SAlexander V. Chernikov     void *ta_buf)
165974b941f0SAlexander V. Chernikov {
166074b941f0SAlexander V. Chernikov 	struct ta_buf_chash *tb;
166174b941f0SAlexander V. Chernikov 
166274b941f0SAlexander V. Chernikov 	tb = (struct ta_buf_chash *)ta_buf;
166374b941f0SAlexander V. Chernikov 
166474b941f0SAlexander V. Chernikov 	if (tb->ent_ptr != NULL)
166574b941f0SAlexander V. Chernikov 		free(tb->ent_ptr, M_IPFW_TBL);
166674b941f0SAlexander V. Chernikov }
166774b941f0SAlexander V. Chernikov 
1668ce2817b5SAlexander V. Chernikov /*
1669ce2817b5SAlexander V. Chernikov  * Hash growing callbacks.
1670ce2817b5SAlexander V. Chernikov  */
1671ce2817b5SAlexander V. Chernikov 
1672b6ee846eSAlexander V. Chernikov static int
1673301290bcSAlexander V. Chernikov ta_need_modify_chash(void *ta_state, struct table_info *ti, uint32_t count,
1674b6ee846eSAlexander V. Chernikov     uint64_t *pflags)
1675b6ee846eSAlexander V. Chernikov {
1676b6ee846eSAlexander V. Chernikov 	struct chash_cfg *cfg;
1677b6ee846eSAlexander V. Chernikov 	uint64_t data;
1678b6ee846eSAlexander V. Chernikov 
1679b6ee846eSAlexander V. Chernikov 	/*
1680b6ee846eSAlexander V. Chernikov 	 * Since we don't know exact number of IPv4/IPv6 records in @count,
1681b6ee846eSAlexander V. Chernikov 	 * ignore non-zero @count value at all. Check current hash sizes
1682b6ee846eSAlexander V. Chernikov 	 * and return appropriate data.
1683b6ee846eSAlexander V. Chernikov 	 */
1684b6ee846eSAlexander V. Chernikov 
1685b6ee846eSAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
1686b6ee846eSAlexander V. Chernikov 
1687b6ee846eSAlexander V. Chernikov 	data = 0;
1688b6ee846eSAlexander V. Chernikov 	if (cfg->items4 > cfg->size4 && cfg->size4 < 65536)
1689b6ee846eSAlexander V. Chernikov 		data |= (cfg->size4 * 2) << 16;
1690b6ee846eSAlexander V. Chernikov 	if (cfg->items6 > cfg->size6 && cfg->size6 < 65536)
1691b6ee846eSAlexander V. Chernikov 		data |= cfg->size6 * 2;
1692b6ee846eSAlexander V. Chernikov 
1693b6ee846eSAlexander V. Chernikov 	if (data != 0) {
1694b6ee846eSAlexander V. Chernikov 		*pflags = data;
1695301290bcSAlexander V. Chernikov 		return (1);
1696b6ee846eSAlexander V. Chernikov 	}
1697b6ee846eSAlexander V. Chernikov 
1698301290bcSAlexander V. Chernikov 	return (0);
1699b6ee846eSAlexander V. Chernikov }
1700b6ee846eSAlexander V. Chernikov 
1701ce2817b5SAlexander V. Chernikov /*
1702ce2817b5SAlexander V. Chernikov  * Allocate new, larger chash.
1703ce2817b5SAlexander V. Chernikov  */
1704ce2817b5SAlexander V. Chernikov static int
1705ce2817b5SAlexander V. Chernikov ta_prepare_mod_chash(void *ta_buf, uint64_t *pflags)
1706ce2817b5SAlexander V. Chernikov {
1707ce2817b5SAlexander V. Chernikov 	struct mod_item *mi;
1708ce2817b5SAlexander V. Chernikov 	struct chashbhead *head;
1709ce2817b5SAlexander V. Chernikov 	int i;
1710ce2817b5SAlexander V. Chernikov 
1711ce2817b5SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
1712ce2817b5SAlexander V. Chernikov 
1713ce2817b5SAlexander V. Chernikov 	memset(mi, 0, sizeof(struct mod_item));
1714b6ee846eSAlexander V. Chernikov 	mi->size = (*pflags >> 16) & 0xFFFF;
1715b6ee846eSAlexander V. Chernikov 	mi->size6 = *pflags & 0xFFFF;
1716b6ee846eSAlexander V. Chernikov 	if (mi->size > 0) {
1717b6ee846eSAlexander V. Chernikov 		head = malloc(sizeof(struct chashbhead) * mi->size,
1718b6ee846eSAlexander V. Chernikov 		    M_IPFW, M_WAITOK | M_ZERO);
1719ce2817b5SAlexander V. Chernikov 		for (i = 0; i < mi->size; i++)
1720ce2817b5SAlexander V. Chernikov 			SLIST_INIT(&head[i]);
1721ce2817b5SAlexander V. Chernikov 		mi->main_ptr = head;
1722b6ee846eSAlexander V. Chernikov 	}
1723b6ee846eSAlexander V. Chernikov 
1724b6ee846eSAlexander V. Chernikov 	if (mi->size6 > 0) {
1725b6ee846eSAlexander V. Chernikov 		head = malloc(sizeof(struct chashbhead) * mi->size6,
1726b6ee846eSAlexander V. Chernikov 		    M_IPFW, M_WAITOK | M_ZERO);
1727b6ee846eSAlexander V. Chernikov 		for (i = 0; i < mi->size6; i++)
1728b6ee846eSAlexander V. Chernikov 			SLIST_INIT(&head[i]);
1729b6ee846eSAlexander V. Chernikov 		mi->main_ptr6 = head;
1730b6ee846eSAlexander V. Chernikov 	}
1731ce2817b5SAlexander V. Chernikov 
1732ce2817b5SAlexander V. Chernikov 	return (0);
1733ce2817b5SAlexander V. Chernikov }
1734ce2817b5SAlexander V. Chernikov 
1735ce2817b5SAlexander V. Chernikov /*
1736ce2817b5SAlexander V. Chernikov  * Copy data from old runtime array to new one.
1737ce2817b5SAlexander V. Chernikov  */
1738ce2817b5SAlexander V. Chernikov static int
1739ce2817b5SAlexander V. Chernikov ta_fill_mod_chash(void *ta_state, struct table_info *ti, void *ta_buf,
1740ce2817b5SAlexander V. Chernikov     uint64_t *pflags)
1741ce2817b5SAlexander V. Chernikov {
1742ce2817b5SAlexander V. Chernikov 
1743ce2817b5SAlexander V. Chernikov 	/* In is not possible to do rehash if we're not holidng WLOCK. */
1744ce2817b5SAlexander V. Chernikov 	return (0);
1745ce2817b5SAlexander V. Chernikov }
1746ce2817b5SAlexander V. Chernikov 
1747ce2817b5SAlexander V. Chernikov /*
1748ce2817b5SAlexander V. Chernikov  * Switch old & new arrays.
1749ce2817b5SAlexander V. Chernikov  */
1750301290bcSAlexander V. Chernikov static void
1751ce2817b5SAlexander V. Chernikov ta_modify_chash(void *ta_state, struct table_info *ti, void *ta_buf,
1752ce2817b5SAlexander V. Chernikov     uint64_t pflags)
1753ce2817b5SAlexander V. Chernikov {
1754ce2817b5SAlexander V. Chernikov 	struct mod_item *mi;
1755b6ee846eSAlexander V. Chernikov 	struct chash_cfg *cfg;
1756ce2817b5SAlexander V. Chernikov 	struct chashbhead *old_head, *new_head;
1757ce2817b5SAlexander V. Chernikov 	struct chashentry *ent, *ent_next;
1758ce2817b5SAlexander V. Chernikov 	int af, i, mlen;
1759ce2817b5SAlexander V. Chernikov 	uint32_t nhash;
1760b6ee846eSAlexander V. Chernikov 	size_t old_size, new_size;
1761ce2817b5SAlexander V. Chernikov 
1762ce2817b5SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
1763b6ee846eSAlexander V. Chernikov 	cfg = (struct chash_cfg *)ta_state;
1764ce2817b5SAlexander V. Chernikov 
1765ce2817b5SAlexander V. Chernikov 	/* Check which hash we need to grow and do we still need that */
1766b6ee846eSAlexander V. Chernikov 	if (mi->size > 0 && cfg->size4 < mi->size) {
1767ce2817b5SAlexander V. Chernikov 		new_head = (struct chashbhead *)mi->main_ptr;
1768b6ee846eSAlexander V. Chernikov 		new_size = mi->size;
1769b6ee846eSAlexander V. Chernikov 		old_size = cfg->size4;
1770b6ee846eSAlexander V. Chernikov 		old_head = ti->state;
1771b6ee846eSAlexander V. Chernikov 		mlen = cfg->mask4;
1772b6ee846eSAlexander V. Chernikov 		af = AF_INET;
1773b6ee846eSAlexander V. Chernikov 
1774ce2817b5SAlexander V. Chernikov 		for (i = 0; i < old_size; i++) {
1775ce2817b5SAlexander V. Chernikov 			SLIST_FOREACH_SAFE(ent, &old_head[i], next, ent_next) {
1776b6ee846eSAlexander V. Chernikov 				nhash = hash_ent(ent, af, mlen, new_size);
1777ce2817b5SAlexander V. Chernikov 				SLIST_INSERT_HEAD(&new_head[nhash], ent, next);
1778ce2817b5SAlexander V. Chernikov 			}
1779ce2817b5SAlexander V. Chernikov 		}
1780ce2817b5SAlexander V. Chernikov 
1781ce2817b5SAlexander V. Chernikov 		ti->state = new_head;
1782b6ee846eSAlexander V. Chernikov 		cfg->head4 = new_head;
1783b6ee846eSAlexander V. Chernikov 		cfg->size4 = mi->size;
1784b6ee846eSAlexander V. Chernikov 		mi->main_ptr = old_head;
1785ce2817b5SAlexander V. Chernikov 	}
1786ce2817b5SAlexander V. Chernikov 
1787b6ee846eSAlexander V. Chernikov 	if (mi->size6 > 0 && cfg->size6 < mi->size6) {
1788b6ee846eSAlexander V. Chernikov 		new_head = (struct chashbhead *)mi->main_ptr6;
1789b6ee846eSAlexander V. Chernikov 		new_size = mi->size6;
1790b6ee846eSAlexander V. Chernikov 		old_size = cfg->size6;
1791b6ee846eSAlexander V. Chernikov 		old_head = ti->xstate;
1792b6ee846eSAlexander V. Chernikov 		mlen = cfg->mask6;
1793b6ee846eSAlexander V. Chernikov 		af = AF_INET6;
1794914bffb6SAlexander V. Chernikov 
1795b6ee846eSAlexander V. Chernikov 		for (i = 0; i < old_size; i++) {
1796b6ee846eSAlexander V. Chernikov 			SLIST_FOREACH_SAFE(ent, &old_head[i], next, ent_next) {
1797b6ee846eSAlexander V. Chernikov 				nhash = hash_ent(ent, af, mlen, new_size);
1798b6ee846eSAlexander V. Chernikov 				SLIST_INSERT_HEAD(&new_head[nhash], ent, next);
1799b6ee846eSAlexander V. Chernikov 			}
1800b6ee846eSAlexander V. Chernikov 		}
1801b6ee846eSAlexander V. Chernikov 
1802b6ee846eSAlexander V. Chernikov 		ti->xstate = new_head;
1803b6ee846eSAlexander V. Chernikov 		cfg->head6 = new_head;
1804b6ee846eSAlexander V. Chernikov 		cfg->size6 = mi->size6;
1805b6ee846eSAlexander V. Chernikov 		mi->main_ptr6 = old_head;
1806b6ee846eSAlexander V. Chernikov 	}
1807b6ee846eSAlexander V. Chernikov 
1808b6ee846eSAlexander V. Chernikov 	/* Update lower 32 bits with new values */
1809b6ee846eSAlexander V. Chernikov 	ti->data &= 0xFFFFFFFF00000000;
1810b6ee846eSAlexander V. Chernikov 	ti->data |= log2(cfg->size4) << 8 | log2(cfg->size6);
1811ce2817b5SAlexander V. Chernikov }
1812ce2817b5SAlexander V. Chernikov 
1813ce2817b5SAlexander V. Chernikov /*
1814ce2817b5SAlexander V. Chernikov  * Free unneded array.
1815ce2817b5SAlexander V. Chernikov  */
1816ce2817b5SAlexander V. Chernikov static void
1817ce2817b5SAlexander V. Chernikov ta_flush_mod_chash(void *ta_buf)
1818ce2817b5SAlexander V. Chernikov {
1819ce2817b5SAlexander V. Chernikov 	struct mod_item *mi;
1820ce2817b5SAlexander V. Chernikov 
1821ce2817b5SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
1822ce2817b5SAlexander V. Chernikov 	if (mi->main_ptr != NULL)
1823ce2817b5SAlexander V. Chernikov 		free(mi->main_ptr, M_IPFW);
1824b6ee846eSAlexander V. Chernikov 	if (mi->main_ptr6 != NULL)
1825b6ee846eSAlexander V. Chernikov 		free(mi->main_ptr6, M_IPFW);
1826ce2817b5SAlexander V. Chernikov }
1827ce2817b5SAlexander V. Chernikov 
1828c21034b7SAlexander V. Chernikov struct table_algo addr_hash = {
1829c21034b7SAlexander V. Chernikov 	.name		= "addr:hash",
1830c21034b7SAlexander V. Chernikov 	.type		= IPFW_TABLE_ADDR,
183157a1cf95SAlexander V. Chernikov 	.ta_buf_size	= sizeof(struct ta_buf_chash),
183274b941f0SAlexander V. Chernikov 	.init		= ta_init_chash,
183374b941f0SAlexander V. Chernikov 	.destroy	= ta_destroy_chash,
183474b941f0SAlexander V. Chernikov 	.prepare_add	= ta_prepare_add_chash,
183574b941f0SAlexander V. Chernikov 	.prepare_del	= ta_prepare_del_chash,
183674b941f0SAlexander V. Chernikov 	.add		= ta_add_chash,
183774b941f0SAlexander V. Chernikov 	.del		= ta_del_chash,
183874b941f0SAlexander V. Chernikov 	.flush_entry	= ta_flush_chash_entry,
183974b941f0SAlexander V. Chernikov 	.foreach	= ta_foreach_chash,
184074b941f0SAlexander V. Chernikov 	.dump_tentry	= ta_dump_chash_tentry,
184174b941f0SAlexander V. Chernikov 	.find_tentry	= ta_find_chash_tentry,
184274b941f0SAlexander V. Chernikov 	.print_config	= ta_print_chash_config,
18435f379342SAlexander V. Chernikov 	.dump_tinfo	= ta_dump_chash_tinfo,
1844301290bcSAlexander V. Chernikov 	.need_modify	= ta_need_modify_chash,
1845ce2817b5SAlexander V. Chernikov 	.prepare_mod	= ta_prepare_mod_chash,
1846ce2817b5SAlexander V. Chernikov 	.fill_mod	= ta_fill_mod_chash,
1847ce2817b5SAlexander V. Chernikov 	.modify		= ta_modify_chash,
1848ce2817b5SAlexander V. Chernikov 	.flush_mod	= ta_flush_mod_chash,
184974b941f0SAlexander V. Chernikov };
185074b941f0SAlexander V. Chernikov 
185174b941f0SAlexander V. Chernikov 
185274b941f0SAlexander V. Chernikov /*
185368394ec8SAlexander V. Chernikov  * Iface table cmds.
185468394ec8SAlexander V. Chernikov  *
185568394ec8SAlexander V. Chernikov  * Implementation:
185668394ec8SAlexander V. Chernikov  *
185768394ec8SAlexander V. Chernikov  * Runtime part:
185868394ec8SAlexander V. Chernikov  * - sorted array of "struct ifidx" pointed by ti->state.
1859b23d5de9SAlexander V. Chernikov  *   Array is allocated with rounding up to IFIDX_CHUNK. Only existing
186068394ec8SAlexander V. Chernikov  *   interfaces are stored in array, however its allocated size is
186168394ec8SAlexander V. Chernikov  *   sufficient to hold all table records if needed.
186268394ec8SAlexander V. Chernikov  * - current array size is stored in ti->data
186368394ec8SAlexander V. Chernikov  *
186468394ec8SAlexander V. Chernikov  * Table data:
186568394ec8SAlexander V. Chernikov  * - "struct iftable_cfg" is allocated to store table state (ta_state).
186668394ec8SAlexander V. Chernikov  * - All table records are stored inside namedobj instance.
18679f7d47b0SAlexander V. Chernikov  *
18689f7d47b0SAlexander V. Chernikov  */
18699f7d47b0SAlexander V. Chernikov 
187068394ec8SAlexander V. Chernikov struct ifidx {
187168394ec8SAlexander V. Chernikov 	uint16_t	kidx;
187268394ec8SAlexander V. Chernikov 	uint16_t	spare;
187368394ec8SAlexander V. Chernikov 	uint32_t	value;
187468394ec8SAlexander V. Chernikov };
18750cba2b28SAlexander V. Chernikov #define	DEFAULT_IFIDX_SIZE	64
187668394ec8SAlexander V. Chernikov 
187768394ec8SAlexander V. Chernikov struct iftable_cfg;
187868394ec8SAlexander V. Chernikov 
187968394ec8SAlexander V. Chernikov struct ifentry {
188068394ec8SAlexander V. Chernikov 	struct named_object	no;
188168394ec8SAlexander V. Chernikov 	struct ipfw_ifc		ic;
188268394ec8SAlexander V. Chernikov 	struct iftable_cfg	*icfg;
188368394ec8SAlexander V. Chernikov 	uint32_t		value;
188468394ec8SAlexander V. Chernikov 	int			linked;
188568394ec8SAlexander V. Chernikov };
188668394ec8SAlexander V. Chernikov 
188768394ec8SAlexander V. Chernikov struct iftable_cfg {
188868394ec8SAlexander V. Chernikov 	struct namedobj_instance	*ii;
188968394ec8SAlexander V. Chernikov 	struct ip_fw_chain	*ch;
189068394ec8SAlexander V. Chernikov 	struct table_info	*ti;
189168394ec8SAlexander V. Chernikov 	void	*main_ptr;
189268394ec8SAlexander V. Chernikov 	size_t	size;	/* Number of items allocated in array */
189368394ec8SAlexander V. Chernikov 	size_t	count;	/* Number of all items */
189468394ec8SAlexander V. Chernikov 	size_t	used;	/* Number of items _active_ now */
189568394ec8SAlexander V. Chernikov };
189668394ec8SAlexander V. Chernikov 
18970bce0c23SAlexander V. Chernikov struct ta_buf_ifidx
18980bce0c23SAlexander V. Chernikov {
18990bce0c23SAlexander V. Chernikov 	struct ifentry *ife;
19000bce0c23SAlexander V. Chernikov 	uint32_t value;
19010bce0c23SAlexander V. Chernikov };
190268394ec8SAlexander V. Chernikov 
190368394ec8SAlexander V. Chernikov int compare_ifidx(const void *k, const void *v);
190468394ec8SAlexander V. Chernikov static void if_notifier(struct ip_fw_chain *ch, void *cbdata, uint16_t ifindex);
1905*9fe15d06SAlexander V. Chernikov static struct ifidx * ifidx_find(struct table_info *ti, void *key);
1906*9fe15d06SAlexander V. Chernikov static int ta_lookup_ifidx(struct table_info *ti, void *key, uint32_t keylen,
1907*9fe15d06SAlexander V. Chernikov     uint32_t *val);
1908*9fe15d06SAlexander V. Chernikov static int ta_init_ifidx(struct ip_fw_chain *ch, void **ta_state,
1909*9fe15d06SAlexander V. Chernikov     struct table_info *ti, char *data, uint8_t tflags);
1910*9fe15d06SAlexander V. Chernikov static void ta_change_ti_ifidx(void *ta_state, struct table_info *ti);
1911*9fe15d06SAlexander V. Chernikov static void destroy_ifidx_locked(struct namedobj_instance *ii,
1912*9fe15d06SAlexander V. Chernikov     struct named_object *no, void *arg);
1913*9fe15d06SAlexander V. Chernikov static void ta_destroy_ifidx(void *ta_state, struct table_info *ti);
1914*9fe15d06SAlexander V. Chernikov static void ta_dump_ifidx_tinfo(void *ta_state, struct table_info *ti,
1915*9fe15d06SAlexander V. Chernikov     ipfw_ta_tinfo *tinfo);
1916*9fe15d06SAlexander V. Chernikov static int ta_prepare_add_ifidx(struct ip_fw_chain *ch, struct tentry_info *tei,
1917*9fe15d06SAlexander V. Chernikov     void *ta_buf);
1918*9fe15d06SAlexander V. Chernikov static int ta_add_ifidx(void *ta_state, struct table_info *ti,
1919*9fe15d06SAlexander V. Chernikov     struct tentry_info *tei, void *ta_buf, uint32_t *pnum);
1920*9fe15d06SAlexander V. Chernikov static int ta_prepare_del_ifidx(struct ip_fw_chain *ch, struct tentry_info *tei,
1921*9fe15d06SAlexander V. Chernikov     void *ta_buf);
1922*9fe15d06SAlexander V. Chernikov static int ta_del_ifidx(void *ta_state, struct table_info *ti,
1923*9fe15d06SAlexander V. Chernikov     struct tentry_info *tei, void *ta_buf, uint32_t *pnum);
1924*9fe15d06SAlexander V. Chernikov static void ta_flush_ifidx_entry(struct ip_fw_chain *ch,
1925*9fe15d06SAlexander V. Chernikov     struct tentry_info *tei, void *ta_buf);
1926*9fe15d06SAlexander V. Chernikov static void if_notifier(struct ip_fw_chain *ch, void *cbdata, uint16_t ifindex);
1927*9fe15d06SAlexander V. Chernikov static int ta_need_modify_ifidx(void *ta_state, struct table_info *ti,
1928*9fe15d06SAlexander V. Chernikov     uint32_t count, uint64_t *pflags);
1929*9fe15d06SAlexander V. Chernikov static int ta_prepare_mod_ifidx(void *ta_buf, uint64_t *pflags);
1930*9fe15d06SAlexander V. Chernikov static int ta_fill_mod_ifidx(void *ta_state, struct table_info *ti,
1931*9fe15d06SAlexander V. Chernikov     void *ta_buf, uint64_t *pflags);
1932*9fe15d06SAlexander V. Chernikov static void ta_modify_ifidx(void *ta_state, struct table_info *ti, void *ta_buf,
1933*9fe15d06SAlexander V. Chernikov     uint64_t pflags);
1934*9fe15d06SAlexander V. Chernikov static void ta_flush_mod_ifidx(void *ta_buf);
1935*9fe15d06SAlexander V. Chernikov static int ta_dump_ifidx_tentry(void *ta_state, struct table_info *ti, void *e,
1936*9fe15d06SAlexander V. Chernikov     ipfw_obj_tentry *tent);
1937*9fe15d06SAlexander V. Chernikov static int ta_find_ifidx_tentry(void *ta_state, struct table_info *ti,
1938*9fe15d06SAlexander V. Chernikov     ipfw_obj_tentry *tent);
1939*9fe15d06SAlexander V. Chernikov static void foreach_ifidx(struct namedobj_instance *ii, struct named_object *no,
1940*9fe15d06SAlexander V. Chernikov     void *arg);
1941*9fe15d06SAlexander V. Chernikov static void ta_foreach_ifidx(void *ta_state, struct table_info *ti,
1942*9fe15d06SAlexander V. Chernikov     ta_foreach_f *f, void *arg);
194368394ec8SAlexander V. Chernikov 
194468394ec8SAlexander V. Chernikov int
194568394ec8SAlexander V. Chernikov compare_ifidx(const void *k, const void *v)
194668394ec8SAlexander V. Chernikov {
1947d4e1b515SAlexander V. Chernikov 	const struct ifidx *ifidx;
194868394ec8SAlexander V. Chernikov 	uint16_t key;
194968394ec8SAlexander V. Chernikov 
1950d4e1b515SAlexander V. Chernikov 	key = *((const uint16_t *)k);
1951d4e1b515SAlexander V. Chernikov 	ifidx = (const struct ifidx *)v;
195268394ec8SAlexander V. Chernikov 
195368394ec8SAlexander V. Chernikov 	if (key < ifidx->kidx)
195468394ec8SAlexander V. Chernikov 		return (-1);
195568394ec8SAlexander V. Chernikov 	else if (key > ifidx->kidx)
195668394ec8SAlexander V. Chernikov 		return (1);
195768394ec8SAlexander V. Chernikov 
195868394ec8SAlexander V. Chernikov 	return (0);
195968394ec8SAlexander V. Chernikov }
196068394ec8SAlexander V. Chernikov 
196168394ec8SAlexander V. Chernikov /*
196268394ec8SAlexander V. Chernikov  * Adds item @item with key @key into ascending-sorted array @base.
196368394ec8SAlexander V. Chernikov  * Assumes @base has enough additional storage.
196468394ec8SAlexander V. Chernikov  *
196568394ec8SAlexander V. Chernikov  * Returns 1 on success, 0 on duplicate key.
196668394ec8SAlexander V. Chernikov  */
19679f7d47b0SAlexander V. Chernikov static int
196868394ec8SAlexander V. Chernikov badd(const void *key, void *item, void *base, size_t nmemb,
196968394ec8SAlexander V. Chernikov     size_t size, int (*compar) (const void *, const void *))
197068394ec8SAlexander V. Chernikov {
197168394ec8SAlexander V. Chernikov 	int min, max, mid, shift, res;
197268394ec8SAlexander V. Chernikov 	caddr_t paddr;
197368394ec8SAlexander V. Chernikov 
197468394ec8SAlexander V. Chernikov 	if (nmemb == 0) {
197568394ec8SAlexander V. Chernikov 		memcpy(base, item, size);
197668394ec8SAlexander V. Chernikov 		return (1);
197768394ec8SAlexander V. Chernikov 	}
197868394ec8SAlexander V. Chernikov 
197968394ec8SAlexander V. Chernikov 	/* Binary search */
198068394ec8SAlexander V. Chernikov 	min = 0;
198168394ec8SAlexander V. Chernikov 	max = nmemb - 1;
198268394ec8SAlexander V. Chernikov 	mid = 0;
198368394ec8SAlexander V. Chernikov 	while (min <= max) {
198468394ec8SAlexander V. Chernikov 		mid = (min + max) / 2;
198568394ec8SAlexander V. Chernikov 		res = compar(key, (const void *)((caddr_t)base + mid * size));
198668394ec8SAlexander V. Chernikov 		if (res == 0)
198768394ec8SAlexander V. Chernikov 			return (0);
198868394ec8SAlexander V. Chernikov 
198968394ec8SAlexander V. Chernikov 		if (res > 0)
199068394ec8SAlexander V. Chernikov 			min = mid + 1;
199168394ec8SAlexander V. Chernikov 		else
199268394ec8SAlexander V. Chernikov 			max = mid - 1;
199368394ec8SAlexander V. Chernikov 	}
199468394ec8SAlexander V. Chernikov 
199568394ec8SAlexander V. Chernikov 	/* Item not found. */
199668394ec8SAlexander V. Chernikov 	res = compar(key, (const void *)((caddr_t)base + mid * size));
199768394ec8SAlexander V. Chernikov 	if (res > 0)
199868394ec8SAlexander V. Chernikov 		shift = mid + 1;
199968394ec8SAlexander V. Chernikov 	else
200068394ec8SAlexander V. Chernikov 		shift = mid;
200168394ec8SAlexander V. Chernikov 
200268394ec8SAlexander V. Chernikov 	paddr = (caddr_t)base + shift * size;
200368394ec8SAlexander V. Chernikov 	if (nmemb > shift)
200468394ec8SAlexander V. Chernikov 		memmove(paddr + size, paddr, (nmemb - shift) * size);
200568394ec8SAlexander V. Chernikov 
200668394ec8SAlexander V. Chernikov 	memcpy(paddr, item, size);
200768394ec8SAlexander V. Chernikov 
200868394ec8SAlexander V. Chernikov 	return (1);
200968394ec8SAlexander V. Chernikov }
201068394ec8SAlexander V. Chernikov 
201168394ec8SAlexander V. Chernikov /*
201268394ec8SAlexander V. Chernikov  * Deletes item with key @key from ascending-sorted array @base.
201368394ec8SAlexander V. Chernikov  *
201468394ec8SAlexander V. Chernikov  * Returns 1 on success, 0 for non-existent key.
201568394ec8SAlexander V. Chernikov  */
201668394ec8SAlexander V. Chernikov static int
201768394ec8SAlexander V. Chernikov bdel(const void *key, void *base, size_t nmemb, size_t size,
201868394ec8SAlexander V. Chernikov     int (*compar) (const void *, const void *))
201968394ec8SAlexander V. Chernikov {
202068394ec8SAlexander V. Chernikov 	caddr_t item;
202168394ec8SAlexander V. Chernikov 	size_t sz;
202268394ec8SAlexander V. Chernikov 
202368394ec8SAlexander V. Chernikov 	item = (caddr_t)bsearch(key, base, nmemb, size, compar);
202468394ec8SAlexander V. Chernikov 
202568394ec8SAlexander V. Chernikov 	if (item == NULL)
202668394ec8SAlexander V. Chernikov 		return (0);
202768394ec8SAlexander V. Chernikov 
202868394ec8SAlexander V. Chernikov 	sz = (caddr_t)base + nmemb * size - item;
202968394ec8SAlexander V. Chernikov 
203068394ec8SAlexander V. Chernikov 	if (sz > 0)
203168394ec8SAlexander V. Chernikov 		memmove(item, item + size, sz);
203268394ec8SAlexander V. Chernikov 
203368394ec8SAlexander V. Chernikov 	return (1);
203468394ec8SAlexander V. Chernikov }
203568394ec8SAlexander V. Chernikov 
203668394ec8SAlexander V. Chernikov static struct ifidx *
203768394ec8SAlexander V. Chernikov ifidx_find(struct table_info *ti, void *key)
203868394ec8SAlexander V. Chernikov {
203968394ec8SAlexander V. Chernikov 	struct ifidx *ifi;
204068394ec8SAlexander V. Chernikov 
204168394ec8SAlexander V. Chernikov 	ifi = bsearch(key, ti->state, ti->data, sizeof(struct ifidx),
204268394ec8SAlexander V. Chernikov 	    compare_ifidx);
204368394ec8SAlexander V. Chernikov 
204468394ec8SAlexander V. Chernikov 	return (ifi);
204568394ec8SAlexander V. Chernikov }
204668394ec8SAlexander V. Chernikov 
204768394ec8SAlexander V. Chernikov static int
204868394ec8SAlexander V. Chernikov ta_lookup_ifidx(struct table_info *ti, void *key, uint32_t keylen,
20499f7d47b0SAlexander V. Chernikov     uint32_t *val)
20509f7d47b0SAlexander V. Chernikov {
205168394ec8SAlexander V. Chernikov 	struct ifidx *ifi;
20529f7d47b0SAlexander V. Chernikov 
205368394ec8SAlexander V. Chernikov 	ifi = ifidx_find(ti, key);
20549f7d47b0SAlexander V. Chernikov 
205568394ec8SAlexander V. Chernikov 	if (ifi != NULL) {
205668394ec8SAlexander V. Chernikov 		*val = ifi->value;
20579f7d47b0SAlexander V. Chernikov 		return (1);
20589f7d47b0SAlexander V. Chernikov 	}
20599f7d47b0SAlexander V. Chernikov 
20609f7d47b0SAlexander V. Chernikov 	return (0);
20619f7d47b0SAlexander V. Chernikov }
20629f7d47b0SAlexander V. Chernikov 
20639f7d47b0SAlexander V. Chernikov static int
206468394ec8SAlexander V. Chernikov ta_init_ifidx(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
2065914bffb6SAlexander V. Chernikov     char *data, uint8_t tflags)
20669f7d47b0SAlexander V. Chernikov {
206768394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
20689f7d47b0SAlexander V. Chernikov 
206968394ec8SAlexander V. Chernikov 	icfg = malloc(sizeof(struct iftable_cfg), M_IPFW, M_WAITOK | M_ZERO);
20709f7d47b0SAlexander V. Chernikov 
20710cba2b28SAlexander V. Chernikov 	icfg->ii = ipfw_objhash_create(DEFAULT_IFIDX_SIZE);
20720cba2b28SAlexander V. Chernikov 	icfg->size = DEFAULT_IFIDX_SIZE;
20730bce0c23SAlexander V. Chernikov 	icfg->main_ptr = malloc(sizeof(struct ifidx) * icfg->size, M_IPFW,
207468394ec8SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
207568394ec8SAlexander V. Chernikov 	icfg->ch = ch;
20769f7d47b0SAlexander V. Chernikov 
207768394ec8SAlexander V. Chernikov 	*ta_state = icfg;
207868394ec8SAlexander V. Chernikov 	ti->state = icfg->main_ptr;
207968394ec8SAlexander V. Chernikov 	ti->lookup = ta_lookup_ifidx;
20809f7d47b0SAlexander V. Chernikov 
20819f7d47b0SAlexander V. Chernikov 	return (0);
20829f7d47b0SAlexander V. Chernikov }
20839f7d47b0SAlexander V. Chernikov 
208468394ec8SAlexander V. Chernikov /*
208568394ec8SAlexander V. Chernikov  * Handle tableinfo @ti pointer change (on table array resize).
208668394ec8SAlexander V. Chernikov  */
208768394ec8SAlexander V. Chernikov static void
208868394ec8SAlexander V. Chernikov ta_change_ti_ifidx(void *ta_state, struct table_info *ti)
208968394ec8SAlexander V. Chernikov {
209068394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
209168394ec8SAlexander V. Chernikov 
209268394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
209368394ec8SAlexander V. Chernikov 	icfg->ti = ti;
209468394ec8SAlexander V. Chernikov }
20959f7d47b0SAlexander V. Chernikov 
20969f7d47b0SAlexander V. Chernikov static void
209768394ec8SAlexander V. Chernikov destroy_ifidx_locked(struct namedobj_instance *ii, struct named_object *no,
209868394ec8SAlexander V. Chernikov     void *arg)
20999f7d47b0SAlexander V. Chernikov {
210068394ec8SAlexander V. Chernikov 	struct ifentry *ife;
210168394ec8SAlexander V. Chernikov 	struct ip_fw_chain *ch;
21029f7d47b0SAlexander V. Chernikov 
210368394ec8SAlexander V. Chernikov 	ch = (struct ip_fw_chain *)arg;
210468394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)no;
210568394ec8SAlexander V. Chernikov 
210668394ec8SAlexander V. Chernikov 	ipfw_iface_del_notify(ch, &ife->ic);
210768394ec8SAlexander V. Chernikov 	free(ife, M_IPFW_TBL);
21089f7d47b0SAlexander V. Chernikov }
21099f7d47b0SAlexander V. Chernikov 
211068394ec8SAlexander V. Chernikov 
211168394ec8SAlexander V. Chernikov /*
211268394ec8SAlexander V. Chernikov  * Destroys table @ti
211368394ec8SAlexander V. Chernikov  */
211468394ec8SAlexander V. Chernikov static void
211568394ec8SAlexander V. Chernikov ta_destroy_ifidx(void *ta_state, struct table_info *ti)
21169f7d47b0SAlexander V. Chernikov {
211768394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
211868394ec8SAlexander V. Chernikov 	struct ip_fw_chain *ch;
211968394ec8SAlexander V. Chernikov 
212068394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
212168394ec8SAlexander V. Chernikov 	ch = icfg->ch;
212268394ec8SAlexander V. Chernikov 
212368394ec8SAlexander V. Chernikov 	if (icfg->main_ptr != NULL)
212468394ec8SAlexander V. Chernikov 		free(icfg->main_ptr, M_IPFW);
212568394ec8SAlexander V. Chernikov 
212668394ec8SAlexander V. Chernikov 	ipfw_objhash_foreach(icfg->ii, destroy_ifidx_locked, ch);
212768394ec8SAlexander V. Chernikov 
212868394ec8SAlexander V. Chernikov 	ipfw_objhash_destroy(icfg->ii);
212968394ec8SAlexander V. Chernikov 
213068394ec8SAlexander V. Chernikov 	free(icfg, M_IPFW);
213168394ec8SAlexander V. Chernikov }
213268394ec8SAlexander V. Chernikov 
213368394ec8SAlexander V. Chernikov /*
21345f379342SAlexander V. Chernikov  * Provide algo-specific table info
21355f379342SAlexander V. Chernikov  */
21365f379342SAlexander V. Chernikov static void
21375f379342SAlexander V. Chernikov ta_dump_ifidx_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo)
21385f379342SAlexander V. Chernikov {
21395f379342SAlexander V. Chernikov 	struct iftable_cfg *cfg;
21405f379342SAlexander V. Chernikov 
21415f379342SAlexander V. Chernikov 	cfg = (struct iftable_cfg *)ta_state;
21425f379342SAlexander V. Chernikov 
21435f379342SAlexander V. Chernikov 	tinfo->taclass4 = IPFW_TACLASS_ARRAY;
21445f379342SAlexander V. Chernikov 	tinfo->size4 = cfg->size;
21455f379342SAlexander V. Chernikov 	tinfo->count4 = cfg->used;
21465f379342SAlexander V. Chernikov 	tinfo->itemsize4 = sizeof(struct ifidx);
21475f379342SAlexander V. Chernikov }
21485f379342SAlexander V. Chernikov 
21495f379342SAlexander V. Chernikov /*
215068394ec8SAlexander V. Chernikov  * Prepare state to add to the table:
215168394ec8SAlexander V. Chernikov  * allocate ifentry and reference needed interface.
215268394ec8SAlexander V. Chernikov  */
21539f7d47b0SAlexander V. Chernikov static int
215468394ec8SAlexander V. Chernikov ta_prepare_add_ifidx(struct ip_fw_chain *ch, struct tentry_info *tei,
215568394ec8SAlexander V. Chernikov     void *ta_buf)
215668394ec8SAlexander V. Chernikov {
215768394ec8SAlexander V. Chernikov 	struct ta_buf_ifidx *tb;
215868394ec8SAlexander V. Chernikov 	char *ifname;
215968394ec8SAlexander V. Chernikov 	struct ifentry *ife;
216068394ec8SAlexander V. Chernikov 
216168394ec8SAlexander V. Chernikov 	tb = (struct ta_buf_ifidx *)ta_buf;
216268394ec8SAlexander V. Chernikov 
216368394ec8SAlexander V. Chernikov 	/* Check if string is terminated */
216468394ec8SAlexander V. Chernikov 	ifname = (char *)tei->paddr;
216568394ec8SAlexander V. Chernikov 	if (strnlen(ifname, IF_NAMESIZE) == IF_NAMESIZE)
216668394ec8SAlexander V. Chernikov 		return (EINVAL);
216768394ec8SAlexander V. Chernikov 
216868394ec8SAlexander V. Chernikov 	ife = malloc(sizeof(struct ifentry), M_IPFW_TBL, M_WAITOK | M_ZERO);
216968394ec8SAlexander V. Chernikov 	ife->ic.cb = if_notifier;
217068394ec8SAlexander V. Chernikov 	ife->ic.cbdata = ife;
217168394ec8SAlexander V. Chernikov 
21728ebca97fSAlexander V. Chernikov 	if (ipfw_iface_ref(ch, ifname, &ife->ic) != 0) {
21738ebca97fSAlexander V. Chernikov 		free(ife, M_IPFW_TBL);
217468394ec8SAlexander V. Chernikov 		return (EINVAL);
21758ebca97fSAlexander V. Chernikov 	}
217668394ec8SAlexander V. Chernikov 
217768394ec8SAlexander V. Chernikov 	/* Use ipfw_iface 'ifname' field as stable storage */
217868394ec8SAlexander V. Chernikov 	ife->no.name = ife->ic.iface->ifname;
217968394ec8SAlexander V. Chernikov 
218068394ec8SAlexander V. Chernikov 	tb->ife = ife;
218168394ec8SAlexander V. Chernikov 
218268394ec8SAlexander V. Chernikov 	return (0);
218368394ec8SAlexander V. Chernikov }
218468394ec8SAlexander V. Chernikov 
218568394ec8SAlexander V. Chernikov static int
2186adea6201SAlexander V. Chernikov ta_add_ifidx(void *ta_state, struct table_info *ti, struct tentry_info *tei,
2187b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
218868394ec8SAlexander V. Chernikov {
218968394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
219068394ec8SAlexander V. Chernikov 	struct ifentry *ife, *tmp;
219168394ec8SAlexander V. Chernikov 	struct ta_buf_ifidx *tb;
219268394ec8SAlexander V. Chernikov 	struct ipfw_iface *iif;
219368394ec8SAlexander V. Chernikov 	struct ifidx *ifi;
219468394ec8SAlexander V. Chernikov 	char *ifname;
2195648e8380SAlexander V. Chernikov 	uint32_t value;
219668394ec8SAlexander V. Chernikov 
219768394ec8SAlexander V. Chernikov 	tb = (struct ta_buf_ifidx *)ta_buf;
219868394ec8SAlexander V. Chernikov 	ifname = (char *)tei->paddr;
219968394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
220068394ec8SAlexander V. Chernikov 	ife = tb->ife;
220168394ec8SAlexander V. Chernikov 
220268394ec8SAlexander V. Chernikov 	ife->icfg = icfg;
220313263632SAlexander V. Chernikov 	ife->value = tei->value;
220468394ec8SAlexander V. Chernikov 
220568394ec8SAlexander V. Chernikov 	tmp = (struct ifentry *)ipfw_objhash_lookup_name(icfg->ii, 0, ifname);
220668394ec8SAlexander V. Chernikov 
220768394ec8SAlexander V. Chernikov 	if (tmp != NULL) {
220868394ec8SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
220968394ec8SAlexander V. Chernikov 			return (EEXIST);
221068394ec8SAlexander V. Chernikov 
2211648e8380SAlexander V. Chernikov 		/* Exchange values in @tmp and @tei */
2212648e8380SAlexander V. Chernikov 		value = tmp->value;
2213648e8380SAlexander V. Chernikov 		tmp->value = tei->value;
2214648e8380SAlexander V. Chernikov 		tei->value = value;
221568394ec8SAlexander V. Chernikov 
2216648e8380SAlexander V. Chernikov 		iif = tmp->ic.iface;
221768394ec8SAlexander V. Chernikov 		if (iif->resolved != 0) {
2218648e8380SAlexander V. Chernikov 			/* We have to update runtime value, too */
221968394ec8SAlexander V. Chernikov 			ifi = ifidx_find(ti, &iif->ifindex);
222068394ec8SAlexander V. Chernikov 			ifi->value = ife->value;
222168394ec8SAlexander V. Chernikov 		}
222268394ec8SAlexander V. Chernikov 
222368394ec8SAlexander V. Chernikov 		/* Indicate that update has happened instead of addition */
222468394ec8SAlexander V. Chernikov 		tei->flags |= TEI_FLAGS_UPDATED;
2225adea6201SAlexander V. Chernikov 		*pnum = 0;
222668394ec8SAlexander V. Chernikov 		return (0);
222768394ec8SAlexander V. Chernikov 	}
222868394ec8SAlexander V. Chernikov 
22294c0c07a5SAlexander V. Chernikov 	if ((tei->flags & TEI_FLAGS_DONTADD) != 0)
22304c0c07a5SAlexander V. Chernikov 		return (EFBIG);
22314c0c07a5SAlexander V. Chernikov 
223268394ec8SAlexander V. Chernikov 	/* Link to internal list */
223368394ec8SAlexander V. Chernikov 	ipfw_objhash_add(icfg->ii, &ife->no);
223468394ec8SAlexander V. Chernikov 
223568394ec8SAlexander V. Chernikov 	/* Link notifier (possible running its callback) */
223668394ec8SAlexander V. Chernikov 	ipfw_iface_add_notify(icfg->ch, &ife->ic);
223768394ec8SAlexander V. Chernikov 	icfg->count++;
223868394ec8SAlexander V. Chernikov 
223968394ec8SAlexander V. Chernikov 	tb->ife = NULL;
2240adea6201SAlexander V. Chernikov 	*pnum = 1;
224168394ec8SAlexander V. Chernikov 
224268394ec8SAlexander V. Chernikov 	return (0);
224368394ec8SAlexander V. Chernikov }
224468394ec8SAlexander V. Chernikov 
224568394ec8SAlexander V. Chernikov /*
224668394ec8SAlexander V. Chernikov  * Prepare to delete key from table.
224768394ec8SAlexander V. Chernikov  * Do basic interface name checks.
224868394ec8SAlexander V. Chernikov  */
224968394ec8SAlexander V. Chernikov static int
225068394ec8SAlexander V. Chernikov ta_prepare_del_ifidx(struct ip_fw_chain *ch, struct tentry_info *tei,
225168394ec8SAlexander V. Chernikov     void *ta_buf)
22529f7d47b0SAlexander V. Chernikov {
225374b941f0SAlexander V. Chernikov 	struct ta_buf_ifidx *tb;
2254e0a8b9eeSAlexander V. Chernikov 	char *ifname;
22559f7d47b0SAlexander V. Chernikov 
225674b941f0SAlexander V. Chernikov 	tb = (struct ta_buf_ifidx *)ta_buf;
22579f7d47b0SAlexander V. Chernikov 
22589f7d47b0SAlexander V. Chernikov 	/* Check if string is terminated */
2259e0a8b9eeSAlexander V. Chernikov 	ifname = (char *)tei->paddr;
2260e0a8b9eeSAlexander V. Chernikov 	if (strnlen(ifname, IF_NAMESIZE) == IF_NAMESIZE)
22619f7d47b0SAlexander V. Chernikov 		return (EINVAL);
22629f7d47b0SAlexander V. Chernikov 
22639f7d47b0SAlexander V. Chernikov 	return (0);
22649f7d47b0SAlexander V. Chernikov }
22659f7d47b0SAlexander V. Chernikov 
226668394ec8SAlexander V. Chernikov /*
226768394ec8SAlexander V. Chernikov  * Remove key from both configuration list and
226868394ec8SAlexander V. Chernikov  * runtime array. Removed interface notification.
226968394ec8SAlexander V. Chernikov  */
22709f7d47b0SAlexander V. Chernikov static int
2271adea6201SAlexander V. Chernikov ta_del_ifidx(void *ta_state, struct table_info *ti, struct tentry_info *tei,
2272b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
22739f7d47b0SAlexander V. Chernikov {
227468394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
227568394ec8SAlexander V. Chernikov 	struct ifentry *ife;
227668394ec8SAlexander V. Chernikov 	struct ta_buf_ifidx *tb;
227768394ec8SAlexander V. Chernikov 	char *ifname;
227868394ec8SAlexander V. Chernikov 	uint16_t ifindex;
227968394ec8SAlexander V. Chernikov 	int res;
22809f7d47b0SAlexander V. Chernikov 
228168394ec8SAlexander V. Chernikov 	tb = (struct ta_buf_ifidx *)ta_buf;
228268394ec8SAlexander V. Chernikov 	ifname = (char *)tei->paddr;
228368394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
228468394ec8SAlexander V. Chernikov 	ife = tb->ife;
22859f7d47b0SAlexander V. Chernikov 
228668394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)ipfw_objhash_lookup_name(icfg->ii, 0, ifname);
22879f7d47b0SAlexander V. Chernikov 
228868394ec8SAlexander V. Chernikov 	if (ife == NULL)
228981d3153dSAlexander V. Chernikov 		return (ENOENT);
22909f7d47b0SAlexander V. Chernikov 
229168394ec8SAlexander V. Chernikov 	if (ife->linked != 0) {
229268394ec8SAlexander V. Chernikov 		/* We have to remove item from runtime */
229368394ec8SAlexander V. Chernikov 		ifindex = ife->ic.iface->ifindex;
229468394ec8SAlexander V. Chernikov 
229568394ec8SAlexander V. Chernikov 		res = bdel(&ifindex, icfg->main_ptr, icfg->used,
229668394ec8SAlexander V. Chernikov 		    sizeof(struct ifidx), compare_ifidx);
229768394ec8SAlexander V. Chernikov 
229868394ec8SAlexander V. Chernikov 		KASSERT(res == 1, ("index %d does not exist", ifindex));
229968394ec8SAlexander V. Chernikov 		icfg->used--;
230068394ec8SAlexander V. Chernikov 		ti->data = icfg->used;
230168394ec8SAlexander V. Chernikov 		ife->linked = 0;
230268394ec8SAlexander V. Chernikov 	}
230368394ec8SAlexander V. Chernikov 
230468394ec8SAlexander V. Chernikov 	/* Unlink from local list */
230568394ec8SAlexander V. Chernikov 	ipfw_objhash_del(icfg->ii, &ife->no);
230668394ec8SAlexander V. Chernikov 	/* Unlink notifier */
230768394ec8SAlexander V. Chernikov 	ipfw_iface_del_notify(icfg->ch, &ife->ic);
230868394ec8SAlexander V. Chernikov 
230968394ec8SAlexander V. Chernikov 	icfg->count--;
2310648e8380SAlexander V. Chernikov 	tei->value = ife->value;
231168394ec8SAlexander V. Chernikov 
231268394ec8SAlexander V. Chernikov 	tb->ife = ife;
2313adea6201SAlexander V. Chernikov 	*pnum = 1;
231468394ec8SAlexander V. Chernikov 
23159f7d47b0SAlexander V. Chernikov 	return (0);
23169f7d47b0SAlexander V. Chernikov }
23179f7d47b0SAlexander V. Chernikov 
231868394ec8SAlexander V. Chernikov /*
231968394ec8SAlexander V. Chernikov  * Flush deleted entry.
232068394ec8SAlexander V. Chernikov  * Drops interface reference and frees entry.
232168394ec8SAlexander V. Chernikov  */
23229f7d47b0SAlexander V. Chernikov static void
232368394ec8SAlexander V. Chernikov ta_flush_ifidx_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
232468394ec8SAlexander V. Chernikov     void *ta_buf)
23259f7d47b0SAlexander V. Chernikov {
232668394ec8SAlexander V. Chernikov 	struct ta_buf_ifidx *tb;
23279f7d47b0SAlexander V. Chernikov 
232868394ec8SAlexander V. Chernikov 	tb = (struct ta_buf_ifidx *)ta_buf;
23299f7d47b0SAlexander V. Chernikov 
233068394ec8SAlexander V. Chernikov 	if (tb->ife != NULL) {
233168394ec8SAlexander V. Chernikov 		/* Unlink first */
233268394ec8SAlexander V. Chernikov 		ipfw_iface_unref(ch, &tb->ife->ic);
233368394ec8SAlexander V. Chernikov 		free(tb->ife, M_IPFW_TBL);
233468394ec8SAlexander V. Chernikov 	}
233568394ec8SAlexander V. Chernikov }
233668394ec8SAlexander V. Chernikov 
233768394ec8SAlexander V. Chernikov 
233868394ec8SAlexander V. Chernikov /*
233968394ec8SAlexander V. Chernikov  * Handle interface announce/withdrawal for particular table.
234068394ec8SAlexander V. Chernikov  * Every real runtime array modification happens here.
234168394ec8SAlexander V. Chernikov  */
234268394ec8SAlexander V. Chernikov static void
234368394ec8SAlexander V. Chernikov if_notifier(struct ip_fw_chain *ch, void *cbdata, uint16_t ifindex)
234468394ec8SAlexander V. Chernikov {
234568394ec8SAlexander V. Chernikov 	struct ifentry *ife;
234668394ec8SAlexander V. Chernikov 	struct ifidx ifi;
234768394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
234868394ec8SAlexander V. Chernikov 	struct table_info *ti;
234968394ec8SAlexander V. Chernikov 	int res;
235068394ec8SAlexander V. Chernikov 
235168394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)cbdata;
235268394ec8SAlexander V. Chernikov 	icfg = ife->icfg;
235368394ec8SAlexander V. Chernikov 	ti = icfg->ti;
235468394ec8SAlexander V. Chernikov 
235568394ec8SAlexander V. Chernikov 	KASSERT(ti != NULL, ("ti=NULL, check change_ti handler"));
235668394ec8SAlexander V. Chernikov 
235768394ec8SAlexander V. Chernikov 	if (ife->linked == 0 && ifindex != 0) {
235868394ec8SAlexander V. Chernikov 		/* Interface announce */
235968394ec8SAlexander V. Chernikov 		ifi.kidx = ifindex;
236068394ec8SAlexander V. Chernikov 		ifi.spare = 0;
236168394ec8SAlexander V. Chernikov 		ifi.value = ife->value;
236268394ec8SAlexander V. Chernikov 		res = badd(&ifindex, &ifi, icfg->main_ptr, icfg->used,
236368394ec8SAlexander V. Chernikov 		    sizeof(struct ifidx), compare_ifidx);
236468394ec8SAlexander V. Chernikov 		KASSERT(res == 1, ("index %d already exists", ifindex));
236568394ec8SAlexander V. Chernikov 		icfg->used++;
236668394ec8SAlexander V. Chernikov 		ti->data = icfg->used;
236768394ec8SAlexander V. Chernikov 		ife->linked = 1;
236868394ec8SAlexander V. Chernikov 	} else if (ife->linked != 0 && ifindex == 0) {
236968394ec8SAlexander V. Chernikov 		/* Interface withdrawal */
237068394ec8SAlexander V. Chernikov 		ifindex = ife->ic.iface->ifindex;
237168394ec8SAlexander V. Chernikov 
237268394ec8SAlexander V. Chernikov 		res = bdel(&ifindex, icfg->main_ptr, icfg->used,
237368394ec8SAlexander V. Chernikov 		    sizeof(struct ifidx), compare_ifidx);
237468394ec8SAlexander V. Chernikov 
237568394ec8SAlexander V. Chernikov 		KASSERT(res == 1, ("index %d does not exist", ifindex));
237668394ec8SAlexander V. Chernikov 		icfg->used--;
237768394ec8SAlexander V. Chernikov 		ti->data = icfg->used;
237868394ec8SAlexander V. Chernikov 		ife->linked = 0;
237968394ec8SAlexander V. Chernikov 	}
238068394ec8SAlexander V. Chernikov }
238168394ec8SAlexander V. Chernikov 
238268394ec8SAlexander V. Chernikov 
238368394ec8SAlexander V. Chernikov /*
238468394ec8SAlexander V. Chernikov  * Table growing callbacks.
238568394ec8SAlexander V. Chernikov  */
238668394ec8SAlexander V. Chernikov 
2387b6ee846eSAlexander V. Chernikov static int
2388301290bcSAlexander V. Chernikov ta_need_modify_ifidx(void *ta_state, struct table_info *ti, uint32_t count,
2389b6ee846eSAlexander V. Chernikov     uint64_t *pflags)
2390b6ee846eSAlexander V. Chernikov {
2391b6ee846eSAlexander V. Chernikov 	struct iftable_cfg *cfg;
23920bce0c23SAlexander V. Chernikov 	uint32_t size;
2393b6ee846eSAlexander V. Chernikov 
2394b6ee846eSAlexander V. Chernikov 	cfg = (struct iftable_cfg *)ta_state;
2395b6ee846eSAlexander V. Chernikov 
23960bce0c23SAlexander V. Chernikov 	size = cfg->size;
23970bce0c23SAlexander V. Chernikov 	while (size < cfg->count + count)
23980bce0c23SAlexander V. Chernikov 		size *= 2;
23990bce0c23SAlexander V. Chernikov 
24000bce0c23SAlexander V. Chernikov 	if (size != cfg->size) {
24010bce0c23SAlexander V. Chernikov 		*pflags = size;
2402301290bcSAlexander V. Chernikov 		return (1);
2403b6ee846eSAlexander V. Chernikov 	}
2404b6ee846eSAlexander V. Chernikov 
2405301290bcSAlexander V. Chernikov 	return (0);
2406b6ee846eSAlexander V. Chernikov }
2407b6ee846eSAlexander V. Chernikov 
240868394ec8SAlexander V. Chernikov /*
240968394ec8SAlexander V. Chernikov  * Allocate ned, larger runtime ifidx array.
241068394ec8SAlexander V. Chernikov  */
241168394ec8SAlexander V. Chernikov static int
241268394ec8SAlexander V. Chernikov ta_prepare_mod_ifidx(void *ta_buf, uint64_t *pflags)
241368394ec8SAlexander V. Chernikov {
24140bce0c23SAlexander V. Chernikov 	struct mod_item *mi;
241568394ec8SAlexander V. Chernikov 
24160bce0c23SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
241768394ec8SAlexander V. Chernikov 
24180bce0c23SAlexander V. Chernikov 	memset(mi, 0, sizeof(struct mod_item));
241968394ec8SAlexander V. Chernikov 	mi->size = *pflags;
242068394ec8SAlexander V. Chernikov 	mi->main_ptr = malloc(sizeof(struct ifidx) * mi->size, M_IPFW,
242168394ec8SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
242268394ec8SAlexander V. Chernikov 
242368394ec8SAlexander V. Chernikov 	return (0);
242468394ec8SAlexander V. Chernikov }
242568394ec8SAlexander V. Chernikov 
242668394ec8SAlexander V. Chernikov /*
242768394ec8SAlexander V. Chernikov  * Copy data from old runtime array to new one.
242868394ec8SAlexander V. Chernikov  */
242968394ec8SAlexander V. Chernikov static int
243068394ec8SAlexander V. Chernikov ta_fill_mod_ifidx(void *ta_state, struct table_info *ti, void *ta_buf,
243168394ec8SAlexander V. Chernikov     uint64_t *pflags)
243268394ec8SAlexander V. Chernikov {
24330bce0c23SAlexander V. Chernikov 	struct mod_item *mi;
243468394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
243568394ec8SAlexander V. Chernikov 
24360bce0c23SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
243768394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
243868394ec8SAlexander V. Chernikov 
243968394ec8SAlexander V. Chernikov 	/* Check if we still need to grow array */
244068394ec8SAlexander V. Chernikov 	if (icfg->size >= mi->size) {
244168394ec8SAlexander V. Chernikov 		*pflags = 0;
244268394ec8SAlexander V. Chernikov 		return (0);
244368394ec8SAlexander V. Chernikov 	}
244468394ec8SAlexander V. Chernikov 
244568394ec8SAlexander V. Chernikov 	memcpy(mi->main_ptr, icfg->main_ptr, icfg->used * sizeof(struct ifidx));
244668394ec8SAlexander V. Chernikov 
244768394ec8SAlexander V. Chernikov 	return (0);
244868394ec8SAlexander V. Chernikov }
244968394ec8SAlexander V. Chernikov 
245068394ec8SAlexander V. Chernikov /*
245168394ec8SAlexander V. Chernikov  * Switch old & new arrays.
245268394ec8SAlexander V. Chernikov  */
2453301290bcSAlexander V. Chernikov static void
245468394ec8SAlexander V. Chernikov ta_modify_ifidx(void *ta_state, struct table_info *ti, void *ta_buf,
245568394ec8SAlexander V. Chernikov     uint64_t pflags)
245668394ec8SAlexander V. Chernikov {
24570bce0c23SAlexander V. Chernikov 	struct mod_item *mi;
245868394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
245968394ec8SAlexander V. Chernikov 	void *old_ptr;
246068394ec8SAlexander V. Chernikov 
24610bce0c23SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
246268394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
246368394ec8SAlexander V. Chernikov 
246468394ec8SAlexander V. Chernikov 	old_ptr = icfg->main_ptr;
246568394ec8SAlexander V. Chernikov 	icfg->main_ptr = mi->main_ptr;
246668394ec8SAlexander V. Chernikov 	icfg->size = mi->size;
246768394ec8SAlexander V. Chernikov 	ti->state = icfg->main_ptr;
246868394ec8SAlexander V. Chernikov 
246968394ec8SAlexander V. Chernikov 	mi->main_ptr = old_ptr;
247068394ec8SAlexander V. Chernikov }
247168394ec8SAlexander V. Chernikov 
247268394ec8SAlexander V. Chernikov /*
247368394ec8SAlexander V. Chernikov  * Free unneded array.
247468394ec8SAlexander V. Chernikov  */
247568394ec8SAlexander V. Chernikov static void
247668394ec8SAlexander V. Chernikov ta_flush_mod_ifidx(void *ta_buf)
247768394ec8SAlexander V. Chernikov {
24780bce0c23SAlexander V. Chernikov 	struct mod_item *mi;
247968394ec8SAlexander V. Chernikov 
24800bce0c23SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
248168394ec8SAlexander V. Chernikov 	if (mi->main_ptr != NULL)
248268394ec8SAlexander V. Chernikov 		free(mi->main_ptr, M_IPFW);
24839f7d47b0SAlexander V. Chernikov }
24849f7d47b0SAlexander V. Chernikov 
24859f7d47b0SAlexander V. Chernikov static int
248668394ec8SAlexander V. Chernikov ta_dump_ifidx_tentry(void *ta_state, struct table_info *ti, void *e,
248781d3153dSAlexander V. Chernikov     ipfw_obj_tentry *tent)
24889f7d47b0SAlexander V. Chernikov {
248968394ec8SAlexander V. Chernikov 	struct ifentry *ife;
24909f7d47b0SAlexander V. Chernikov 
249168394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)e;
249268394ec8SAlexander V. Chernikov 
249381d3153dSAlexander V. Chernikov 	tent->masklen = 8 * IF_NAMESIZE;
249468394ec8SAlexander V. Chernikov 	memcpy(&tent->k, ife->no.name, IF_NAMESIZE);
24950cba2b28SAlexander V. Chernikov 	tent->v.kidx = ife->value;
24969f7d47b0SAlexander V. Chernikov 
24979f7d47b0SAlexander V. Chernikov 	return (0);
24989f7d47b0SAlexander V. Chernikov }
24999f7d47b0SAlexander V. Chernikov 
250081d3153dSAlexander V. Chernikov static int
2501914bffb6SAlexander V. Chernikov ta_find_ifidx_tentry(void *ta_state, struct table_info *ti,
2502914bffb6SAlexander V. Chernikov     ipfw_obj_tentry *tent)
250381d3153dSAlexander V. Chernikov {
250468394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
250568394ec8SAlexander V. Chernikov 	struct ifentry *ife;
250668394ec8SAlexander V. Chernikov 	char *ifname;
250781d3153dSAlexander V. Chernikov 
250868394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
2509914bffb6SAlexander V. Chernikov 	ifname = tent->k.iface;
251081d3153dSAlexander V. Chernikov 
251168394ec8SAlexander V. Chernikov 	if (strnlen(ifname, IF_NAMESIZE) == IF_NAMESIZE)
251268394ec8SAlexander V. Chernikov 		return (EINVAL);
251381d3153dSAlexander V. Chernikov 
251468394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)ipfw_objhash_lookup_name(icfg->ii, 0, ifname);
251568394ec8SAlexander V. Chernikov 
251668394ec8SAlexander V. Chernikov 	if (ife != NULL) {
251768394ec8SAlexander V. Chernikov 		ta_dump_ifidx_tentry(ta_state, ti, ife, tent);
251881d3153dSAlexander V. Chernikov 		return (0);
251981d3153dSAlexander V. Chernikov 	}
252081d3153dSAlexander V. Chernikov 
252181d3153dSAlexander V. Chernikov 	return (ENOENT);
252281d3153dSAlexander V. Chernikov }
252381d3153dSAlexander V. Chernikov 
252468394ec8SAlexander V. Chernikov struct wa_ifidx {
252568394ec8SAlexander V. Chernikov 	ta_foreach_f	*f;
252668394ec8SAlexander V. Chernikov 	void		*arg;
252768394ec8SAlexander V. Chernikov };
252868394ec8SAlexander V. Chernikov 
25299f7d47b0SAlexander V. Chernikov static void
253068394ec8SAlexander V. Chernikov foreach_ifidx(struct namedobj_instance *ii, struct named_object *no,
25319f7d47b0SAlexander V. Chernikov     void *arg)
25329f7d47b0SAlexander V. Chernikov {
253368394ec8SAlexander V. Chernikov 	struct ifentry *ife;
253468394ec8SAlexander V. Chernikov 	struct wa_ifidx *wa;
25359f7d47b0SAlexander V. Chernikov 
253668394ec8SAlexander V. Chernikov 	ife = (struct ifentry *)no;
253768394ec8SAlexander V. Chernikov 	wa = (struct wa_ifidx *)arg;
253868394ec8SAlexander V. Chernikov 
253968394ec8SAlexander V. Chernikov 	wa->f(ife, wa->arg);
25409f7d47b0SAlexander V. Chernikov }
25419f7d47b0SAlexander V. Chernikov 
254268394ec8SAlexander V. Chernikov static void
254368394ec8SAlexander V. Chernikov ta_foreach_ifidx(void *ta_state, struct table_info *ti, ta_foreach_f *f,
254468394ec8SAlexander V. Chernikov     void *arg)
254568394ec8SAlexander V. Chernikov {
254668394ec8SAlexander V. Chernikov 	struct iftable_cfg *icfg;
254768394ec8SAlexander V. Chernikov 	struct wa_ifidx wa;
254868394ec8SAlexander V. Chernikov 
254968394ec8SAlexander V. Chernikov 	icfg = (struct iftable_cfg *)ta_state;
255068394ec8SAlexander V. Chernikov 
255168394ec8SAlexander V. Chernikov 	wa.f = f;
255268394ec8SAlexander V. Chernikov 	wa.arg = arg;
255368394ec8SAlexander V. Chernikov 
255468394ec8SAlexander V. Chernikov 	ipfw_objhash_foreach(icfg->ii, foreach_ifidx, &wa);
255568394ec8SAlexander V. Chernikov }
255668394ec8SAlexander V. Chernikov 
255774b941f0SAlexander V. Chernikov struct table_algo iface_idx = {
2558adea6201SAlexander V. Chernikov 	.name		= "iface:array",
25599d099b4fSAlexander V. Chernikov 	.type		= IPFW_TABLE_INTERFACE,
256057a1cf95SAlexander V. Chernikov 	.flags		= TA_FLAG_DEFAULT,
256157a1cf95SAlexander V. Chernikov 	.ta_buf_size	= sizeof(struct ta_buf_ifidx),
256268394ec8SAlexander V. Chernikov 	.init		= ta_init_ifidx,
256368394ec8SAlexander V. Chernikov 	.destroy	= ta_destroy_ifidx,
256468394ec8SAlexander V. Chernikov 	.prepare_add	= ta_prepare_add_ifidx,
256568394ec8SAlexander V. Chernikov 	.prepare_del	= ta_prepare_del_ifidx,
256668394ec8SAlexander V. Chernikov 	.add		= ta_add_ifidx,
256768394ec8SAlexander V. Chernikov 	.del		= ta_del_ifidx,
256868394ec8SAlexander V. Chernikov 	.flush_entry	= ta_flush_ifidx_entry,
256968394ec8SAlexander V. Chernikov 	.foreach	= ta_foreach_ifidx,
257068394ec8SAlexander V. Chernikov 	.dump_tentry	= ta_dump_ifidx_tentry,
257168394ec8SAlexander V. Chernikov 	.find_tentry	= ta_find_ifidx_tentry,
25725f379342SAlexander V. Chernikov 	.dump_tinfo	= ta_dump_ifidx_tinfo,
2573301290bcSAlexander V. Chernikov 	.need_modify	= ta_need_modify_ifidx,
257468394ec8SAlexander V. Chernikov 	.prepare_mod	= ta_prepare_mod_ifidx,
257568394ec8SAlexander V. Chernikov 	.fill_mod	= ta_fill_mod_ifidx,
257668394ec8SAlexander V. Chernikov 	.modify		= ta_modify_ifidx,
257768394ec8SAlexander V. Chernikov 	.flush_mod	= ta_flush_mod_ifidx,
257868394ec8SAlexander V. Chernikov 	.change_ti	= ta_change_ti_ifidx,
25799f7d47b0SAlexander V. Chernikov };
25809f7d47b0SAlexander V. Chernikov 
2581b23d5de9SAlexander V. Chernikov /*
2582b23d5de9SAlexander V. Chernikov  * Number array cmds.
2583b23d5de9SAlexander V. Chernikov  *
2584b23d5de9SAlexander V. Chernikov  * Implementation:
2585b23d5de9SAlexander V. Chernikov  *
2586b23d5de9SAlexander V. Chernikov  * Runtime part:
2587b23d5de9SAlexander V. Chernikov  * - sorted array of "struct numarray" pointed by ti->state.
2588b23d5de9SAlexander V. Chernikov  *   Array is allocated with rounding up to NUMARRAY_CHUNK.
2589b23d5de9SAlexander V. Chernikov  * - current array size is stored in ti->data
2590b23d5de9SAlexander V. Chernikov  *
2591b23d5de9SAlexander V. Chernikov  */
2592b23d5de9SAlexander V. Chernikov 
2593b23d5de9SAlexander V. Chernikov struct numarray {
2594b23d5de9SAlexander V. Chernikov 	uint32_t	number;
2595b23d5de9SAlexander V. Chernikov 	uint32_t	value;
2596b23d5de9SAlexander V. Chernikov };
2597b23d5de9SAlexander V. Chernikov 
2598b23d5de9SAlexander V. Chernikov struct numarray_cfg {
2599b23d5de9SAlexander V. Chernikov 	void	*main_ptr;
2600b23d5de9SAlexander V. Chernikov 	size_t	size;	/* Number of items allocated in array */
2601b23d5de9SAlexander V. Chernikov 	size_t	used;	/* Number of items _active_ now */
2602b23d5de9SAlexander V. Chernikov };
2603b23d5de9SAlexander V. Chernikov 
26040bce0c23SAlexander V. Chernikov struct ta_buf_numarray
26050bce0c23SAlexander V. Chernikov {
26060bce0c23SAlexander V. Chernikov 	struct numarray na;
26070bce0c23SAlexander V. Chernikov };
2608b23d5de9SAlexander V. Chernikov 
2609b23d5de9SAlexander V. Chernikov int compare_numarray(const void *k, const void *v);
2610*9fe15d06SAlexander V. Chernikov static struct numarray *numarray_find(struct table_info *ti, void *key);
2611*9fe15d06SAlexander V. Chernikov static int ta_lookup_numarray(struct table_info *ti, void *key,
2612*9fe15d06SAlexander V. Chernikov     uint32_t keylen, uint32_t *val);
2613*9fe15d06SAlexander V. Chernikov static int ta_init_numarray(struct ip_fw_chain *ch, void **ta_state,
2614*9fe15d06SAlexander V. Chernikov     struct table_info *ti, char *data, uint8_t tflags);
2615*9fe15d06SAlexander V. Chernikov static void ta_destroy_numarray(void *ta_state, struct table_info *ti);
2616*9fe15d06SAlexander V. Chernikov static void ta_dump_numarray_tinfo(void *ta_state, struct table_info *ti,
2617*9fe15d06SAlexander V. Chernikov     ipfw_ta_tinfo *tinfo);
2618*9fe15d06SAlexander V. Chernikov static int ta_prepare_add_numarray(struct ip_fw_chain *ch,
2619*9fe15d06SAlexander V. Chernikov     struct tentry_info *tei, void *ta_buf);
2620*9fe15d06SAlexander V. Chernikov static int ta_add_numarray(void *ta_state, struct table_info *ti,
2621*9fe15d06SAlexander V. Chernikov     struct tentry_info *tei, void *ta_buf, uint32_t *pnum);
2622*9fe15d06SAlexander V. Chernikov static int ta_del_numarray(void *ta_state, struct table_info *ti,
2623*9fe15d06SAlexander V. Chernikov     struct tentry_info *tei, void *ta_buf, uint32_t *pnum);
2624*9fe15d06SAlexander V. Chernikov static void ta_flush_numarray_entry(struct ip_fw_chain *ch,
2625*9fe15d06SAlexander V. Chernikov     struct tentry_info *tei, void *ta_buf);
2626*9fe15d06SAlexander V. Chernikov static int ta_need_modify_numarray(void *ta_state, struct table_info *ti,
2627*9fe15d06SAlexander V. Chernikov     uint32_t count, uint64_t *pflags);
2628*9fe15d06SAlexander V. Chernikov static int ta_prepare_mod_numarray(void *ta_buf, uint64_t *pflags);
2629*9fe15d06SAlexander V. Chernikov static int ta_fill_mod_numarray(void *ta_state, struct table_info *ti,
2630*9fe15d06SAlexander V. Chernikov     void *ta_buf, uint64_t *pflags);
2631*9fe15d06SAlexander V. Chernikov static void ta_modify_numarray(void *ta_state, struct table_info *ti,
2632*9fe15d06SAlexander V. Chernikov     void *ta_buf, uint64_t pflags);
2633*9fe15d06SAlexander V. Chernikov static void ta_flush_mod_numarray(void *ta_buf);
2634*9fe15d06SAlexander V. Chernikov static int ta_dump_numarray_tentry(void *ta_state, struct table_info *ti,
2635*9fe15d06SAlexander V. Chernikov     void *e, ipfw_obj_tentry *tent);
2636*9fe15d06SAlexander V. Chernikov static int ta_find_numarray_tentry(void *ta_state, struct table_info *ti,
2637*9fe15d06SAlexander V. Chernikov     ipfw_obj_tentry *tent);
2638*9fe15d06SAlexander V. Chernikov static void ta_foreach_numarray(void *ta_state, struct table_info *ti,
2639*9fe15d06SAlexander V. Chernikov     ta_foreach_f *f, void *arg);
2640b23d5de9SAlexander V. Chernikov 
2641b23d5de9SAlexander V. Chernikov int
2642b23d5de9SAlexander V. Chernikov compare_numarray(const void *k, const void *v)
2643b23d5de9SAlexander V. Chernikov {
2644d4e1b515SAlexander V. Chernikov 	const struct numarray *na;
2645b23d5de9SAlexander V. Chernikov 	uint32_t key;
2646b23d5de9SAlexander V. Chernikov 
2647d4e1b515SAlexander V. Chernikov 	key = *((const uint32_t *)k);
2648d4e1b515SAlexander V. Chernikov 	na = (const struct numarray *)v;
2649b23d5de9SAlexander V. Chernikov 
2650b23d5de9SAlexander V. Chernikov 	if (key < na->number)
2651b23d5de9SAlexander V. Chernikov 		return (-1);
2652b23d5de9SAlexander V. Chernikov 	else if (key > na->number)
2653b23d5de9SAlexander V. Chernikov 		return (1);
2654b23d5de9SAlexander V. Chernikov 
2655b23d5de9SAlexander V. Chernikov 	return (0);
2656b23d5de9SAlexander V. Chernikov }
2657b23d5de9SAlexander V. Chernikov 
2658b23d5de9SAlexander V. Chernikov static struct numarray *
2659b23d5de9SAlexander V. Chernikov numarray_find(struct table_info *ti, void *key)
2660b23d5de9SAlexander V. Chernikov {
2661b23d5de9SAlexander V. Chernikov 	struct numarray *ri;
2662b23d5de9SAlexander V. Chernikov 
2663b23d5de9SAlexander V. Chernikov 	ri = bsearch(key, ti->state, ti->data, sizeof(struct numarray),
2664b23d5de9SAlexander V. Chernikov 	    compare_ifidx);
2665b23d5de9SAlexander V. Chernikov 
2666b23d5de9SAlexander V. Chernikov 	return (ri);
2667b23d5de9SAlexander V. Chernikov }
2668b23d5de9SAlexander V. Chernikov 
2669b23d5de9SAlexander V. Chernikov static int
2670b23d5de9SAlexander V. Chernikov ta_lookup_numarray(struct table_info *ti, void *key, uint32_t keylen,
2671b23d5de9SAlexander V. Chernikov     uint32_t *val)
2672b23d5de9SAlexander V. Chernikov {
2673b23d5de9SAlexander V. Chernikov 	struct numarray *ri;
2674b23d5de9SAlexander V. Chernikov 
2675b23d5de9SAlexander V. Chernikov 	ri = numarray_find(ti, key);
2676b23d5de9SAlexander V. Chernikov 
2677b23d5de9SAlexander V. Chernikov 	if (ri != NULL) {
2678b23d5de9SAlexander V. Chernikov 		*val = ri->value;
2679b23d5de9SAlexander V. Chernikov 		return (1);
2680b23d5de9SAlexander V. Chernikov 	}
2681b23d5de9SAlexander V. Chernikov 
2682b23d5de9SAlexander V. Chernikov 	return (0);
2683b23d5de9SAlexander V. Chernikov }
2684b23d5de9SAlexander V. Chernikov 
2685b23d5de9SAlexander V. Chernikov static int
2686b23d5de9SAlexander V. Chernikov ta_init_numarray(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
2687914bffb6SAlexander V. Chernikov     char *data, uint8_t tflags)
2688b23d5de9SAlexander V. Chernikov {
2689b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2690b23d5de9SAlexander V. Chernikov 
2691b23d5de9SAlexander V. Chernikov 	cfg = malloc(sizeof(*cfg), M_IPFW, M_WAITOK | M_ZERO);
2692b23d5de9SAlexander V. Chernikov 
26930bce0c23SAlexander V. Chernikov 	cfg->size = 16;
2694b23d5de9SAlexander V. Chernikov 	cfg->main_ptr = malloc(sizeof(struct numarray) * cfg->size, M_IPFW,
2695b23d5de9SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
2696b23d5de9SAlexander V. Chernikov 
2697b23d5de9SAlexander V. Chernikov 	*ta_state = cfg;
2698b23d5de9SAlexander V. Chernikov 	ti->state = cfg->main_ptr;
2699b23d5de9SAlexander V. Chernikov 	ti->lookup = ta_lookup_numarray;
2700b23d5de9SAlexander V. Chernikov 
2701b23d5de9SAlexander V. Chernikov 	return (0);
2702b23d5de9SAlexander V. Chernikov }
2703b23d5de9SAlexander V. Chernikov 
2704b23d5de9SAlexander V. Chernikov /*
2705b23d5de9SAlexander V. Chernikov  * Destroys table @ti
2706b23d5de9SAlexander V. Chernikov  */
2707b23d5de9SAlexander V. Chernikov static void
2708b23d5de9SAlexander V. Chernikov ta_destroy_numarray(void *ta_state, struct table_info *ti)
2709b23d5de9SAlexander V. Chernikov {
2710b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2711b23d5de9SAlexander V. Chernikov 
2712b23d5de9SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2713b23d5de9SAlexander V. Chernikov 
2714b23d5de9SAlexander V. Chernikov 	if (cfg->main_ptr != NULL)
2715b23d5de9SAlexander V. Chernikov 		free(cfg->main_ptr, M_IPFW);
2716b23d5de9SAlexander V. Chernikov 
2717b23d5de9SAlexander V. Chernikov 	free(cfg, M_IPFW);
2718b23d5de9SAlexander V. Chernikov }
2719b23d5de9SAlexander V. Chernikov 
2720b23d5de9SAlexander V. Chernikov /*
27215f379342SAlexander V. Chernikov  * Provide algo-specific table info
27225f379342SAlexander V. Chernikov  */
27235f379342SAlexander V. Chernikov static void
27245f379342SAlexander V. Chernikov ta_dump_numarray_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo)
27255f379342SAlexander V. Chernikov {
27265f379342SAlexander V. Chernikov 	struct numarray_cfg *cfg;
27275f379342SAlexander V. Chernikov 
27285f379342SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
27295f379342SAlexander V. Chernikov 
27305f379342SAlexander V. Chernikov 	tinfo->taclass4 = IPFW_TACLASS_ARRAY;
27315f379342SAlexander V. Chernikov 	tinfo->size4 = cfg->size;
27325f379342SAlexander V. Chernikov 	tinfo->count4 = cfg->used;
27335f379342SAlexander V. Chernikov 	tinfo->itemsize4 = sizeof(struct numarray);
27345f379342SAlexander V. Chernikov }
27355f379342SAlexander V. Chernikov 
27365f379342SAlexander V. Chernikov /*
2737b23d5de9SAlexander V. Chernikov  * Prepare for addition/deletion to an array.
2738b23d5de9SAlexander V. Chernikov  */
2739b23d5de9SAlexander V. Chernikov static int
2740b23d5de9SAlexander V. Chernikov ta_prepare_add_numarray(struct ip_fw_chain *ch, struct tentry_info *tei,
2741b23d5de9SAlexander V. Chernikov     void *ta_buf)
2742b23d5de9SAlexander V. Chernikov {
2743b23d5de9SAlexander V. Chernikov 	struct ta_buf_numarray *tb;
2744b23d5de9SAlexander V. Chernikov 
2745b23d5de9SAlexander V. Chernikov 	tb = (struct ta_buf_numarray *)ta_buf;
2746b23d5de9SAlexander V. Chernikov 
2747b23d5de9SAlexander V. Chernikov 	tb->na.number = *((uint32_t *)tei->paddr);
2748b23d5de9SAlexander V. Chernikov 
2749b23d5de9SAlexander V. Chernikov 	return (0);
2750b23d5de9SAlexander V. Chernikov }
2751b23d5de9SAlexander V. Chernikov 
2752b23d5de9SAlexander V. Chernikov static int
2753b23d5de9SAlexander V. Chernikov ta_add_numarray(void *ta_state, struct table_info *ti, struct tentry_info *tei,
2754b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
2755b23d5de9SAlexander V. Chernikov {
2756b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2757b23d5de9SAlexander V. Chernikov 	struct ta_buf_numarray *tb;
2758b23d5de9SAlexander V. Chernikov 	struct numarray *ri;
2759b23d5de9SAlexander V. Chernikov 	int res;
2760648e8380SAlexander V. Chernikov 	uint32_t value;
2761b23d5de9SAlexander V. Chernikov 
2762b23d5de9SAlexander V. Chernikov 	tb = (struct ta_buf_numarray *)ta_buf;
2763b23d5de9SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2764b23d5de9SAlexander V. Chernikov 
276513263632SAlexander V. Chernikov 	/* Read current value from @tei */
276613263632SAlexander V. Chernikov 	tb->na.value = tei->value;
276713263632SAlexander V. Chernikov 
2768b23d5de9SAlexander V. Chernikov 	ri = numarray_find(ti, &tb->na.number);
2769b23d5de9SAlexander V. Chernikov 
2770b23d5de9SAlexander V. Chernikov 	if (ri != NULL) {
2771b23d5de9SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
2772b23d5de9SAlexander V. Chernikov 			return (EEXIST);
2773b23d5de9SAlexander V. Chernikov 
2774648e8380SAlexander V. Chernikov 		/* Exchange values between ri and @tei */
2775648e8380SAlexander V. Chernikov 		value = ri->value;
2776648e8380SAlexander V. Chernikov 		ri->value = tei->value;
2777648e8380SAlexander V. Chernikov 		tei->value = value;
2778b23d5de9SAlexander V. Chernikov 		/* Indicate that update has happened instead of addition */
2779b23d5de9SAlexander V. Chernikov 		tei->flags |= TEI_FLAGS_UPDATED;
2780b23d5de9SAlexander V. Chernikov 		*pnum = 0;
2781b23d5de9SAlexander V. Chernikov 		return (0);
2782b23d5de9SAlexander V. Chernikov 	}
2783b23d5de9SAlexander V. Chernikov 
27844c0c07a5SAlexander V. Chernikov 	if ((tei->flags & TEI_FLAGS_DONTADD) != 0)
27854c0c07a5SAlexander V. Chernikov 		return (EFBIG);
27864c0c07a5SAlexander V. Chernikov 
2787b23d5de9SAlexander V. Chernikov 	res = badd(&tb->na.number, &tb->na, cfg->main_ptr, cfg->used,
2788b23d5de9SAlexander V. Chernikov 	    sizeof(struct numarray), compare_numarray);
2789b23d5de9SAlexander V. Chernikov 
2790b23d5de9SAlexander V. Chernikov 	KASSERT(res == 1, ("number %d already exists", tb->na.number));
2791b23d5de9SAlexander V. Chernikov 	cfg->used++;
2792b23d5de9SAlexander V. Chernikov 	ti->data = cfg->used;
2793b23d5de9SAlexander V. Chernikov 	*pnum = 1;
2794b23d5de9SAlexander V. Chernikov 
2795b23d5de9SAlexander V. Chernikov 	return (0);
2796b23d5de9SAlexander V. Chernikov }
2797b23d5de9SAlexander V. Chernikov 
2798b23d5de9SAlexander V. Chernikov /*
2799b23d5de9SAlexander V. Chernikov  * Remove key from both configuration list and
2800b23d5de9SAlexander V. Chernikov  * runtime array. Removed interface notification.
2801b23d5de9SAlexander V. Chernikov  */
2802b23d5de9SAlexander V. Chernikov static int
2803b23d5de9SAlexander V. Chernikov ta_del_numarray(void *ta_state, struct table_info *ti, struct tentry_info *tei,
2804b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
2805b23d5de9SAlexander V. Chernikov {
2806b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2807b23d5de9SAlexander V. Chernikov 	struct ta_buf_numarray *tb;
2808b23d5de9SAlexander V. Chernikov 	struct numarray *ri;
2809b23d5de9SAlexander V. Chernikov 	int res;
2810b23d5de9SAlexander V. Chernikov 
2811b23d5de9SAlexander V. Chernikov 	tb = (struct ta_buf_numarray *)ta_buf;
2812b23d5de9SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2813b23d5de9SAlexander V. Chernikov 
2814b23d5de9SAlexander V. Chernikov 	ri = numarray_find(ti, &tb->na.number);
2815b23d5de9SAlexander V. Chernikov 	if (ri == NULL)
2816b23d5de9SAlexander V. Chernikov 		return (ENOENT);
2817b23d5de9SAlexander V. Chernikov 
2818648e8380SAlexander V. Chernikov 	tei->value = ri->value;
2819648e8380SAlexander V. Chernikov 
2820b23d5de9SAlexander V. Chernikov 	res = bdel(&tb->na.number, cfg->main_ptr, cfg->used,
2821b23d5de9SAlexander V. Chernikov 	    sizeof(struct numarray), compare_numarray);
2822b23d5de9SAlexander V. Chernikov 
2823b23d5de9SAlexander V. Chernikov 	KASSERT(res == 1, ("number %u does not exist", tb->na.number));
2824b23d5de9SAlexander V. Chernikov 	cfg->used--;
2825b23d5de9SAlexander V. Chernikov 	ti->data = cfg->used;
2826b23d5de9SAlexander V. Chernikov 	*pnum = 1;
2827b23d5de9SAlexander V. Chernikov 
2828b23d5de9SAlexander V. Chernikov 	return (0);
2829b23d5de9SAlexander V. Chernikov }
2830b23d5de9SAlexander V. Chernikov 
2831b23d5de9SAlexander V. Chernikov static void
2832b23d5de9SAlexander V. Chernikov ta_flush_numarray_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
2833b23d5de9SAlexander V. Chernikov     void *ta_buf)
2834b23d5de9SAlexander V. Chernikov {
2835b23d5de9SAlexander V. Chernikov 
28360bce0c23SAlexander V. Chernikov 	/* We don't have any state, do nothing */
2837b23d5de9SAlexander V. Chernikov }
2838b23d5de9SAlexander V. Chernikov 
2839b23d5de9SAlexander V. Chernikov 
2840b23d5de9SAlexander V. Chernikov /*
2841b23d5de9SAlexander V. Chernikov  * Table growing callbacks.
2842b23d5de9SAlexander V. Chernikov  */
2843b23d5de9SAlexander V. Chernikov 
2844b6ee846eSAlexander V. Chernikov static int
2845301290bcSAlexander V. Chernikov ta_need_modify_numarray(void *ta_state, struct table_info *ti, uint32_t count,
2846b6ee846eSAlexander V. Chernikov     uint64_t *pflags)
2847b6ee846eSAlexander V. Chernikov {
2848b6ee846eSAlexander V. Chernikov 	struct numarray_cfg *cfg;
28490bce0c23SAlexander V. Chernikov 	size_t size;
2850b6ee846eSAlexander V. Chernikov 
2851b6ee846eSAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2852b6ee846eSAlexander V. Chernikov 
28530bce0c23SAlexander V. Chernikov 	size = cfg->size;
28540bce0c23SAlexander V. Chernikov 	while (size < cfg->used + count)
28550bce0c23SAlexander V. Chernikov 		size *= 2;
28560bce0c23SAlexander V. Chernikov 
28570bce0c23SAlexander V. Chernikov 	if (size != cfg->size) {
28580bce0c23SAlexander V. Chernikov 		*pflags = size;
2859301290bcSAlexander V. Chernikov 		return (1);
2860b6ee846eSAlexander V. Chernikov 	}
2861b6ee846eSAlexander V. Chernikov 
2862301290bcSAlexander V. Chernikov 	return (0);
2863b6ee846eSAlexander V. Chernikov }
2864b6ee846eSAlexander V. Chernikov 
2865b23d5de9SAlexander V. Chernikov /*
2866b6ee846eSAlexander V. Chernikov  * Allocate new, larger runtime array.
2867b23d5de9SAlexander V. Chernikov  */
2868b23d5de9SAlexander V. Chernikov static int
2869b23d5de9SAlexander V. Chernikov ta_prepare_mod_numarray(void *ta_buf, uint64_t *pflags)
2870b23d5de9SAlexander V. Chernikov {
2871b23d5de9SAlexander V. Chernikov 	struct mod_item *mi;
2872b23d5de9SAlexander V. Chernikov 
2873b23d5de9SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
2874b23d5de9SAlexander V. Chernikov 
2875b23d5de9SAlexander V. Chernikov 	memset(mi, 0, sizeof(struct mod_item));
2876b23d5de9SAlexander V. Chernikov 	mi->size = *pflags;
2877b23d5de9SAlexander V. Chernikov 	mi->main_ptr = malloc(sizeof(struct numarray) * mi->size, M_IPFW,
2878b23d5de9SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
2879b23d5de9SAlexander V. Chernikov 
2880b23d5de9SAlexander V. Chernikov 	return (0);
2881b23d5de9SAlexander V. Chernikov }
2882b23d5de9SAlexander V. Chernikov 
2883b23d5de9SAlexander V. Chernikov /*
2884b23d5de9SAlexander V. Chernikov  * Copy data from old runtime array to new one.
2885b23d5de9SAlexander V. Chernikov  */
2886b23d5de9SAlexander V. Chernikov static int
2887b23d5de9SAlexander V. Chernikov ta_fill_mod_numarray(void *ta_state, struct table_info *ti, void *ta_buf,
2888b23d5de9SAlexander V. Chernikov     uint64_t *pflags)
2889b23d5de9SAlexander V. Chernikov {
2890b23d5de9SAlexander V. Chernikov 	struct mod_item *mi;
2891b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2892b23d5de9SAlexander V. Chernikov 
2893b23d5de9SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
2894b23d5de9SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2895b23d5de9SAlexander V. Chernikov 
2896b23d5de9SAlexander V. Chernikov 	/* Check if we still need to grow array */
2897b23d5de9SAlexander V. Chernikov 	if (cfg->size >= mi->size) {
2898b23d5de9SAlexander V. Chernikov 		*pflags = 0;
2899b23d5de9SAlexander V. Chernikov 		return (0);
2900b23d5de9SAlexander V. Chernikov 	}
2901b23d5de9SAlexander V. Chernikov 
2902b23d5de9SAlexander V. Chernikov 	memcpy(mi->main_ptr, cfg->main_ptr, cfg->used * sizeof(struct numarray));
2903b23d5de9SAlexander V. Chernikov 
2904b23d5de9SAlexander V. Chernikov 	return (0);
2905b23d5de9SAlexander V. Chernikov }
2906b23d5de9SAlexander V. Chernikov 
2907b23d5de9SAlexander V. Chernikov /*
2908b23d5de9SAlexander V. Chernikov  * Switch old & new arrays.
2909b23d5de9SAlexander V. Chernikov  */
2910301290bcSAlexander V. Chernikov static void
2911b23d5de9SAlexander V. Chernikov ta_modify_numarray(void *ta_state, struct table_info *ti, void *ta_buf,
2912b23d5de9SAlexander V. Chernikov     uint64_t pflags)
2913b23d5de9SAlexander V. Chernikov {
2914b23d5de9SAlexander V. Chernikov 	struct mod_item *mi;
2915b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2916b23d5de9SAlexander V. Chernikov 	void *old_ptr;
2917b23d5de9SAlexander V. Chernikov 
2918b23d5de9SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
2919b23d5de9SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2920b23d5de9SAlexander V. Chernikov 
2921b23d5de9SAlexander V. Chernikov 	old_ptr = cfg->main_ptr;
2922b23d5de9SAlexander V. Chernikov 	cfg->main_ptr = mi->main_ptr;
2923b23d5de9SAlexander V. Chernikov 	cfg->size = mi->size;
2924b23d5de9SAlexander V. Chernikov 	ti->state = cfg->main_ptr;
2925b23d5de9SAlexander V. Chernikov 
2926b23d5de9SAlexander V. Chernikov 	mi->main_ptr = old_ptr;
2927b23d5de9SAlexander V. Chernikov }
2928b23d5de9SAlexander V. Chernikov 
2929b23d5de9SAlexander V. Chernikov /*
2930b23d5de9SAlexander V. Chernikov  * Free unneded array.
2931b23d5de9SAlexander V. Chernikov  */
2932b23d5de9SAlexander V. Chernikov static void
2933b23d5de9SAlexander V. Chernikov ta_flush_mod_numarray(void *ta_buf)
2934b23d5de9SAlexander V. Chernikov {
2935b23d5de9SAlexander V. Chernikov 	struct mod_item *mi;
2936b23d5de9SAlexander V. Chernikov 
2937b23d5de9SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
2938b23d5de9SAlexander V. Chernikov 	if (mi->main_ptr != NULL)
2939b23d5de9SAlexander V. Chernikov 		free(mi->main_ptr, M_IPFW);
2940b23d5de9SAlexander V. Chernikov }
2941b23d5de9SAlexander V. Chernikov 
2942b23d5de9SAlexander V. Chernikov static int
2943b23d5de9SAlexander V. Chernikov ta_dump_numarray_tentry(void *ta_state, struct table_info *ti, void *e,
2944b23d5de9SAlexander V. Chernikov     ipfw_obj_tentry *tent)
2945b23d5de9SAlexander V. Chernikov {
2946b23d5de9SAlexander V. Chernikov 	struct numarray *na;
2947b23d5de9SAlexander V. Chernikov 
2948b23d5de9SAlexander V. Chernikov 	na = (struct numarray *)e;
2949b23d5de9SAlexander V. Chernikov 
2950b23d5de9SAlexander V. Chernikov 	tent->k.key = na->number;
29510cba2b28SAlexander V. Chernikov 	tent->v.kidx = na->value;
2952b23d5de9SAlexander V. Chernikov 
2953b23d5de9SAlexander V. Chernikov 	return (0);
2954b23d5de9SAlexander V. Chernikov }
2955b23d5de9SAlexander V. Chernikov 
2956b23d5de9SAlexander V. Chernikov static int
2957914bffb6SAlexander V. Chernikov ta_find_numarray_tentry(void *ta_state, struct table_info *ti,
2958914bffb6SAlexander V. Chernikov     ipfw_obj_tentry *tent)
2959b23d5de9SAlexander V. Chernikov {
2960b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2961b23d5de9SAlexander V. Chernikov 	struct numarray *ri;
2962b23d5de9SAlexander V. Chernikov 
2963b23d5de9SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2964b23d5de9SAlexander V. Chernikov 
2965914bffb6SAlexander V. Chernikov 	ri = numarray_find(ti, &tent->k.key);
2966b23d5de9SAlexander V. Chernikov 
2967b23d5de9SAlexander V. Chernikov 	if (ri != NULL) {
2968b23d5de9SAlexander V. Chernikov 		ta_dump_numarray_tentry(ta_state, ti, ri, tent);
2969b23d5de9SAlexander V. Chernikov 		return (0);
2970b23d5de9SAlexander V. Chernikov 	}
2971b23d5de9SAlexander V. Chernikov 
2972b23d5de9SAlexander V. Chernikov 	return (ENOENT);
2973b23d5de9SAlexander V. Chernikov }
2974b23d5de9SAlexander V. Chernikov 
2975b23d5de9SAlexander V. Chernikov static void
2976b23d5de9SAlexander V. Chernikov ta_foreach_numarray(void *ta_state, struct table_info *ti, ta_foreach_f *f,
2977b23d5de9SAlexander V. Chernikov     void *arg)
2978b23d5de9SAlexander V. Chernikov {
2979b23d5de9SAlexander V. Chernikov 	struct numarray_cfg *cfg;
2980b23d5de9SAlexander V. Chernikov 	struct numarray *array;
2981b23d5de9SAlexander V. Chernikov 	int i;
2982b23d5de9SAlexander V. Chernikov 
2983b23d5de9SAlexander V. Chernikov 	cfg = (struct numarray_cfg *)ta_state;
2984b23d5de9SAlexander V. Chernikov 	array = cfg->main_ptr;
2985b23d5de9SAlexander V. Chernikov 
2986b23d5de9SAlexander V. Chernikov 	for (i = 0; i < cfg->used; i++)
2987b23d5de9SAlexander V. Chernikov 		f(&array[i], arg);
2988b23d5de9SAlexander V. Chernikov }
2989b23d5de9SAlexander V. Chernikov 
2990b23d5de9SAlexander V. Chernikov struct table_algo number_array = {
2991b23d5de9SAlexander V. Chernikov 	.name		= "number:array",
2992b23d5de9SAlexander V. Chernikov 	.type		= IPFW_TABLE_NUMBER,
299357a1cf95SAlexander V. Chernikov 	.ta_buf_size	= sizeof(struct ta_buf_numarray),
2994b23d5de9SAlexander V. Chernikov 	.init		= ta_init_numarray,
2995b23d5de9SAlexander V. Chernikov 	.destroy	= ta_destroy_numarray,
2996b23d5de9SAlexander V. Chernikov 	.prepare_add	= ta_prepare_add_numarray,
2997b23d5de9SAlexander V. Chernikov 	.prepare_del	= ta_prepare_add_numarray,
2998b23d5de9SAlexander V. Chernikov 	.add		= ta_add_numarray,
2999b23d5de9SAlexander V. Chernikov 	.del		= ta_del_numarray,
3000b23d5de9SAlexander V. Chernikov 	.flush_entry	= ta_flush_numarray_entry,
3001b23d5de9SAlexander V. Chernikov 	.foreach	= ta_foreach_numarray,
3002b23d5de9SAlexander V. Chernikov 	.dump_tentry	= ta_dump_numarray_tentry,
3003b23d5de9SAlexander V. Chernikov 	.find_tentry	= ta_find_numarray_tentry,
30045f379342SAlexander V. Chernikov 	.dump_tinfo	= ta_dump_numarray_tinfo,
3005301290bcSAlexander V. Chernikov 	.need_modify	= ta_need_modify_numarray,
3006b23d5de9SAlexander V. Chernikov 	.prepare_mod	= ta_prepare_mod_numarray,
3007b23d5de9SAlexander V. Chernikov 	.fill_mod	= ta_fill_mod_numarray,
3008b23d5de9SAlexander V. Chernikov 	.modify		= ta_modify_numarray,
3009b23d5de9SAlexander V. Chernikov 	.flush_mod	= ta_flush_mod_numarray,
3010b23d5de9SAlexander V. Chernikov };
3011b23d5de9SAlexander V. Chernikov 
3012914bffb6SAlexander V. Chernikov /*
3013914bffb6SAlexander V. Chernikov  * flow:hash cmds
3014914bffb6SAlexander V. Chernikov  *
3015914bffb6SAlexander V. Chernikov  *
3016914bffb6SAlexander V. Chernikov  * ti->data:
3017914bffb6SAlexander V. Chernikov  * [inv.mask4][inv.mask6][log2hsize4][log2hsize6]
3018914bffb6SAlexander V. Chernikov  * [        8][        8[          8][         8]
3019914bffb6SAlexander V. Chernikov  *
3020914bffb6SAlexander V. Chernikov  * inv.mask4: 32 - mask
3021914bffb6SAlexander V. Chernikov  * inv.mask6:
3022914bffb6SAlexander V. Chernikov  * 1) _slow lookup: mask
3023914bffb6SAlexander V. Chernikov  * 2) _aligned: (128 - mask) / 8
3024914bffb6SAlexander V. Chernikov  * 3) _64: 8
3025914bffb6SAlexander V. Chernikov  *
3026914bffb6SAlexander V. Chernikov  *
3027914bffb6SAlexander V. Chernikov  * pflags:
3028b6ee846eSAlexander V. Chernikov  * [hsize4][hsize6]
3029b6ee846eSAlexander V. Chernikov  * [    16][    16]
3030914bffb6SAlexander V. Chernikov  */
3031914bffb6SAlexander V. Chernikov 
3032914bffb6SAlexander V. Chernikov struct fhashentry;
3033914bffb6SAlexander V. Chernikov 
3034914bffb6SAlexander V. Chernikov SLIST_HEAD(fhashbhead, fhashentry);
3035914bffb6SAlexander V. Chernikov 
3036914bffb6SAlexander V. Chernikov struct fhashentry {
3037914bffb6SAlexander V. Chernikov 	SLIST_ENTRY(fhashentry)	next;
3038914bffb6SAlexander V. Chernikov 	uint8_t		af;
3039914bffb6SAlexander V. Chernikov 	uint8_t		proto;
3040914bffb6SAlexander V. Chernikov 	uint16_t	spare0;
3041914bffb6SAlexander V. Chernikov 	uint16_t	dport;
3042914bffb6SAlexander V. Chernikov 	uint16_t	sport;
3043914bffb6SAlexander V. Chernikov 	uint32_t	value;
3044914bffb6SAlexander V. Chernikov 	uint32_t	spare1;
3045914bffb6SAlexander V. Chernikov };
3046914bffb6SAlexander V. Chernikov 
3047914bffb6SAlexander V. Chernikov struct fhashentry4 {
3048914bffb6SAlexander V. Chernikov 	struct fhashentry	e;
3049914bffb6SAlexander V. Chernikov 	struct in_addr		dip;
3050914bffb6SAlexander V. Chernikov 	struct in_addr		sip;
3051914bffb6SAlexander V. Chernikov };
3052914bffb6SAlexander V. Chernikov 
3053914bffb6SAlexander V. Chernikov struct fhashentry6 {
3054914bffb6SAlexander V. Chernikov 	struct fhashentry	e;
3055914bffb6SAlexander V. Chernikov 	struct in6_addr		dip6;
3056914bffb6SAlexander V. Chernikov 	struct in6_addr		sip6;
3057914bffb6SAlexander V. Chernikov };
3058914bffb6SAlexander V. Chernikov 
3059914bffb6SAlexander V. Chernikov struct fhash_cfg {
3060914bffb6SAlexander V. Chernikov 	struct fhashbhead	*head;
3061914bffb6SAlexander V. Chernikov 	size_t			size;
3062914bffb6SAlexander V. Chernikov 	size_t			items;
3063914bffb6SAlexander V. Chernikov 	struct fhashentry4	fe4;
3064914bffb6SAlexander V. Chernikov 	struct fhashentry6	fe6;
3065914bffb6SAlexander V. Chernikov };
3066914bffb6SAlexander V. Chernikov 
30673fe2ef91SAlexander V. Chernikov struct ta_buf_fhash {
30680bce0c23SAlexander V. Chernikov 	void	*ent_ptr;
30690bce0c23SAlexander V. Chernikov 	struct fhashentry6 fe6;
30700bce0c23SAlexander V. Chernikov };
30710bce0c23SAlexander V. Chernikov 
3072*9fe15d06SAlexander V. Chernikov static __inline int cmp_flow_ent(struct fhashentry *a,
3073*9fe15d06SAlexander V. Chernikov     struct fhashentry *b, size_t sz);
3074*9fe15d06SAlexander V. Chernikov static __inline uint32_t hash_flow4(struct fhashentry4 *f, int hsize);
3075*9fe15d06SAlexander V. Chernikov static __inline uint32_t hash_flow6(struct fhashentry6 *f, int hsize);
3076*9fe15d06SAlexander V. Chernikov static uint32_t hash_flow_ent(struct fhashentry *ent, uint32_t size);
3077*9fe15d06SAlexander V. Chernikov static int ta_lookup_fhash(struct table_info *ti, void *key, uint32_t keylen,
3078*9fe15d06SAlexander V. Chernikov     uint32_t *val);
3079*9fe15d06SAlexander V. Chernikov static int ta_init_fhash(struct ip_fw_chain *ch, void **ta_state,
3080*9fe15d06SAlexander V. Chernikov struct table_info *ti, char *data, uint8_t tflags);
3081*9fe15d06SAlexander V. Chernikov static void ta_destroy_fhash(void *ta_state, struct table_info *ti);
3082*9fe15d06SAlexander V. Chernikov static void ta_dump_fhash_tinfo(void *ta_state, struct table_info *ti,
3083*9fe15d06SAlexander V. Chernikov     ipfw_ta_tinfo *tinfo);
3084*9fe15d06SAlexander V. Chernikov static int ta_dump_fhash_tentry(void *ta_state, struct table_info *ti,
3085*9fe15d06SAlexander V. Chernikov     void *e, ipfw_obj_tentry *tent);
3086*9fe15d06SAlexander V. Chernikov static int tei_to_fhash_ent(struct tentry_info *tei, struct fhashentry *ent);
3087*9fe15d06SAlexander V. Chernikov static int ta_find_fhash_tentry(void *ta_state, struct table_info *ti,
3088*9fe15d06SAlexander V. Chernikov     ipfw_obj_tentry *tent);
3089*9fe15d06SAlexander V. Chernikov static void ta_foreach_fhash(void *ta_state, struct table_info *ti,
3090*9fe15d06SAlexander V. Chernikov     ta_foreach_f *f, void *arg);
3091*9fe15d06SAlexander V. Chernikov static int ta_prepare_add_fhash(struct ip_fw_chain *ch,
3092*9fe15d06SAlexander V. Chernikov     struct tentry_info *tei, void *ta_buf);
3093*9fe15d06SAlexander V. Chernikov static int ta_add_fhash(void *ta_state, struct table_info *ti,
3094*9fe15d06SAlexander V. Chernikov     struct tentry_info *tei, void *ta_buf, uint32_t *pnum);
3095*9fe15d06SAlexander V. Chernikov static int ta_prepare_del_fhash(struct ip_fw_chain *ch, struct tentry_info *tei,
3096*9fe15d06SAlexander V. Chernikov     void *ta_buf);
3097*9fe15d06SAlexander V. Chernikov static int ta_del_fhash(void *ta_state, struct table_info *ti,
3098*9fe15d06SAlexander V. Chernikov     struct tentry_info *tei, void *ta_buf, uint32_t *pnum);
3099*9fe15d06SAlexander V. Chernikov static void ta_flush_fhash_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
3100*9fe15d06SAlexander V. Chernikov     void *ta_buf);
3101*9fe15d06SAlexander V. Chernikov static int ta_need_modify_fhash(void *ta_state, struct table_info *ti,
3102*9fe15d06SAlexander V. Chernikov     uint32_t count, uint64_t *pflags);
3103*9fe15d06SAlexander V. Chernikov static int ta_prepare_mod_fhash(void *ta_buf, uint64_t *pflags);
3104*9fe15d06SAlexander V. Chernikov static int ta_fill_mod_fhash(void *ta_state, struct table_info *ti,
3105*9fe15d06SAlexander V. Chernikov     void *ta_buf, uint64_t *pflags);
3106*9fe15d06SAlexander V. Chernikov static void ta_modify_fhash(void *ta_state, struct table_info *ti, void *ta_buf,
3107*9fe15d06SAlexander V. Chernikov     uint64_t pflags);
3108*9fe15d06SAlexander V. Chernikov static void ta_flush_mod_fhash(void *ta_buf);
3109*9fe15d06SAlexander V. Chernikov 
3110914bffb6SAlexander V. Chernikov static __inline int
3111914bffb6SAlexander V. Chernikov cmp_flow_ent(struct fhashentry *a, struct fhashentry *b, size_t sz)
3112914bffb6SAlexander V. Chernikov {
3113914bffb6SAlexander V. Chernikov 	uint64_t *ka, *kb;
3114914bffb6SAlexander V. Chernikov 
3115914bffb6SAlexander V. Chernikov 	ka = (uint64_t *)(&a->next + 1);
3116914bffb6SAlexander V. Chernikov 	kb = (uint64_t *)(&b->next + 1);
3117914bffb6SAlexander V. Chernikov 
3118914bffb6SAlexander V. Chernikov 	if (*ka == *kb && (memcmp(a + 1, b + 1, sz) == 0))
3119914bffb6SAlexander V. Chernikov 		return (1);
3120914bffb6SAlexander V. Chernikov 
3121914bffb6SAlexander V. Chernikov 	return (0);
3122914bffb6SAlexander V. Chernikov }
3123914bffb6SAlexander V. Chernikov 
3124914bffb6SAlexander V. Chernikov static __inline uint32_t
3125914bffb6SAlexander V. Chernikov hash_flow4(struct fhashentry4 *f, int hsize)
3126914bffb6SAlexander V. Chernikov {
3127914bffb6SAlexander V. Chernikov 	uint32_t i;
3128914bffb6SAlexander V. Chernikov 
3129914bffb6SAlexander V. Chernikov 	i = (f->dip.s_addr) ^ (f->sip.s_addr) ^ (f->e.dport) ^ (f->e.sport);
3130914bffb6SAlexander V. Chernikov 
3131914bffb6SAlexander V. Chernikov 	return (i % (hsize - 1));
3132914bffb6SAlexander V. Chernikov }
3133914bffb6SAlexander V. Chernikov 
3134914bffb6SAlexander V. Chernikov static __inline uint32_t
3135914bffb6SAlexander V. Chernikov hash_flow6(struct fhashentry6 *f, int hsize)
3136914bffb6SAlexander V. Chernikov {
3137914bffb6SAlexander V. Chernikov 	uint32_t i;
3138914bffb6SAlexander V. Chernikov 
3139914bffb6SAlexander V. Chernikov 	i = (f->dip6.__u6_addr.__u6_addr32[2]) ^
3140914bffb6SAlexander V. Chernikov 	    (f->dip6.__u6_addr.__u6_addr32[3]) ^
3141914bffb6SAlexander V. Chernikov 	    (f->sip6.__u6_addr.__u6_addr32[2]) ^
3142914bffb6SAlexander V. Chernikov 	    (f->sip6.__u6_addr.__u6_addr32[3]) ^
3143914bffb6SAlexander V. Chernikov 	    (f->e.dport) ^ (f->e.sport);
3144914bffb6SAlexander V. Chernikov 
3145914bffb6SAlexander V. Chernikov 	return (i % (hsize - 1));
3146914bffb6SAlexander V. Chernikov }
3147914bffb6SAlexander V. Chernikov 
3148914bffb6SAlexander V. Chernikov static uint32_t
3149914bffb6SAlexander V. Chernikov hash_flow_ent(struct fhashentry *ent, uint32_t size)
3150914bffb6SAlexander V. Chernikov {
3151914bffb6SAlexander V. Chernikov 	uint32_t hash;
3152914bffb6SAlexander V. Chernikov 
3153914bffb6SAlexander V. Chernikov 	if (ent->af == AF_INET) {
3154914bffb6SAlexander V. Chernikov 		hash = hash_flow4((struct fhashentry4 *)ent, size);
3155914bffb6SAlexander V. Chernikov 	} else {
3156914bffb6SAlexander V. Chernikov 		hash = hash_flow6((struct fhashentry6 *)ent, size);
3157914bffb6SAlexander V. Chernikov 	}
3158914bffb6SAlexander V. Chernikov 
3159914bffb6SAlexander V. Chernikov 	return (hash);
3160914bffb6SAlexander V. Chernikov }
3161914bffb6SAlexander V. Chernikov 
3162914bffb6SAlexander V. Chernikov static int
3163914bffb6SAlexander V. Chernikov ta_lookup_fhash(struct table_info *ti, void *key, uint32_t keylen,
3164914bffb6SAlexander V. Chernikov     uint32_t *val)
3165914bffb6SAlexander V. Chernikov {
3166914bffb6SAlexander V. Chernikov 	struct fhashbhead *head;
3167914bffb6SAlexander V. Chernikov 	struct fhashentry *ent;
3168914bffb6SAlexander V. Chernikov 	struct fhashentry4 *m4;
3169914bffb6SAlexander V. Chernikov 	struct ipfw_flow_id *id;
3170914bffb6SAlexander V. Chernikov 	uint16_t hash, hsize;
3171914bffb6SAlexander V. Chernikov 
3172914bffb6SAlexander V. Chernikov 	id = (struct ipfw_flow_id *)key;
3173914bffb6SAlexander V. Chernikov 	head = (struct fhashbhead *)ti->state;
3174914bffb6SAlexander V. Chernikov 	hsize = ti->data;
3175914bffb6SAlexander V. Chernikov 	m4 = (struct fhashentry4 *)ti->xstate;
3176914bffb6SAlexander V. Chernikov 
3177914bffb6SAlexander V. Chernikov 	if (id->addr_type == 4) {
3178914bffb6SAlexander V. Chernikov 		struct fhashentry4 f;
3179914bffb6SAlexander V. Chernikov 
3180914bffb6SAlexander V. Chernikov 		/* Copy hash mask */
3181914bffb6SAlexander V. Chernikov 		f = *m4;
3182914bffb6SAlexander V. Chernikov 
3183914bffb6SAlexander V. Chernikov 		f.dip.s_addr &= id->dst_ip;
3184914bffb6SAlexander V. Chernikov 		f.sip.s_addr &= id->src_ip;
3185914bffb6SAlexander V. Chernikov 		f.e.dport &= id->dst_port;
3186914bffb6SAlexander V. Chernikov 		f.e.sport &= id->src_port;
3187914bffb6SAlexander V. Chernikov 		f.e.proto &= id->proto;
3188914bffb6SAlexander V. Chernikov 		hash = hash_flow4(&f, hsize);
3189914bffb6SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
3190914bffb6SAlexander V. Chernikov 			if (cmp_flow_ent(ent, &f.e, 2 * 4) != 0) {
3191914bffb6SAlexander V. Chernikov 				*val = ent->value;
3192914bffb6SAlexander V. Chernikov 				return (1);
3193914bffb6SAlexander V. Chernikov 			}
3194914bffb6SAlexander V. Chernikov 		}
3195914bffb6SAlexander V. Chernikov 	} else if (id->addr_type == 6) {
3196914bffb6SAlexander V. Chernikov 		struct fhashentry6 f;
3197914bffb6SAlexander V. Chernikov 		uint64_t *fp, *idp;
3198914bffb6SAlexander V. Chernikov 
3199914bffb6SAlexander V. Chernikov 		/* Copy hash mask */
3200914bffb6SAlexander V. Chernikov 		f = *((struct fhashentry6 *)(m4 + 1));
3201914bffb6SAlexander V. Chernikov 
3202914bffb6SAlexander V. Chernikov 		/* Handle lack of __u6_addr.__u6_addr64 */
3203914bffb6SAlexander V. Chernikov 		fp = (uint64_t *)&f.dip6;
3204914bffb6SAlexander V. Chernikov 		idp = (uint64_t *)&id->dst_ip6;
3205914bffb6SAlexander V. Chernikov 		/* src IPv6 is stored after dst IPv6 */
3206914bffb6SAlexander V. Chernikov 		*fp++ &= *idp++;
3207914bffb6SAlexander V. Chernikov 		*fp++ &= *idp++;
3208914bffb6SAlexander V. Chernikov 		*fp++ &= *idp++;
3209914bffb6SAlexander V. Chernikov 		*fp &= *idp;
3210914bffb6SAlexander V. Chernikov 		f.e.dport &= id->dst_port;
3211914bffb6SAlexander V. Chernikov 		f.e.sport &= id->src_port;
3212914bffb6SAlexander V. Chernikov 		f.e.proto &= id->proto;
3213914bffb6SAlexander V. Chernikov 		hash = hash_flow6(&f, hsize);
3214914bffb6SAlexander V. Chernikov 		SLIST_FOREACH(ent, &head[hash], next) {
3215914bffb6SAlexander V. Chernikov 			if (cmp_flow_ent(ent, &f.e, 2 * 16) != 0) {
3216914bffb6SAlexander V. Chernikov 				*val = ent->value;
3217914bffb6SAlexander V. Chernikov 				return (1);
3218914bffb6SAlexander V. Chernikov 			}
3219914bffb6SAlexander V. Chernikov 		}
3220914bffb6SAlexander V. Chernikov 	}
3221914bffb6SAlexander V. Chernikov 
3222914bffb6SAlexander V. Chernikov 	return (0);
3223914bffb6SAlexander V. Chernikov }
3224914bffb6SAlexander V. Chernikov 
3225914bffb6SAlexander V. Chernikov /*
3226914bffb6SAlexander V. Chernikov  * New table.
3227914bffb6SAlexander V. Chernikov  */
3228914bffb6SAlexander V. Chernikov static int
3229914bffb6SAlexander V. Chernikov ta_init_fhash(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
3230914bffb6SAlexander V. Chernikov     char *data, uint8_t tflags)
3231914bffb6SAlexander V. Chernikov {
3232914bffb6SAlexander V. Chernikov 	int i;
3233914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
3234914bffb6SAlexander V. Chernikov 	struct fhashentry4 *fe4;
3235914bffb6SAlexander V. Chernikov 	struct fhashentry6 *fe6;
3236914bffb6SAlexander V. Chernikov 
3237914bffb6SAlexander V. Chernikov 	cfg = malloc(sizeof(struct fhash_cfg), M_IPFW, M_WAITOK | M_ZERO);
3238914bffb6SAlexander V. Chernikov 
3239914bffb6SAlexander V. Chernikov 	cfg->size = 512;
3240914bffb6SAlexander V. Chernikov 
3241914bffb6SAlexander V. Chernikov 	cfg->head = malloc(sizeof(struct fhashbhead) * cfg->size, M_IPFW,
3242914bffb6SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
3243914bffb6SAlexander V. Chernikov 	for (i = 0; i < cfg->size; i++)
3244914bffb6SAlexander V. Chernikov 		SLIST_INIT(&cfg->head[i]);
3245914bffb6SAlexander V. Chernikov 
3246914bffb6SAlexander V. Chernikov 	/* Fill in fe masks based on @tflags */
3247914bffb6SAlexander V. Chernikov 	fe4 = &cfg->fe4;
3248914bffb6SAlexander V. Chernikov 	fe6 = &cfg->fe6;
3249914bffb6SAlexander V. Chernikov 	if (tflags & IPFW_TFFLAG_SRCIP) {
3250914bffb6SAlexander V. Chernikov 		memset(&fe4->sip, 0xFF, sizeof(fe4->sip));
3251914bffb6SAlexander V. Chernikov 		memset(&fe6->sip6, 0xFF, sizeof(fe6->sip6));
3252914bffb6SAlexander V. Chernikov 	}
3253914bffb6SAlexander V. Chernikov 	if (tflags & IPFW_TFFLAG_DSTIP) {
3254914bffb6SAlexander V. Chernikov 		memset(&fe4->dip, 0xFF, sizeof(fe4->dip));
3255914bffb6SAlexander V. Chernikov 		memset(&fe6->dip6, 0xFF, sizeof(fe6->dip6));
3256914bffb6SAlexander V. Chernikov 	}
3257914bffb6SAlexander V. Chernikov 	if (tflags & IPFW_TFFLAG_SRCPORT) {
3258914bffb6SAlexander V. Chernikov 		memset(&fe4->e.sport, 0xFF, sizeof(fe4->e.sport));
3259914bffb6SAlexander V. Chernikov 		memset(&fe6->e.sport, 0xFF, sizeof(fe6->e.sport));
3260914bffb6SAlexander V. Chernikov 	}
3261914bffb6SAlexander V. Chernikov 	if (tflags & IPFW_TFFLAG_DSTPORT) {
3262914bffb6SAlexander V. Chernikov 		memset(&fe4->e.dport, 0xFF, sizeof(fe4->e.dport));
3263914bffb6SAlexander V. Chernikov 		memset(&fe6->e.dport, 0xFF, sizeof(fe6->e.dport));
3264914bffb6SAlexander V. Chernikov 	}
3265914bffb6SAlexander V. Chernikov 	if (tflags & IPFW_TFFLAG_PROTO) {
3266914bffb6SAlexander V. Chernikov 		memset(&fe4->e.proto, 0xFF, sizeof(fe4->e.proto));
3267914bffb6SAlexander V. Chernikov 		memset(&fe6->e.proto, 0xFF, sizeof(fe6->e.proto));
3268914bffb6SAlexander V. Chernikov 	}
3269914bffb6SAlexander V. Chernikov 
3270914bffb6SAlexander V. Chernikov 	fe4->e.af = AF_INET;
3271914bffb6SAlexander V. Chernikov 	fe6->e.af = AF_INET6;
3272914bffb6SAlexander V. Chernikov 
3273914bffb6SAlexander V. Chernikov 	*ta_state = cfg;
3274914bffb6SAlexander V. Chernikov 	ti->state = cfg->head;
3275914bffb6SAlexander V. Chernikov 	ti->xstate = &cfg->fe4;
3276914bffb6SAlexander V. Chernikov 	ti->data = cfg->size;
3277914bffb6SAlexander V. Chernikov 	ti->lookup = ta_lookup_fhash;
3278914bffb6SAlexander V. Chernikov 
3279914bffb6SAlexander V. Chernikov 	return (0);
3280914bffb6SAlexander V. Chernikov }
3281914bffb6SAlexander V. Chernikov 
3282914bffb6SAlexander V. Chernikov static void
3283914bffb6SAlexander V. Chernikov ta_destroy_fhash(void *ta_state, struct table_info *ti)
3284914bffb6SAlexander V. Chernikov {
3285914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
3286914bffb6SAlexander V. Chernikov 	struct fhashentry *ent, *ent_next;
3287914bffb6SAlexander V. Chernikov 	int i;
3288914bffb6SAlexander V. Chernikov 
3289914bffb6SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
3290914bffb6SAlexander V. Chernikov 
3291914bffb6SAlexander V. Chernikov 	for (i = 0; i < cfg->size; i++)
3292914bffb6SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &cfg->head[i], next, ent_next)
3293914bffb6SAlexander V. Chernikov 			free(ent, M_IPFW_TBL);
3294914bffb6SAlexander V. Chernikov 
3295914bffb6SAlexander V. Chernikov 	free(cfg->head, M_IPFW);
3296914bffb6SAlexander V. Chernikov 	free(cfg, M_IPFW);
3297914bffb6SAlexander V. Chernikov }
3298914bffb6SAlexander V. Chernikov 
32995f379342SAlexander V. Chernikov /*
33005f379342SAlexander V. Chernikov  * Provide algo-specific table info
33015f379342SAlexander V. Chernikov  */
33025f379342SAlexander V. Chernikov static void
33035f379342SAlexander V. Chernikov ta_dump_fhash_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo)
33045f379342SAlexander V. Chernikov {
33055f379342SAlexander V. Chernikov 	struct fhash_cfg *cfg;
33065f379342SAlexander V. Chernikov 
33075f379342SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
33085f379342SAlexander V. Chernikov 
33095f379342SAlexander V. Chernikov 	tinfo->flags = IPFW_TATFLAGS_AFITEM;
33105f379342SAlexander V. Chernikov 	tinfo->taclass4 = IPFW_TACLASS_HASH;
33115f379342SAlexander V. Chernikov 	tinfo->size4 = cfg->size;
33125f379342SAlexander V. Chernikov 	tinfo->count4 = cfg->items;
33135f379342SAlexander V. Chernikov 	tinfo->itemsize4 = sizeof(struct fhashentry4);
33145f379342SAlexander V. Chernikov 	tinfo->itemsize6 = sizeof(struct fhashentry6);
33155f379342SAlexander V. Chernikov }
33165f379342SAlexander V. Chernikov 
3317914bffb6SAlexander V. Chernikov static int
3318914bffb6SAlexander V. Chernikov ta_dump_fhash_tentry(void *ta_state, struct table_info *ti, void *e,
3319914bffb6SAlexander V. Chernikov     ipfw_obj_tentry *tent)
3320914bffb6SAlexander V. Chernikov {
3321914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
3322914bffb6SAlexander V. Chernikov 	struct fhashentry *ent;
3323914bffb6SAlexander V. Chernikov 	struct fhashentry4 *fe4;
3324*9fe15d06SAlexander V. Chernikov #ifdef INET6
3325914bffb6SAlexander V. Chernikov 	struct fhashentry6 *fe6;
3326*9fe15d06SAlexander V. Chernikov #endif
3327914bffb6SAlexander V. Chernikov 	struct tflow_entry *tfe;
3328914bffb6SAlexander V. Chernikov 
3329914bffb6SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
3330914bffb6SAlexander V. Chernikov 	ent = (struct fhashentry *)e;
3331914bffb6SAlexander V. Chernikov 	tfe = &tent->k.flow;
3332914bffb6SAlexander V. Chernikov 
3333914bffb6SAlexander V. Chernikov 	tfe->af = ent->af;
3334914bffb6SAlexander V. Chernikov 	tfe->proto = ent->proto;
3335914bffb6SAlexander V. Chernikov 	tfe->dport = htons(ent->dport);
3336914bffb6SAlexander V. Chernikov 	tfe->sport = htons(ent->sport);
33370cba2b28SAlexander V. Chernikov 	tent->v.kidx = ent->value;
3338914bffb6SAlexander V. Chernikov 	tent->subtype = ent->af;
3339914bffb6SAlexander V. Chernikov 
3340914bffb6SAlexander V. Chernikov 	if (ent->af == AF_INET) {
3341914bffb6SAlexander V. Chernikov 		fe4 = (struct fhashentry4 *)ent;
3342914bffb6SAlexander V. Chernikov 		tfe->a.a4.sip.s_addr = htonl(fe4->sip.s_addr);
3343914bffb6SAlexander V. Chernikov 		tfe->a.a4.dip.s_addr = htonl(fe4->dip.s_addr);
3344914bffb6SAlexander V. Chernikov 		tent->masklen = 32;
3345914bffb6SAlexander V. Chernikov #ifdef INET6
3346914bffb6SAlexander V. Chernikov 	} else {
3347914bffb6SAlexander V. Chernikov 		fe6 = (struct fhashentry6 *)ent;
3348914bffb6SAlexander V. Chernikov 		tfe->a.a6.sip6 = fe6->sip6;
3349914bffb6SAlexander V. Chernikov 		tfe->a.a6.dip6 = fe6->dip6;
3350914bffb6SAlexander V. Chernikov 		tent->masklen = 128;
3351914bffb6SAlexander V. Chernikov #endif
3352914bffb6SAlexander V. Chernikov 	}
3353914bffb6SAlexander V. Chernikov 
3354914bffb6SAlexander V. Chernikov 	return (0);
3355914bffb6SAlexander V. Chernikov }
3356914bffb6SAlexander V. Chernikov 
3357914bffb6SAlexander V. Chernikov static int
3358914bffb6SAlexander V. Chernikov tei_to_fhash_ent(struct tentry_info *tei, struct fhashentry *ent)
3359914bffb6SAlexander V. Chernikov {
3360914bffb6SAlexander V. Chernikov 	struct fhashentry4 *fe4;
3361914bffb6SAlexander V. Chernikov 	struct fhashentry6 *fe6;
3362914bffb6SAlexander V. Chernikov 	struct tflow_entry *tfe;
3363914bffb6SAlexander V. Chernikov 
3364914bffb6SAlexander V. Chernikov 	tfe = (struct tflow_entry *)tei->paddr;
3365914bffb6SAlexander V. Chernikov 
3366914bffb6SAlexander V. Chernikov 	ent->af = tei->subtype;
3367914bffb6SAlexander V. Chernikov 	ent->proto = tfe->proto;
3368914bffb6SAlexander V. Chernikov 	ent->dport = ntohs(tfe->dport);
3369914bffb6SAlexander V. Chernikov 	ent->sport = ntohs(tfe->sport);
3370914bffb6SAlexander V. Chernikov 
3371914bffb6SAlexander V. Chernikov 	if (tei->subtype == AF_INET) {
3372914bffb6SAlexander V. Chernikov #ifdef INET
3373914bffb6SAlexander V. Chernikov 		fe4 = (struct fhashentry4 *)ent;
3374914bffb6SAlexander V. Chernikov 		fe4->sip.s_addr = ntohl(tfe->a.a4.sip.s_addr);
3375914bffb6SAlexander V. Chernikov 		fe4->dip.s_addr = ntohl(tfe->a.a4.dip.s_addr);
3376914bffb6SAlexander V. Chernikov #endif
3377914bffb6SAlexander V. Chernikov #ifdef INET6
3378914bffb6SAlexander V. Chernikov 	} else if (tei->subtype == AF_INET6) {
3379914bffb6SAlexander V. Chernikov 		fe6 = (struct fhashentry6 *)ent;
3380914bffb6SAlexander V. Chernikov 		fe6->sip6 = tfe->a.a6.sip6;
3381914bffb6SAlexander V. Chernikov 		fe6->dip6 = tfe->a.a6.dip6;
3382914bffb6SAlexander V. Chernikov #endif
3383914bffb6SAlexander V. Chernikov 	} else {
3384914bffb6SAlexander V. Chernikov 		/* Unknown CIDR type */
3385914bffb6SAlexander V. Chernikov 		return (EINVAL);
3386914bffb6SAlexander V. Chernikov 	}
3387914bffb6SAlexander V. Chernikov 
3388914bffb6SAlexander V. Chernikov 	return (0);
3389914bffb6SAlexander V. Chernikov }
3390914bffb6SAlexander V. Chernikov 
3391914bffb6SAlexander V. Chernikov 
3392914bffb6SAlexander V. Chernikov static int
3393914bffb6SAlexander V. Chernikov ta_find_fhash_tentry(void *ta_state, struct table_info *ti,
3394914bffb6SAlexander V. Chernikov     ipfw_obj_tentry *tent)
3395914bffb6SAlexander V. Chernikov {
3396914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
3397914bffb6SAlexander V. Chernikov 	struct fhashbhead *head;
3398914bffb6SAlexander V. Chernikov 	struct fhashentry *ent, *tmp;
3399914bffb6SAlexander V. Chernikov 	struct fhashentry6 fe6;
3400914bffb6SAlexander V. Chernikov 	struct tentry_info tei;
3401914bffb6SAlexander V. Chernikov 	int error;
3402914bffb6SAlexander V. Chernikov 	uint32_t hash;
3403914bffb6SAlexander V. Chernikov 	size_t sz;
3404914bffb6SAlexander V. Chernikov 
3405914bffb6SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
3406914bffb6SAlexander V. Chernikov 
3407914bffb6SAlexander V. Chernikov 	ent = &fe6.e;
3408914bffb6SAlexander V. Chernikov 
3409914bffb6SAlexander V. Chernikov 	memset(&fe6, 0, sizeof(fe6));
3410914bffb6SAlexander V. Chernikov 	memset(&tei, 0, sizeof(tei));
3411914bffb6SAlexander V. Chernikov 
3412914bffb6SAlexander V. Chernikov 	tei.paddr = &tent->k.flow;
3413914bffb6SAlexander V. Chernikov 	tei.subtype = tent->subtype;
3414914bffb6SAlexander V. Chernikov 
3415914bffb6SAlexander V. Chernikov 	if ((error = tei_to_fhash_ent(&tei, ent)) != 0)
3416914bffb6SAlexander V. Chernikov 		return (error);
3417914bffb6SAlexander V. Chernikov 
3418914bffb6SAlexander V. Chernikov 	head = cfg->head;
3419914bffb6SAlexander V. Chernikov 	hash = hash_flow_ent(ent, cfg->size);
3420914bffb6SAlexander V. Chernikov 
3421914bffb6SAlexander V. Chernikov 	if (tei.subtype == AF_INET)
3422914bffb6SAlexander V. Chernikov 		sz = 2 * sizeof(struct in_addr);
3423914bffb6SAlexander V. Chernikov 	else
3424914bffb6SAlexander V. Chernikov 		sz = 2 * sizeof(struct in6_addr);
3425914bffb6SAlexander V. Chernikov 
3426914bffb6SAlexander V. Chernikov 	/* Check for existence */
3427914bffb6SAlexander V. Chernikov 	SLIST_FOREACH(tmp, &head[hash], next) {
3428914bffb6SAlexander V. Chernikov 		if (cmp_flow_ent(tmp, ent, sz) != 0) {
3429914bffb6SAlexander V. Chernikov 			ta_dump_fhash_tentry(ta_state, ti, tmp, tent);
3430914bffb6SAlexander V. Chernikov 			return (0);
3431914bffb6SAlexander V. Chernikov 		}
3432914bffb6SAlexander V. Chernikov 	}
3433914bffb6SAlexander V. Chernikov 
3434914bffb6SAlexander V. Chernikov 	return (ENOENT);
3435914bffb6SAlexander V. Chernikov }
3436914bffb6SAlexander V. Chernikov 
3437914bffb6SAlexander V. Chernikov static void
3438914bffb6SAlexander V. Chernikov ta_foreach_fhash(void *ta_state, struct table_info *ti, ta_foreach_f *f,
3439914bffb6SAlexander V. Chernikov     void *arg)
3440914bffb6SAlexander V. Chernikov {
3441914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
3442914bffb6SAlexander V. Chernikov 	struct fhashentry *ent, *ent_next;
3443914bffb6SAlexander V. Chernikov 	int i;
3444914bffb6SAlexander V. Chernikov 
3445914bffb6SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
3446914bffb6SAlexander V. Chernikov 
3447914bffb6SAlexander V. Chernikov 	for (i = 0; i < cfg->size; i++)
3448914bffb6SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &cfg->head[i], next, ent_next)
3449914bffb6SAlexander V. Chernikov 			f(ent, arg);
3450914bffb6SAlexander V. Chernikov }
3451914bffb6SAlexander V. Chernikov 
3452914bffb6SAlexander V. Chernikov static int
3453914bffb6SAlexander V. Chernikov ta_prepare_add_fhash(struct ip_fw_chain *ch, struct tentry_info *tei,
3454914bffb6SAlexander V. Chernikov     void *ta_buf)
3455914bffb6SAlexander V. Chernikov {
3456914bffb6SAlexander V. Chernikov 	struct ta_buf_fhash *tb;
3457914bffb6SAlexander V. Chernikov 	struct fhashentry *ent;
3458914bffb6SAlexander V. Chernikov 	size_t sz;
3459914bffb6SAlexander V. Chernikov 	int error;
3460914bffb6SAlexander V. Chernikov 
3461914bffb6SAlexander V. Chernikov 	tb = (struct ta_buf_fhash *)ta_buf;
3462914bffb6SAlexander V. Chernikov 
3463914bffb6SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
3464914bffb6SAlexander V. Chernikov 		sz = sizeof(struct fhashentry4);
3465914bffb6SAlexander V. Chernikov 	else if (tei->subtype == AF_INET6)
3466914bffb6SAlexander V. Chernikov 		sz = sizeof(struct fhashentry6);
3467914bffb6SAlexander V. Chernikov 	else
3468914bffb6SAlexander V. Chernikov 		return (EINVAL);
3469914bffb6SAlexander V. Chernikov 
3470914bffb6SAlexander V. Chernikov 	ent = malloc(sz, M_IPFW_TBL, M_WAITOK | M_ZERO);
3471914bffb6SAlexander V. Chernikov 
3472914bffb6SAlexander V. Chernikov 	error = tei_to_fhash_ent(tei, ent);
3473914bffb6SAlexander V. Chernikov 	if (error != 0) {
3474914bffb6SAlexander V. Chernikov 		free(ent, M_IPFW_TBL);
3475914bffb6SAlexander V. Chernikov 		return (error);
3476914bffb6SAlexander V. Chernikov 	}
3477914bffb6SAlexander V. Chernikov 	tb->ent_ptr = ent;
3478914bffb6SAlexander V. Chernikov 
3479914bffb6SAlexander V. Chernikov 	return (0);
3480914bffb6SAlexander V. Chernikov }
3481914bffb6SAlexander V. Chernikov 
3482914bffb6SAlexander V. Chernikov static int
3483914bffb6SAlexander V. Chernikov ta_add_fhash(void *ta_state, struct table_info *ti, struct tentry_info *tei,
3484b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
3485914bffb6SAlexander V. Chernikov {
3486914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
3487914bffb6SAlexander V. Chernikov 	struct fhashbhead *head;
3488914bffb6SAlexander V. Chernikov 	struct fhashentry *ent, *tmp;
3489914bffb6SAlexander V. Chernikov 	struct ta_buf_fhash *tb;
3490914bffb6SAlexander V. Chernikov 	int exists;
3491648e8380SAlexander V. Chernikov 	uint32_t hash, value;
3492914bffb6SAlexander V. Chernikov 	size_t sz;
3493914bffb6SAlexander V. Chernikov 
3494914bffb6SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
3495914bffb6SAlexander V. Chernikov 	tb = (struct ta_buf_fhash *)ta_buf;
3496914bffb6SAlexander V. Chernikov 	ent = (struct fhashentry *)tb->ent_ptr;
3497914bffb6SAlexander V. Chernikov 	exists = 0;
3498914bffb6SAlexander V. Chernikov 
349913263632SAlexander V. Chernikov 	/* Read current value from @tei */
350013263632SAlexander V. Chernikov 	ent->value = tei->value;
350113263632SAlexander V. Chernikov 
3502914bffb6SAlexander V. Chernikov 	head = cfg->head;
3503914bffb6SAlexander V. Chernikov 	hash = hash_flow_ent(ent, cfg->size);
3504914bffb6SAlexander V. Chernikov 
3505914bffb6SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
3506914bffb6SAlexander V. Chernikov 		sz = 2 * sizeof(struct in_addr);
3507914bffb6SAlexander V. Chernikov 	else
3508914bffb6SAlexander V. Chernikov 		sz = 2 * sizeof(struct in6_addr);
3509914bffb6SAlexander V. Chernikov 
3510914bffb6SAlexander V. Chernikov 	/* Check for existence */
3511914bffb6SAlexander V. Chernikov 	SLIST_FOREACH(tmp, &head[hash], next) {
3512914bffb6SAlexander V. Chernikov 		if (cmp_flow_ent(tmp, ent, sz) != 0) {
3513914bffb6SAlexander V. Chernikov 			exists = 1;
3514914bffb6SAlexander V. Chernikov 			break;
3515914bffb6SAlexander V. Chernikov 		}
3516914bffb6SAlexander V. Chernikov 	}
3517914bffb6SAlexander V. Chernikov 
3518914bffb6SAlexander V. Chernikov 	if (exists == 1) {
3519914bffb6SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
3520914bffb6SAlexander V. Chernikov 			return (EEXIST);
3521914bffb6SAlexander V. Chernikov 		/* Record already exists. Update value if we're asked to */
3522648e8380SAlexander V. Chernikov 		/* Exchange values between tmp and @tei */
3523648e8380SAlexander V. Chernikov 		value = tmp->value;
3524914bffb6SAlexander V. Chernikov 		tmp->value = tei->value;
3525648e8380SAlexander V. Chernikov 		tei->value = value;
3526914bffb6SAlexander V. Chernikov 		/* Indicate that update has happened instead of addition */
3527914bffb6SAlexander V. Chernikov 		tei->flags |= TEI_FLAGS_UPDATED;
3528914bffb6SAlexander V. Chernikov 		*pnum = 0;
3529914bffb6SAlexander V. Chernikov 	} else {
35304c0c07a5SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_DONTADD) != 0)
35314c0c07a5SAlexander V. Chernikov 			return (EFBIG);
35324c0c07a5SAlexander V. Chernikov 
3533914bffb6SAlexander V. Chernikov 		SLIST_INSERT_HEAD(&head[hash], ent, next);
3534914bffb6SAlexander V. Chernikov 		tb->ent_ptr = NULL;
3535914bffb6SAlexander V. Chernikov 		*pnum = 1;
3536914bffb6SAlexander V. Chernikov 
3537914bffb6SAlexander V. Chernikov 		/* Update counters and check if we need to grow hash */
3538914bffb6SAlexander V. Chernikov 		cfg->items++;
3539914bffb6SAlexander V. Chernikov 	}
3540914bffb6SAlexander V. Chernikov 
3541914bffb6SAlexander V. Chernikov 	return (0);
3542914bffb6SAlexander V. Chernikov }
3543914bffb6SAlexander V. Chernikov 
3544914bffb6SAlexander V. Chernikov static int
3545914bffb6SAlexander V. Chernikov ta_prepare_del_fhash(struct ip_fw_chain *ch, struct tentry_info *tei,
3546914bffb6SAlexander V. Chernikov     void *ta_buf)
3547914bffb6SAlexander V. Chernikov {
3548914bffb6SAlexander V. Chernikov 	struct ta_buf_fhash *tb;
3549914bffb6SAlexander V. Chernikov 
3550914bffb6SAlexander V. Chernikov 	tb = (struct ta_buf_fhash *)ta_buf;
3551914bffb6SAlexander V. Chernikov 
3552914bffb6SAlexander V. Chernikov 	return (tei_to_fhash_ent(tei, &tb->fe6.e));
3553914bffb6SAlexander V. Chernikov }
3554914bffb6SAlexander V. Chernikov 
3555914bffb6SAlexander V. Chernikov static int
3556914bffb6SAlexander V. Chernikov ta_del_fhash(void *ta_state, struct table_info *ti, struct tentry_info *tei,
3557b6ee846eSAlexander V. Chernikov     void *ta_buf, uint32_t *pnum)
3558914bffb6SAlexander V. Chernikov {
3559914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
3560914bffb6SAlexander V. Chernikov 	struct fhashbhead *head;
3561914bffb6SAlexander V. Chernikov 	struct fhashentry *ent, *tmp;
3562914bffb6SAlexander V. Chernikov 	struct ta_buf_fhash *tb;
3563914bffb6SAlexander V. Chernikov 	uint32_t hash;
3564914bffb6SAlexander V. Chernikov 	size_t sz;
3565914bffb6SAlexander V. Chernikov 
3566914bffb6SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
3567914bffb6SAlexander V. Chernikov 	tb = (struct ta_buf_fhash *)ta_buf;
3568914bffb6SAlexander V. Chernikov 	ent = &tb->fe6.e;
3569914bffb6SAlexander V. Chernikov 
3570914bffb6SAlexander V. Chernikov 	head = cfg->head;
3571914bffb6SAlexander V. Chernikov 	hash = hash_flow_ent(ent, cfg->size);
3572914bffb6SAlexander V. Chernikov 
3573914bffb6SAlexander V. Chernikov 	if (tei->subtype == AF_INET)
3574914bffb6SAlexander V. Chernikov 		sz = 2 * sizeof(struct in_addr);
3575914bffb6SAlexander V. Chernikov 	else
3576914bffb6SAlexander V. Chernikov 		sz = 2 * sizeof(struct in6_addr);
3577914bffb6SAlexander V. Chernikov 
3578914bffb6SAlexander V. Chernikov 	/* Check for existence */
3579914bffb6SAlexander V. Chernikov 	SLIST_FOREACH(tmp, &head[hash], next) {
3580648e8380SAlexander V. Chernikov 		if (cmp_flow_ent(tmp, ent, sz) == 0)
3581648e8380SAlexander V. Chernikov 			continue;
3582648e8380SAlexander V. Chernikov 
3583914bffb6SAlexander V. Chernikov 		SLIST_REMOVE(&head[hash], tmp, fhashentry, next);
3584648e8380SAlexander V. Chernikov 		tei->value = tmp->value;
3585914bffb6SAlexander V. Chernikov 		*pnum = 1;
3586914bffb6SAlexander V. Chernikov 		cfg->items--;
3587648e8380SAlexander V. Chernikov 		tb->ent_ptr = tmp;
3588914bffb6SAlexander V. Chernikov 		return (0);
3589914bffb6SAlexander V. Chernikov 	}
3590914bffb6SAlexander V. Chernikov 
3591914bffb6SAlexander V. Chernikov 	return (ENOENT);
3592914bffb6SAlexander V. Chernikov }
3593914bffb6SAlexander V. Chernikov 
3594914bffb6SAlexander V. Chernikov static void
3595914bffb6SAlexander V. Chernikov ta_flush_fhash_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
3596914bffb6SAlexander V. Chernikov     void *ta_buf)
3597914bffb6SAlexander V. Chernikov {
3598914bffb6SAlexander V. Chernikov 	struct ta_buf_fhash *tb;
3599914bffb6SAlexander V. Chernikov 
3600914bffb6SAlexander V. Chernikov 	tb = (struct ta_buf_fhash *)ta_buf;
3601914bffb6SAlexander V. Chernikov 
3602914bffb6SAlexander V. Chernikov 	if (tb->ent_ptr != NULL)
3603914bffb6SAlexander V. Chernikov 		free(tb->ent_ptr, M_IPFW_TBL);
3604914bffb6SAlexander V. Chernikov }
3605914bffb6SAlexander V. Chernikov 
3606914bffb6SAlexander V. Chernikov /*
3607914bffb6SAlexander V. Chernikov  * Hash growing callbacks.
3608914bffb6SAlexander V. Chernikov  */
3609914bffb6SAlexander V. Chernikov 
3610b6ee846eSAlexander V. Chernikov static int
3611301290bcSAlexander V. Chernikov ta_need_modify_fhash(void *ta_state, struct table_info *ti, uint32_t count,
3612b6ee846eSAlexander V. Chernikov     uint64_t *pflags)
3613b6ee846eSAlexander V. Chernikov {
3614b6ee846eSAlexander V. Chernikov 	struct fhash_cfg *cfg;
3615b6ee846eSAlexander V. Chernikov 
3616b6ee846eSAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
3617b6ee846eSAlexander V. Chernikov 
3618b6ee846eSAlexander V. Chernikov 	if (cfg->items > cfg->size && cfg->size < 65536) {
3619b6ee846eSAlexander V. Chernikov 		*pflags = cfg->size * 2;
3620301290bcSAlexander V. Chernikov 		return (1);
3621b6ee846eSAlexander V. Chernikov 	}
3622b6ee846eSAlexander V. Chernikov 
3623301290bcSAlexander V. Chernikov 	return (0);
3624b6ee846eSAlexander V. Chernikov }
3625b6ee846eSAlexander V. Chernikov 
3626914bffb6SAlexander V. Chernikov /*
3627914bffb6SAlexander V. Chernikov  * Allocate new, larger fhash.
3628914bffb6SAlexander V. Chernikov  */
3629914bffb6SAlexander V. Chernikov static int
3630914bffb6SAlexander V. Chernikov ta_prepare_mod_fhash(void *ta_buf, uint64_t *pflags)
3631914bffb6SAlexander V. Chernikov {
3632914bffb6SAlexander V. Chernikov 	struct mod_item *mi;
3633914bffb6SAlexander V. Chernikov 	struct fhashbhead *head;
3634914bffb6SAlexander V. Chernikov 	int i;
3635914bffb6SAlexander V. Chernikov 
3636914bffb6SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
3637914bffb6SAlexander V. Chernikov 
3638914bffb6SAlexander V. Chernikov 	memset(mi, 0, sizeof(struct mod_item));
3639914bffb6SAlexander V. Chernikov 	mi->size = *pflags;
3640914bffb6SAlexander V. Chernikov 	head = malloc(sizeof(struct fhashbhead) * mi->size, M_IPFW,
3641914bffb6SAlexander V. Chernikov 	    M_WAITOK | M_ZERO);
3642914bffb6SAlexander V. Chernikov 	for (i = 0; i < mi->size; i++)
3643914bffb6SAlexander V. Chernikov 		SLIST_INIT(&head[i]);
3644914bffb6SAlexander V. Chernikov 
3645914bffb6SAlexander V. Chernikov 	mi->main_ptr = head;
3646914bffb6SAlexander V. Chernikov 
3647914bffb6SAlexander V. Chernikov 	return (0);
3648914bffb6SAlexander V. Chernikov }
3649914bffb6SAlexander V. Chernikov 
3650914bffb6SAlexander V. Chernikov /*
3651914bffb6SAlexander V. Chernikov  * Copy data from old runtime array to new one.
3652914bffb6SAlexander V. Chernikov  */
3653914bffb6SAlexander V. Chernikov static int
3654914bffb6SAlexander V. Chernikov ta_fill_mod_fhash(void *ta_state, struct table_info *ti, void *ta_buf,
3655914bffb6SAlexander V. Chernikov     uint64_t *pflags)
3656914bffb6SAlexander V. Chernikov {
3657914bffb6SAlexander V. Chernikov 
3658914bffb6SAlexander V. Chernikov 	/* In is not possible to do rehash if we're not holidng WLOCK. */
3659914bffb6SAlexander V. Chernikov 	return (0);
3660914bffb6SAlexander V. Chernikov }
3661914bffb6SAlexander V. Chernikov 
3662914bffb6SAlexander V. Chernikov /*
3663914bffb6SAlexander V. Chernikov  * Switch old & new arrays.
3664914bffb6SAlexander V. Chernikov  */
3665301290bcSAlexander V. Chernikov static void
3666914bffb6SAlexander V. Chernikov ta_modify_fhash(void *ta_state, struct table_info *ti, void *ta_buf,
3667914bffb6SAlexander V. Chernikov     uint64_t pflags)
3668914bffb6SAlexander V. Chernikov {
3669914bffb6SAlexander V. Chernikov 	struct mod_item *mi;
3670914bffb6SAlexander V. Chernikov 	struct fhash_cfg *cfg;
3671914bffb6SAlexander V. Chernikov 	struct fhashbhead *old_head, *new_head;
3672914bffb6SAlexander V. Chernikov 	struct fhashentry *ent, *ent_next;
3673914bffb6SAlexander V. Chernikov 	int i;
3674914bffb6SAlexander V. Chernikov 	uint32_t nhash;
3675914bffb6SAlexander V. Chernikov 	size_t old_size;
3676914bffb6SAlexander V. Chernikov 
3677914bffb6SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
3678914bffb6SAlexander V. Chernikov 	cfg = (struct fhash_cfg *)ta_state;
3679914bffb6SAlexander V. Chernikov 
3680914bffb6SAlexander V. Chernikov 	old_size = cfg->size;
3681914bffb6SAlexander V. Chernikov 	old_head = ti->state;
3682914bffb6SAlexander V. Chernikov 
3683914bffb6SAlexander V. Chernikov 	new_head = (struct fhashbhead *)mi->main_ptr;
3684914bffb6SAlexander V. Chernikov 	for (i = 0; i < old_size; i++) {
3685914bffb6SAlexander V. Chernikov 		SLIST_FOREACH_SAFE(ent, &old_head[i], next, ent_next) {
3686914bffb6SAlexander V. Chernikov 			nhash = hash_flow_ent(ent, mi->size);
3687914bffb6SAlexander V. Chernikov 			SLIST_INSERT_HEAD(&new_head[nhash], ent, next);
3688914bffb6SAlexander V. Chernikov 		}
3689914bffb6SAlexander V. Chernikov 	}
3690914bffb6SAlexander V. Chernikov 
3691914bffb6SAlexander V. Chernikov 	ti->state = new_head;
3692914bffb6SAlexander V. Chernikov 	ti->data = mi->size;
3693914bffb6SAlexander V. Chernikov 	cfg->head = new_head;
3694914bffb6SAlexander V. Chernikov 	cfg->size = mi->size;
3695914bffb6SAlexander V. Chernikov 
3696914bffb6SAlexander V. Chernikov 	mi->main_ptr = old_head;
3697914bffb6SAlexander V. Chernikov }
3698914bffb6SAlexander V. Chernikov 
3699914bffb6SAlexander V. Chernikov /*
3700914bffb6SAlexander V. Chernikov  * Free unneded array.
3701914bffb6SAlexander V. Chernikov  */
3702914bffb6SAlexander V. Chernikov static void
3703914bffb6SAlexander V. Chernikov ta_flush_mod_fhash(void *ta_buf)
3704914bffb6SAlexander V. Chernikov {
3705914bffb6SAlexander V. Chernikov 	struct mod_item *mi;
3706914bffb6SAlexander V. Chernikov 
3707914bffb6SAlexander V. Chernikov 	mi = (struct mod_item *)ta_buf;
3708914bffb6SAlexander V. Chernikov 	if (mi->main_ptr != NULL)
3709914bffb6SAlexander V. Chernikov 		free(mi->main_ptr, M_IPFW);
3710914bffb6SAlexander V. Chernikov }
3711914bffb6SAlexander V. Chernikov 
3712914bffb6SAlexander V. Chernikov struct table_algo flow_hash = {
3713914bffb6SAlexander V. Chernikov 	.name		= "flow:hash",
3714914bffb6SAlexander V. Chernikov 	.type		= IPFW_TABLE_FLOW,
371557a1cf95SAlexander V. Chernikov 	.flags		= TA_FLAG_DEFAULT,
371657a1cf95SAlexander V. Chernikov 	.ta_buf_size	= sizeof(struct ta_buf_fhash),
3717914bffb6SAlexander V. Chernikov 	.init		= ta_init_fhash,
3718914bffb6SAlexander V. Chernikov 	.destroy	= ta_destroy_fhash,
3719914bffb6SAlexander V. Chernikov 	.prepare_add	= ta_prepare_add_fhash,
3720914bffb6SAlexander V. Chernikov 	.prepare_del	= ta_prepare_del_fhash,
3721914bffb6SAlexander V. Chernikov 	.add		= ta_add_fhash,
3722914bffb6SAlexander V. Chernikov 	.del		= ta_del_fhash,
3723914bffb6SAlexander V. Chernikov 	.flush_entry	= ta_flush_fhash_entry,
3724914bffb6SAlexander V. Chernikov 	.foreach	= ta_foreach_fhash,
3725914bffb6SAlexander V. Chernikov 	.dump_tentry	= ta_dump_fhash_tentry,
3726914bffb6SAlexander V. Chernikov 	.find_tentry	= ta_find_fhash_tentry,
37275f379342SAlexander V. Chernikov 	.dump_tinfo	= ta_dump_fhash_tinfo,
3728301290bcSAlexander V. Chernikov 	.need_modify	= ta_need_modify_fhash,
3729914bffb6SAlexander V. Chernikov 	.prepare_mod	= ta_prepare_mod_fhash,
3730914bffb6SAlexander V. Chernikov 	.fill_mod	= ta_fill_mod_fhash,
3731914bffb6SAlexander V. Chernikov 	.modify		= ta_modify_fhash,
3732914bffb6SAlexander V. Chernikov 	.flush_mod	= ta_flush_mod_fhash,
3733914bffb6SAlexander V. Chernikov };
37343fe2ef91SAlexander V. Chernikov 
3735d3b00c08SAlexander V. Chernikov /*
3736d3b00c08SAlexander V. Chernikov  * Kernel fibs bindings.
3737d3b00c08SAlexander V. Chernikov  *
3738d3b00c08SAlexander V. Chernikov  * Implementation:
3739d3b00c08SAlexander V. Chernikov  *
3740d3b00c08SAlexander V. Chernikov  * Runtime part:
3741d3b00c08SAlexander V. Chernikov  * - fully relies on route API
3742d3b00c08SAlexander V. Chernikov  * - fib number is stored in ti->data
3743d3b00c08SAlexander V. Chernikov  *
3744d3b00c08SAlexander V. Chernikov  */
3745d3b00c08SAlexander V. Chernikov 
3746*9fe15d06SAlexander V. Chernikov static struct rtentry *lookup_kfib(void *key, int keylen, int fib);
3747*9fe15d06SAlexander V. Chernikov static int ta_lookup_kfib(struct table_info *ti, void *key, uint32_t keylen,
3748*9fe15d06SAlexander V. Chernikov     uint32_t *val);
3749*9fe15d06SAlexander V. Chernikov static int kfib_parse_opts(int *pfib, char *data);
3750*9fe15d06SAlexander V. Chernikov static void ta_print_kfib_config(void *ta_state, struct table_info *ti,
3751*9fe15d06SAlexander V. Chernikov     char *buf, size_t bufsize);
3752*9fe15d06SAlexander V. Chernikov static int ta_init_kfib(struct ip_fw_chain *ch, void **ta_state,
3753*9fe15d06SAlexander V. Chernikov     struct table_info *ti, char *data, uint8_t tflags);
3754*9fe15d06SAlexander V. Chernikov static void ta_destroy_kfib(void *ta_state, struct table_info *ti);
3755*9fe15d06SAlexander V. Chernikov static void ta_dump_kfib_tinfo(void *ta_state, struct table_info *ti,
3756*9fe15d06SAlexander V. Chernikov     ipfw_ta_tinfo *tinfo);
3757*9fe15d06SAlexander V. Chernikov static int contigmask(uint8_t *p, int len);
3758*9fe15d06SAlexander V. Chernikov static int ta_dump_kfib_tentry(void *ta_state, struct table_info *ti, void *e,
3759*9fe15d06SAlexander V. Chernikov     ipfw_obj_tentry *tent);
3760*9fe15d06SAlexander V. Chernikov static int ta_find_kfib_tentry(void *ta_state, struct table_info *ti,
3761*9fe15d06SAlexander V. Chernikov     ipfw_obj_tentry *tent);
3762*9fe15d06SAlexander V. Chernikov static void ta_foreach_kfib(void *ta_state, struct table_info *ti,
3763*9fe15d06SAlexander V. Chernikov     ta_foreach_f *f, void *arg);
3764*9fe15d06SAlexander V. Chernikov 
3765d3b00c08SAlexander V. Chernikov static struct rtentry *
3766d3b00c08SAlexander V. Chernikov lookup_kfib(void *key, int keylen, int fib)
3767d3b00c08SAlexander V. Chernikov {
3768d3b00c08SAlexander V. Chernikov 	struct sockaddr *s;
3769d3b00c08SAlexander V. Chernikov 
3770d3b00c08SAlexander V. Chernikov 	if (keylen == 4) {
3771d3b00c08SAlexander V. Chernikov 		struct sockaddr_in sin;
3772d3b00c08SAlexander V. Chernikov 		bzero(&sin, sizeof(sin));
3773d3b00c08SAlexander V. Chernikov 		sin.sin_len = sizeof(struct sockaddr_in);
3774d3b00c08SAlexander V. Chernikov 		sin.sin_family = AF_INET;
3775d3b00c08SAlexander V. Chernikov 		sin.sin_addr.s_addr = *(in_addr_t *)key;
3776d3b00c08SAlexander V. Chernikov 		s = (struct sockaddr *)&sin;
3777d3b00c08SAlexander V. Chernikov 	} else {
3778d3b00c08SAlexander V. Chernikov 		struct sockaddr_in6 sin6;
3779d3b00c08SAlexander V. Chernikov 		bzero(&sin6, sizeof(sin6));
3780d3b00c08SAlexander V. Chernikov 		sin6.sin6_len = sizeof(struct sockaddr_in6);
3781d3b00c08SAlexander V. Chernikov 		sin6.sin6_family = AF_INET6;
3782d3b00c08SAlexander V. Chernikov 		sin6.sin6_addr = *(struct in6_addr *)key;
3783d3b00c08SAlexander V. Chernikov 		s = (struct sockaddr *)&sin6;
3784d3b00c08SAlexander V. Chernikov 	}
3785d3b00c08SAlexander V. Chernikov 
3786d3b00c08SAlexander V. Chernikov 	return (rtalloc1_fib(s, 0, 0, fib));
3787d3b00c08SAlexander V. Chernikov }
3788d3b00c08SAlexander V. Chernikov 
3789d3b00c08SAlexander V. Chernikov static int
3790d3b00c08SAlexander V. Chernikov ta_lookup_kfib(struct table_info *ti, void *key, uint32_t keylen,
3791d3b00c08SAlexander V. Chernikov     uint32_t *val)
3792d3b00c08SAlexander V. Chernikov {
3793d3b00c08SAlexander V. Chernikov 	struct rtentry *rte;
3794d3b00c08SAlexander V. Chernikov 
3795d3b00c08SAlexander V. Chernikov 	if ((rte = lookup_kfib(key, keylen, ti->data)) == NULL)
3796d3b00c08SAlexander V. Chernikov 		return (0);
3797d3b00c08SAlexander V. Chernikov 
3798d3b00c08SAlexander V. Chernikov 	*val = 0;
3799d3b00c08SAlexander V. Chernikov 	RTFREE_LOCKED(rte);
3800d3b00c08SAlexander V. Chernikov 
3801d3b00c08SAlexander V. Chernikov 	return (1);
3802d3b00c08SAlexander V. Chernikov }
3803d3b00c08SAlexander V. Chernikov 
3804d3b00c08SAlexander V. Chernikov /* Parse 'fib=%d' */
3805d3b00c08SAlexander V. Chernikov static int
3806d3b00c08SAlexander V. Chernikov kfib_parse_opts(int *pfib, char *data)
3807d3b00c08SAlexander V. Chernikov {
3808d3b00c08SAlexander V. Chernikov 	char *pdel, *pend, *s;
3809d3b00c08SAlexander V. Chernikov 	int fibnum;
3810d3b00c08SAlexander V. Chernikov 
3811d3b00c08SAlexander V. Chernikov 	if (data == NULL)
3812d3b00c08SAlexander V. Chernikov 		return (0);
3813d3b00c08SAlexander V. Chernikov 	if ((pdel = strchr(data, ' ')) == NULL)
3814d3b00c08SAlexander V. Chernikov 		return (0);
3815d3b00c08SAlexander V. Chernikov 	while (*pdel == ' ')
3816d3b00c08SAlexander V. Chernikov 		pdel++;
3817d3b00c08SAlexander V. Chernikov 	if (strncmp(pdel, "fib=", 4) != 0)
3818d3b00c08SAlexander V. Chernikov 		return (EINVAL);
3819d3b00c08SAlexander V. Chernikov 	if ((s = strchr(pdel, ' ')) != NULL)
3820d3b00c08SAlexander V. Chernikov 		*s++ = '\0';
3821d3b00c08SAlexander V. Chernikov 
3822d3b00c08SAlexander V. Chernikov 	pdel += 4;
3823d3b00c08SAlexander V. Chernikov 	/* Need \d+ */
3824d3b00c08SAlexander V. Chernikov 	fibnum = strtol(pdel, &pend, 10);
3825d3b00c08SAlexander V. Chernikov 	if (*pend != '\0')
3826d3b00c08SAlexander V. Chernikov 		return (EINVAL);
3827d3b00c08SAlexander V. Chernikov 
3828d3b00c08SAlexander V. Chernikov 	*pfib = fibnum;
3829d3b00c08SAlexander V. Chernikov 
3830d3b00c08SAlexander V. Chernikov 	return (0);
3831d3b00c08SAlexander V. Chernikov }
3832d3b00c08SAlexander V. Chernikov 
3833d3b00c08SAlexander V. Chernikov static void
3834d3b00c08SAlexander V. Chernikov ta_print_kfib_config(void *ta_state, struct table_info *ti, char *buf,
3835d3b00c08SAlexander V. Chernikov     size_t bufsize)
3836d3b00c08SAlexander V. Chernikov {
3837d3b00c08SAlexander V. Chernikov 
3838d3b00c08SAlexander V. Chernikov 	if (ti->data != 0)
3839c21034b7SAlexander V. Chernikov 		snprintf(buf, bufsize, "%s fib=%lu", "addr:kfib", ti->data);
3840d3b00c08SAlexander V. Chernikov 	else
3841c21034b7SAlexander V. Chernikov 		snprintf(buf, bufsize, "%s", "addr:kfib");
3842d3b00c08SAlexander V. Chernikov }
3843d3b00c08SAlexander V. Chernikov 
3844d3b00c08SAlexander V. Chernikov static int
3845d3b00c08SAlexander V. Chernikov ta_init_kfib(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
3846d3b00c08SAlexander V. Chernikov     char *data, uint8_t tflags)
3847d3b00c08SAlexander V. Chernikov {
3848d3b00c08SAlexander V. Chernikov 	int error, fibnum;
3849d3b00c08SAlexander V. Chernikov 
3850d3b00c08SAlexander V. Chernikov 	fibnum = 0;
3851d3b00c08SAlexander V. Chernikov 	if ((error = kfib_parse_opts(&fibnum, data)) != 0)
3852d3b00c08SAlexander V. Chernikov 		return (error);
3853d3b00c08SAlexander V. Chernikov 
3854d3b00c08SAlexander V. Chernikov 	if (fibnum >= rt_numfibs)
3855d3b00c08SAlexander V. Chernikov 		return (E2BIG);
3856d3b00c08SAlexander V. Chernikov 
3857d3b00c08SAlexander V. Chernikov 	ti->data = fibnum;
3858d3b00c08SAlexander V. Chernikov 	ti->lookup = ta_lookup_kfib;
3859d3b00c08SAlexander V. Chernikov 
3860d3b00c08SAlexander V. Chernikov 	return (0);
3861d3b00c08SAlexander V. Chernikov }
3862d3b00c08SAlexander V. Chernikov 
3863d3b00c08SAlexander V. Chernikov /*
3864d3b00c08SAlexander V. Chernikov  * Destroys table @ti
3865d3b00c08SAlexander V. Chernikov  */
3866d3b00c08SAlexander V. Chernikov static void
3867d3b00c08SAlexander V. Chernikov ta_destroy_kfib(void *ta_state, struct table_info *ti)
3868d3b00c08SAlexander V. Chernikov {
3869d3b00c08SAlexander V. Chernikov 
3870d3b00c08SAlexander V. Chernikov }
3871d3b00c08SAlexander V. Chernikov 
3872d3b00c08SAlexander V. Chernikov /*
3873d3b00c08SAlexander V. Chernikov  * Provide algo-specific table info
3874d3b00c08SAlexander V. Chernikov  */
3875d3b00c08SAlexander V. Chernikov static void
3876d3b00c08SAlexander V. Chernikov ta_dump_kfib_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo)
3877d3b00c08SAlexander V. Chernikov {
3878d3b00c08SAlexander V. Chernikov 
3879d3b00c08SAlexander V. Chernikov 	tinfo->flags = IPFW_TATFLAGS_AFDATA;
3880d3b00c08SAlexander V. Chernikov 	tinfo->taclass4 = IPFW_TACLASS_RADIX;
3881d3b00c08SAlexander V. Chernikov 	tinfo->count4 = 0;
3882d3b00c08SAlexander V. Chernikov 	tinfo->itemsize4 = sizeof(struct rtentry);
3883d3b00c08SAlexander V. Chernikov 	tinfo->taclass6 = IPFW_TACLASS_RADIX;
3884d3b00c08SAlexander V. Chernikov 	tinfo->count6 = 0;
3885d3b00c08SAlexander V. Chernikov 	tinfo->itemsize6 = sizeof(struct rtentry);
3886d3b00c08SAlexander V. Chernikov }
3887d3b00c08SAlexander V. Chernikov 
3888d3b00c08SAlexander V. Chernikov static int
3889d3b00c08SAlexander V. Chernikov contigmask(uint8_t *p, int len)
3890d3b00c08SAlexander V. Chernikov {
3891d3b00c08SAlexander V. Chernikov 	int i, n;
3892d3b00c08SAlexander V. Chernikov 
3893d3b00c08SAlexander V. Chernikov 	for (i = 0; i < len ; i++)
3894d3b00c08SAlexander V. Chernikov 		if ( (p[i/8] & (1 << (7 - (i%8)))) == 0) /* first bit unset */
3895d3b00c08SAlexander V. Chernikov 			break;
3896d3b00c08SAlexander V. Chernikov 	for (n= i + 1; n < len; n++)
3897d3b00c08SAlexander V. Chernikov 		if ( (p[n/8] & (1 << (7 - (n % 8)))) != 0)
3898d3b00c08SAlexander V. Chernikov 			return (-1); /* mask not contiguous */
3899d3b00c08SAlexander V. Chernikov 	return (i);
3900d3b00c08SAlexander V. Chernikov }
3901d3b00c08SAlexander V. Chernikov 
3902d3b00c08SAlexander V. Chernikov 
3903d3b00c08SAlexander V. Chernikov static int
3904d3b00c08SAlexander V. Chernikov ta_dump_kfib_tentry(void *ta_state, struct table_info *ti, void *e,
3905d3b00c08SAlexander V. Chernikov     ipfw_obj_tentry *tent)
3906d3b00c08SAlexander V. Chernikov {
3907d3b00c08SAlexander V. Chernikov 	struct rtentry *rte;
3908d3b00c08SAlexander V. Chernikov 	struct sockaddr_in *addr, *mask;
3909d3b00c08SAlexander V. Chernikov 	struct sockaddr_in6 *addr6, *mask6;
3910d3b00c08SAlexander V. Chernikov 	int len;
3911d3b00c08SAlexander V. Chernikov 
3912d3b00c08SAlexander V. Chernikov 	rte = (struct rtentry *)e;
3913d3b00c08SAlexander V. Chernikov 	addr = (struct sockaddr_in *)rt_key(rte);
3914d3b00c08SAlexander V. Chernikov 	mask = (struct sockaddr_in *)rt_mask(rte);
3915d3b00c08SAlexander V. Chernikov 	len = 0;
3916d3b00c08SAlexander V. Chernikov 
3917d3b00c08SAlexander V. Chernikov 	/* Guess IPv4/IPv6 radix by sockaddr family */
3918d3b00c08SAlexander V. Chernikov 	if (addr->sin_family == AF_INET) {
3919d3b00c08SAlexander V. Chernikov 		tent->k.addr.s_addr = addr->sin_addr.s_addr;
3920d3b00c08SAlexander V. Chernikov 		len = 32;
3921d3b00c08SAlexander V. Chernikov 		if (mask != NULL)
3922d3b00c08SAlexander V. Chernikov 			len = contigmask((uint8_t *)&mask->sin_addr, 32);
3923d3b00c08SAlexander V. Chernikov 		if (len == -1)
3924d3b00c08SAlexander V. Chernikov 			len = 0;
3925d3b00c08SAlexander V. Chernikov 		tent->masklen = len;
3926d3b00c08SAlexander V. Chernikov 		tent->subtype = AF_INET;
39270cba2b28SAlexander V. Chernikov 		tent->v.kidx = 0; /* Do we need to put GW here? */
3928d3b00c08SAlexander V. Chernikov #ifdef INET6
3929d3b00c08SAlexander V. Chernikov 	} else if (addr->sin_family == AF_INET6) {
3930d3b00c08SAlexander V. Chernikov 		addr6 = (struct sockaddr_in6 *)addr;
3931d3b00c08SAlexander V. Chernikov 		mask6 = (struct sockaddr_in6 *)mask;
3932d3b00c08SAlexander V. Chernikov 		memcpy(&tent->k, &addr6->sin6_addr, sizeof(struct in6_addr));
3933d3b00c08SAlexander V. Chernikov 		len = 128;
3934d3b00c08SAlexander V. Chernikov 		if (mask6 != NULL)
3935d3b00c08SAlexander V. Chernikov 			len = contigmask((uint8_t *)&mask6->sin6_addr, 128);
3936d3b00c08SAlexander V. Chernikov 		if (len == -1)
3937d3b00c08SAlexander V. Chernikov 			len = 0;
3938d3b00c08SAlexander V. Chernikov 		tent->masklen = len;
3939d3b00c08SAlexander V. Chernikov 		tent->subtype = AF_INET6;
39400cba2b28SAlexander V. Chernikov 		tent->v.kidx = 0;
3941d3b00c08SAlexander V. Chernikov #endif
3942d3b00c08SAlexander V. Chernikov 	}
3943d3b00c08SAlexander V. Chernikov 
3944d3b00c08SAlexander V. Chernikov 	return (0);
3945d3b00c08SAlexander V. Chernikov }
3946d3b00c08SAlexander V. Chernikov 
3947d3b00c08SAlexander V. Chernikov static int
3948d3b00c08SAlexander V. Chernikov ta_find_kfib_tentry(void *ta_state, struct table_info *ti,
3949d3b00c08SAlexander V. Chernikov     ipfw_obj_tentry *tent)
3950d3b00c08SAlexander V. Chernikov {
3951d3b00c08SAlexander V. Chernikov 	struct rtentry *rte;
3952d3b00c08SAlexander V. Chernikov 	void *key;
3953d3b00c08SAlexander V. Chernikov 	int keylen;
3954d3b00c08SAlexander V. Chernikov 
3955d3b00c08SAlexander V. Chernikov 	if (tent->subtype == AF_INET) {
3956d3b00c08SAlexander V. Chernikov 		key = &tent->k.addr;
3957d3b00c08SAlexander V. Chernikov 		keylen = sizeof(struct in_addr);
3958d3b00c08SAlexander V. Chernikov 	} else {
3959d3b00c08SAlexander V. Chernikov 		key = &tent->k.addr6;
3960d3b00c08SAlexander V. Chernikov 		keylen = sizeof(struct in6_addr);
3961d3b00c08SAlexander V. Chernikov 	}
3962d3b00c08SAlexander V. Chernikov 
3963d3b00c08SAlexander V. Chernikov 	if ((rte = lookup_kfib(key, keylen, ti->data)) == NULL)
3964d3b00c08SAlexander V. Chernikov 		return (0);
3965d3b00c08SAlexander V. Chernikov 
3966d3b00c08SAlexander V. Chernikov 	if (rte != NULL) {
3967d3b00c08SAlexander V. Chernikov 		ta_dump_kfib_tentry(ta_state, ti, rte, tent);
3968d3b00c08SAlexander V. Chernikov 		RTFREE_LOCKED(rte);
3969d3b00c08SAlexander V. Chernikov 		return (0);
3970d3b00c08SAlexander V. Chernikov 	}
3971d3b00c08SAlexander V. Chernikov 
3972d3b00c08SAlexander V. Chernikov 	return (ENOENT);
3973d3b00c08SAlexander V. Chernikov }
3974d3b00c08SAlexander V. Chernikov 
3975d3b00c08SAlexander V. Chernikov static void
3976d3b00c08SAlexander V. Chernikov ta_foreach_kfib(void *ta_state, struct table_info *ti, ta_foreach_f *f,
3977d3b00c08SAlexander V. Chernikov     void *arg)
3978d3b00c08SAlexander V. Chernikov {
3979d3b00c08SAlexander V. Chernikov 	struct radix_node_head *rnh;
3980d3b00c08SAlexander V. Chernikov 	int error;
3981d3b00c08SAlexander V. Chernikov 
3982d3b00c08SAlexander V. Chernikov 	rnh = rt_tables_get_rnh(ti->data, AF_INET);
3983d3b00c08SAlexander V. Chernikov 	if (rnh != NULL) {
3984d3b00c08SAlexander V. Chernikov 		RADIX_NODE_HEAD_RLOCK(rnh);
3985d3b00c08SAlexander V. Chernikov 		error = rnh->rnh_walktree(rnh, (walktree_f_t *)f, arg);
3986d3b00c08SAlexander V. Chernikov 		RADIX_NODE_HEAD_RUNLOCK(rnh);
3987d3b00c08SAlexander V. Chernikov 	}
3988d3b00c08SAlexander V. Chernikov 
3989d3b00c08SAlexander V. Chernikov 	rnh = rt_tables_get_rnh(ti->data, AF_INET6);
3990d3b00c08SAlexander V. Chernikov 	if (rnh != NULL) {
3991d3b00c08SAlexander V. Chernikov 		RADIX_NODE_HEAD_RLOCK(rnh);
3992d3b00c08SAlexander V. Chernikov 		error = rnh->rnh_walktree(rnh, (walktree_f_t *)f, arg);
3993d3b00c08SAlexander V. Chernikov 		RADIX_NODE_HEAD_RUNLOCK(rnh);
3994d3b00c08SAlexander V. Chernikov 	}
3995d3b00c08SAlexander V. Chernikov }
3996d3b00c08SAlexander V. Chernikov 
3997c21034b7SAlexander V. Chernikov struct table_algo addr_kfib = {
3998c21034b7SAlexander V. Chernikov 	.name		= "addr:kfib",
3999c21034b7SAlexander V. Chernikov 	.type		= IPFW_TABLE_ADDR,
4000d3b00c08SAlexander V. Chernikov 	.flags		= TA_FLAG_READONLY,
4001d3b00c08SAlexander V. Chernikov 	.ta_buf_size	= 0,
4002d3b00c08SAlexander V. Chernikov 	.init		= ta_init_kfib,
4003d3b00c08SAlexander V. Chernikov 	.destroy	= ta_destroy_kfib,
4004d3b00c08SAlexander V. Chernikov 	.foreach	= ta_foreach_kfib,
4005d3b00c08SAlexander V. Chernikov 	.dump_tentry	= ta_dump_kfib_tentry,
4006d3b00c08SAlexander V. Chernikov 	.find_tentry	= ta_find_kfib_tentry,
4007d3b00c08SAlexander V. Chernikov 	.dump_tinfo	= ta_dump_kfib_tinfo,
4008d3b00c08SAlexander V. Chernikov 	.print_config	= ta_print_kfib_config,
4009d3b00c08SAlexander V. Chernikov };
4010d3b00c08SAlexander V. Chernikov 
40119f7d47b0SAlexander V. Chernikov void
40120b565ac0SAlexander V. Chernikov ipfw_table_algo_init(struct ip_fw_chain *ch)
40139f7d47b0SAlexander V. Chernikov {
40140b565ac0SAlexander V. Chernikov 	size_t sz;
40150b565ac0SAlexander V. Chernikov 
40169f7d47b0SAlexander V. Chernikov 	/*
40179f7d47b0SAlexander V. Chernikov 	 * Register all algorithms presented here.
40189f7d47b0SAlexander V. Chernikov 	 */
40190b565ac0SAlexander V. Chernikov 	sz = sizeof(struct table_algo);
4020c21034b7SAlexander V. Chernikov 	ipfw_add_table_algo(ch, &addr_radix, sz, &addr_radix.idx);
4021c21034b7SAlexander V. Chernikov 	ipfw_add_table_algo(ch, &addr_hash, sz, &addr_hash.idx);
40220b565ac0SAlexander V. Chernikov 	ipfw_add_table_algo(ch, &iface_idx, sz, &iface_idx.idx);
4023b23d5de9SAlexander V. Chernikov 	ipfw_add_table_algo(ch, &number_array, sz, &number_array.idx);
4024914bffb6SAlexander V. Chernikov 	ipfw_add_table_algo(ch, &flow_hash, sz, &flow_hash.idx);
4025c21034b7SAlexander V. Chernikov 	ipfw_add_table_algo(ch, &addr_kfib, sz, &addr_kfib.idx);
40269f7d47b0SAlexander V. Chernikov }
40279f7d47b0SAlexander V. Chernikov 
40289f7d47b0SAlexander V. Chernikov void
40290b565ac0SAlexander V. Chernikov ipfw_table_algo_destroy(struct ip_fw_chain *ch)
40309f7d47b0SAlexander V. Chernikov {
40310b565ac0SAlexander V. Chernikov 
4032c21034b7SAlexander V. Chernikov 	ipfw_del_table_algo(ch, addr_radix.idx);
4033c21034b7SAlexander V. Chernikov 	ipfw_del_table_algo(ch, addr_hash.idx);
40340b565ac0SAlexander V. Chernikov 	ipfw_del_table_algo(ch, iface_idx.idx);
4035b23d5de9SAlexander V. Chernikov 	ipfw_del_table_algo(ch, number_array.idx);
4036914bffb6SAlexander V. Chernikov 	ipfw_del_table_algo(ch, flow_hash.idx);
4037c21034b7SAlexander V. Chernikov 	ipfw_del_table_algo(ch, addr_kfib.idx);
40389f7d47b0SAlexander V. Chernikov }
40399f7d47b0SAlexander V. Chernikov 
40409f7d47b0SAlexander V. Chernikov 
4041