13b3a8eb9SGleb Smirnoff /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3fe267a55SPedro F. Giffuni *
43b3a8eb9SGleb Smirnoff * Copyright (c) 2002-2009 Luigi Rizzo, Universita` di Pisa
51a33e799SAlexander V. Chernikov * Copyright (c) 2014 Yandex LLC
61a33e799SAlexander V. Chernikov * Copyright (c) 2014 Alexander V. Chernikov
73b3a8eb9SGleb Smirnoff *
83b3a8eb9SGleb Smirnoff * Supported by: Valeria Paoli
93b3a8eb9SGleb Smirnoff *
103b3a8eb9SGleb Smirnoff * Redistribution and use in source and binary forms, with or without
113b3a8eb9SGleb Smirnoff * modification, are permitted provided that the following conditions
123b3a8eb9SGleb Smirnoff * are met:
133b3a8eb9SGleb Smirnoff * 1. Redistributions of source code must retain the above copyright
143b3a8eb9SGleb Smirnoff * notice, this list of conditions and the following disclaimer.
153b3a8eb9SGleb Smirnoff * 2. Redistributions in binary form must reproduce the above copyright
163b3a8eb9SGleb Smirnoff * notice, this list of conditions and the following disclaimer in the
173b3a8eb9SGleb Smirnoff * documentation and/or other materials provided with the distribution.
183b3a8eb9SGleb Smirnoff *
193b3a8eb9SGleb Smirnoff * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
203b3a8eb9SGleb Smirnoff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
213b3a8eb9SGleb Smirnoff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
223b3a8eb9SGleb Smirnoff * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
233b3a8eb9SGleb Smirnoff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
243b3a8eb9SGleb Smirnoff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
253b3a8eb9SGleb Smirnoff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
263b3a8eb9SGleb Smirnoff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
273b3a8eb9SGleb Smirnoff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
283b3a8eb9SGleb Smirnoff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
293b3a8eb9SGleb Smirnoff * SUCH DAMAGE.
303b3a8eb9SGleb Smirnoff */
313b3a8eb9SGleb Smirnoff
323b3a8eb9SGleb Smirnoff #include <sys/cdefs.h>
333b3a8eb9SGleb Smirnoff /*
341a33e799SAlexander V. Chernikov * Control socket and rule management routines for ipfw.
351a33e799SAlexander V. Chernikov * Control is currently implemented via IP_FW3 setsockopt() code.
363b3a8eb9SGleb Smirnoff */
373b3a8eb9SGleb Smirnoff
383b3a8eb9SGleb Smirnoff #include "opt_ipfw.h"
393b3a8eb9SGleb Smirnoff #include "opt_inet.h"
403b3a8eb9SGleb Smirnoff #ifndef INET
413b3a8eb9SGleb Smirnoff #error IPFIREWALL requires INET.
423b3a8eb9SGleb Smirnoff #endif /* INET */
433b3a8eb9SGleb Smirnoff #include "opt_inet6.h"
443b3a8eb9SGleb Smirnoff
453b3a8eb9SGleb Smirnoff #include <sys/param.h>
463b3a8eb9SGleb Smirnoff #include <sys/systm.h>
473b3a8eb9SGleb Smirnoff #include <sys/malloc.h>
483b3a8eb9SGleb Smirnoff #include <sys/mbuf.h> /* struct m_tag used by nested headers */
493b3a8eb9SGleb Smirnoff #include <sys/kernel.h>
503b3a8eb9SGleb Smirnoff #include <sys/lock.h>
513b3a8eb9SGleb Smirnoff #include <sys/priv.h>
523b3a8eb9SGleb Smirnoff #include <sys/proc.h>
533b3a8eb9SGleb Smirnoff #include <sys/rwlock.h>
54ccba94b8SAlexander V. Chernikov #include <sys/rmlock.h>
553b3a8eb9SGleb Smirnoff #include <sys/socket.h>
563b3a8eb9SGleb Smirnoff #include <sys/socketvar.h>
573b3a8eb9SGleb Smirnoff #include <sys/sysctl.h>
583b3a8eb9SGleb Smirnoff #include <sys/syslog.h>
59b074b7bbSAlexander V. Chernikov #include <sys/fnv_hash.h>
603b3a8eb9SGleb Smirnoff #include <net/if.h>
613b3a8eb9SGleb Smirnoff #include <net/route.h>
623b3a8eb9SGleb Smirnoff #include <net/vnet.h>
63ce575f53SAlexander V. Chernikov #include <vm/vm.h>
64ce575f53SAlexander V. Chernikov #include <vm/vm_extern.h>
653b3a8eb9SGleb Smirnoff
663b3a8eb9SGleb Smirnoff #include <netinet/in.h>
673b3a8eb9SGleb Smirnoff #include <netinet/ip_var.h> /* hooks */
683b3a8eb9SGleb Smirnoff #include <netinet/ip_fw.h>
693b3a8eb9SGleb Smirnoff
703b3a8eb9SGleb Smirnoff #include <netpfil/ipfw/ip_fw_private.h>
71ea761a5dSAlexander V. Chernikov #include <netpfil/ipfw/ip_fw_table.h>
723b3a8eb9SGleb Smirnoff
733b3a8eb9SGleb Smirnoff #ifdef MAC
743b3a8eb9SGleb Smirnoff #include <security/mac/mac_framework.h>
753b3a8eb9SGleb Smirnoff #endif
763b3a8eb9SGleb Smirnoff
7791e721d7SAlexander V. Chernikov static int ipfw_ctl(struct sockopt *sopt);
787e767c79SAlexander V. Chernikov static int check_ipfw_rule_body(ipfw_insn *cmd, int cmd_len,
797e767c79SAlexander V. Chernikov struct rule_check_info *ci);
807e767c79SAlexander V. Chernikov static int check_ipfw_rule1(struct ip_fw_rule *rule, int size,
817e767c79SAlexander V. Chernikov struct rule_check_info *ci);
827e767c79SAlexander V. Chernikov static int check_ipfw_rule0(struct ip_fw_rule0 *rule, int size,
837e767c79SAlexander V. Chernikov struct rule_check_info *ci);
84f976a4edSAndrey V. Elsukov static int rewrite_rule_uidx(struct ip_fw_chain *chain,
85f976a4edSAndrey V. Elsukov struct rule_check_info *ci);
867e767c79SAlexander V. Chernikov
87b074b7bbSAlexander V. Chernikov #define NAMEDOBJ_HASH_SIZE 32
88b074b7bbSAlexander V. Chernikov
89b074b7bbSAlexander V. Chernikov struct namedobj_instance {
90b074b7bbSAlexander V. Chernikov struct namedobjects_head *names;
91b074b7bbSAlexander V. Chernikov struct namedobjects_head *values;
92b074b7bbSAlexander V. Chernikov uint32_t nn_size; /* names hash size */
93b074b7bbSAlexander V. Chernikov uint32_t nv_size; /* number hash size */
94b074b7bbSAlexander V. Chernikov u_long *idx_mask; /* used items bitmask */
95b074b7bbSAlexander V. Chernikov uint32_t max_blocks; /* number of "long" blocks in bitmask */
969f7d47b0SAlexander V. Chernikov uint32_t count; /* number of items */
97b074b7bbSAlexander V. Chernikov uint16_t free_off[IPFW_MAX_SETS]; /* first possible free offset */
9813263632SAlexander V. Chernikov objhash_hash_f *hash_f;
9913263632SAlexander V. Chernikov objhash_cmp_f *cmp_f;
100b074b7bbSAlexander V. Chernikov };
101b074b7bbSAlexander V. Chernikov #define BLOCK_ITEMS (8 * sizeof(u_long)) /* Number of items for ffsl() */
102b074b7bbSAlexander V. Chernikov
1032acdf79fSAndrey V. Elsukov static uint32_t objhash_hash_name(struct namedobj_instance *ni,
1042acdf79fSAndrey V. Elsukov const void *key, uint32_t kopt);
10513263632SAlexander V. Chernikov static uint32_t objhash_hash_idx(struct namedobj_instance *ni, uint32_t val);
1062acdf79fSAndrey V. Elsukov static int objhash_cmp_name(struct named_object *no, const void *name,
1072acdf79fSAndrey V. Elsukov uint32_t set);
108b074b7bbSAlexander V. Chernikov
1096b988f3aSAlexander V. Chernikov MALLOC_DEFINE(M_IPFW, "IpFw/IpAcct", "IpFw/IpAcct chain's");
1106b988f3aSAlexander V. Chernikov
1116b988f3aSAlexander V. Chernikov static int dump_config(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
1126b988f3aSAlexander V. Chernikov struct sockopt_data *sd);
1136b988f3aSAlexander V. Chernikov static int add_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
1146b988f3aSAlexander V. Chernikov struct sockopt_data *sd);
1156b988f3aSAlexander V. Chernikov static int del_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
1166b988f3aSAlexander V. Chernikov struct sockopt_data *sd);
1176b988f3aSAlexander V. Chernikov static int clear_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
1186b988f3aSAlexander V. Chernikov struct sockopt_data *sd);
1196b988f3aSAlexander V. Chernikov static int move_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
1206b988f3aSAlexander V. Chernikov struct sockopt_data *sd);
1216b988f3aSAlexander V. Chernikov static int manage_sets(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
1226b988f3aSAlexander V. Chernikov struct sockopt_data *sd);
123be8bc457SAlexander V. Chernikov static int dump_soptcodes(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
124be8bc457SAlexander V. Chernikov struct sockopt_data *sd);
1255dc5a0e0SAndrey V. Elsukov static int dump_srvobjects(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
1265dc5a0e0SAndrey V. Elsukov struct sockopt_data *sd);
1276b988f3aSAlexander V. Chernikov
1286b988f3aSAlexander V. Chernikov /* ctl3 handler data */
1296b988f3aSAlexander V. Chernikov struct mtx ctl3_lock;
1306b988f3aSAlexander V. Chernikov #define CTL3_LOCK_INIT() mtx_init(&ctl3_lock, "ctl3_lock", NULL, MTX_DEF)
1316b988f3aSAlexander V. Chernikov #define CTL3_LOCK_DESTROY() mtx_destroy(&ctl3_lock)
1326b988f3aSAlexander V. Chernikov #define CTL3_LOCK() mtx_lock(&ctl3_lock)
1336b988f3aSAlexander V. Chernikov #define CTL3_UNLOCK() mtx_unlock(&ctl3_lock)
1346b988f3aSAlexander V. Chernikov
1356b988f3aSAlexander V. Chernikov static struct ipfw_sopt_handler *ctl3_handlers;
1366b988f3aSAlexander V. Chernikov static size_t ctl3_hsize;
1376b988f3aSAlexander V. Chernikov static uint64_t ctl3_refct, ctl3_gencnt;
1386b988f3aSAlexander V. Chernikov #define CTL3_SMALLBUF 4096 /* small page-size write buffer */
1396b988f3aSAlexander V. Chernikov #define CTL3_LARGEBUF 16 * 1024 * 1024 /* handle large rulesets */
1406b988f3aSAlexander V. Chernikov
1412d99a349SAlexander V. Chernikov static int ipfw_flush_sopt_data(struct sockopt_data *sd);
142b074b7bbSAlexander V. Chernikov
1436b988f3aSAlexander V. Chernikov static struct ipfw_sopt_handler scodes[] = {
1446b988f3aSAlexander V. Chernikov { IP_FW_XGET, 0, HDIR_GET, dump_config },
1456b988f3aSAlexander V. Chernikov { IP_FW_XADD, 0, HDIR_BOTH, add_rules },
1466b988f3aSAlexander V. Chernikov { IP_FW_XDEL, 0, HDIR_BOTH, del_rules },
1476b988f3aSAlexander V. Chernikov { IP_FW_XZERO, 0, HDIR_SET, clear_rules },
1486b988f3aSAlexander V. Chernikov { IP_FW_XRESETLOG, 0, HDIR_SET, clear_rules },
1496b988f3aSAlexander V. Chernikov { IP_FW_XMOVE, 0, HDIR_SET, move_rules },
1506b988f3aSAlexander V. Chernikov { IP_FW_SET_SWAP, 0, HDIR_SET, manage_sets },
1516b988f3aSAlexander V. Chernikov { IP_FW_SET_MOVE, 0, HDIR_SET, manage_sets },
1526b988f3aSAlexander V. Chernikov { IP_FW_SET_ENABLE, 0, HDIR_SET, manage_sets },
153be8bc457SAlexander V. Chernikov { IP_FW_DUMP_SOPTCODES, 0, HDIR_GET, dump_soptcodes },
1545dc5a0e0SAndrey V. Elsukov { IP_FW_DUMP_SRVOBJECTS,0, HDIR_GET, dump_srvobjects },
1556b988f3aSAlexander V. Chernikov };
1563b3a8eb9SGleb Smirnoff
15774b22066SAlexander V. Chernikov static int
15874b22066SAlexander V. Chernikov set_legacy_obj_kidx(struct ip_fw_chain *ch, struct ip_fw_rule0 *rule);
159b2df1f7eSAndrey V. Elsukov static struct opcode_obj_rewrite *find_op_rw(ipfw_insn *cmd,
160b2df1f7eSAndrey V. Elsukov uint16_t *puidx, uint8_t *ptype);
161f976a4edSAndrey V. Elsukov static int ref_rule_objects(struct ip_fw_chain *ch, struct ip_fw *rule,
162f976a4edSAndrey V. Elsukov struct rule_check_info *ci, struct obj_idx *oib, struct tid_info *ti);
163f976a4edSAndrey V. Elsukov static int ref_opcode_object(struct ip_fw_chain *ch, ipfw_insn *cmd,
164b2df1f7eSAndrey V. Elsukov struct tid_info *ti, struct obj_idx *pidx, int *unresolved);
16574b22066SAlexander V. Chernikov static void unref_rule_objects(struct ip_fw_chain *chain, struct ip_fw *rule);
166f976a4edSAndrey V. Elsukov static void unref_oib_objects(struct ip_fw_chain *ch, ipfw_insn *cmd,
167f976a4edSAndrey V. Elsukov struct obj_idx *oib, struct obj_idx *end);
16874b22066SAlexander V. Chernikov static int export_objhash_ntlv(struct namedobj_instance *ni, uint16_t kidx,
16974b22066SAlexander V. Chernikov struct sockopt_data *sd);
17074b22066SAlexander V. Chernikov
17174b22066SAlexander V. Chernikov /*
17274b22066SAlexander V. Chernikov * Opcode object rewriter variables
17374b22066SAlexander V. Chernikov */
17474b22066SAlexander V. Chernikov struct opcode_obj_rewrite *ctl3_rewriters;
17574b22066SAlexander V. Chernikov static size_t ctl3_rsize;
17674b22066SAlexander V. Chernikov
1773b3a8eb9SGleb Smirnoff /*
1787e767c79SAlexander V. Chernikov * static variables followed by global ones
1793b3a8eb9SGleb Smirnoff */
1803b3a8eb9SGleb Smirnoff
1815f901c92SAndrew Turner VNET_DEFINE_STATIC(uma_zone_t, ipfw_cntr_zone);
1827e767c79SAlexander V. Chernikov #define V_ipfw_cntr_zone VNET(ipfw_cntr_zone)
1837e767c79SAlexander V. Chernikov
1847e767c79SAlexander V. Chernikov void
ipfw_init_counters(void)18562030bb8SDimitry Andric ipfw_init_counters(void)
1867e767c79SAlexander V. Chernikov {
1877e767c79SAlexander V. Chernikov
1887e767c79SAlexander V. Chernikov V_ipfw_cntr_zone = uma_zcreate("IPFW counters",
1890d90989bSAlexander V. Chernikov IPFW_RULE_CNTR_SIZE, NULL, NULL, NULL, NULL,
1907e767c79SAlexander V. Chernikov UMA_ALIGN_PTR, UMA_ZONE_PCPU);
1917e767c79SAlexander V. Chernikov }
1927e767c79SAlexander V. Chernikov
1937e767c79SAlexander V. Chernikov void
ipfw_destroy_counters(void)19462030bb8SDimitry Andric ipfw_destroy_counters(void)
1957e767c79SAlexander V. Chernikov {
1967e767c79SAlexander V. Chernikov
1977e767c79SAlexander V. Chernikov uma_zdestroy(V_ipfw_cntr_zone);
1987e767c79SAlexander V. Chernikov }
1997e767c79SAlexander V. Chernikov
2007e767c79SAlexander V. Chernikov struct ip_fw *
ipfw_alloc_rule(struct ip_fw_chain * chain,size_t rulesize)2017e767c79SAlexander V. Chernikov ipfw_alloc_rule(struct ip_fw_chain *chain, size_t rulesize)
2027e767c79SAlexander V. Chernikov {
2037e767c79SAlexander V. Chernikov struct ip_fw *rule;
2047e767c79SAlexander V. Chernikov
2057e767c79SAlexander V. Chernikov rule = malloc(rulesize, M_IPFW, M_WAITOK | M_ZERO);
2064e180881SMateusz Guzik rule->cntr = uma_zalloc_pcpu(V_ipfw_cntr_zone, M_WAITOK | M_ZERO);
207cefe3d67SAndrey V. Elsukov rule->refcnt = 1;
2087e767c79SAlexander V. Chernikov
2097e767c79SAlexander V. Chernikov return (rule);
2107e767c79SAlexander V. Chernikov }
2117e767c79SAlexander V. Chernikov
212cefe3d67SAndrey V. Elsukov void
ipfw_free_rule(struct ip_fw * rule)213cefe3d67SAndrey V. Elsukov ipfw_free_rule(struct ip_fw *rule)
2147e767c79SAlexander V. Chernikov {
2157e767c79SAlexander V. Chernikov
216cefe3d67SAndrey V. Elsukov /*
217cefe3d67SAndrey V. Elsukov * We don't release refcnt here, since this function
218cefe3d67SAndrey V. Elsukov * can be called without any locks held. The caller
219cefe3d67SAndrey V. Elsukov * must release reference under IPFW_UH_WLOCK, and then
220cefe3d67SAndrey V. Elsukov * call this function if refcount becomes 1.
221cefe3d67SAndrey V. Elsukov */
222cefe3d67SAndrey V. Elsukov if (rule->refcnt > 1)
223cefe3d67SAndrey V. Elsukov return;
2244e180881SMateusz Guzik uma_zfree_pcpu(V_ipfw_cntr_zone, rule->cntr);
2257e767c79SAlexander V. Chernikov free(rule, M_IPFW);
2267e767c79SAlexander V. Chernikov }
2277e767c79SAlexander V. Chernikov
2283b3a8eb9SGleb Smirnoff /*
2293b3a8eb9SGleb Smirnoff * Find the smallest rule >= key, id.
2303b3a8eb9SGleb Smirnoff * We could use bsearch but it is so simple that we code it directly
2313b3a8eb9SGleb Smirnoff */
2323b3a8eb9SGleb Smirnoff int
ipfw_find_rule(struct ip_fw_chain * chain,uint32_t key,uint32_t id)2333b3a8eb9SGleb Smirnoff ipfw_find_rule(struct ip_fw_chain *chain, uint32_t key, uint32_t id)
2343b3a8eb9SGleb Smirnoff {
2353b3a8eb9SGleb Smirnoff int i, lo, hi;
2363b3a8eb9SGleb Smirnoff struct ip_fw *r;
2373b3a8eb9SGleb Smirnoff
2383b3a8eb9SGleb Smirnoff for (lo = 0, hi = chain->n_rules - 1; lo < hi;) {
2393b3a8eb9SGleb Smirnoff i = (lo + hi) / 2;
2403b3a8eb9SGleb Smirnoff r = chain->map[i];
2413b3a8eb9SGleb Smirnoff if (r->rulenum < key)
2423b3a8eb9SGleb Smirnoff lo = i + 1; /* continue from the next one */
2433b3a8eb9SGleb Smirnoff else if (r->rulenum > key)
2443b3a8eb9SGleb Smirnoff hi = i; /* this might be good */
2453b3a8eb9SGleb Smirnoff else if (r->id < id)
2463b3a8eb9SGleb Smirnoff lo = i + 1; /* continue from the next one */
2473b3a8eb9SGleb Smirnoff else /* r->id >= id */
2483b3a8eb9SGleb Smirnoff hi = i; /* this might be good */
24974b8d63dSPedro F. Giffuni }
2503b3a8eb9SGleb Smirnoff return hi;
2513b3a8eb9SGleb Smirnoff }
2523b3a8eb9SGleb Smirnoff
2533b3a8eb9SGleb Smirnoff /*
254d5eb80cbSAlexander V. Chernikov * Builds skipto cache on rule set @map.
255d5eb80cbSAlexander V. Chernikov */
256d5eb80cbSAlexander V. Chernikov static void
update_skipto_cache(struct ip_fw_chain * chain,struct ip_fw ** map)257d5eb80cbSAlexander V. Chernikov update_skipto_cache(struct ip_fw_chain *chain, struct ip_fw **map)
258d5eb80cbSAlexander V. Chernikov {
259d5eb80cbSAlexander V. Chernikov int *smap, rulenum;
260d5eb80cbSAlexander V. Chernikov int i, mi;
261d5eb80cbSAlexander V. Chernikov
262d5eb80cbSAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(chain);
263d5eb80cbSAlexander V. Chernikov
264d5eb80cbSAlexander V. Chernikov mi = 0;
265d5eb80cbSAlexander V. Chernikov rulenum = map[mi]->rulenum;
266d5eb80cbSAlexander V. Chernikov smap = chain->idxmap_back;
267d5eb80cbSAlexander V. Chernikov
268d5eb80cbSAlexander V. Chernikov if (smap == NULL)
269d5eb80cbSAlexander V. Chernikov return;
270d5eb80cbSAlexander V. Chernikov
271d5eb80cbSAlexander V. Chernikov for (i = 0; i < 65536; i++) {
272d5eb80cbSAlexander V. Chernikov smap[i] = mi;
273d5eb80cbSAlexander V. Chernikov /* Use the same rule index until i < rulenum */
274d5eb80cbSAlexander V. Chernikov if (i != rulenum || i == 65535)
275d5eb80cbSAlexander V. Chernikov continue;
276d5eb80cbSAlexander V. Chernikov /* Find next rule with num > i */
277d5eb80cbSAlexander V. Chernikov rulenum = map[++mi]->rulenum;
278d5eb80cbSAlexander V. Chernikov while (rulenum == i)
279d5eb80cbSAlexander V. Chernikov rulenum = map[++mi]->rulenum;
280d5eb80cbSAlexander V. Chernikov }
281d5eb80cbSAlexander V. Chernikov }
282d5eb80cbSAlexander V. Chernikov
283d5eb80cbSAlexander V. Chernikov /*
284d5eb80cbSAlexander V. Chernikov * Swaps prepared (backup) index with current one.
285d5eb80cbSAlexander V. Chernikov */
286d5eb80cbSAlexander V. Chernikov static void
swap_skipto_cache(struct ip_fw_chain * chain)287d5eb80cbSAlexander V. Chernikov swap_skipto_cache(struct ip_fw_chain *chain)
288d5eb80cbSAlexander V. Chernikov {
289d5eb80cbSAlexander V. Chernikov int *map;
290d5eb80cbSAlexander V. Chernikov
291d5eb80cbSAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(chain);
292d5eb80cbSAlexander V. Chernikov IPFW_WLOCK_ASSERT(chain);
293d5eb80cbSAlexander V. Chernikov
294d5eb80cbSAlexander V. Chernikov map = chain->idxmap;
295d5eb80cbSAlexander V. Chernikov chain->idxmap = chain->idxmap_back;
296d5eb80cbSAlexander V. Chernikov chain->idxmap_back = map;
297d5eb80cbSAlexander V. Chernikov }
298d5eb80cbSAlexander V. Chernikov
299d5eb80cbSAlexander V. Chernikov /*
300d5eb80cbSAlexander V. Chernikov * Allocate and initialize skipto cache.
301d5eb80cbSAlexander V. Chernikov */
302d5eb80cbSAlexander V. Chernikov void
ipfw_init_skipto_cache(struct ip_fw_chain * chain)303d5eb80cbSAlexander V. Chernikov ipfw_init_skipto_cache(struct ip_fw_chain *chain)
304d5eb80cbSAlexander V. Chernikov {
305d5eb80cbSAlexander V. Chernikov int *idxmap, *idxmap_back;
306d5eb80cbSAlexander V. Chernikov
3070a2c13d3SAndrey V. Elsukov idxmap = malloc(65536 * sizeof(int), M_IPFW, M_WAITOK | M_ZERO);
3080a2c13d3SAndrey V. Elsukov idxmap_back = malloc(65536 * sizeof(int), M_IPFW, M_WAITOK);
309d5eb80cbSAlexander V. Chernikov
310d5eb80cbSAlexander V. Chernikov /*
311d5eb80cbSAlexander V. Chernikov * Note we may be called at any time after initialization,
312d5eb80cbSAlexander V. Chernikov * for example, on first skipto rule, so we need to
313d5eb80cbSAlexander V. Chernikov * provide valid chain->idxmap on return
314d5eb80cbSAlexander V. Chernikov */
315d5eb80cbSAlexander V. Chernikov
316d5eb80cbSAlexander V. Chernikov IPFW_UH_WLOCK(chain);
317d5eb80cbSAlexander V. Chernikov if (chain->idxmap != NULL) {
318d5eb80cbSAlexander V. Chernikov IPFW_UH_WUNLOCK(chain);
319d5eb80cbSAlexander V. Chernikov free(idxmap, M_IPFW);
320d5eb80cbSAlexander V. Chernikov free(idxmap_back, M_IPFW);
321d5eb80cbSAlexander V. Chernikov return;
322d5eb80cbSAlexander V. Chernikov }
323d5eb80cbSAlexander V. Chernikov
324d5eb80cbSAlexander V. Chernikov /* Set backup pointer first to permit building cache */
325d5eb80cbSAlexander V. Chernikov chain->idxmap_back = idxmap_back;
326d5eb80cbSAlexander V. Chernikov update_skipto_cache(chain, chain->map);
327d5eb80cbSAlexander V. Chernikov IPFW_WLOCK(chain);
328d5eb80cbSAlexander V. Chernikov /* It is now safe to set chain->idxmap ptr */
329d5eb80cbSAlexander V. Chernikov chain->idxmap = idxmap;
330d5eb80cbSAlexander V. Chernikov swap_skipto_cache(chain);
331d5eb80cbSAlexander V. Chernikov IPFW_WUNLOCK(chain);
332d5eb80cbSAlexander V. Chernikov IPFW_UH_WUNLOCK(chain);
333d5eb80cbSAlexander V. Chernikov }
334d5eb80cbSAlexander V. Chernikov
335d5eb80cbSAlexander V. Chernikov /*
336d5eb80cbSAlexander V. Chernikov * Destroys skipto cache.
337d5eb80cbSAlexander V. Chernikov */
338d5eb80cbSAlexander V. Chernikov void
ipfw_destroy_skipto_cache(struct ip_fw_chain * chain)339d5eb80cbSAlexander V. Chernikov ipfw_destroy_skipto_cache(struct ip_fw_chain *chain)
340d5eb80cbSAlexander V. Chernikov {
341d5eb80cbSAlexander V. Chernikov
342d5eb80cbSAlexander V. Chernikov if (chain->idxmap != NULL)
343d5eb80cbSAlexander V. Chernikov free(chain->idxmap, M_IPFW);
344d5eb80cbSAlexander V. Chernikov if (chain->idxmap != NULL)
345d5eb80cbSAlexander V. Chernikov free(chain->idxmap_back, M_IPFW);
346d5eb80cbSAlexander V. Chernikov }
347d5eb80cbSAlexander V. Chernikov
348d5eb80cbSAlexander V. Chernikov /*
3493b3a8eb9SGleb Smirnoff * allocate a new map, returns the chain locked. extra is the number
3503b3a8eb9SGleb Smirnoff * of entries to add or delete.
3513b3a8eb9SGleb Smirnoff */
3523b3a8eb9SGleb Smirnoff static struct ip_fw **
get_map(struct ip_fw_chain * chain,int extra,int locked)3533b3a8eb9SGleb Smirnoff get_map(struct ip_fw_chain *chain, int extra, int locked)
3543b3a8eb9SGleb Smirnoff {
3553b3a8eb9SGleb Smirnoff
3563b3a8eb9SGleb Smirnoff for (;;) {
3573b3a8eb9SGleb Smirnoff struct ip_fw **map;
358d821d364SPedro F. Giffuni u_int i, mflags;
359a73d728dSAlexander V. Chernikov
360a73d728dSAlexander V. Chernikov mflags = M_ZERO | ((locked != 0) ? M_NOWAIT : M_WAITOK);
3613b3a8eb9SGleb Smirnoff
3623b3a8eb9SGleb Smirnoff i = chain->n_rules + extra;
363a73d728dSAlexander V. Chernikov map = malloc(i * sizeof(struct ip_fw *), M_IPFW, mflags);
3643b3a8eb9SGleb Smirnoff if (map == NULL) {
3653b3a8eb9SGleb Smirnoff printf("%s: cannot allocate map\n", __FUNCTION__);
3663b3a8eb9SGleb Smirnoff return NULL;
3673b3a8eb9SGleb Smirnoff }
3683b3a8eb9SGleb Smirnoff if (!locked)
3693b3a8eb9SGleb Smirnoff IPFW_UH_WLOCK(chain);
3703b3a8eb9SGleb Smirnoff if (i >= chain->n_rules + extra) /* good */
3713b3a8eb9SGleb Smirnoff return map;
3723b3a8eb9SGleb Smirnoff /* otherwise we lost the race, free and retry */
3733b3a8eb9SGleb Smirnoff if (!locked)
3743b3a8eb9SGleb Smirnoff IPFW_UH_WUNLOCK(chain);
3753b3a8eb9SGleb Smirnoff free(map, M_IPFW);
3763b3a8eb9SGleb Smirnoff }
3773b3a8eb9SGleb Smirnoff }
3783b3a8eb9SGleb Smirnoff
3793b3a8eb9SGleb Smirnoff /*
3803b3a8eb9SGleb Smirnoff * swap the maps. It is supposed to be called with IPFW_UH_WLOCK
3813b3a8eb9SGleb Smirnoff */
3823b3a8eb9SGleb Smirnoff static struct ip_fw **
swap_map(struct ip_fw_chain * chain,struct ip_fw ** new_map,int new_len)3833b3a8eb9SGleb Smirnoff swap_map(struct ip_fw_chain *chain, struct ip_fw **new_map, int new_len)
3843b3a8eb9SGleb Smirnoff {
3853b3a8eb9SGleb Smirnoff struct ip_fw **old_map;
3863b3a8eb9SGleb Smirnoff
3873b3a8eb9SGleb Smirnoff IPFW_WLOCK(chain);
3883b3a8eb9SGleb Smirnoff chain->id++;
3893b3a8eb9SGleb Smirnoff chain->n_rules = new_len;
3903b3a8eb9SGleb Smirnoff old_map = chain->map;
3913b3a8eb9SGleb Smirnoff chain->map = new_map;
392d5eb80cbSAlexander V. Chernikov swap_skipto_cache(chain);
3933b3a8eb9SGleb Smirnoff IPFW_WUNLOCK(chain);
3943b3a8eb9SGleb Smirnoff return old_map;
3953b3a8eb9SGleb Smirnoff }
3963b3a8eb9SGleb Smirnoff
3977e767c79SAlexander V. Chernikov static void
export_cntr1_base(struct ip_fw * krule,struct ip_fw_bcounter * cntr)3987e767c79SAlexander V. Chernikov export_cntr1_base(struct ip_fw *krule, struct ip_fw_bcounter *cntr)
3997e767c79SAlexander V. Chernikov {
400584b675eSKonstantin Belousov struct timeval boottime;
4017e767c79SAlexander V. Chernikov
4027e767c79SAlexander V. Chernikov cntr->size = sizeof(*cntr);
4037e767c79SAlexander V. Chernikov
4047e767c79SAlexander V. Chernikov if (krule->cntr != NULL) {
4057e767c79SAlexander V. Chernikov cntr->pcnt = counter_u64_fetch(krule->cntr);
4067e767c79SAlexander V. Chernikov cntr->bcnt = counter_u64_fetch(krule->cntr + 1);
4077e767c79SAlexander V. Chernikov cntr->timestamp = krule->timestamp;
4087e767c79SAlexander V. Chernikov }
409584b675eSKonstantin Belousov if (cntr->timestamp > 0) {
410584b675eSKonstantin Belousov getboottime(&boottime);
4117e767c79SAlexander V. Chernikov cntr->timestamp += boottime.tv_sec;
4127e767c79SAlexander V. Chernikov }
413584b675eSKonstantin Belousov }
4147e767c79SAlexander V. Chernikov
4157e767c79SAlexander V. Chernikov static void
export_cntr0_base(struct ip_fw * krule,struct ip_fw_bcounter0 * cntr)4167e767c79SAlexander V. Chernikov export_cntr0_base(struct ip_fw *krule, struct ip_fw_bcounter0 *cntr)
4177e767c79SAlexander V. Chernikov {
418584b675eSKonstantin Belousov struct timeval boottime;
4197e767c79SAlexander V. Chernikov
4207e767c79SAlexander V. Chernikov if (krule->cntr != NULL) {
4217e767c79SAlexander V. Chernikov cntr->pcnt = counter_u64_fetch(krule->cntr);
4227e767c79SAlexander V. Chernikov cntr->bcnt = counter_u64_fetch(krule->cntr + 1);
4237e767c79SAlexander V. Chernikov cntr->timestamp = krule->timestamp;
4247e767c79SAlexander V. Chernikov }
425584b675eSKonstantin Belousov if (cntr->timestamp > 0) {
426584b675eSKonstantin Belousov getboottime(&boottime);
4277e767c79SAlexander V. Chernikov cntr->timestamp += boottime.tv_sec;
4287e767c79SAlexander V. Chernikov }
429584b675eSKonstantin Belousov }
4307e767c79SAlexander V. Chernikov
4313b3a8eb9SGleb Smirnoff /*
4321940fa77SAlexander V. Chernikov * Copies rule @urule from v1 userland format (current).
4337e767c79SAlexander V. Chernikov * to kernel @krule.
4347e767c79SAlexander V. Chernikov * Assume @krule is zeroed.
4356c2997ffSAlexander V. Chernikov */
4366c2997ffSAlexander V. Chernikov static void
import_rule1(struct rule_check_info * ci)4377e767c79SAlexander V. Chernikov import_rule1(struct rule_check_info *ci)
4386c2997ffSAlexander V. Chernikov {
4397e767c79SAlexander V. Chernikov struct ip_fw_rule *urule;
4407e767c79SAlexander V. Chernikov struct ip_fw *krule;
4416c2997ffSAlexander V. Chernikov
4427e767c79SAlexander V. Chernikov urule = (struct ip_fw_rule *)ci->urule;
4437e767c79SAlexander V. Chernikov krule = (struct ip_fw *)ci->krule;
4447e767c79SAlexander V. Chernikov
4457e767c79SAlexander V. Chernikov /* copy header */
4467e767c79SAlexander V. Chernikov krule->act_ofs = urule->act_ofs;
4477e767c79SAlexander V. Chernikov krule->cmd_len = urule->cmd_len;
4487e767c79SAlexander V. Chernikov krule->rulenum = urule->rulenum;
4497e767c79SAlexander V. Chernikov krule->set = urule->set;
4507e767c79SAlexander V. Chernikov krule->flags = urule->flags;
4517e767c79SAlexander V. Chernikov
4527e767c79SAlexander V. Chernikov /* Save rulenum offset */
4537e767c79SAlexander V. Chernikov ci->urule_numoff = offsetof(struct ip_fw_rule, rulenum);
4547e767c79SAlexander V. Chernikov
4557e767c79SAlexander V. Chernikov /* Copy opcodes */
4567e767c79SAlexander V. Chernikov memcpy(krule->cmd, urule->cmd, krule->cmd_len * sizeof(uint32_t));
4577e767c79SAlexander V. Chernikov }
4587e767c79SAlexander V. Chernikov
4597e767c79SAlexander V. Chernikov /*
4607e767c79SAlexander V. Chernikov * Export rule into v1 format (Current).
4617e767c79SAlexander V. Chernikov * Layout:
4627e767c79SAlexander V. Chernikov * [ ipfw_obj_tlv(IPFW_TLV_RULE_ENT)
4637e767c79SAlexander V. Chernikov * [ ip_fw_rule ] OR
4647e767c79SAlexander V. Chernikov * [ ip_fw_bcounter ip_fw_rule] (depends on rcntrs).
4657e767c79SAlexander V. Chernikov * ]
4667e767c79SAlexander V. Chernikov * Assume @data is zeroed.
4677e767c79SAlexander V. Chernikov */
4687e767c79SAlexander V. Chernikov static void
export_rule1(struct ip_fw * krule,caddr_t data,int len,int rcntrs)4697e767c79SAlexander V. Chernikov export_rule1(struct ip_fw *krule, caddr_t data, int len, int rcntrs)
4707e767c79SAlexander V. Chernikov {
4717e767c79SAlexander V. Chernikov struct ip_fw_bcounter *cntr;
4727e767c79SAlexander V. Chernikov struct ip_fw_rule *urule;
4737e767c79SAlexander V. Chernikov ipfw_obj_tlv *tlv;
4747e767c79SAlexander V. Chernikov
4757e767c79SAlexander V. Chernikov /* Fill in TLV header */
4767e767c79SAlexander V. Chernikov tlv = (ipfw_obj_tlv *)data;
4777e767c79SAlexander V. Chernikov tlv->type = IPFW_TLV_RULE_ENT;
4787e767c79SAlexander V. Chernikov tlv->length = len;
4797e767c79SAlexander V. Chernikov
4807e767c79SAlexander V. Chernikov if (rcntrs != 0) {
4817e767c79SAlexander V. Chernikov /* Copy counters */
4827e767c79SAlexander V. Chernikov cntr = (struct ip_fw_bcounter *)(tlv + 1);
4837e767c79SAlexander V. Chernikov urule = (struct ip_fw_rule *)(cntr + 1);
4847e767c79SAlexander V. Chernikov export_cntr1_base(krule, cntr);
4857e767c79SAlexander V. Chernikov } else
4867e767c79SAlexander V. Chernikov urule = (struct ip_fw_rule *)(tlv + 1);
4877e767c79SAlexander V. Chernikov
4887e767c79SAlexander V. Chernikov /* copy header */
4897e767c79SAlexander V. Chernikov urule->act_ofs = krule->act_ofs;
4907e767c79SAlexander V. Chernikov urule->cmd_len = krule->cmd_len;
4917e767c79SAlexander V. Chernikov urule->rulenum = krule->rulenum;
4927e767c79SAlexander V. Chernikov urule->set = krule->set;
4937e767c79SAlexander V. Chernikov urule->flags = krule->flags;
4947e767c79SAlexander V. Chernikov urule->id = krule->id;
4957e767c79SAlexander V. Chernikov
4967e767c79SAlexander V. Chernikov /* Copy opcodes */
4977e767c79SAlexander V. Chernikov memcpy(urule->cmd, krule->cmd, krule->cmd_len * sizeof(uint32_t));
4987e767c79SAlexander V. Chernikov }
4997e767c79SAlexander V. Chernikov
5007e767c79SAlexander V. Chernikov /*
5017e767c79SAlexander V. Chernikov * Copies rule @urule from FreeBSD8 userland format (v0)
5027e767c79SAlexander V. Chernikov * to kernel @krule.
5037e767c79SAlexander V. Chernikov * Assume @krule is zeroed.
5047e767c79SAlexander V. Chernikov */
5057e767c79SAlexander V. Chernikov static void
import_rule0(struct rule_check_info * ci)5067e767c79SAlexander V. Chernikov import_rule0(struct rule_check_info *ci)
5077e767c79SAlexander V. Chernikov {
5087e767c79SAlexander V. Chernikov struct ip_fw_rule0 *urule;
5097e767c79SAlexander V. Chernikov struct ip_fw *krule;
5102c452b20SAlexander V. Chernikov int cmdlen, l;
5112c452b20SAlexander V. Chernikov ipfw_insn *cmd;
5121940fa77SAlexander V. Chernikov ipfw_insn_limit *lcmd;
5132c452b20SAlexander V. Chernikov ipfw_insn_if *cmdif;
5147e767c79SAlexander V. Chernikov
5157e767c79SAlexander V. Chernikov urule = (struct ip_fw_rule0 *)ci->urule;
5167e767c79SAlexander V. Chernikov krule = (struct ip_fw *)ci->krule;
5177e767c79SAlexander V. Chernikov
5187e767c79SAlexander V. Chernikov /* copy header */
5197e767c79SAlexander V. Chernikov krule->act_ofs = urule->act_ofs;
5207e767c79SAlexander V. Chernikov krule->cmd_len = urule->cmd_len;
5217e767c79SAlexander V. Chernikov krule->rulenum = urule->rulenum;
5227e767c79SAlexander V. Chernikov krule->set = urule->set;
5237e767c79SAlexander V. Chernikov if ((urule->_pad & 1) != 0)
5247e767c79SAlexander V. Chernikov krule->flags |= IPFW_RULE_NOOPT;
5257e767c79SAlexander V. Chernikov
5267e767c79SAlexander V. Chernikov /* Save rulenum offset */
5277e767c79SAlexander V. Chernikov ci->urule_numoff = offsetof(struct ip_fw_rule0, rulenum);
5287e767c79SAlexander V. Chernikov
5297e767c79SAlexander V. Chernikov /* Copy opcodes */
5307e767c79SAlexander V. Chernikov memcpy(krule->cmd, urule->cmd, krule->cmd_len * sizeof(uint32_t));
5312c452b20SAlexander V. Chernikov
5322c452b20SAlexander V. Chernikov /*
5332c452b20SAlexander V. Chernikov * Alter opcodes:
534d6eb9b02SAndrey V. Elsukov * 1) convert tablearg value from 65535 to 0
535d6eb9b02SAndrey V. Elsukov * 2) Add high bit to O_SETFIB/O_SETDSCP values (to make room
536d6eb9b02SAndrey V. Elsukov * for targ).
5371940fa77SAlexander V. Chernikov * 3) convert table number in iface opcodes to u16
538d6eb9b02SAndrey V. Elsukov * 4) convert old `nat global` into new 65535
5392c452b20SAlexander V. Chernikov */
5401940fa77SAlexander V. Chernikov l = krule->cmd_len;
5411940fa77SAlexander V. Chernikov cmd = krule->cmd;
5422c452b20SAlexander V. Chernikov cmdlen = 0;
5432c452b20SAlexander V. Chernikov
5442c452b20SAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) {
5452c452b20SAlexander V. Chernikov cmdlen = F_LEN(cmd);
5462c452b20SAlexander V. Chernikov
5472c452b20SAlexander V. Chernikov switch (cmd->opcode) {
5481940fa77SAlexander V. Chernikov /* Opcodes supporting tablearg */
5491940fa77SAlexander V. Chernikov case O_TAG:
5501940fa77SAlexander V. Chernikov case O_TAGGED:
5511940fa77SAlexander V. Chernikov case O_PIPE:
5521940fa77SAlexander V. Chernikov case O_QUEUE:
5531940fa77SAlexander V. Chernikov case O_DIVERT:
5541940fa77SAlexander V. Chernikov case O_TEE:
5551940fa77SAlexander V. Chernikov case O_SKIPTO:
5561940fa77SAlexander V. Chernikov case O_CALLRETURN:
5571940fa77SAlexander V. Chernikov case O_NETGRAPH:
5581940fa77SAlexander V. Chernikov case O_NGTEE:
5591940fa77SAlexander V. Chernikov case O_NAT:
560d6eb9b02SAndrey V. Elsukov if (cmd->arg1 == IP_FW_TABLEARG)
5611940fa77SAlexander V. Chernikov cmd->arg1 = IP_FW_TARG;
562d6eb9b02SAndrey V. Elsukov else if (cmd->arg1 == 0)
563d6eb9b02SAndrey V. Elsukov cmd->arg1 = IP_FW_NAT44_GLOBAL;
5641940fa77SAlexander V. Chernikov break;
5651940fa77SAlexander V. Chernikov case O_SETFIB:
5661940fa77SAlexander V. Chernikov case O_SETDSCP:
567fc727ad6SBoris Lytochkin case O_SETMARK:
568fc727ad6SBoris Lytochkin case O_MARK:
569d6eb9b02SAndrey V. Elsukov if (cmd->arg1 == IP_FW_TABLEARG)
5701940fa77SAlexander V. Chernikov cmd->arg1 = IP_FW_TARG;
5711940fa77SAlexander V. Chernikov else
5721940fa77SAlexander V. Chernikov cmd->arg1 |= 0x8000;
5731940fa77SAlexander V. Chernikov break;
5741940fa77SAlexander V. Chernikov case O_LIMIT:
5751940fa77SAlexander V. Chernikov lcmd = (ipfw_insn_limit *)cmd;
576d6eb9b02SAndrey V. Elsukov if (lcmd->conn_limit == IP_FW_TABLEARG)
5771940fa77SAlexander V. Chernikov lcmd->conn_limit = IP_FW_TARG;
5781940fa77SAlexander V. Chernikov break;
5792c452b20SAlexander V. Chernikov /* Interface tables */
5802c452b20SAlexander V. Chernikov case O_XMIT:
5812c452b20SAlexander V. Chernikov case O_RECV:
5822c452b20SAlexander V. Chernikov case O_VIA:
5832c452b20SAlexander V. Chernikov /* Interface table, possibly */
5842c452b20SAlexander V. Chernikov cmdif = (ipfw_insn_if *)cmd;
5852c452b20SAlexander V. Chernikov if (cmdif->name[0] != '\1')
5862c452b20SAlexander V. Chernikov break;
5872c452b20SAlexander V. Chernikov
5881940fa77SAlexander V. Chernikov cmdif->p.kidx = (uint16_t)cmdif->p.glob;
5892c452b20SAlexander V. Chernikov break;
5902c452b20SAlexander V. Chernikov }
5912c452b20SAlexander V. Chernikov }
5927e767c79SAlexander V. Chernikov }
5937e767c79SAlexander V. Chernikov
5941940fa77SAlexander V. Chernikov /*
5951940fa77SAlexander V. Chernikov * Copies rule @krule from kernel to FreeBSD8 userland format (v0)
5961940fa77SAlexander V. Chernikov */
5977e767c79SAlexander V. Chernikov static void
export_rule0(struct ip_fw * krule,struct ip_fw_rule0 * urule,int len)5987e767c79SAlexander V. Chernikov export_rule0(struct ip_fw *krule, struct ip_fw_rule0 *urule, int len)
5997e767c79SAlexander V. Chernikov {
6002c452b20SAlexander V. Chernikov int cmdlen, l;
6012c452b20SAlexander V. Chernikov ipfw_insn *cmd;
6021940fa77SAlexander V. Chernikov ipfw_insn_limit *lcmd;
6032c452b20SAlexander V. Chernikov ipfw_insn_if *cmdif;
6042c452b20SAlexander V. Chernikov
6057e767c79SAlexander V. Chernikov /* copy header */
6067e767c79SAlexander V. Chernikov memset(urule, 0, len);
6077e767c79SAlexander V. Chernikov urule->act_ofs = krule->act_ofs;
6087e767c79SAlexander V. Chernikov urule->cmd_len = krule->cmd_len;
6097e767c79SAlexander V. Chernikov urule->rulenum = krule->rulenum;
6107e767c79SAlexander V. Chernikov urule->set = krule->set;
6117e767c79SAlexander V. Chernikov if ((krule->flags & IPFW_RULE_NOOPT) != 0)
6127e767c79SAlexander V. Chernikov urule->_pad |= 1;
6137e767c79SAlexander V. Chernikov
6147e767c79SAlexander V. Chernikov /* Copy opcodes */
6157e767c79SAlexander V. Chernikov memcpy(urule->cmd, krule->cmd, krule->cmd_len * sizeof(uint32_t));
6167e767c79SAlexander V. Chernikov
6177e767c79SAlexander V. Chernikov /* Export counters */
6187e767c79SAlexander V. Chernikov export_cntr0_base(krule, (struct ip_fw_bcounter0 *)&urule->pcnt);
6192c452b20SAlexander V. Chernikov
6202c452b20SAlexander V. Chernikov /*
6212c452b20SAlexander V. Chernikov * Alter opcodes:
622d6eb9b02SAndrey V. Elsukov * 1) convert tablearg value from 0 to 65535
6231940fa77SAlexander V. Chernikov * 2) Remove highest bit from O_SETFIB/O_SETDSCP values.
6241940fa77SAlexander V. Chernikov * 3) convert table number in iface opcodes to int
6252c452b20SAlexander V. Chernikov */
6262c452b20SAlexander V. Chernikov l = urule->cmd_len;
6272c452b20SAlexander V. Chernikov cmd = urule->cmd;
6282c452b20SAlexander V. Chernikov cmdlen = 0;
6292c452b20SAlexander V. Chernikov
6302c452b20SAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) {
6312c452b20SAlexander V. Chernikov cmdlen = F_LEN(cmd);
6322c452b20SAlexander V. Chernikov
6332c452b20SAlexander V. Chernikov switch (cmd->opcode) {
6341940fa77SAlexander V. Chernikov /* Opcodes supporting tablearg */
6351940fa77SAlexander V. Chernikov case O_TAG:
6361940fa77SAlexander V. Chernikov case O_TAGGED:
6371940fa77SAlexander V. Chernikov case O_PIPE:
6381940fa77SAlexander V. Chernikov case O_QUEUE:
6391940fa77SAlexander V. Chernikov case O_DIVERT:
6401940fa77SAlexander V. Chernikov case O_TEE:
6411940fa77SAlexander V. Chernikov case O_SKIPTO:
6421940fa77SAlexander V. Chernikov case O_CALLRETURN:
6431940fa77SAlexander V. Chernikov case O_NETGRAPH:
6441940fa77SAlexander V. Chernikov case O_NGTEE:
6451940fa77SAlexander V. Chernikov case O_NAT:
6461940fa77SAlexander V. Chernikov if (cmd->arg1 == IP_FW_TARG)
647d6eb9b02SAndrey V. Elsukov cmd->arg1 = IP_FW_TABLEARG;
648d6eb9b02SAndrey V. Elsukov else if (cmd->arg1 == IP_FW_NAT44_GLOBAL)
649d6eb9b02SAndrey V. Elsukov cmd->arg1 = 0;
6501940fa77SAlexander V. Chernikov break;
6511940fa77SAlexander V. Chernikov case O_SETFIB:
6521940fa77SAlexander V. Chernikov case O_SETDSCP:
653fc727ad6SBoris Lytochkin case O_SETMARK:
654fc727ad6SBoris Lytochkin case O_MARK:
6551940fa77SAlexander V. Chernikov if (cmd->arg1 == IP_FW_TARG)
656d6eb9b02SAndrey V. Elsukov cmd->arg1 = IP_FW_TABLEARG;
6571940fa77SAlexander V. Chernikov else
6581940fa77SAlexander V. Chernikov cmd->arg1 &= ~0x8000;
6591940fa77SAlexander V. Chernikov break;
6601940fa77SAlexander V. Chernikov case O_LIMIT:
6611940fa77SAlexander V. Chernikov lcmd = (ipfw_insn_limit *)cmd;
6621940fa77SAlexander V. Chernikov if (lcmd->conn_limit == IP_FW_TARG)
663d6eb9b02SAndrey V. Elsukov lcmd->conn_limit = IP_FW_TABLEARG;
6641940fa77SAlexander V. Chernikov break;
6652c452b20SAlexander V. Chernikov /* Interface tables */
6662c452b20SAlexander V. Chernikov case O_XMIT:
6672c452b20SAlexander V. Chernikov case O_RECV:
6682c452b20SAlexander V. Chernikov case O_VIA:
6692c452b20SAlexander V. Chernikov /* Interface table, possibly */
6702c452b20SAlexander V. Chernikov cmdif = (ipfw_insn_if *)cmd;
6712c452b20SAlexander V. Chernikov if (cmdif->name[0] != '\1')
6722c452b20SAlexander V. Chernikov break;
6732c452b20SAlexander V. Chernikov
6742c452b20SAlexander V. Chernikov cmdif->p.glob = cmdif->p.kidx;
6752c452b20SAlexander V. Chernikov break;
6762c452b20SAlexander V. Chernikov }
6772c452b20SAlexander V. Chernikov }
6786c2997ffSAlexander V. Chernikov }
6796c2997ffSAlexander V. Chernikov
6806c2997ffSAlexander V. Chernikov /*
6816c2997ffSAlexander V. Chernikov * Add new rule(s) to the list possibly creating rule number for each.
6823b3a8eb9SGleb Smirnoff * Update the rule_number in the input struct so the caller knows it as well.
6833b3a8eb9SGleb Smirnoff * Must be called without IPFW_UH held
6843b3a8eb9SGleb Smirnoff */
685b074b7bbSAlexander V. Chernikov static int
commit_rules(struct ip_fw_chain * chain,struct rule_check_info * rci,int count)6866c2997ffSAlexander V. Chernikov commit_rules(struct ip_fw_chain *chain, struct rule_check_info *rci, int count)
6873b3a8eb9SGleb Smirnoff {
6887e767c79SAlexander V. Chernikov int error, i, insert_before, tcount;
6897e767c79SAlexander V. Chernikov uint16_t rulenum, *pnum;
6906c2997ffSAlexander V. Chernikov struct rule_check_info *ci;
6917e767c79SAlexander V. Chernikov struct ip_fw *krule;
6923b3a8eb9SGleb Smirnoff struct ip_fw **map; /* the new array of pointers */
6933b3a8eb9SGleb Smirnoff
69474b22066SAlexander V. Chernikov /* Check if we need to do table/obj index remap */
6956c2997ffSAlexander V. Chernikov tcount = 0;
6966c2997ffSAlexander V. Chernikov for (ci = rci, i = 0; i < count; ci++, i++) {
69774b22066SAlexander V. Chernikov if (ci->object_opcodes == 0)
6986c2997ffSAlexander V. Chernikov continue;
6996c2997ffSAlexander V. Chernikov
7006c2997ffSAlexander V. Chernikov /*
70174b22066SAlexander V. Chernikov * Rule has some object opcodes.
70274b22066SAlexander V. Chernikov * We need to find (and create non-existing)
70374b22066SAlexander V. Chernikov * kernel objects, and reference existing ones.
7046c2997ffSAlexander V. Chernikov */
705f976a4edSAndrey V. Elsukov error = rewrite_rule_uidx(chain, ci);
7066c2997ffSAlexander V. Chernikov if (error != 0) {
7076c2997ffSAlexander V. Chernikov /*
7086c2997ffSAlexander V. Chernikov * rewrite failed, state for current rule
7096c2997ffSAlexander V. Chernikov * has been reverted. Check if we need to
7106c2997ffSAlexander V. Chernikov * revert more.
7116c2997ffSAlexander V. Chernikov */
7126c2997ffSAlexander V. Chernikov if (tcount > 0) {
7136c2997ffSAlexander V. Chernikov /*
7146c2997ffSAlexander V. Chernikov * We have some more table rules
7156c2997ffSAlexander V. Chernikov * we need to rollback.
7166c2997ffSAlexander V. Chernikov */
7176c2997ffSAlexander V. Chernikov
7186c2997ffSAlexander V. Chernikov IPFW_UH_WLOCK(chain);
7196c2997ffSAlexander V. Chernikov while (ci != rci) {
7206c2997ffSAlexander V. Chernikov ci--;
72174b22066SAlexander V. Chernikov if (ci->object_opcodes == 0)
7226c2997ffSAlexander V. Chernikov continue;
72374b22066SAlexander V. Chernikov unref_rule_objects(chain,ci->krule);
724b074b7bbSAlexander V. Chernikov }
7256c2997ffSAlexander V. Chernikov IPFW_UH_WUNLOCK(chain);
7266c2997ffSAlexander V. Chernikov }
7276c2997ffSAlexander V. Chernikov
7286c2997ffSAlexander V. Chernikov return (error);
7296c2997ffSAlexander V. Chernikov }
7306c2997ffSAlexander V. Chernikov
7316c2997ffSAlexander V. Chernikov tcount++;
732b074b7bbSAlexander V. Chernikov }
733b074b7bbSAlexander V. Chernikov
734b074b7bbSAlexander V. Chernikov /* get_map returns with IPFW_UH_WLOCK if successful */
7356c2997ffSAlexander V. Chernikov map = get_map(chain, count, 0 /* not locked */);
736b074b7bbSAlexander V. Chernikov if (map == NULL) {
7376c2997ffSAlexander V. Chernikov if (tcount > 0) {
7386c2997ffSAlexander V. Chernikov /* Unbind tables */
739b074b7bbSAlexander V. Chernikov IPFW_UH_WLOCK(chain);
7406c2997ffSAlexander V. Chernikov for (ci = rci, i = 0; i < count; ci++, i++) {
74174b22066SAlexander V. Chernikov if (ci->object_opcodes == 0)
7426c2997ffSAlexander V. Chernikov continue;
7436c2997ffSAlexander V. Chernikov
74474b22066SAlexander V. Chernikov unref_rule_objects(chain, ci->krule);
7456c2997ffSAlexander V. Chernikov }
746b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(chain);
747b074b7bbSAlexander V. Chernikov }
748b074b7bbSAlexander V. Chernikov
749b074b7bbSAlexander V. Chernikov return (ENOSPC);
750b074b7bbSAlexander V. Chernikov }
751b074b7bbSAlexander V. Chernikov
7523b3a8eb9SGleb Smirnoff if (V_autoinc_step < 1)
7533b3a8eb9SGleb Smirnoff V_autoinc_step = 1;
7543b3a8eb9SGleb Smirnoff else if (V_autoinc_step > 1000)
7553b3a8eb9SGleb Smirnoff V_autoinc_step = 1000;
7566c2997ffSAlexander V. Chernikov
7576c2997ffSAlexander V. Chernikov /* FIXME: Handle count > 1 */
7586c2997ffSAlexander V. Chernikov ci = rci;
7597e767c79SAlexander V. Chernikov krule = ci->krule;
7607e767c79SAlexander V. Chernikov rulenum = krule->rulenum;
7616c2997ffSAlexander V. Chernikov
7623b3a8eb9SGleb Smirnoff /* find the insertion point, we will insert before */
7637e767c79SAlexander V. Chernikov insert_before = rulenum ? rulenum + 1 : IPFW_DEFAULT_RULE;
7643b3a8eb9SGleb Smirnoff i = ipfw_find_rule(chain, insert_before, 0);
7653b3a8eb9SGleb Smirnoff /* duplicate first part */
7663b3a8eb9SGleb Smirnoff if (i > 0)
7673b3a8eb9SGleb Smirnoff bcopy(chain->map, map, i * sizeof(struct ip_fw *));
7687e767c79SAlexander V. Chernikov map[i] = krule;
7693b3a8eb9SGleb Smirnoff /* duplicate remaining part, we always have the default rule */
7703b3a8eb9SGleb Smirnoff bcopy(chain->map + i, map + i + 1,
7713b3a8eb9SGleb Smirnoff sizeof(struct ip_fw *) *(chain->n_rules - i));
7727e767c79SAlexander V. Chernikov if (rulenum == 0) {
7737e767c79SAlexander V. Chernikov /* Compute rule number and write it back */
7747e767c79SAlexander V. Chernikov rulenum = i > 0 ? map[i-1]->rulenum : 0;
7757e767c79SAlexander V. Chernikov if (rulenum < IPFW_DEFAULT_RULE - V_autoinc_step)
7767e767c79SAlexander V. Chernikov rulenum += V_autoinc_step;
7777e767c79SAlexander V. Chernikov krule->rulenum = rulenum;
7787e767c79SAlexander V. Chernikov /* Save number to userland rule */
7797e767c79SAlexander V. Chernikov pnum = (uint16_t *)((caddr_t)ci->urule + ci->urule_numoff);
7807e767c79SAlexander V. Chernikov *pnum = rulenum;
7813b3a8eb9SGleb Smirnoff }
7823b3a8eb9SGleb Smirnoff
7837e767c79SAlexander V. Chernikov krule->id = chain->id + 1;
784d5eb80cbSAlexander V. Chernikov update_skipto_cache(chain, map);
7853b3a8eb9SGleb Smirnoff map = swap_map(chain, map, chain->n_rules + 1);
7867e767c79SAlexander V. Chernikov chain->static_len += RULEUSIZE0(krule);
7873b3a8eb9SGleb Smirnoff IPFW_UH_WUNLOCK(chain);
7883b3a8eb9SGleb Smirnoff if (map)
7893b3a8eb9SGleb Smirnoff free(map, M_IPFW);
7903b3a8eb9SGleb Smirnoff return (0);
7913b3a8eb9SGleb Smirnoff }
7923b3a8eb9SGleb Smirnoff
7937143bb76SAndrey V. Elsukov int
ipfw_add_protected_rule(struct ip_fw_chain * chain,struct ip_fw * rule,int locked)7947143bb76SAndrey V. Elsukov ipfw_add_protected_rule(struct ip_fw_chain *chain, struct ip_fw *rule,
7957143bb76SAndrey V. Elsukov int locked)
7967143bb76SAndrey V. Elsukov {
7977143bb76SAndrey V. Elsukov struct ip_fw **map;
7987143bb76SAndrey V. Elsukov
7997143bb76SAndrey V. Elsukov map = get_map(chain, 1, locked);
8007143bb76SAndrey V. Elsukov if (map == NULL)
8017143bb76SAndrey V. Elsukov return (ENOMEM);
8027143bb76SAndrey V. Elsukov if (chain->n_rules > 0)
8037143bb76SAndrey V. Elsukov bcopy(chain->map, map,
8047143bb76SAndrey V. Elsukov chain->n_rules * sizeof(struct ip_fw *));
8057143bb76SAndrey V. Elsukov map[chain->n_rules] = rule;
8067143bb76SAndrey V. Elsukov rule->rulenum = IPFW_DEFAULT_RULE;
8077143bb76SAndrey V. Elsukov rule->set = RESVD_SET;
8087143bb76SAndrey V. Elsukov rule->id = chain->id + 1;
8097143bb76SAndrey V. Elsukov /* We add rule in the end of chain, no need to update skipto cache */
8107143bb76SAndrey V. Elsukov map = swap_map(chain, map, chain->n_rules + 1);
8117143bb76SAndrey V. Elsukov chain->static_len += RULEUSIZE0(rule);
8127143bb76SAndrey V. Elsukov IPFW_UH_WUNLOCK(chain);
8137143bb76SAndrey V. Elsukov free(map, M_IPFW);
8147143bb76SAndrey V. Elsukov return (0);
8157143bb76SAndrey V. Elsukov }
8167143bb76SAndrey V. Elsukov
8173b3a8eb9SGleb Smirnoff /*
818030b184fSAlexander V. Chernikov * Adds @rule to the list of rules to reap
819030b184fSAlexander V. Chernikov */
820030b184fSAlexander V. Chernikov void
ipfw_reap_add(struct ip_fw_chain * chain,struct ip_fw ** head,struct ip_fw * rule)821030b184fSAlexander V. Chernikov ipfw_reap_add(struct ip_fw_chain *chain, struct ip_fw **head,
822030b184fSAlexander V. Chernikov struct ip_fw *rule)
823030b184fSAlexander V. Chernikov {
824030b184fSAlexander V. Chernikov
825030b184fSAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(chain);
826030b184fSAlexander V. Chernikov
827030b184fSAlexander V. Chernikov /* Unlink rule from everywhere */
82874b22066SAlexander V. Chernikov unref_rule_objects(chain, rule);
829030b184fSAlexander V. Chernikov
830cefe3d67SAndrey V. Elsukov rule->next = *head;
831030b184fSAlexander V. Chernikov *head = rule;
832030b184fSAlexander V. Chernikov }
833030b184fSAlexander V. Chernikov
834030b184fSAlexander V. Chernikov /*
8353b3a8eb9SGleb Smirnoff * Reclaim storage associated with a list of rules. This is
8363b3a8eb9SGleb Smirnoff * typically the list created using remove_rule.
8373b3a8eb9SGleb Smirnoff * A NULL pointer on input is handled correctly.
8383b3a8eb9SGleb Smirnoff */
8393b3a8eb9SGleb Smirnoff void
ipfw_reap_rules(struct ip_fw * head)8403b3a8eb9SGleb Smirnoff ipfw_reap_rules(struct ip_fw *head)
8413b3a8eb9SGleb Smirnoff {
8423b3a8eb9SGleb Smirnoff struct ip_fw *rule;
8433b3a8eb9SGleb Smirnoff
8443b3a8eb9SGleb Smirnoff while ((rule = head) != NULL) {
845cefe3d67SAndrey V. Elsukov head = head->next;
846cefe3d67SAndrey V. Elsukov ipfw_free_rule(rule);
8473b3a8eb9SGleb Smirnoff }
8483b3a8eb9SGleb Smirnoff }
8493b3a8eb9SGleb Smirnoff
8503b3a8eb9SGleb Smirnoff /*
8513b3a8eb9SGleb Smirnoff * Rules to keep are
8523b3a8eb9SGleb Smirnoff * (default || reserved || !match_set || !match_number)
8533b3a8eb9SGleb Smirnoff * where
8543b3a8eb9SGleb Smirnoff * default ::= (rule->rulenum == IPFW_DEFAULT_RULE)
8553b3a8eb9SGleb Smirnoff * // the default rule is always protected
8563b3a8eb9SGleb Smirnoff *
8573b3a8eb9SGleb Smirnoff * reserved ::= (cmd == 0 && n == 0 && rule->set == RESVD_SET)
8583b3a8eb9SGleb Smirnoff * // RESVD_SET is protected only if cmd == 0 and n == 0 ("ipfw flush")
8593b3a8eb9SGleb Smirnoff *
8603b3a8eb9SGleb Smirnoff * match_set ::= (cmd == 0 || rule->set == set)
8613b3a8eb9SGleb Smirnoff * // set number is ignored for cmd == 0
8623b3a8eb9SGleb Smirnoff *
8633b3a8eb9SGleb Smirnoff * match_number ::= (cmd == 1 || n == 0 || n == rule->rulenum)
8643b3a8eb9SGleb Smirnoff * // number is ignored for cmd == 1 or n == 0
8653b3a8eb9SGleb Smirnoff *
8663b3a8eb9SGleb Smirnoff */
867a73d728dSAlexander V. Chernikov int
ipfw_match_range(struct ip_fw * rule,ipfw_range_tlv * rt)868a73d728dSAlexander V. Chernikov ipfw_match_range(struct ip_fw *rule, ipfw_range_tlv *rt)
8693b3a8eb9SGleb Smirnoff {
870a73d728dSAlexander V. Chernikov
8712930362fSAlexander V. Chernikov /* Don't match default rule for modification queries */
8722930362fSAlexander V. Chernikov if (rule->rulenum == IPFW_DEFAULT_RULE &&
8732930362fSAlexander V. Chernikov (rt->flags & IPFW_RCFLAG_DEFAULT) == 0)
874a73d728dSAlexander V. Chernikov return (0);
875a73d728dSAlexander V. Chernikov
876a73d728dSAlexander V. Chernikov /* Don't match rules in reserved set for flush requests */
877a73d728dSAlexander V. Chernikov if ((rt->flags & IPFW_RCFLAG_ALL) != 0 && rule->set == RESVD_SET)
878a73d728dSAlexander V. Chernikov return (0);
879a73d728dSAlexander V. Chernikov
880a73d728dSAlexander V. Chernikov /* If we're filtering by set, don't match other sets */
881a73d728dSAlexander V. Chernikov if ((rt->flags & IPFW_RCFLAG_SET) != 0 && rule->set != rt->set)
882a73d728dSAlexander V. Chernikov return (0);
883a73d728dSAlexander V. Chernikov
884a73d728dSAlexander V. Chernikov if ((rt->flags & IPFW_RCFLAG_RANGE) != 0 &&
885a73d728dSAlexander V. Chernikov (rule->rulenum < rt->start_rule || rule->rulenum > rt->end_rule))
886a73d728dSAlexander V. Chernikov return (0);
887a73d728dSAlexander V. Chernikov
888a73d728dSAlexander V. Chernikov return (1);
889a73d728dSAlexander V. Chernikov }
890a73d728dSAlexander V. Chernikov
8912685841bSAndrey V. Elsukov struct manage_sets_args {
8922685841bSAndrey V. Elsukov uint16_t set;
8932685841bSAndrey V. Elsukov uint8_t new_set;
8942685841bSAndrey V. Elsukov };
8952685841bSAndrey V. Elsukov
8962685841bSAndrey V. Elsukov static int
swap_sets_cb(struct namedobj_instance * ni,struct named_object * no,void * arg)8972685841bSAndrey V. Elsukov swap_sets_cb(struct namedobj_instance *ni, struct named_object *no,
8982685841bSAndrey V. Elsukov void *arg)
8992685841bSAndrey V. Elsukov {
9002685841bSAndrey V. Elsukov struct manage_sets_args *args;
9012685841bSAndrey V. Elsukov
9022685841bSAndrey V. Elsukov args = (struct manage_sets_args *)arg;
9032685841bSAndrey V. Elsukov if (no->set == (uint8_t)args->set)
9042685841bSAndrey V. Elsukov no->set = args->new_set;
9052685841bSAndrey V. Elsukov else if (no->set == args->new_set)
9062685841bSAndrey V. Elsukov no->set = (uint8_t)args->set;
9072685841bSAndrey V. Elsukov return (0);
9082685841bSAndrey V. Elsukov }
9092685841bSAndrey V. Elsukov
9102685841bSAndrey V. Elsukov static int
move_sets_cb(struct namedobj_instance * ni,struct named_object * no,void * arg)9112685841bSAndrey V. Elsukov move_sets_cb(struct namedobj_instance *ni, struct named_object *no,
9122685841bSAndrey V. Elsukov void *arg)
9132685841bSAndrey V. Elsukov {
9142685841bSAndrey V. Elsukov struct manage_sets_args *args;
9152685841bSAndrey V. Elsukov
9162685841bSAndrey V. Elsukov args = (struct manage_sets_args *)arg;
9172685841bSAndrey V. Elsukov if (no->set == (uint8_t)args->set)
9182685841bSAndrey V. Elsukov no->set = args->new_set;
9192685841bSAndrey V. Elsukov return (0);
9202685841bSAndrey V. Elsukov }
9212685841bSAndrey V. Elsukov
9222685841bSAndrey V. Elsukov static int
test_sets_cb(struct namedobj_instance * ni,struct named_object * no,void * arg)9232685841bSAndrey V. Elsukov test_sets_cb(struct namedobj_instance *ni, struct named_object *no,
9242685841bSAndrey V. Elsukov void *arg)
9252685841bSAndrey V. Elsukov {
9262685841bSAndrey V. Elsukov struct manage_sets_args *args;
9272685841bSAndrey V. Elsukov
9282685841bSAndrey V. Elsukov args = (struct manage_sets_args *)arg;
9292685841bSAndrey V. Elsukov if (no->set != (uint8_t)args->set)
9302685841bSAndrey V. Elsukov return (0);
9312685841bSAndrey V. Elsukov if (ipfw_objhash_lookup_name_type(ni, args->new_set,
9322685841bSAndrey V. Elsukov no->etlv, no->name) != NULL)
9332685841bSAndrey V. Elsukov return (EEXIST);
9342685841bSAndrey V. Elsukov return (0);
9352685841bSAndrey V. Elsukov }
9362685841bSAndrey V. Elsukov
9372685841bSAndrey V. Elsukov /*
9382685841bSAndrey V. Elsukov * Generic function to handler moving and swapping sets.
9392685841bSAndrey V. Elsukov */
9402685841bSAndrey V. Elsukov int
ipfw_obj_manage_sets(struct namedobj_instance * ni,uint16_t type,uint16_t set,uint8_t new_set,enum ipfw_sets_cmd cmd)9412685841bSAndrey V. Elsukov ipfw_obj_manage_sets(struct namedobj_instance *ni, uint16_t type,
9422685841bSAndrey V. Elsukov uint16_t set, uint8_t new_set, enum ipfw_sets_cmd cmd)
9432685841bSAndrey V. Elsukov {
9442685841bSAndrey V. Elsukov struct manage_sets_args args;
9452685841bSAndrey V. Elsukov struct named_object *no;
9462685841bSAndrey V. Elsukov
9472685841bSAndrey V. Elsukov args.set = set;
9482685841bSAndrey V. Elsukov args.new_set = new_set;
9492685841bSAndrey V. Elsukov switch (cmd) {
9502685841bSAndrey V. Elsukov case SWAP_ALL:
9512685841bSAndrey V. Elsukov return (ipfw_objhash_foreach_type(ni, swap_sets_cb,
9522685841bSAndrey V. Elsukov &args, type));
9532685841bSAndrey V. Elsukov case TEST_ALL:
9542685841bSAndrey V. Elsukov return (ipfw_objhash_foreach_type(ni, test_sets_cb,
9552685841bSAndrey V. Elsukov &args, type));
9562685841bSAndrey V. Elsukov case MOVE_ALL:
9572685841bSAndrey V. Elsukov return (ipfw_objhash_foreach_type(ni, move_sets_cb,
9582685841bSAndrey V. Elsukov &args, type));
9592685841bSAndrey V. Elsukov case COUNT_ONE:
9602685841bSAndrey V. Elsukov /*
9612685841bSAndrey V. Elsukov * @set used to pass kidx.
9622685841bSAndrey V. Elsukov * When @new_set is zero - reset object counter,
9632685841bSAndrey V. Elsukov * otherwise increment it.
9642685841bSAndrey V. Elsukov */
9652685841bSAndrey V. Elsukov no = ipfw_objhash_lookup_kidx(ni, set);
9662685841bSAndrey V. Elsukov if (new_set != 0)
9672685841bSAndrey V. Elsukov no->ocnt++;
9682685841bSAndrey V. Elsukov else
9692685841bSAndrey V. Elsukov no->ocnt = 0;
9702685841bSAndrey V. Elsukov return (0);
9712685841bSAndrey V. Elsukov case TEST_ONE:
9722685841bSAndrey V. Elsukov /* @set used to pass kidx */
9732685841bSAndrey V. Elsukov no = ipfw_objhash_lookup_kidx(ni, set);
9742685841bSAndrey V. Elsukov /*
9752685841bSAndrey V. Elsukov * First check number of references:
9762685841bSAndrey V. Elsukov * when it differs, this mean other rules are holding
9772685841bSAndrey V. Elsukov * reference to given object, so it is not possible to
9782685841bSAndrey V. Elsukov * change its set. Note that refcnt may account references
9792685841bSAndrey V. Elsukov * to some going-to-be-added rules. Since we don't know
9802685841bSAndrey V. Elsukov * their numbers (and even if they will be added) it is
9812685841bSAndrey V. Elsukov * perfectly OK to return error here.
9822685841bSAndrey V. Elsukov */
9832685841bSAndrey V. Elsukov if (no->ocnt != no->refcnt)
9842685841bSAndrey V. Elsukov return (EBUSY);
9852685841bSAndrey V. Elsukov if (ipfw_objhash_lookup_name_type(ni, new_set, type,
9862685841bSAndrey V. Elsukov no->name) != NULL)
9872685841bSAndrey V. Elsukov return (EEXIST);
9882685841bSAndrey V. Elsukov return (0);
9892685841bSAndrey V. Elsukov case MOVE_ONE:
9902685841bSAndrey V. Elsukov /* @set used to pass kidx */
9912685841bSAndrey V. Elsukov no = ipfw_objhash_lookup_kidx(ni, set);
9922685841bSAndrey V. Elsukov no->set = new_set;
9932685841bSAndrey V. Elsukov return (0);
9942685841bSAndrey V. Elsukov }
9952685841bSAndrey V. Elsukov return (EINVAL);
9962685841bSAndrey V. Elsukov }
9972685841bSAndrey V. Elsukov
998a73d728dSAlexander V. Chernikov /*
999a73d728dSAlexander V. Chernikov * Delete rules matching range @rt.
1000a73d728dSAlexander V. Chernikov * Saves number of deleted rules in @ndel.
1001a73d728dSAlexander V. Chernikov *
1002a73d728dSAlexander V. Chernikov * Returns 0 on success.
1003a73d728dSAlexander V. Chernikov */
1004a73d728dSAlexander V. Chernikov static int
delete_range(struct ip_fw_chain * chain,ipfw_range_tlv * rt,int * ndel)1005a73d728dSAlexander V. Chernikov delete_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int *ndel)
1006a73d728dSAlexander V. Chernikov {
1007a73d728dSAlexander V. Chernikov struct ip_fw *reap, *rule, **map;
1008a73d728dSAlexander V. Chernikov int end, start;
1009a73d728dSAlexander V. Chernikov int i, n, ndyn, ofs;
1010a73d728dSAlexander V. Chernikov
1011a73d728dSAlexander V. Chernikov reap = NULL;
1012a73d728dSAlexander V. Chernikov IPFW_UH_WLOCK(chain); /* arbitrate writers */
1013a73d728dSAlexander V. Chernikov
1014a73d728dSAlexander V. Chernikov /*
1015a73d728dSAlexander V. Chernikov * Stage 1: Determine range to inspect.
1016a73d728dSAlexander V. Chernikov * Range is half-inclusive, e.g [start, end).
1017a73d728dSAlexander V. Chernikov */
1018a73d728dSAlexander V. Chernikov start = 0;
1019a73d728dSAlexander V. Chernikov end = chain->n_rules - 1;
1020a73d728dSAlexander V. Chernikov
1021a73d728dSAlexander V. Chernikov if ((rt->flags & IPFW_RCFLAG_RANGE) != 0) {
1022a73d728dSAlexander V. Chernikov start = ipfw_find_rule(chain, rt->start_rule, 0);
1023a73d728dSAlexander V. Chernikov
1024288bf455SAndrey V. Elsukov if (rt->end_rule >= IPFW_DEFAULT_RULE)
1025288bf455SAndrey V. Elsukov rt->end_rule = IPFW_DEFAULT_RULE - 1;
1026288bf455SAndrey V. Elsukov end = ipfw_find_rule(chain, rt->end_rule, UINT32_MAX);
1027a73d728dSAlexander V. Chernikov }
1028a73d728dSAlexander V. Chernikov
1029d66f9c86SAndrey V. Elsukov if (rt->flags & IPFW_RCFLAG_DYNAMIC) {
1030d66f9c86SAndrey V. Elsukov /*
1031d66f9c86SAndrey V. Elsukov * Requested deleting only for dynamic states.
1032d66f9c86SAndrey V. Elsukov */
1033d66f9c86SAndrey V. Elsukov *ndel = 0;
1034d66f9c86SAndrey V. Elsukov ipfw_expire_dyn_states(chain, rt);
1035d66f9c86SAndrey V. Elsukov IPFW_UH_WUNLOCK(chain);
1036d66f9c86SAndrey V. Elsukov return (0);
1037d66f9c86SAndrey V. Elsukov }
1038d66f9c86SAndrey V. Elsukov
1039a73d728dSAlexander V. Chernikov /* Allocate new map of the same size */
1040a73d728dSAlexander V. Chernikov map = get_map(chain, 0, 1 /* locked */);
1041a73d728dSAlexander V. Chernikov if (map == NULL) {
1042a73d728dSAlexander V. Chernikov IPFW_UH_WUNLOCK(chain);
1043a73d728dSAlexander V. Chernikov return (ENOMEM);
1044a73d728dSAlexander V. Chernikov }
1045a73d728dSAlexander V. Chernikov
1046a73d728dSAlexander V. Chernikov n = 0;
1047a73d728dSAlexander V. Chernikov ndyn = 0;
1048a73d728dSAlexander V. Chernikov ofs = start;
1049a73d728dSAlexander V. Chernikov /* 1. bcopy the initial part of the map */
1050a73d728dSAlexander V. Chernikov if (start > 0)
1051a73d728dSAlexander V. Chernikov bcopy(chain->map, map, start * sizeof(struct ip_fw *));
1052a73d728dSAlexander V. Chernikov /* 2. copy active rules between start and end */
1053a73d728dSAlexander V. Chernikov for (i = start; i < end; i++) {
1054a73d728dSAlexander V. Chernikov rule = chain->map[i];
1055a73d728dSAlexander V. Chernikov if (ipfw_match_range(rule, rt) == 0) {
1056a73d728dSAlexander V. Chernikov map[ofs++] = rule;
1057a73d728dSAlexander V. Chernikov continue;
1058a73d728dSAlexander V. Chernikov }
1059a73d728dSAlexander V. Chernikov
1060a73d728dSAlexander V. Chernikov n++;
1061a73d728dSAlexander V. Chernikov if (ipfw_is_dyn_rule(rule) != 0)
1062a73d728dSAlexander V. Chernikov ndyn++;
1063a73d728dSAlexander V. Chernikov }
1064a73d728dSAlexander V. Chernikov /* 3. copy the final part of the map */
1065a73d728dSAlexander V. Chernikov bcopy(chain->map + end, map + ofs,
1066a73d728dSAlexander V. Chernikov (chain->n_rules - end) * sizeof(struct ip_fw *));
1067a73d728dSAlexander V. Chernikov /* 4. recalculate skipto cache */
1068a73d728dSAlexander V. Chernikov update_skipto_cache(chain, map);
1069a73d728dSAlexander V. Chernikov /* 5. swap the maps (under UH_WLOCK + WHLOCK) */
1070a73d728dSAlexander V. Chernikov map = swap_map(chain, map, chain->n_rules - n);
1071a73d728dSAlexander V. Chernikov /* 6. Remove all dynamic states originated by deleted rules */
1072a73d728dSAlexander V. Chernikov if (ndyn > 0)
1073b99a6823SAndrey V. Elsukov ipfw_expire_dyn_states(chain, rt);
1074a73d728dSAlexander V. Chernikov /* 7. now remove the rules deleted from the old map */
1075a73d728dSAlexander V. Chernikov for (i = start; i < end; i++) {
1076a73d728dSAlexander V. Chernikov rule = map[i];
1077a73d728dSAlexander V. Chernikov if (ipfw_match_range(rule, rt) == 0)
1078a73d728dSAlexander V. Chernikov continue;
1079a73d728dSAlexander V. Chernikov chain->static_len -= RULEUSIZE0(rule);
1080030b184fSAlexander V. Chernikov ipfw_reap_add(chain, &reap, rule);
1081a73d728dSAlexander V. Chernikov }
1082a73d728dSAlexander V. Chernikov IPFW_UH_WUNLOCK(chain);
1083030b184fSAlexander V. Chernikov
1084a73d728dSAlexander V. Chernikov ipfw_reap_rules(reap);
1085a73d728dSAlexander V. Chernikov if (map != NULL)
1086a73d728dSAlexander V. Chernikov free(map, M_IPFW);
1087a73d728dSAlexander V. Chernikov *ndel = n;
1088a73d728dSAlexander V. Chernikov return (0);
1089a73d728dSAlexander V. Chernikov }
1090a73d728dSAlexander V. Chernikov
10912685841bSAndrey V. Elsukov static int
move_objects(struct ip_fw_chain * ch,ipfw_range_tlv * rt)10922685841bSAndrey V. Elsukov move_objects(struct ip_fw_chain *ch, ipfw_range_tlv *rt)
10932685841bSAndrey V. Elsukov {
10942685841bSAndrey V. Elsukov struct opcode_obj_rewrite *rw;
10952685841bSAndrey V. Elsukov struct ip_fw *rule;
10962685841bSAndrey V. Elsukov ipfw_insn *cmd;
10972685841bSAndrey V. Elsukov int cmdlen, i, l, c;
10982685841bSAndrey V. Elsukov uint16_t kidx;
10992685841bSAndrey V. Elsukov
11002685841bSAndrey V. Elsukov IPFW_UH_WLOCK_ASSERT(ch);
11012685841bSAndrey V. Elsukov
11022685841bSAndrey V. Elsukov /* Stage 1: count number of references by given rules */
11032685841bSAndrey V. Elsukov for (c = 0, i = 0; i < ch->n_rules - 1; i++) {
11042685841bSAndrey V. Elsukov rule = ch->map[i];
11052685841bSAndrey V. Elsukov if (ipfw_match_range(rule, rt) == 0)
11062685841bSAndrey V. Elsukov continue;
11072685841bSAndrey V. Elsukov if (rule->set == rt->new_set) /* nothing to do */
11082685841bSAndrey V. Elsukov continue;
11092685841bSAndrey V. Elsukov /* Search opcodes with named objects */
11102685841bSAndrey V. Elsukov for (l = rule->cmd_len, cmdlen = 0, cmd = rule->cmd;
11112685841bSAndrey V. Elsukov l > 0; l -= cmdlen, cmd += cmdlen) {
11122685841bSAndrey V. Elsukov cmdlen = F_LEN(cmd);
11132685841bSAndrey V. Elsukov rw = find_op_rw(cmd, &kidx, NULL);
11142685841bSAndrey V. Elsukov if (rw == NULL || rw->manage_sets == NULL)
11152685841bSAndrey V. Elsukov continue;
1116a73d728dSAlexander V. Chernikov /*
11172685841bSAndrey V. Elsukov * When manage_sets() returns non-zero value to
11182685841bSAndrey V. Elsukov * COUNT_ONE command, consider this as an object
11192685841bSAndrey V. Elsukov * doesn't support sets (e.g. disabled with sysctl).
11202685841bSAndrey V. Elsukov * So, skip checks for this object.
11212685841bSAndrey V. Elsukov */
11222685841bSAndrey V. Elsukov if (rw->manage_sets(ch, kidx, 1, COUNT_ONE) != 0)
11232685841bSAndrey V. Elsukov continue;
11242685841bSAndrey V. Elsukov c++;
11252685841bSAndrey V. Elsukov }
11262685841bSAndrey V. Elsukov }
11272685841bSAndrey V. Elsukov if (c == 0) /* No objects found */
11282685841bSAndrey V. Elsukov return (0);
11292685841bSAndrey V. Elsukov /* Stage 2: verify "ownership" */
11302685841bSAndrey V. Elsukov for (c = 0, i = 0; (i < ch->n_rules - 1) && c == 0; i++) {
11312685841bSAndrey V. Elsukov rule = ch->map[i];
11322685841bSAndrey V. Elsukov if (ipfw_match_range(rule, rt) == 0)
11332685841bSAndrey V. Elsukov continue;
11342685841bSAndrey V. Elsukov if (rule->set == rt->new_set) /* nothing to do */
11352685841bSAndrey V. Elsukov continue;
11362685841bSAndrey V. Elsukov /* Search opcodes with named objects */
11372685841bSAndrey V. Elsukov for (l = rule->cmd_len, cmdlen = 0, cmd = rule->cmd;
11382685841bSAndrey V. Elsukov l > 0 && c == 0; l -= cmdlen, cmd += cmdlen) {
11392685841bSAndrey V. Elsukov cmdlen = F_LEN(cmd);
11402685841bSAndrey V. Elsukov rw = find_op_rw(cmd, &kidx, NULL);
11412685841bSAndrey V. Elsukov if (rw == NULL || rw->manage_sets == NULL)
11422685841bSAndrey V. Elsukov continue;
11432685841bSAndrey V. Elsukov /* Test for ownership and conflicting names */
11442685841bSAndrey V. Elsukov c = rw->manage_sets(ch, kidx,
11452685841bSAndrey V. Elsukov (uint8_t)rt->new_set, TEST_ONE);
11462685841bSAndrey V. Elsukov }
11472685841bSAndrey V. Elsukov }
11482685841bSAndrey V. Elsukov /* Stage 3: change set and cleanup */
11492685841bSAndrey V. Elsukov for (i = 0; i < ch->n_rules - 1; i++) {
11502685841bSAndrey V. Elsukov rule = ch->map[i];
11512685841bSAndrey V. Elsukov if (ipfw_match_range(rule, rt) == 0)
11522685841bSAndrey V. Elsukov continue;
11532685841bSAndrey V. Elsukov if (rule->set == rt->new_set) /* nothing to do */
11542685841bSAndrey V. Elsukov continue;
11552685841bSAndrey V. Elsukov /* Search opcodes with named objects */
11562685841bSAndrey V. Elsukov for (l = rule->cmd_len, cmdlen = 0, cmd = rule->cmd;
11572685841bSAndrey V. Elsukov l > 0; l -= cmdlen, cmd += cmdlen) {
11582685841bSAndrey V. Elsukov cmdlen = F_LEN(cmd);
11592685841bSAndrey V. Elsukov rw = find_op_rw(cmd, &kidx, NULL);
11602685841bSAndrey V. Elsukov if (rw == NULL || rw->manage_sets == NULL)
11612685841bSAndrey V. Elsukov continue;
11622685841bSAndrey V. Elsukov /* cleanup object counter */
11632685841bSAndrey V. Elsukov rw->manage_sets(ch, kidx,
11642685841bSAndrey V. Elsukov 0 /* reset counter */, COUNT_ONE);
11652685841bSAndrey V. Elsukov if (c != 0)
11662685841bSAndrey V. Elsukov continue;
11672685841bSAndrey V. Elsukov /* change set */
11682685841bSAndrey V. Elsukov rw->manage_sets(ch, kidx,
11692685841bSAndrey V. Elsukov (uint8_t)rt->new_set, MOVE_ONE);
11702685841bSAndrey V. Elsukov }
11712685841bSAndrey V. Elsukov }
11722685841bSAndrey V. Elsukov return (c);
1173978f2d17SAndrey V. Elsukov }
1174978f2d17SAndrey V. Elsukov
1175978f2d17SAndrey V. Elsukov /*
1176a73d728dSAlexander V. Chernikov * Changes set of given rule rannge @rt
1177a73d728dSAlexander V. Chernikov * with each other.
1178a73d728dSAlexander V. Chernikov *
1179a73d728dSAlexander V. Chernikov * Returns 0 on success.
1180a73d728dSAlexander V. Chernikov */
1181a73d728dSAlexander V. Chernikov static int
move_range(struct ip_fw_chain * chain,ipfw_range_tlv * rt)1182a73d728dSAlexander V. Chernikov move_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt)
1183a73d728dSAlexander V. Chernikov {
1184a73d728dSAlexander V. Chernikov struct ip_fw *rule;
1185a73d728dSAlexander V. Chernikov int i;
1186a73d728dSAlexander V. Chernikov
1187a73d728dSAlexander V. Chernikov IPFW_UH_WLOCK(chain);
1188a73d728dSAlexander V. Chernikov
1189a73d728dSAlexander V. Chernikov /*
1190a73d728dSAlexander V. Chernikov * Move rules with matching paramenerts to a new set.
1191a73d728dSAlexander V. Chernikov * This one is much more complex. We have to ensure
1192a73d728dSAlexander V. Chernikov * that all referenced tables (if any) are referenced
1193a73d728dSAlexander V. Chernikov * by given rule subset only. Otherwise, we can't move
1194a73d728dSAlexander V. Chernikov * them to new set and have to return error.
1195a73d728dSAlexander V. Chernikov */
11962685841bSAndrey V. Elsukov if ((i = move_objects(chain, rt)) != 0) {
1197a73d728dSAlexander V. Chernikov IPFW_UH_WUNLOCK(chain);
11982685841bSAndrey V. Elsukov return (i);
1199a73d728dSAlexander V. Chernikov }
1200a73d728dSAlexander V. Chernikov
1201a73d728dSAlexander V. Chernikov /* XXX: We have to do swap holding WLOCK */
12022930362fSAlexander V. Chernikov for (i = 0; i < chain->n_rules; i++) {
1203a73d728dSAlexander V. Chernikov rule = chain->map[i];
1204a73d728dSAlexander V. Chernikov if (ipfw_match_range(rule, rt) == 0)
1205a73d728dSAlexander V. Chernikov continue;
1206a73d728dSAlexander V. Chernikov rule->set = rt->new_set;
1207a73d728dSAlexander V. Chernikov }
1208a73d728dSAlexander V. Chernikov
1209a73d728dSAlexander V. Chernikov IPFW_UH_WUNLOCK(chain);
1210a73d728dSAlexander V. Chernikov
1211a73d728dSAlexander V. Chernikov return (0);
1212a73d728dSAlexander V. Chernikov }
1213a73d728dSAlexander V. Chernikov
1214a73d728dSAlexander V. Chernikov /*
1215e758846cSAndrey V. Elsukov * Returns pointer to action instruction, skips all possible rule
1216e758846cSAndrey V. Elsukov * modifiers like O_LOG, O_TAG, O_ALTQ.
1217e758846cSAndrey V. Elsukov */
1218e758846cSAndrey V. Elsukov ipfw_insn *
ipfw_get_action(struct ip_fw * rule)1219e758846cSAndrey V. Elsukov ipfw_get_action(struct ip_fw *rule)
1220e758846cSAndrey V. Elsukov {
1221e758846cSAndrey V. Elsukov ipfw_insn *cmd;
1222e758846cSAndrey V. Elsukov int l, cmdlen;
1223e758846cSAndrey V. Elsukov
1224e758846cSAndrey V. Elsukov cmd = ACTION_PTR(rule);
1225e758846cSAndrey V. Elsukov l = rule->cmd_len - rule->act_ofs;
1226e758846cSAndrey V. Elsukov while (l > 0) {
1227e758846cSAndrey V. Elsukov switch (cmd->opcode) {
1228e758846cSAndrey V. Elsukov case O_ALTQ:
1229e758846cSAndrey V. Elsukov case O_LOG:
1230e758846cSAndrey V. Elsukov case O_TAG:
1231e758846cSAndrey V. Elsukov break;
1232e758846cSAndrey V. Elsukov default:
1233e758846cSAndrey V. Elsukov return (cmd);
1234e758846cSAndrey V. Elsukov }
1235e758846cSAndrey V. Elsukov cmdlen = F_LEN(cmd);
1236e758846cSAndrey V. Elsukov l -= cmdlen;
1237e758846cSAndrey V. Elsukov cmd += cmdlen;
1238e758846cSAndrey V. Elsukov }
1239e758846cSAndrey V. Elsukov panic("%s: rule (%p) has not action opcode", __func__, rule);
1240e758846cSAndrey V. Elsukov return (NULL);
1241e758846cSAndrey V. Elsukov }
1242e758846cSAndrey V. Elsukov
1243e758846cSAndrey V. Elsukov /*
1244a73d728dSAlexander V. Chernikov * Clear counters for a specific rule.
1245a73d728dSAlexander V. Chernikov * Normally run under IPFW_UH_RLOCK, but these are idempotent ops
1246a73d728dSAlexander V. Chernikov * so we only care that rules do not disappear.
1247a73d728dSAlexander V. Chernikov */
1248a73d728dSAlexander V. Chernikov static void
clear_counters(struct ip_fw * rule,int log_only)1249a73d728dSAlexander V. Chernikov clear_counters(struct ip_fw *rule, int log_only)
1250a73d728dSAlexander V. Chernikov {
1251a73d728dSAlexander V. Chernikov ipfw_insn_log *l = (ipfw_insn_log *)ACTION_PTR(rule);
1252a73d728dSAlexander V. Chernikov
1253a73d728dSAlexander V. Chernikov if (log_only == 0)
1254a73d728dSAlexander V. Chernikov IPFW_ZERO_RULE_COUNTER(rule);
1255a73d728dSAlexander V. Chernikov if (l->o.opcode == O_LOG)
1256a73d728dSAlexander V. Chernikov l->log_left = l->max_log;
1257a73d728dSAlexander V. Chernikov }
1258a73d728dSAlexander V. Chernikov
1259a73d728dSAlexander V. Chernikov /*
1260a73d728dSAlexander V. Chernikov * Flushes rules counters and/or log values on matching range.
1261a73d728dSAlexander V. Chernikov *
1262a73d728dSAlexander V. Chernikov * Returns number of items cleared.
1263a73d728dSAlexander V. Chernikov */
1264a73d728dSAlexander V. Chernikov static int
clear_range(struct ip_fw_chain * chain,ipfw_range_tlv * rt,int log_only)1265a73d728dSAlexander V. Chernikov clear_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int log_only)
1266a73d728dSAlexander V. Chernikov {
1267a73d728dSAlexander V. Chernikov struct ip_fw *rule;
1268a73d728dSAlexander V. Chernikov int num;
1269a73d728dSAlexander V. Chernikov int i;
1270a73d728dSAlexander V. Chernikov
1271a73d728dSAlexander V. Chernikov num = 0;
12722930362fSAlexander V. Chernikov rt->flags |= IPFW_RCFLAG_DEFAULT;
1273a73d728dSAlexander V. Chernikov
1274a73d728dSAlexander V. Chernikov IPFW_UH_WLOCK(chain); /* arbitrate writers */
12752930362fSAlexander V. Chernikov for (i = 0; i < chain->n_rules; i++) {
1276a73d728dSAlexander V. Chernikov rule = chain->map[i];
1277a73d728dSAlexander V. Chernikov if (ipfw_match_range(rule, rt) == 0)
1278a73d728dSAlexander V. Chernikov continue;
1279a73d728dSAlexander V. Chernikov clear_counters(rule, log_only);
1280a73d728dSAlexander V. Chernikov num++;
1281a73d728dSAlexander V. Chernikov }
1282a73d728dSAlexander V. Chernikov IPFW_UH_WUNLOCK(chain);
1283a73d728dSAlexander V. Chernikov
1284a73d728dSAlexander V. Chernikov return (num);
1285a73d728dSAlexander V. Chernikov }
1286a73d728dSAlexander V. Chernikov
1287a73d728dSAlexander V. Chernikov static int
check_range_tlv(ipfw_range_tlv * rt)1288a73d728dSAlexander V. Chernikov check_range_tlv(ipfw_range_tlv *rt)
1289a73d728dSAlexander V. Chernikov {
1290a73d728dSAlexander V. Chernikov
1291a73d728dSAlexander V. Chernikov if (rt->head.length != sizeof(*rt))
1292a73d728dSAlexander V. Chernikov return (1);
1293a73d728dSAlexander V. Chernikov if (rt->start_rule > rt->end_rule)
1294a73d728dSAlexander V. Chernikov return (1);
1295a73d728dSAlexander V. Chernikov if (rt->set >= IPFW_MAX_SETS || rt->new_set >= IPFW_MAX_SETS)
1296a73d728dSAlexander V. Chernikov return (1);
1297a73d728dSAlexander V. Chernikov
12982930362fSAlexander V. Chernikov if ((rt->flags & IPFW_RCFLAG_USER) != rt->flags)
12992930362fSAlexander V. Chernikov return (1);
13002930362fSAlexander V. Chernikov
1301a73d728dSAlexander V. Chernikov return (0);
1302a73d728dSAlexander V. Chernikov }
1303a73d728dSAlexander V. Chernikov
1304a73d728dSAlexander V. Chernikov /*
1305a73d728dSAlexander V. Chernikov * Delete rules matching specified parameters
1306a73d728dSAlexander V. Chernikov * Data layout (v0)(current):
1307a73d728dSAlexander V. Chernikov * Request: [ ipfw_obj_header ipfw_range_tlv ]
1308a73d728dSAlexander V. Chernikov * Reply: [ ipfw_obj_header ipfw_range_tlv ]
1309a73d728dSAlexander V. Chernikov *
1310a73d728dSAlexander V. Chernikov * Saves number of deleted rules in ipfw_range_tlv->new_set.
1311a73d728dSAlexander V. Chernikov *
1312a73d728dSAlexander V. Chernikov * Returns 0 on success.
1313a73d728dSAlexander V. Chernikov */
1314a73d728dSAlexander V. Chernikov static int
del_rules(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)1315a73d728dSAlexander V. Chernikov del_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
1316a73d728dSAlexander V. Chernikov struct sockopt_data *sd)
1317a73d728dSAlexander V. Chernikov {
1318a73d728dSAlexander V. Chernikov ipfw_range_header *rh;
1319a73d728dSAlexander V. Chernikov int error, ndel;
1320a73d728dSAlexander V. Chernikov
1321a73d728dSAlexander V. Chernikov if (sd->valsize != sizeof(*rh))
1322a73d728dSAlexander V. Chernikov return (EINVAL);
1323a73d728dSAlexander V. Chernikov
1324a73d728dSAlexander V. Chernikov rh = (ipfw_range_header *)ipfw_get_sopt_space(sd, sd->valsize);
1325a73d728dSAlexander V. Chernikov
1326a73d728dSAlexander V. Chernikov if (check_range_tlv(&rh->range) != 0)
1327a73d728dSAlexander V. Chernikov return (EINVAL);
1328a73d728dSAlexander V. Chernikov
1329a73d728dSAlexander V. Chernikov ndel = 0;
1330a73d728dSAlexander V. Chernikov if ((error = delete_range(chain, &rh->range, &ndel)) != 0)
1331a73d728dSAlexander V. Chernikov return (error);
1332a73d728dSAlexander V. Chernikov
1333a73d728dSAlexander V. Chernikov /* Save number of rules deleted */
1334a73d728dSAlexander V. Chernikov rh->range.new_set = ndel;
1335a73d728dSAlexander V. Chernikov return (0);
1336a73d728dSAlexander V. Chernikov }
1337a73d728dSAlexander V. Chernikov
1338a73d728dSAlexander V. Chernikov /*
1339a73d728dSAlexander V. Chernikov * Move rules/sets matching specified parameters
1340a73d728dSAlexander V. Chernikov * Data layout (v0)(current):
1341a73d728dSAlexander V. Chernikov * Request: [ ipfw_obj_header ipfw_range_tlv ]
1342a73d728dSAlexander V. Chernikov *
1343a73d728dSAlexander V. Chernikov * Returns 0 on success.
1344a73d728dSAlexander V. Chernikov */
1345a73d728dSAlexander V. Chernikov static int
move_rules(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)1346a73d728dSAlexander V. Chernikov move_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
1347a73d728dSAlexander V. Chernikov struct sockopt_data *sd)
1348a73d728dSAlexander V. Chernikov {
1349a73d728dSAlexander V. Chernikov ipfw_range_header *rh;
1350a73d728dSAlexander V. Chernikov
1351a73d728dSAlexander V. Chernikov if (sd->valsize != sizeof(*rh))
1352a73d728dSAlexander V. Chernikov return (EINVAL);
1353a73d728dSAlexander V. Chernikov
1354a73d728dSAlexander V. Chernikov rh = (ipfw_range_header *)ipfw_get_sopt_space(sd, sd->valsize);
1355a73d728dSAlexander V. Chernikov
1356a73d728dSAlexander V. Chernikov if (check_range_tlv(&rh->range) != 0)
1357a73d728dSAlexander V. Chernikov return (EINVAL);
1358a73d728dSAlexander V. Chernikov
1359a73d728dSAlexander V. Chernikov return (move_range(chain, &rh->range));
1360a73d728dSAlexander V. Chernikov }
1361a73d728dSAlexander V. Chernikov
1362a73d728dSAlexander V. Chernikov /*
1363a73d728dSAlexander V. Chernikov * Clear rule accounting data matching specified parameters
1364a73d728dSAlexander V. Chernikov * Data layout (v0)(current):
1365a73d728dSAlexander V. Chernikov * Request: [ ipfw_obj_header ipfw_range_tlv ]
1366a73d728dSAlexander V. Chernikov * Reply: [ ipfw_obj_header ipfw_range_tlv ]
1367a73d728dSAlexander V. Chernikov *
1368a73d728dSAlexander V. Chernikov * Saves number of cleared rules in ipfw_range_tlv->new_set.
1369a73d728dSAlexander V. Chernikov *
1370a73d728dSAlexander V. Chernikov * Returns 0 on success.
1371a73d728dSAlexander V. Chernikov */
1372a73d728dSAlexander V. Chernikov static int
clear_rules(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)1373a73d728dSAlexander V. Chernikov clear_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
1374a73d728dSAlexander V. Chernikov struct sockopt_data *sd)
1375a73d728dSAlexander V. Chernikov {
1376a73d728dSAlexander V. Chernikov ipfw_range_header *rh;
1377a73d728dSAlexander V. Chernikov int log_only, num;
1378a73d728dSAlexander V. Chernikov char *msg;
1379a73d728dSAlexander V. Chernikov
1380a73d728dSAlexander V. Chernikov if (sd->valsize != sizeof(*rh))
1381a73d728dSAlexander V. Chernikov return (EINVAL);
1382a73d728dSAlexander V. Chernikov
1383a73d728dSAlexander V. Chernikov rh = (ipfw_range_header *)ipfw_get_sopt_space(sd, sd->valsize);
1384a73d728dSAlexander V. Chernikov
1385a73d728dSAlexander V. Chernikov if (check_range_tlv(&rh->range) != 0)
1386a73d728dSAlexander V. Chernikov return (EINVAL);
1387a73d728dSAlexander V. Chernikov
1388a73d728dSAlexander V. Chernikov log_only = (op3->opcode == IP_FW_XRESETLOG);
1389a73d728dSAlexander V. Chernikov
1390a73d728dSAlexander V. Chernikov num = clear_range(chain, &rh->range, log_only);
1391a73d728dSAlexander V. Chernikov
1392a73d728dSAlexander V. Chernikov if (rh->range.flags & IPFW_RCFLAG_ALL)
1393a73d728dSAlexander V. Chernikov msg = log_only ? "All logging counts reset" :
1394a73d728dSAlexander V. Chernikov "Accounting cleared";
1395a73d728dSAlexander V. Chernikov else
1396a73d728dSAlexander V. Chernikov msg = log_only ? "logging count reset" : "cleared";
1397a73d728dSAlexander V. Chernikov
1398a73d728dSAlexander V. Chernikov if (V_fw_verbose) {
1399a73d728dSAlexander V. Chernikov int lev = LOG_SECURITY | LOG_NOTICE;
1400a73d728dSAlexander V. Chernikov log(lev, "ipfw: %s.\n", msg);
1401a73d728dSAlexander V. Chernikov }
1402a73d728dSAlexander V. Chernikov
1403a73d728dSAlexander V. Chernikov /* Save number of rules cleared */
1404a73d728dSAlexander V. Chernikov rh->range.new_set = num;
1405a73d728dSAlexander V. Chernikov return (0);
1406a73d728dSAlexander V. Chernikov }
1407a73d728dSAlexander V. Chernikov
1408a73d728dSAlexander V. Chernikov static void
enable_sets(struct ip_fw_chain * chain,ipfw_range_tlv * rt)1409a73d728dSAlexander V. Chernikov enable_sets(struct ip_fw_chain *chain, ipfw_range_tlv *rt)
1410a73d728dSAlexander V. Chernikov {
1411a73d728dSAlexander V. Chernikov uint32_t v_set;
1412a73d728dSAlexander V. Chernikov
1413a73d728dSAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(chain);
1414a73d728dSAlexander V. Chernikov
1415a73d728dSAlexander V. Chernikov /* Change enabled/disabled sets mask */
1416a73d728dSAlexander V. Chernikov v_set = (V_set_disable | rt->set) & ~rt->new_set;
1417a73d728dSAlexander V. Chernikov v_set &= ~(1 << RESVD_SET); /* set RESVD_SET always enabled */
1418a73d728dSAlexander V. Chernikov IPFW_WLOCK(chain);
1419a73d728dSAlexander V. Chernikov V_set_disable = v_set;
1420a73d728dSAlexander V. Chernikov IPFW_WUNLOCK(chain);
1421a73d728dSAlexander V. Chernikov }
1422a73d728dSAlexander V. Chernikov
14232685841bSAndrey V. Elsukov static int
swap_sets(struct ip_fw_chain * chain,ipfw_range_tlv * rt,int mv)1424a73d728dSAlexander V. Chernikov swap_sets(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int mv)
1425a73d728dSAlexander V. Chernikov {
14262685841bSAndrey V. Elsukov struct opcode_obj_rewrite *rw;
1427a73d728dSAlexander V. Chernikov struct ip_fw *rule;
1428a73d728dSAlexander V. Chernikov int i;
1429a73d728dSAlexander V. Chernikov
1430a73d728dSAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(chain);
1431a73d728dSAlexander V. Chernikov
14322685841bSAndrey V. Elsukov if (rt->set == rt->new_set) /* nothing to do */
14332685841bSAndrey V. Elsukov return (0);
14342685841bSAndrey V. Elsukov
14352685841bSAndrey V. Elsukov if (mv != 0) {
14362685841bSAndrey V. Elsukov /*
14372685841bSAndrey V. Elsukov * Berfore moving the rules we need to check that
14382685841bSAndrey V. Elsukov * there aren't any conflicting named objects.
14392685841bSAndrey V. Elsukov */
14402685841bSAndrey V. Elsukov for (rw = ctl3_rewriters;
14412685841bSAndrey V. Elsukov rw < ctl3_rewriters + ctl3_rsize; rw++) {
14422685841bSAndrey V. Elsukov if (rw->manage_sets == NULL)
14432685841bSAndrey V. Elsukov continue;
14442685841bSAndrey V. Elsukov i = rw->manage_sets(chain, (uint8_t)rt->set,
14452685841bSAndrey V. Elsukov (uint8_t)rt->new_set, TEST_ALL);
14462685841bSAndrey V. Elsukov if (i != 0)
14472685841bSAndrey V. Elsukov return (EEXIST);
14482685841bSAndrey V. Elsukov }
14492685841bSAndrey V. Elsukov }
1450a73d728dSAlexander V. Chernikov /* Swap or move two sets */
1451a73d728dSAlexander V. Chernikov for (i = 0; i < chain->n_rules - 1; i++) {
1452a73d728dSAlexander V. Chernikov rule = chain->map[i];
14532685841bSAndrey V. Elsukov if (rule->set == (uint8_t)rt->set)
14542685841bSAndrey V. Elsukov rule->set = (uint8_t)rt->new_set;
14552685841bSAndrey V. Elsukov else if (rule->set == (uint8_t)rt->new_set && mv == 0)
14562685841bSAndrey V. Elsukov rule->set = (uint8_t)rt->set;
1457a73d728dSAlexander V. Chernikov }
14582685841bSAndrey V. Elsukov for (rw = ctl3_rewriters; rw < ctl3_rewriters + ctl3_rsize; rw++) {
14592685841bSAndrey V. Elsukov if (rw->manage_sets == NULL)
14602685841bSAndrey V. Elsukov continue;
14612685841bSAndrey V. Elsukov rw->manage_sets(chain, (uint8_t)rt->set,
14622685841bSAndrey V. Elsukov (uint8_t)rt->new_set, mv != 0 ? MOVE_ALL: SWAP_ALL);
14632685841bSAndrey V. Elsukov }
14642685841bSAndrey V. Elsukov return (0);
1465a73d728dSAlexander V. Chernikov }
1466a73d728dSAlexander V. Chernikov
1467a73d728dSAlexander V. Chernikov /*
1468a73d728dSAlexander V. Chernikov * Swaps or moves set
1469a73d728dSAlexander V. Chernikov * Data layout (v0)(current):
1470a73d728dSAlexander V. Chernikov * Request: [ ipfw_obj_header ipfw_range_tlv ]
1471a73d728dSAlexander V. Chernikov *
1472a73d728dSAlexander V. Chernikov * Returns 0 on success.
1473a73d728dSAlexander V. Chernikov */
1474a73d728dSAlexander V. Chernikov static int
manage_sets(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)1475a73d728dSAlexander V. Chernikov manage_sets(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
1476a73d728dSAlexander V. Chernikov struct sockopt_data *sd)
1477a73d728dSAlexander V. Chernikov {
1478a73d728dSAlexander V. Chernikov ipfw_range_header *rh;
14792685841bSAndrey V. Elsukov int ret;
1480a73d728dSAlexander V. Chernikov
1481a73d728dSAlexander V. Chernikov if (sd->valsize != sizeof(*rh))
1482a73d728dSAlexander V. Chernikov return (EINVAL);
1483a73d728dSAlexander V. Chernikov
1484a73d728dSAlexander V. Chernikov rh = (ipfw_range_header *)ipfw_get_sopt_space(sd, sd->valsize);
1485a73d728dSAlexander V. Chernikov
1486a73d728dSAlexander V. Chernikov if (rh->range.head.length != sizeof(ipfw_range_tlv))
1487a73d728dSAlexander V. Chernikov return (1);
1488e7560c83SOleg Bulyzhin /* enable_sets() expects bitmasks. */
1489e7560c83SOleg Bulyzhin if (op3->opcode != IP_FW_SET_ENABLE &&
1490e7560c83SOleg Bulyzhin (rh->range.set >= IPFW_MAX_SETS ||
1491e7560c83SOleg Bulyzhin rh->range.new_set >= IPFW_MAX_SETS))
14922685841bSAndrey V. Elsukov return (EINVAL);
1493a73d728dSAlexander V. Chernikov
14942685841bSAndrey V. Elsukov ret = 0;
1495a73d728dSAlexander V. Chernikov IPFW_UH_WLOCK(chain);
1496a73d728dSAlexander V. Chernikov switch (op3->opcode) {
1497a73d728dSAlexander V. Chernikov case IP_FW_SET_SWAP:
1498a73d728dSAlexander V. Chernikov case IP_FW_SET_MOVE:
14992685841bSAndrey V. Elsukov ret = swap_sets(chain, &rh->range,
15002685841bSAndrey V. Elsukov op3->opcode == IP_FW_SET_MOVE);
1501a73d728dSAlexander V. Chernikov break;
1502a73d728dSAlexander V. Chernikov case IP_FW_SET_ENABLE:
1503a73d728dSAlexander V. Chernikov enable_sets(chain, &rh->range);
1504a73d728dSAlexander V. Chernikov break;
1505a73d728dSAlexander V. Chernikov }
1506a73d728dSAlexander V. Chernikov IPFW_UH_WUNLOCK(chain);
1507a73d728dSAlexander V. Chernikov
15082685841bSAndrey V. Elsukov return (ret);
15093b3a8eb9SGleb Smirnoff }
15103b3a8eb9SGleb Smirnoff
15113b3a8eb9SGleb Smirnoff /**
15123b3a8eb9SGleb Smirnoff * Remove all rules with given number, or do set manipulation.
15133b3a8eb9SGleb Smirnoff * Assumes chain != NULL && *chain != NULL.
15143b3a8eb9SGleb Smirnoff *
15153b3a8eb9SGleb Smirnoff * The argument is an uint32_t. The low 16 bit are the rule or set number;
15163b3a8eb9SGleb Smirnoff * the next 8 bits are the new set; the top 8 bits indicate the command:
15173b3a8eb9SGleb Smirnoff *
15183b3a8eb9SGleb Smirnoff * 0 delete rules numbered "rulenum"
15193b3a8eb9SGleb Smirnoff * 1 delete rules in set "rulenum"
15203b3a8eb9SGleb Smirnoff * 2 move rules "rulenum" to set "new_set"
15213b3a8eb9SGleb Smirnoff * 3 move rules from set "rulenum" to set "new_set"
15223b3a8eb9SGleb Smirnoff * 4 swap sets "rulenum" and "new_set"
15233b3a8eb9SGleb Smirnoff * 5 delete rules "rulenum" and set "new_set"
15243b3a8eb9SGleb Smirnoff */
15253b3a8eb9SGleb Smirnoff static int
del_entry(struct ip_fw_chain * chain,uint32_t arg)15263b3a8eb9SGleb Smirnoff del_entry(struct ip_fw_chain *chain, uint32_t arg)
15273b3a8eb9SGleb Smirnoff {
15283b3a8eb9SGleb Smirnoff uint32_t num; /* rule number or old_set */
15293b3a8eb9SGleb Smirnoff uint8_t cmd, new_set;
1530a73d728dSAlexander V. Chernikov int do_del, ndel;
15313b3a8eb9SGleb Smirnoff int error = 0;
1532a73d728dSAlexander V. Chernikov ipfw_range_tlv rt;
15333b3a8eb9SGleb Smirnoff
15343b3a8eb9SGleb Smirnoff num = arg & 0xffff;
15353b3a8eb9SGleb Smirnoff cmd = (arg >> 24) & 0xff;
15363b3a8eb9SGleb Smirnoff new_set = (arg >> 16) & 0xff;
15373b3a8eb9SGleb Smirnoff
15383b3a8eb9SGleb Smirnoff if (cmd > 5 || new_set > RESVD_SET)
15393b3a8eb9SGleb Smirnoff return EINVAL;
15403b3a8eb9SGleb Smirnoff if (cmd == 0 || cmd == 2 || cmd == 5) {
15413b3a8eb9SGleb Smirnoff if (num >= IPFW_DEFAULT_RULE)
15423b3a8eb9SGleb Smirnoff return EINVAL;
15433b3a8eb9SGleb Smirnoff } else {
15443b3a8eb9SGleb Smirnoff if (num > RESVD_SET) /* old_set */
15453b3a8eb9SGleb Smirnoff return EINVAL;
15463b3a8eb9SGleb Smirnoff }
15473b3a8eb9SGleb Smirnoff
1548a73d728dSAlexander V. Chernikov /* Convert old requests into new representation */
1549a73d728dSAlexander V. Chernikov memset(&rt, 0, sizeof(rt));
1550a73d728dSAlexander V. Chernikov rt.start_rule = num;
1551a73d728dSAlexander V. Chernikov rt.end_rule = num;
1552a73d728dSAlexander V. Chernikov rt.set = num;
1553a73d728dSAlexander V. Chernikov rt.new_set = new_set;
1554a73d728dSAlexander V. Chernikov do_del = 0;
15553b3a8eb9SGleb Smirnoff
15563b3a8eb9SGleb Smirnoff switch (cmd) {
1557a73d728dSAlexander V. Chernikov case 0: /* delete rules numbered "rulenum" */
1558a73d728dSAlexander V. Chernikov if (num == 0)
1559a73d728dSAlexander V. Chernikov rt.flags |= IPFW_RCFLAG_ALL;
1560a73d728dSAlexander V. Chernikov else
1561a73d728dSAlexander V. Chernikov rt.flags |= IPFW_RCFLAG_RANGE;
1562a73d728dSAlexander V. Chernikov do_del = 1;
15633b3a8eb9SGleb Smirnoff break;
1564a73d728dSAlexander V. Chernikov case 1: /* delete rules in set "rulenum" */
1565a73d728dSAlexander V. Chernikov rt.flags |= IPFW_RCFLAG_SET;
1566a73d728dSAlexander V. Chernikov do_del = 1;
15673b3a8eb9SGleb Smirnoff break;
1568a73d728dSAlexander V. Chernikov case 5: /* delete rules "rulenum" and set "new_set" */
1569a73d728dSAlexander V. Chernikov rt.flags |= IPFW_RCFLAG_RANGE | IPFW_RCFLAG_SET;
1570a73d728dSAlexander V. Chernikov rt.set = new_set;
1571a73d728dSAlexander V. Chernikov rt.new_set = 0;
1572a73d728dSAlexander V. Chernikov do_del = 1;
15733b3a8eb9SGleb Smirnoff break;
1574a73d728dSAlexander V. Chernikov case 2: /* move rules "rulenum" to set "new_set" */
1575a73d728dSAlexander V. Chernikov rt.flags |= IPFW_RCFLAG_RANGE;
15763b3a8eb9SGleb Smirnoff break;
1577a73d728dSAlexander V. Chernikov case 3: /* move rules from set "rulenum" to set "new_set" */
1578a73d728dSAlexander V. Chernikov IPFW_UH_WLOCK(chain);
15792685841bSAndrey V. Elsukov error = swap_sets(chain, &rt, 1);
15803b3a8eb9SGleb Smirnoff IPFW_UH_WUNLOCK(chain);
15812685841bSAndrey V. Elsukov return (error);
1582a73d728dSAlexander V. Chernikov case 4: /* swap sets "rulenum" and "new_set" */
1583a73d728dSAlexander V. Chernikov IPFW_UH_WLOCK(chain);
15842685841bSAndrey V. Elsukov error = swap_sets(chain, &rt, 0);
1585a73d728dSAlexander V. Chernikov IPFW_UH_WUNLOCK(chain);
15862685841bSAndrey V. Elsukov return (error);
1587a73d728dSAlexander V. Chernikov default:
1588a73d728dSAlexander V. Chernikov return (ENOTSUP);
15893b3a8eb9SGleb Smirnoff }
15903b3a8eb9SGleb Smirnoff
1591a73d728dSAlexander V. Chernikov if (do_del != 0) {
1592a73d728dSAlexander V. Chernikov if ((error = delete_range(chain, &rt, &ndel)) != 0)
1593a73d728dSAlexander V. Chernikov return (error);
1594a73d728dSAlexander V. Chernikov
1595a73d728dSAlexander V. Chernikov if (ndel == 0 && (cmd != 1 && num != 0))
1596a73d728dSAlexander V. Chernikov return (EINVAL);
1597a73d728dSAlexander V. Chernikov
1598a73d728dSAlexander V. Chernikov return (0);
1599a73d728dSAlexander V. Chernikov }
1600a73d728dSAlexander V. Chernikov
1601a73d728dSAlexander V. Chernikov return (move_range(chain, &rt));
16023b3a8eb9SGleb Smirnoff }
16033b3a8eb9SGleb Smirnoff
16043b3a8eb9SGleb Smirnoff /**
16053b3a8eb9SGleb Smirnoff * Reset some or all counters on firewall rules.
16063b3a8eb9SGleb Smirnoff * The argument `arg' is an u_int32_t. The low 16 bit are the rule number,
16073b3a8eb9SGleb Smirnoff * the next 8 bits are the set number, the top 8 bits are the command:
16083b3a8eb9SGleb Smirnoff * 0 work with rules from all set's;
16093b3a8eb9SGleb Smirnoff * 1 work with rules only from specified set.
16103b3a8eb9SGleb Smirnoff * Specified rule number is zero if we want to clear all entries.
16113b3a8eb9SGleb Smirnoff * log_only is 1 if we only want to reset logs, zero otherwise.
16123b3a8eb9SGleb Smirnoff */
16133b3a8eb9SGleb Smirnoff static int
zero_entry(struct ip_fw_chain * chain,u_int32_t arg,int log_only)16143b3a8eb9SGleb Smirnoff zero_entry(struct ip_fw_chain *chain, u_int32_t arg, int log_only)
16153b3a8eb9SGleb Smirnoff {
16163b3a8eb9SGleb Smirnoff struct ip_fw *rule;
16173b3a8eb9SGleb Smirnoff char *msg;
16183b3a8eb9SGleb Smirnoff int i;
16193b3a8eb9SGleb Smirnoff
16203b3a8eb9SGleb Smirnoff uint16_t rulenum = arg & 0xffff;
16213b3a8eb9SGleb Smirnoff uint8_t set = (arg >> 16) & 0xff;
16223b3a8eb9SGleb Smirnoff uint8_t cmd = (arg >> 24) & 0xff;
16233b3a8eb9SGleb Smirnoff
16243b3a8eb9SGleb Smirnoff if (cmd > 1)
16253b3a8eb9SGleb Smirnoff return (EINVAL);
16263b3a8eb9SGleb Smirnoff if (cmd == 1 && set > RESVD_SET)
16273b3a8eb9SGleb Smirnoff return (EINVAL);
16283b3a8eb9SGleb Smirnoff
16293b3a8eb9SGleb Smirnoff IPFW_UH_RLOCK(chain);
16303b3a8eb9SGleb Smirnoff if (rulenum == 0) {
16313b3a8eb9SGleb Smirnoff V_norule_counter = 0;
16323b3a8eb9SGleb Smirnoff for (i = 0; i < chain->n_rules; i++) {
16333b3a8eb9SGleb Smirnoff rule = chain->map[i];
16343b3a8eb9SGleb Smirnoff /* Skip rules not in our set. */
16353b3a8eb9SGleb Smirnoff if (cmd == 1 && rule->set != set)
16363b3a8eb9SGleb Smirnoff continue;
16373b3a8eb9SGleb Smirnoff clear_counters(rule, log_only);
16383b3a8eb9SGleb Smirnoff }
16393b3a8eb9SGleb Smirnoff msg = log_only ? "All logging counts reset" :
16403b3a8eb9SGleb Smirnoff "Accounting cleared";
16413b3a8eb9SGleb Smirnoff } else {
16423b3a8eb9SGleb Smirnoff int cleared = 0;
16433b3a8eb9SGleb Smirnoff for (i = 0; i < chain->n_rules; i++) {
16443b3a8eb9SGleb Smirnoff rule = chain->map[i];
16453b3a8eb9SGleb Smirnoff if (rule->rulenum == rulenum) {
16463b3a8eb9SGleb Smirnoff if (cmd == 0 || rule->set == set)
16473b3a8eb9SGleb Smirnoff clear_counters(rule, log_only);
16483b3a8eb9SGleb Smirnoff cleared = 1;
16493b3a8eb9SGleb Smirnoff }
16503b3a8eb9SGleb Smirnoff if (rule->rulenum > rulenum)
16513b3a8eb9SGleb Smirnoff break;
16523b3a8eb9SGleb Smirnoff }
16533b3a8eb9SGleb Smirnoff if (!cleared) { /* we did not find any matching rules */
16543b3a8eb9SGleb Smirnoff IPFW_UH_RUNLOCK(chain);
16553b3a8eb9SGleb Smirnoff return (EINVAL);
16563b3a8eb9SGleb Smirnoff }
16573b3a8eb9SGleb Smirnoff msg = log_only ? "logging count reset" : "cleared";
16583b3a8eb9SGleb Smirnoff }
16593b3a8eb9SGleb Smirnoff IPFW_UH_RUNLOCK(chain);
16603b3a8eb9SGleb Smirnoff
16613b3a8eb9SGleb Smirnoff if (V_fw_verbose) {
16623b3a8eb9SGleb Smirnoff int lev = LOG_SECURITY | LOG_NOTICE;
16633b3a8eb9SGleb Smirnoff
16643b3a8eb9SGleb Smirnoff if (rulenum)
16653b3a8eb9SGleb Smirnoff log(lev, "ipfw: Entry %d %s.\n", rulenum, msg);
16663b3a8eb9SGleb Smirnoff else
16673b3a8eb9SGleb Smirnoff log(lev, "ipfw: %s.\n", msg);
16683b3a8eb9SGleb Smirnoff }
16693b3a8eb9SGleb Smirnoff return (0);
16703b3a8eb9SGleb Smirnoff }
16713b3a8eb9SGleb Smirnoff
16723b3a8eb9SGleb Smirnoff /*
16737e767c79SAlexander V. Chernikov * Check rule head in FreeBSD11 format
16747e767c79SAlexander V. Chernikov *
16753b3a8eb9SGleb Smirnoff */
16763b3a8eb9SGleb Smirnoff static int
check_ipfw_rule1(struct ip_fw_rule * rule,int size,struct rule_check_info * ci)16777e767c79SAlexander V. Chernikov check_ipfw_rule1(struct ip_fw_rule *rule, int size,
16787e767c79SAlexander V. Chernikov struct rule_check_info *ci)
16793b3a8eb9SGleb Smirnoff {
16807e767c79SAlexander V. Chernikov int l;
16813b3a8eb9SGleb Smirnoff
16823b3a8eb9SGleb Smirnoff if (size < sizeof(*rule)) {
16833b3a8eb9SGleb Smirnoff printf("ipfw: rule too short\n");
16843b3a8eb9SGleb Smirnoff return (EINVAL);
16853b3a8eb9SGleb Smirnoff }
16867e767c79SAlexander V. Chernikov
16877e767c79SAlexander V. Chernikov /* Check for valid cmd_len */
16887e767c79SAlexander V. Chernikov l = roundup2(RULESIZE(rule), sizeof(uint64_t));
16893b3a8eb9SGleb Smirnoff if (l != size) {
16903b3a8eb9SGleb Smirnoff printf("ipfw: size mismatch (have %d want %d)\n", size, l);
16913b3a8eb9SGleb Smirnoff return (EINVAL);
16923b3a8eb9SGleb Smirnoff }
16933b3a8eb9SGleb Smirnoff if (rule->act_ofs >= rule->cmd_len) {
16943b3a8eb9SGleb Smirnoff printf("ipfw: bogus action offset (%u > %u)\n",
16953b3a8eb9SGleb Smirnoff rule->act_ofs, rule->cmd_len - 1);
16963b3a8eb9SGleb Smirnoff return (EINVAL);
16973b3a8eb9SGleb Smirnoff }
16986c2997ffSAlexander V. Chernikov
16996c2997ffSAlexander V. Chernikov if (rule->rulenum > IPFW_DEFAULT_RULE - 1)
17006c2997ffSAlexander V. Chernikov return (EINVAL);
17016c2997ffSAlexander V. Chernikov
17027e767c79SAlexander V. Chernikov return (check_ipfw_rule_body(rule->cmd, rule->cmd_len, ci));
17037e767c79SAlexander V. Chernikov }
17047e767c79SAlexander V. Chernikov
17057e767c79SAlexander V. Chernikov /*
17067e767c79SAlexander V. Chernikov * Check rule head in FreeBSD8 format
17077e767c79SAlexander V. Chernikov *
17087e767c79SAlexander V. Chernikov */
17097e767c79SAlexander V. Chernikov static int
check_ipfw_rule0(struct ip_fw_rule0 * rule,int size,struct rule_check_info * ci)17107e767c79SAlexander V. Chernikov check_ipfw_rule0(struct ip_fw_rule0 *rule, int size,
17117e767c79SAlexander V. Chernikov struct rule_check_info *ci)
17127e767c79SAlexander V. Chernikov {
17137e767c79SAlexander V. Chernikov int l;
17147e767c79SAlexander V. Chernikov
17157e767c79SAlexander V. Chernikov if (size < sizeof(*rule)) {
17167e767c79SAlexander V. Chernikov printf("ipfw: rule too short\n");
17177e767c79SAlexander V. Chernikov return (EINVAL);
17187e767c79SAlexander V. Chernikov }
17197e767c79SAlexander V. Chernikov
17207e767c79SAlexander V. Chernikov /* Check for valid cmd_len */
17217e767c79SAlexander V. Chernikov l = sizeof(*rule) + rule->cmd_len * 4 - 4;
17227e767c79SAlexander V. Chernikov if (l != size) {
17237e767c79SAlexander V. Chernikov printf("ipfw: size mismatch (have %d want %d)\n", size, l);
17247e767c79SAlexander V. Chernikov return (EINVAL);
17257e767c79SAlexander V. Chernikov }
17267e767c79SAlexander V. Chernikov if (rule->act_ofs >= rule->cmd_len) {
17277e767c79SAlexander V. Chernikov printf("ipfw: bogus action offset (%u > %u)\n",
17287e767c79SAlexander V. Chernikov rule->act_ofs, rule->cmd_len - 1);
17297e767c79SAlexander V. Chernikov return (EINVAL);
17307e767c79SAlexander V. Chernikov }
17317e767c79SAlexander V. Chernikov
17327e767c79SAlexander V. Chernikov if (rule->rulenum > IPFW_DEFAULT_RULE - 1)
17337e767c79SAlexander V. Chernikov return (EINVAL);
17347e767c79SAlexander V. Chernikov
17357e767c79SAlexander V. Chernikov return (check_ipfw_rule_body(rule->cmd, rule->cmd_len, ci));
17367e767c79SAlexander V. Chernikov }
17377e767c79SAlexander V. Chernikov
17387e767c79SAlexander V. Chernikov static int
check_ipfw_rule_body(ipfw_insn * cmd,int cmd_len,struct rule_check_info * ci)17397e767c79SAlexander V. Chernikov check_ipfw_rule_body(ipfw_insn *cmd, int cmd_len, struct rule_check_info *ci)
17407e767c79SAlexander V. Chernikov {
17417e767c79SAlexander V. Chernikov int cmdlen, l;
17427e767c79SAlexander V. Chernikov int have_action;
17437e767c79SAlexander V. Chernikov
17447e767c79SAlexander V. Chernikov have_action = 0;
17457e767c79SAlexander V. Chernikov
17463b3a8eb9SGleb Smirnoff /*
17473b3a8eb9SGleb Smirnoff * Now go for the individual checks. Very simple ones, basically only
17483b3a8eb9SGleb Smirnoff * instruction sizes.
17493b3a8eb9SGleb Smirnoff */
17507e767c79SAlexander V. Chernikov for (l = cmd_len; l > 0 ; l -= cmdlen, cmd += cmdlen) {
17513b3a8eb9SGleb Smirnoff cmdlen = F_LEN(cmd);
17523b3a8eb9SGleb Smirnoff if (cmdlen > l) {
17533b3a8eb9SGleb Smirnoff printf("ipfw: opcode %d size truncated\n",
17543b3a8eb9SGleb Smirnoff cmd->opcode);
17553b3a8eb9SGleb Smirnoff return EINVAL;
17563b3a8eb9SGleb Smirnoff }
17573b3a8eb9SGleb Smirnoff switch (cmd->opcode) {
17583b3a8eb9SGleb Smirnoff case O_PROBE_STATE:
17593b3a8eb9SGleb Smirnoff case O_KEEP_STATE:
1760ed22e564SAndrey V. Elsukov if (cmdlen != F_INSN_SIZE(ipfw_insn))
1761ed22e564SAndrey V. Elsukov goto bad_size;
1762ed22e564SAndrey V. Elsukov ci->object_opcodes++;
1763ed22e564SAndrey V. Elsukov break;
17643b3a8eb9SGleb Smirnoff case O_PROTO:
17653b3a8eb9SGleb Smirnoff case O_IP_SRC_ME:
17663b3a8eb9SGleb Smirnoff case O_IP_DST_ME:
17673b3a8eb9SGleb Smirnoff case O_LAYER2:
17683b3a8eb9SGleb Smirnoff case O_IN:
17693b3a8eb9SGleb Smirnoff case O_FRAG:
17703b3a8eb9SGleb Smirnoff case O_DIVERTED:
17713b3a8eb9SGleb Smirnoff case O_IPOPT:
17723b3a8eb9SGleb Smirnoff case O_IPTOS:
17733b3a8eb9SGleb Smirnoff case O_IPPRECEDENCE:
17743b3a8eb9SGleb Smirnoff case O_IPVER:
17753b3a8eb9SGleb Smirnoff case O_SOCKARG:
17763b3a8eb9SGleb Smirnoff case O_TCPFLAGS:
17773b3a8eb9SGleb Smirnoff case O_TCPOPTS:
17783b3a8eb9SGleb Smirnoff case O_ESTAB:
17793b3a8eb9SGleb Smirnoff case O_VERREVPATH:
17803b3a8eb9SGleb Smirnoff case O_VERSRCREACH:
17813b3a8eb9SGleb Smirnoff case O_ANTISPOOF:
17823b3a8eb9SGleb Smirnoff case O_IPSEC:
17833b3a8eb9SGleb Smirnoff #ifdef INET6
17843b3a8eb9SGleb Smirnoff case O_IP6_SRC_ME:
17853b3a8eb9SGleb Smirnoff case O_IP6_DST_ME:
17863b3a8eb9SGleb Smirnoff case O_EXT_HDR:
17873b3a8eb9SGleb Smirnoff case O_IP6:
17883b3a8eb9SGleb Smirnoff #endif
17893b3a8eb9SGleb Smirnoff case O_IP4:
17903b3a8eb9SGleb Smirnoff case O_TAG:
1791f7c4fdeeSAndrey V. Elsukov case O_SKIP_ACTION:
17923b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn))
17933b3a8eb9SGleb Smirnoff goto bad_size;
17943b3a8eb9SGleb Smirnoff break;
17953b3a8eb9SGleb Smirnoff
17962acdf79fSAndrey V. Elsukov case O_EXTERNAL_ACTION:
17972acdf79fSAndrey V. Elsukov if (cmd->arg1 == 0 ||
17982acdf79fSAndrey V. Elsukov cmdlen != F_INSN_SIZE(ipfw_insn)) {
17992acdf79fSAndrey V. Elsukov printf("ipfw: invalid external "
18002acdf79fSAndrey V. Elsukov "action opcode\n");
18012acdf79fSAndrey V. Elsukov return (EINVAL);
18022acdf79fSAndrey V. Elsukov }
18032acdf79fSAndrey V. Elsukov ci->object_opcodes++;
180411c56650SAndrey V. Elsukov /*
180511c56650SAndrey V. Elsukov * Do we have O_EXTERNAL_INSTANCE or O_EXTERNAL_DATA
180611c56650SAndrey V. Elsukov * opcode?
180711c56650SAndrey V. Elsukov */
18082acdf79fSAndrey V. Elsukov if (l != cmdlen) {
18092acdf79fSAndrey V. Elsukov l -= cmdlen;
18102acdf79fSAndrey V. Elsukov cmd += cmdlen;
18112acdf79fSAndrey V. Elsukov cmdlen = F_LEN(cmd);
181211c56650SAndrey V. Elsukov if (cmd->opcode == O_EXTERNAL_DATA)
181311c56650SAndrey V. Elsukov goto check_action;
18142acdf79fSAndrey V. Elsukov if (cmd->opcode != O_EXTERNAL_INSTANCE) {
18152acdf79fSAndrey V. Elsukov printf("ipfw: invalid opcode "
18162acdf79fSAndrey V. Elsukov "next to external action %u\n",
18172acdf79fSAndrey V. Elsukov cmd->opcode);
18182acdf79fSAndrey V. Elsukov return (EINVAL);
18192acdf79fSAndrey V. Elsukov }
18202acdf79fSAndrey V. Elsukov if (cmd->arg1 == 0 ||
18212acdf79fSAndrey V. Elsukov cmdlen != F_INSN_SIZE(ipfw_insn)) {
18222acdf79fSAndrey V. Elsukov printf("ipfw: invalid external "
18232acdf79fSAndrey V. Elsukov "action instance opcode\n");
18242acdf79fSAndrey V. Elsukov return (EINVAL);
18252acdf79fSAndrey V. Elsukov }
18262acdf79fSAndrey V. Elsukov ci->object_opcodes++;
18272acdf79fSAndrey V. Elsukov }
18282acdf79fSAndrey V. Elsukov goto check_action;
18292acdf79fSAndrey V. Elsukov
18303b3a8eb9SGleb Smirnoff case O_FIB:
18313b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn))
18323b3a8eb9SGleb Smirnoff goto bad_size;
18333b3a8eb9SGleb Smirnoff if (cmd->arg1 >= rt_numfibs) {
18343b3a8eb9SGleb Smirnoff printf("ipfw: invalid fib number %d\n",
18353b3a8eb9SGleb Smirnoff cmd->arg1);
18363b3a8eb9SGleb Smirnoff return EINVAL;
18373b3a8eb9SGleb Smirnoff }
18383b3a8eb9SGleb Smirnoff break;
18393b3a8eb9SGleb Smirnoff
18403b3a8eb9SGleb Smirnoff case O_SETFIB:
18413b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn))
18423b3a8eb9SGleb Smirnoff goto bad_size;
18431940fa77SAlexander V. Chernikov if ((cmd->arg1 != IP_FW_TARG) &&
1844b554a278SAlexander V. Chernikov ((cmd->arg1 & 0x7FFF) >= rt_numfibs)) {
18453b3a8eb9SGleb Smirnoff printf("ipfw: invalid fib number %d\n",
1846b554a278SAlexander V. Chernikov cmd->arg1 & 0x7FFF);
18473b3a8eb9SGleb Smirnoff return EINVAL;
18483b3a8eb9SGleb Smirnoff }
18493b3a8eb9SGleb Smirnoff goto check_action;
18503b3a8eb9SGleb Smirnoff
18513b3a8eb9SGleb Smirnoff case O_UID:
18523b3a8eb9SGleb Smirnoff case O_GID:
18533b3a8eb9SGleb Smirnoff case O_JAIL:
18543b3a8eb9SGleb Smirnoff case O_IP_SRC:
18553b3a8eb9SGleb Smirnoff case O_IP_DST:
18563b3a8eb9SGleb Smirnoff case O_TCPSEQ:
18573b3a8eb9SGleb Smirnoff case O_TCPACK:
18583b3a8eb9SGleb Smirnoff case O_PROB:
18593b3a8eb9SGleb Smirnoff case O_ICMPTYPE:
18603b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn_u32))
18613b3a8eb9SGleb Smirnoff goto bad_size;
18623b3a8eb9SGleb Smirnoff break;
18633b3a8eb9SGleb Smirnoff
18643b3a8eb9SGleb Smirnoff case O_LIMIT:
18653b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn_limit))
18663b3a8eb9SGleb Smirnoff goto bad_size;
1867ed22e564SAndrey V. Elsukov ci->object_opcodes++;
18683b3a8eb9SGleb Smirnoff break;
18693b3a8eb9SGleb Smirnoff
18703b3a8eb9SGleb Smirnoff case O_LOG:
18713b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn_log))
18723b3a8eb9SGleb Smirnoff goto bad_size;
18733b3a8eb9SGleb Smirnoff
18743b3a8eb9SGleb Smirnoff ((ipfw_insn_log *)cmd)->log_left =
18753b3a8eb9SGleb Smirnoff ((ipfw_insn_log *)cmd)->max_log;
18763b3a8eb9SGleb Smirnoff
18773b3a8eb9SGleb Smirnoff break;
18783b3a8eb9SGleb Smirnoff
18793b3a8eb9SGleb Smirnoff case O_IP_SRC_MASK:
18803b3a8eb9SGleb Smirnoff case O_IP_DST_MASK:
18813b3a8eb9SGleb Smirnoff /* only odd command lengths */
1882c6fb65b1SAlexander V. Chernikov if ((cmdlen & 1) == 0)
18833b3a8eb9SGleb Smirnoff goto bad_size;
18843b3a8eb9SGleb Smirnoff break;
18853b3a8eb9SGleb Smirnoff
18863b3a8eb9SGleb Smirnoff case O_IP_SRC_SET:
18873b3a8eb9SGleb Smirnoff case O_IP_DST_SET:
18883b3a8eb9SGleb Smirnoff if (cmd->arg1 == 0 || cmd->arg1 > 256) {
18893b3a8eb9SGleb Smirnoff printf("ipfw: invalid set size %d\n",
18903b3a8eb9SGleb Smirnoff cmd->arg1);
18913b3a8eb9SGleb Smirnoff return EINVAL;
18923b3a8eb9SGleb Smirnoff }
18933b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) +
18943b3a8eb9SGleb Smirnoff (cmd->arg1+31)/32 )
18953b3a8eb9SGleb Smirnoff goto bad_size;
18963b3a8eb9SGleb Smirnoff break;
18973b3a8eb9SGleb Smirnoff
18983b3a8eb9SGleb Smirnoff case O_IP_SRC_LOOKUP:
189954e5669dSAndrey V. Elsukov if (cmdlen > F_INSN_SIZE(ipfw_insn_u32))
190054e5669dSAndrey V. Elsukov goto bad_size;
19013b3a8eb9SGleb Smirnoff case O_IP_DST_LOOKUP:
19021058f177SAlexander V. Chernikov if (cmd->arg1 >= V_fw_tables_max) {
19033b3a8eb9SGleb Smirnoff printf("ipfw: invalid table number %d\n",
19043b3a8eb9SGleb Smirnoff cmd->arg1);
19053b3a8eb9SGleb Smirnoff return (EINVAL);
19063b3a8eb9SGleb Smirnoff }
19073b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn) &&
19083b3a8eb9SGleb Smirnoff cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 1 &&
19093b3a8eb9SGleb Smirnoff cmdlen != F_INSN_SIZE(ipfw_insn_u32))
19103b3a8eb9SGleb Smirnoff goto bad_size;
191174b22066SAlexander V. Chernikov ci->object_opcodes++;
19123b3a8eb9SGleb Smirnoff break;
1913914bffb6SAlexander V. Chernikov case O_IP_FLOW_LOOKUP:
191481cac390SArseny Smalyuk case O_MAC_DST_LOOKUP:
191581cac390SArseny Smalyuk case O_MAC_SRC_LOOKUP:
1916914bffb6SAlexander V. Chernikov if (cmd->arg1 >= V_fw_tables_max) {
1917914bffb6SAlexander V. Chernikov printf("ipfw: invalid table number %d\n",
1918914bffb6SAlexander V. Chernikov cmd->arg1);
1919914bffb6SAlexander V. Chernikov return (EINVAL);
1920914bffb6SAlexander V. Chernikov }
1921914bffb6SAlexander V. Chernikov if (cmdlen != F_INSN_SIZE(ipfw_insn) &&
1922914bffb6SAlexander V. Chernikov cmdlen != F_INSN_SIZE(ipfw_insn_u32))
1923914bffb6SAlexander V. Chernikov goto bad_size;
192474b22066SAlexander V. Chernikov ci->object_opcodes++;
1925914bffb6SAlexander V. Chernikov break;
19263b3a8eb9SGleb Smirnoff case O_MACADDR2:
19273b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn_mac))
19283b3a8eb9SGleb Smirnoff goto bad_size;
19293b3a8eb9SGleb Smirnoff break;
19303b3a8eb9SGleb Smirnoff
19313b3a8eb9SGleb Smirnoff case O_NOP:
19323b3a8eb9SGleb Smirnoff case O_IPID:
19333b3a8eb9SGleb Smirnoff case O_IPTTL:
19343b3a8eb9SGleb Smirnoff case O_IPLEN:
19353b3a8eb9SGleb Smirnoff case O_TCPDATALEN:
1936978f2d17SAndrey V. Elsukov case O_TCPMSS:
19373b3a8eb9SGleb Smirnoff case O_TCPWIN:
19383b3a8eb9SGleb Smirnoff case O_TAGGED:
19393b3a8eb9SGleb Smirnoff if (cmdlen < 1 || cmdlen > 31)
19403b3a8eb9SGleb Smirnoff goto bad_size;
19413b3a8eb9SGleb Smirnoff break;
19423b3a8eb9SGleb Smirnoff
19434037b828SAlexander V. Chernikov case O_DSCP:
1944fc727ad6SBoris Lytochkin case O_MARK:
19454037b828SAlexander V. Chernikov if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 1)
19464037b828SAlexander V. Chernikov goto bad_size;
19474037b828SAlexander V. Chernikov break;
19484037b828SAlexander V. Chernikov
19493b3a8eb9SGleb Smirnoff case O_MAC_TYPE:
19503b3a8eb9SGleb Smirnoff case O_IP_SRCPORT:
19513b3a8eb9SGleb Smirnoff case O_IP_DSTPORT: /* XXX artificial limit, 30 port pairs */
19523b3a8eb9SGleb Smirnoff if (cmdlen < 2 || cmdlen > 31)
19533b3a8eb9SGleb Smirnoff goto bad_size;
19543b3a8eb9SGleb Smirnoff break;
19553b3a8eb9SGleb Smirnoff
19563b3a8eb9SGleb Smirnoff case O_RECV:
19573b3a8eb9SGleb Smirnoff case O_XMIT:
19583b3a8eb9SGleb Smirnoff case O_VIA:
19593b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn_if))
19603b3a8eb9SGleb Smirnoff goto bad_size;
1961748c9559SAndrey V. Elsukov ci->object_opcodes++;
19623b3a8eb9SGleb Smirnoff break;
19633b3a8eb9SGleb Smirnoff
19643b3a8eb9SGleb Smirnoff case O_ALTQ:
19653b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn_altq))
19663b3a8eb9SGleb Smirnoff goto bad_size;
19673b3a8eb9SGleb Smirnoff break;
19683b3a8eb9SGleb Smirnoff
19693b3a8eb9SGleb Smirnoff case O_PIPE:
19703b3a8eb9SGleb Smirnoff case O_QUEUE:
19713b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn))
19723b3a8eb9SGleb Smirnoff goto bad_size;
19733b3a8eb9SGleb Smirnoff goto check_action;
19743b3a8eb9SGleb Smirnoff
19753b3a8eb9SGleb Smirnoff case O_FORWARD_IP:
19763b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn_sa))
19773b3a8eb9SGleb Smirnoff goto bad_size;
19783b3a8eb9SGleb Smirnoff goto check_action;
19793b3a8eb9SGleb Smirnoff #ifdef INET6
19803b3a8eb9SGleb Smirnoff case O_FORWARD_IP6:
19813b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn_sa6))
19823b3a8eb9SGleb Smirnoff goto bad_size;
19833b3a8eb9SGleb Smirnoff goto check_action;
19843b3a8eb9SGleb Smirnoff #endif /* INET6 */
19853b3a8eb9SGleb Smirnoff
19863b3a8eb9SGleb Smirnoff case O_DIVERT:
19873b3a8eb9SGleb Smirnoff case O_TEE:
19883b3a8eb9SGleb Smirnoff if (ip_divert_ptr == NULL)
19893b3a8eb9SGleb Smirnoff return EINVAL;
19903b3a8eb9SGleb Smirnoff else
19913b3a8eb9SGleb Smirnoff goto check_size;
19923b3a8eb9SGleb Smirnoff case O_NETGRAPH:
19933b3a8eb9SGleb Smirnoff case O_NGTEE:
19943b3a8eb9SGleb Smirnoff if (ng_ipfw_input_p == NULL)
19953b3a8eb9SGleb Smirnoff return EINVAL;
19963b3a8eb9SGleb Smirnoff else
19973b3a8eb9SGleb Smirnoff goto check_size;
19983b3a8eb9SGleb Smirnoff case O_NAT:
19993b3a8eb9SGleb Smirnoff if (!IPFW_NAT_LOADED)
20003b3a8eb9SGleb Smirnoff return EINVAL;
20013b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn_nat))
20023b3a8eb9SGleb Smirnoff goto bad_size;
20033b3a8eb9SGleb Smirnoff goto check_action;
20043b3a8eb9SGleb Smirnoff case O_CHECK_STATE:
2005ed22e564SAndrey V. Elsukov ci->object_opcodes++;
200605b9737fSGleb Smirnoff goto check_size;
2007fc727ad6SBoris Lytochkin case O_SETMARK:
2008fc727ad6SBoris Lytochkin if (cmdlen != F_INSN_SIZE(ipfw_insn_u32))
2009fc727ad6SBoris Lytochkin goto bad_size;
2010fc727ad6SBoris Lytochkin goto check_action;
201105b9737fSGleb Smirnoff case O_REJECT:
201205b9737fSGleb Smirnoff /* "unreach needfrag" has variable len. */
201305b9737fSGleb Smirnoff if ((cmdlen == F_INSN_SIZE(ipfw_insn) ||
201405b9737fSGleb Smirnoff cmdlen == F_INSN_SIZE(ipfw_insn_u16)))
201505b9737fSGleb Smirnoff goto check_action;
2016ed22e564SAndrey V. Elsukov /* FALLTHROUGH */
2017ed22e564SAndrey V. Elsukov case O_FORWARD_MAC: /* XXX not implemented yet */
20183b3a8eb9SGleb Smirnoff case O_COUNT:
20193b3a8eb9SGleb Smirnoff case O_ACCEPT:
20203b3a8eb9SGleb Smirnoff case O_DENY:
2021ae01d73cSAlexander V. Chernikov case O_SETDSCP:
20223b3a8eb9SGleb Smirnoff #ifdef INET6
20233b3a8eb9SGleb Smirnoff case O_UNREACH6:
20243b3a8eb9SGleb Smirnoff #endif
20253b3a8eb9SGleb Smirnoff case O_SKIPTO:
20263b3a8eb9SGleb Smirnoff case O_REASS:
20273b3a8eb9SGleb Smirnoff case O_CALLRETURN:
20283b3a8eb9SGleb Smirnoff check_size:
20293b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn))
20303b3a8eb9SGleb Smirnoff goto bad_size;
20313b3a8eb9SGleb Smirnoff check_action:
20323b3a8eb9SGleb Smirnoff if (have_action) {
20333b3a8eb9SGleb Smirnoff printf("ipfw: opcode %d, multiple actions"
20343b3a8eb9SGleb Smirnoff " not allowed\n",
20353b3a8eb9SGleb Smirnoff cmd->opcode);
20367e767c79SAlexander V. Chernikov return (EINVAL);
20373b3a8eb9SGleb Smirnoff }
20383b3a8eb9SGleb Smirnoff have_action = 1;
20393b3a8eb9SGleb Smirnoff if (l != cmdlen) {
20403b3a8eb9SGleb Smirnoff printf("ipfw: opcode %d, action must be"
20413b3a8eb9SGleb Smirnoff " last opcode\n",
20423b3a8eb9SGleb Smirnoff cmd->opcode);
20437e767c79SAlexander V. Chernikov return (EINVAL);
20443b3a8eb9SGleb Smirnoff }
20453b3a8eb9SGleb Smirnoff break;
20463b3a8eb9SGleb Smirnoff #ifdef INET6
20473b3a8eb9SGleb Smirnoff case O_IP6_SRC:
20483b3a8eb9SGleb Smirnoff case O_IP6_DST:
20493b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(struct in6_addr) +
20503b3a8eb9SGleb Smirnoff F_INSN_SIZE(ipfw_insn))
20513b3a8eb9SGleb Smirnoff goto bad_size;
20523b3a8eb9SGleb Smirnoff break;
20533b3a8eb9SGleb Smirnoff
20543b3a8eb9SGleb Smirnoff case O_FLOW6ID:
20553b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) +
20563b3a8eb9SGleb Smirnoff ((ipfw_insn_u32 *)cmd)->o.arg1)
20573b3a8eb9SGleb Smirnoff goto bad_size;
20583b3a8eb9SGleb Smirnoff break;
20593b3a8eb9SGleb Smirnoff
20603b3a8eb9SGleb Smirnoff case O_IP6_SRC_MASK:
20613b3a8eb9SGleb Smirnoff case O_IP6_DST_MASK:
20623b3a8eb9SGleb Smirnoff if ( !(cmdlen & 1) || cmdlen > 127)
20633b3a8eb9SGleb Smirnoff goto bad_size;
20643b3a8eb9SGleb Smirnoff break;
20653b3a8eb9SGleb Smirnoff case O_ICMP6TYPE:
20663b3a8eb9SGleb Smirnoff if( cmdlen != F_INSN_SIZE( ipfw_insn_icmp6 ) )
20673b3a8eb9SGleb Smirnoff goto bad_size;
20683b3a8eb9SGleb Smirnoff break;
20693b3a8eb9SGleb Smirnoff #endif
20703b3a8eb9SGleb Smirnoff
20713b3a8eb9SGleb Smirnoff default:
20723b3a8eb9SGleb Smirnoff switch (cmd->opcode) {
20733b3a8eb9SGleb Smirnoff #ifndef INET6
20743b3a8eb9SGleb Smirnoff case O_IP6_SRC_ME:
20753b3a8eb9SGleb Smirnoff case O_IP6_DST_ME:
20763b3a8eb9SGleb Smirnoff case O_EXT_HDR:
20773b3a8eb9SGleb Smirnoff case O_IP6:
20783b3a8eb9SGleb Smirnoff case O_UNREACH6:
20793b3a8eb9SGleb Smirnoff case O_IP6_SRC:
20803b3a8eb9SGleb Smirnoff case O_IP6_DST:
20813b3a8eb9SGleb Smirnoff case O_FLOW6ID:
20823b3a8eb9SGleb Smirnoff case O_IP6_SRC_MASK:
20833b3a8eb9SGleb Smirnoff case O_IP6_DST_MASK:
20843b3a8eb9SGleb Smirnoff case O_ICMP6TYPE:
20853b3a8eb9SGleb Smirnoff printf("ipfw: no IPv6 support in kernel\n");
20867e767c79SAlexander V. Chernikov return (EPROTONOSUPPORT);
20873b3a8eb9SGleb Smirnoff #endif
20883b3a8eb9SGleb Smirnoff default:
20893b3a8eb9SGleb Smirnoff printf("ipfw: opcode %d, unknown opcode\n",
20903b3a8eb9SGleb Smirnoff cmd->opcode);
20917e767c79SAlexander V. Chernikov return (EINVAL);
20923b3a8eb9SGleb Smirnoff }
20933b3a8eb9SGleb Smirnoff }
20943b3a8eb9SGleb Smirnoff }
20953b3a8eb9SGleb Smirnoff if (have_action == 0) {
20963b3a8eb9SGleb Smirnoff printf("ipfw: missing action\n");
20977e767c79SAlexander V. Chernikov return (EINVAL);
20983b3a8eb9SGleb Smirnoff }
20993b3a8eb9SGleb Smirnoff return 0;
21003b3a8eb9SGleb Smirnoff
21013b3a8eb9SGleb Smirnoff bad_size:
21023b3a8eb9SGleb Smirnoff printf("ipfw: opcode %d size %d wrong\n",
21033b3a8eb9SGleb Smirnoff cmd->opcode, cmdlen);
21047e767c79SAlexander V. Chernikov return (EINVAL);
21053b3a8eb9SGleb Smirnoff }
21063b3a8eb9SGleb Smirnoff
21073b3a8eb9SGleb Smirnoff /*
21083b3a8eb9SGleb Smirnoff * Translation of requests for compatibility with FreeBSD 7.2/8.
21093b3a8eb9SGleb Smirnoff * a static variable tells us if we have an old client from userland,
21103b3a8eb9SGleb Smirnoff * and if necessary we translate requests and responses between the
21113b3a8eb9SGleb Smirnoff * two formats.
21123b3a8eb9SGleb Smirnoff */
21133b3a8eb9SGleb Smirnoff static int is7 = 0;
21143b3a8eb9SGleb Smirnoff
21153b3a8eb9SGleb Smirnoff struct ip_fw7 {
21163b3a8eb9SGleb Smirnoff struct ip_fw7 *next; /* linked list of rules */
21173b3a8eb9SGleb Smirnoff struct ip_fw7 *next_rule; /* ptr to next [skipto] rule */
21183b3a8eb9SGleb Smirnoff /* 'next_rule' is used to pass up 'set_disable' status */
21193b3a8eb9SGleb Smirnoff
21203b3a8eb9SGleb Smirnoff uint16_t act_ofs; /* offset of action in 32-bit units */
21213b3a8eb9SGleb Smirnoff uint16_t cmd_len; /* # of 32-bit words in cmd */
21223b3a8eb9SGleb Smirnoff uint16_t rulenum; /* rule number */
21233b3a8eb9SGleb Smirnoff uint8_t set; /* rule set (0..31) */
21243b3a8eb9SGleb Smirnoff // #define RESVD_SET 31 /* set for default and persistent rules */
21253b3a8eb9SGleb Smirnoff uint8_t _pad; /* padding */
21263b3a8eb9SGleb Smirnoff // uint32_t id; /* rule id, only in v.8 */
21273b3a8eb9SGleb Smirnoff /* These fields are present in all rules. */
21283b3a8eb9SGleb Smirnoff uint64_t pcnt; /* Packet counter */
21293b3a8eb9SGleb Smirnoff uint64_t bcnt; /* Byte counter */
21303b3a8eb9SGleb Smirnoff uint32_t timestamp; /* tv_sec of last match */
21313b3a8eb9SGleb Smirnoff
21323b3a8eb9SGleb Smirnoff ipfw_insn cmd[1]; /* storage for commands */
21333b3a8eb9SGleb Smirnoff };
21343b3a8eb9SGleb Smirnoff
21357e767c79SAlexander V. Chernikov static int convert_rule_to_7(struct ip_fw_rule0 *rule);
21367e767c79SAlexander V. Chernikov static int convert_rule_to_8(struct ip_fw_rule0 *rule);
21373b3a8eb9SGleb Smirnoff
21383b3a8eb9SGleb Smirnoff #ifndef RULESIZE7
21393b3a8eb9SGleb Smirnoff #define RULESIZE7(rule) (sizeof(struct ip_fw7) + \
21403b3a8eb9SGleb Smirnoff ((struct ip_fw7 *)(rule))->cmd_len * 4 - 4)
21413b3a8eb9SGleb Smirnoff #endif
21423b3a8eb9SGleb Smirnoff
21433b3a8eb9SGleb Smirnoff /*
21443b3a8eb9SGleb Smirnoff * Copy the static and dynamic rules to the supplied buffer
21453b3a8eb9SGleb Smirnoff * and return the amount of space actually used.
21463b3a8eb9SGleb Smirnoff * Must be run under IPFW_UH_RLOCK
21473b3a8eb9SGleb Smirnoff */
21483b3a8eb9SGleb Smirnoff static size_t
ipfw_getrules(struct ip_fw_chain * chain,void * buf,size_t space)21493b3a8eb9SGleb Smirnoff ipfw_getrules(struct ip_fw_chain *chain, void *buf, size_t space)
21503b3a8eb9SGleb Smirnoff {
21513b3a8eb9SGleb Smirnoff char *bp = buf;
21523b3a8eb9SGleb Smirnoff char *ep = bp + space;
21537e767c79SAlexander V. Chernikov struct ip_fw *rule;
21547e767c79SAlexander V. Chernikov struct ip_fw_rule0 *dst;
2155584b675eSKonstantin Belousov struct timeval boottime;
21561832a7b3SAlexander V. Chernikov int error, i, l, warnflag;
21573b3a8eb9SGleb Smirnoff time_t boot_seconds;
21583b3a8eb9SGleb Smirnoff
21591832a7b3SAlexander V. Chernikov warnflag = 0;
21601832a7b3SAlexander V. Chernikov
2161584b675eSKonstantin Belousov getboottime(&boottime);
21623b3a8eb9SGleb Smirnoff boot_seconds = boottime.tv_sec;
21633b3a8eb9SGleb Smirnoff for (i = 0; i < chain->n_rules; i++) {
21643b3a8eb9SGleb Smirnoff rule = chain->map[i];
21653b3a8eb9SGleb Smirnoff
21663b3a8eb9SGleb Smirnoff if (is7) {
21673b3a8eb9SGleb Smirnoff /* Convert rule to FreeBSd 7.2 format */
21683b3a8eb9SGleb Smirnoff l = RULESIZE7(rule);
21693b3a8eb9SGleb Smirnoff if (bp + l + sizeof(uint32_t) <= ep) {
21703b3a8eb9SGleb Smirnoff bcopy(rule, bp, l + sizeof(uint32_t));
217174b22066SAlexander V. Chernikov error = set_legacy_obj_kidx(chain,
21727e767c79SAlexander V. Chernikov (struct ip_fw_rule0 *)bp);
2173b074b7bbSAlexander V. Chernikov if (error != 0)
2174b074b7bbSAlexander V. Chernikov return (0);
21757e767c79SAlexander V. Chernikov error = convert_rule_to_7((struct ip_fw_rule0 *) bp);
21763b3a8eb9SGleb Smirnoff if (error)
21773b3a8eb9SGleb Smirnoff return 0; /*XXX correct? */
21783b3a8eb9SGleb Smirnoff /*
21793b3a8eb9SGleb Smirnoff * XXX HACK. Store the disable mask in the "next"
21803b3a8eb9SGleb Smirnoff * pointer in a wild attempt to keep the ABI the same.
21813b3a8eb9SGleb Smirnoff * Why do we do this on EVERY rule?
21823b3a8eb9SGleb Smirnoff */
21833b3a8eb9SGleb Smirnoff bcopy(&V_set_disable,
21843b3a8eb9SGleb Smirnoff &(((struct ip_fw7 *)bp)->next_rule),
21853b3a8eb9SGleb Smirnoff sizeof(V_set_disable));
21863b3a8eb9SGleb Smirnoff if (((struct ip_fw7 *)bp)->timestamp)
21873b3a8eb9SGleb Smirnoff ((struct ip_fw7 *)bp)->timestamp += boot_seconds;
21883b3a8eb9SGleb Smirnoff bp += l;
21893b3a8eb9SGleb Smirnoff }
21903b3a8eb9SGleb Smirnoff continue; /* go to next rule */
21913b3a8eb9SGleb Smirnoff }
21923b3a8eb9SGleb Smirnoff
21937e767c79SAlexander V. Chernikov l = RULEUSIZE0(rule);
21943b3a8eb9SGleb Smirnoff if (bp + l > ep) { /* should not happen */
21953b3a8eb9SGleb Smirnoff printf("overflow dumping static rules\n");
21963b3a8eb9SGleb Smirnoff break;
21973b3a8eb9SGleb Smirnoff }
21987e767c79SAlexander V. Chernikov dst = (struct ip_fw_rule0 *)bp;
21997e767c79SAlexander V. Chernikov export_rule0(rule, dst, l);
220074b22066SAlexander V. Chernikov error = set_legacy_obj_kidx(chain, dst);
2201b074b7bbSAlexander V. Chernikov
22023b3a8eb9SGleb Smirnoff /*
22033b3a8eb9SGleb Smirnoff * XXX HACK. Store the disable mask in the "next"
22043b3a8eb9SGleb Smirnoff * pointer in a wild attempt to keep the ABI the same.
22053b3a8eb9SGleb Smirnoff * Why do we do this on EVERY rule?
2206ac35ff17SAlexander V. Chernikov *
2207ac35ff17SAlexander V. Chernikov * XXX: "ipfw set show" (ab)uses IP_FW_GET to read disabled mask
2208ac35ff17SAlexander V. Chernikov * so we need to fail _after_ saving at least one mask.
22093b3a8eb9SGleb Smirnoff */
22103b3a8eb9SGleb Smirnoff bcopy(&V_set_disable, &dst->next_rule, sizeof(V_set_disable));
22113b3a8eb9SGleb Smirnoff if (dst->timestamp)
22123b3a8eb9SGleb Smirnoff dst->timestamp += boot_seconds;
22133b3a8eb9SGleb Smirnoff bp += l;
2214ac35ff17SAlexander V. Chernikov
2215ac35ff17SAlexander V. Chernikov if (error != 0) {
22161832a7b3SAlexander V. Chernikov if (error == 2) {
22171832a7b3SAlexander V. Chernikov /* Non-fatal table rewrite error. */
22181832a7b3SAlexander V. Chernikov warnflag = 1;
22191832a7b3SAlexander V. Chernikov continue;
22201832a7b3SAlexander V. Chernikov }
2221ac35ff17SAlexander V. Chernikov printf("Stop on rule %d. Fail to convert table\n",
2222ac35ff17SAlexander V. Chernikov rule->rulenum);
2223ac35ff17SAlexander V. Chernikov break;
2224ac35ff17SAlexander V. Chernikov }
22253b3a8eb9SGleb Smirnoff }
22261832a7b3SAlexander V. Chernikov if (warnflag != 0)
22271832a7b3SAlexander V. Chernikov printf("ipfw: process %s is using legacy interfaces,"
22281832a7b3SAlexander V. Chernikov " consider rebuilding\n", "");
22292e089d5cSAlexander V. Chernikov ipfw_get_dynamic(chain, &bp, ep); /* protected by the dynamic lock */
22303b3a8eb9SGleb Smirnoff return (bp - (char *)buf);
22313b3a8eb9SGleb Smirnoff }
22323b3a8eb9SGleb Smirnoff
2233563b5ab1SAlexander V. Chernikov struct dump_args {
2234563b5ab1SAlexander V. Chernikov uint32_t b; /* start rule */
2235563b5ab1SAlexander V. Chernikov uint32_t e; /* end rule */
2236563b5ab1SAlexander V. Chernikov uint32_t rcount; /* number of rules */
2237563b5ab1SAlexander V. Chernikov uint32_t rsize; /* rules size */
2238563b5ab1SAlexander V. Chernikov uint32_t tcount; /* number of tables */
22397e767c79SAlexander V. Chernikov int rcounters; /* counters */
2240cefe3d67SAndrey V. Elsukov uint32_t *bmask; /* index bitmask of used named objects */
2241563b5ab1SAlexander V. Chernikov };
2242563b5ab1SAlexander V. Chernikov
22435dc5a0e0SAndrey V. Elsukov void
ipfw_export_obj_ntlv(struct named_object * no,ipfw_obj_ntlv * ntlv)22445dc5a0e0SAndrey V. Elsukov ipfw_export_obj_ntlv(struct named_object *no, ipfw_obj_ntlv *ntlv)
22455dc5a0e0SAndrey V. Elsukov {
22465dc5a0e0SAndrey V. Elsukov
22475dc5a0e0SAndrey V. Elsukov ntlv->head.type = no->etlv;
22485dc5a0e0SAndrey V. Elsukov ntlv->head.length = sizeof(*ntlv);
22495dc5a0e0SAndrey V. Elsukov ntlv->idx = no->kidx;
22505dc5a0e0SAndrey V. Elsukov strlcpy(ntlv->name, no->name, sizeof(ntlv->name));
22515dc5a0e0SAndrey V. Elsukov }
22525dc5a0e0SAndrey V. Elsukov
2253563b5ab1SAlexander V. Chernikov /*
225474b22066SAlexander V. Chernikov * Export named object info in instance @ni, identified by @kidx
225574b22066SAlexander V. Chernikov * to ipfw_obj_ntlv. TLV is allocated from @sd space.
225674b22066SAlexander V. Chernikov *
225774b22066SAlexander V. Chernikov * Returns 0 on success.
225874b22066SAlexander V. Chernikov */
225974b22066SAlexander V. Chernikov static int
export_objhash_ntlv(struct namedobj_instance * ni,uint16_t kidx,struct sockopt_data * sd)226074b22066SAlexander V. Chernikov export_objhash_ntlv(struct namedobj_instance *ni, uint16_t kidx,
226174b22066SAlexander V. Chernikov struct sockopt_data *sd)
226274b22066SAlexander V. Chernikov {
226374b22066SAlexander V. Chernikov struct named_object *no;
226474b22066SAlexander V. Chernikov ipfw_obj_ntlv *ntlv;
226574b22066SAlexander V. Chernikov
226674b22066SAlexander V. Chernikov no = ipfw_objhash_lookup_kidx(ni, kidx);
226774b22066SAlexander V. Chernikov KASSERT(no != NULL, ("invalid object kernel index passed"));
226874b22066SAlexander V. Chernikov
226974b22066SAlexander V. Chernikov ntlv = (ipfw_obj_ntlv *)ipfw_get_sopt_space(sd, sizeof(*ntlv));
227074b22066SAlexander V. Chernikov if (ntlv == NULL)
227174b22066SAlexander V. Chernikov return (ENOMEM);
227274b22066SAlexander V. Chernikov
22735dc5a0e0SAndrey V. Elsukov ipfw_export_obj_ntlv(no, ntlv);
227474b22066SAlexander V. Chernikov return (0);
227574b22066SAlexander V. Chernikov }
227674b22066SAlexander V. Chernikov
2277563b5ab1SAlexander V. Chernikov static int
export_named_objects(struct namedobj_instance * ni,struct dump_args * da,struct sockopt_data * sd)2278cefe3d67SAndrey V. Elsukov export_named_objects(struct namedobj_instance *ni, struct dump_args *da,
2279cefe3d67SAndrey V. Elsukov struct sockopt_data *sd)
2280563b5ab1SAlexander V. Chernikov {
2281cefe3d67SAndrey V. Elsukov int error, i;
2282563b5ab1SAlexander V. Chernikov
2283cefe3d67SAndrey V. Elsukov for (i = 0; i < IPFW_TABLES_MAX && da->tcount > 0; i++) {
2284cefe3d67SAndrey V. Elsukov if ((da->bmask[i / 32] & (1 << (i % 32))) == 0)
2285cefe3d67SAndrey V. Elsukov continue;
2286cefe3d67SAndrey V. Elsukov if ((error = export_objhash_ntlv(ni, i, sd)) != 0)
2287cefe3d67SAndrey V. Elsukov return (error);
2288cefe3d67SAndrey V. Elsukov da->tcount--;
2289cefe3d67SAndrey V. Elsukov }
2290cefe3d67SAndrey V. Elsukov return (0);
2291cefe3d67SAndrey V. Elsukov }
2292cefe3d67SAndrey V. Elsukov
2293cefe3d67SAndrey V. Elsukov static int
dump_named_objects(struct ip_fw_chain * ch,struct dump_args * da,struct sockopt_data * sd)2294cefe3d67SAndrey V. Elsukov dump_named_objects(struct ip_fw_chain *ch, struct dump_args *da,
2295cefe3d67SAndrey V. Elsukov struct sockopt_data *sd)
2296cefe3d67SAndrey V. Elsukov {
2297cefe3d67SAndrey V. Elsukov ipfw_obj_ctlv *ctlv;
2298cefe3d67SAndrey V. Elsukov int error;
2299cefe3d67SAndrey V. Elsukov
2300cefe3d67SAndrey V. Elsukov MPASS(da->tcount > 0);
2301563b5ab1SAlexander V. Chernikov /* Header first */
2302563b5ab1SAlexander V. Chernikov ctlv = (ipfw_obj_ctlv *)ipfw_get_sopt_space(sd, sizeof(*ctlv));
2303563b5ab1SAlexander V. Chernikov if (ctlv == NULL)
2304563b5ab1SAlexander V. Chernikov return (ENOMEM);
2305563b5ab1SAlexander V. Chernikov ctlv->head.type = IPFW_TLV_TBLNAME_LIST;
2306563b5ab1SAlexander V. Chernikov ctlv->head.length = da->tcount * sizeof(ipfw_obj_ntlv) +
2307563b5ab1SAlexander V. Chernikov sizeof(*ctlv);
2308563b5ab1SAlexander V. Chernikov ctlv->count = da->tcount;
2309563b5ab1SAlexander V. Chernikov ctlv->objsize = sizeof(ipfw_obj_ntlv);
2310563b5ab1SAlexander V. Chernikov
2311cefe3d67SAndrey V. Elsukov /* Dump table names first (if any) */
2312cefe3d67SAndrey V. Elsukov error = export_named_objects(ipfw_get_table_objhash(ch), da, sd);
2313cefe3d67SAndrey V. Elsukov if (error != 0)
2314563b5ab1SAlexander V. Chernikov return (error);
2315cefe3d67SAndrey V. Elsukov /* Then dump another named objects */
2316cefe3d67SAndrey V. Elsukov da->bmask += IPFW_TABLES_MAX / 32;
2317cefe3d67SAndrey V. Elsukov return (export_named_objects(CHAIN_TO_SRV(ch), da, sd));
2318563b5ab1SAlexander V. Chernikov }
2319563b5ab1SAlexander V. Chernikov
2320cefe3d67SAndrey V. Elsukov /*
2321cefe3d67SAndrey V. Elsukov * Dumps static rules with table TLVs in buffer @sd.
2322cefe3d67SAndrey V. Elsukov *
2323cefe3d67SAndrey V. Elsukov * Returns 0 on success.
2324cefe3d67SAndrey V. Elsukov */
2325cefe3d67SAndrey V. Elsukov static int
dump_static_rules(struct ip_fw_chain * chain,struct dump_args * da,struct sockopt_data * sd)2326cefe3d67SAndrey V. Elsukov dump_static_rules(struct ip_fw_chain *chain, struct dump_args *da,
2327cefe3d67SAndrey V. Elsukov struct sockopt_data *sd)
2328cefe3d67SAndrey V. Elsukov {
2329cefe3d67SAndrey V. Elsukov ipfw_obj_ctlv *ctlv;
2330cefe3d67SAndrey V. Elsukov struct ip_fw *krule;
2331cefe3d67SAndrey V. Elsukov caddr_t dst;
2332cefe3d67SAndrey V. Elsukov int i, l;
2333cefe3d67SAndrey V. Elsukov
2334563b5ab1SAlexander V. Chernikov /* Dump rules */
2335563b5ab1SAlexander V. Chernikov ctlv = (ipfw_obj_ctlv *)ipfw_get_sopt_space(sd, sizeof(*ctlv));
2336563b5ab1SAlexander V. Chernikov if (ctlv == NULL)
2337563b5ab1SAlexander V. Chernikov return (ENOMEM);
2338563b5ab1SAlexander V. Chernikov ctlv->head.type = IPFW_TLV_RULE_LIST;
2339563b5ab1SAlexander V. Chernikov ctlv->head.length = da->rsize + sizeof(*ctlv);
2340563b5ab1SAlexander V. Chernikov ctlv->count = da->rcount;
2341563b5ab1SAlexander V. Chernikov
2342563b5ab1SAlexander V. Chernikov for (i = da->b; i < da->e; i++) {
23437e767c79SAlexander V. Chernikov krule = chain->map[i];
2344563b5ab1SAlexander V. Chernikov
23457e767c79SAlexander V. Chernikov l = RULEUSIZE1(krule) + sizeof(ipfw_obj_tlv);
23467e767c79SAlexander V. Chernikov if (da->rcounters != 0)
23477e767c79SAlexander V. Chernikov l += sizeof(struct ip_fw_bcounter);
23487e767c79SAlexander V. Chernikov dst = (caddr_t)ipfw_get_sopt_space(sd, l);
23497e767c79SAlexander V. Chernikov if (dst == NULL)
2350563b5ab1SAlexander V. Chernikov return (ENOMEM);
2351563b5ab1SAlexander V. Chernikov
23527e767c79SAlexander V. Chernikov export_rule1(krule, dst, l, da->rcounters);
2353563b5ab1SAlexander V. Chernikov }
2354563b5ab1SAlexander V. Chernikov
2355563b5ab1SAlexander V. Chernikov return (0);
2356563b5ab1SAlexander V. Chernikov }
2357563b5ab1SAlexander V. Chernikov
2358cefe3d67SAndrey V. Elsukov int
ipfw_mark_object_kidx(uint32_t * bmask,uint16_t etlv,uint16_t kidx)2359cefe3d67SAndrey V. Elsukov ipfw_mark_object_kidx(uint32_t *bmask, uint16_t etlv, uint16_t kidx)
2360cefe3d67SAndrey V. Elsukov {
2361cefe3d67SAndrey V. Elsukov uint32_t bidx;
2362cefe3d67SAndrey V. Elsukov
2363cefe3d67SAndrey V. Elsukov /*
2364cefe3d67SAndrey V. Elsukov * Maintain separate bitmasks for table and non-table objects.
2365cefe3d67SAndrey V. Elsukov */
2366cefe3d67SAndrey V. Elsukov bidx = (etlv == IPFW_TLV_TBL_NAME) ? 0: IPFW_TABLES_MAX / 32;
2367cefe3d67SAndrey V. Elsukov bidx += kidx / 32;
2368cefe3d67SAndrey V. Elsukov if ((bmask[bidx] & (1 << (kidx % 32))) != 0)
2369cefe3d67SAndrey V. Elsukov return (0);
2370cefe3d67SAndrey V. Elsukov
2371cefe3d67SAndrey V. Elsukov bmask[bidx] |= 1 << (kidx % 32);
2372cefe3d67SAndrey V. Elsukov return (1);
2373cefe3d67SAndrey V. Elsukov }
2374cefe3d67SAndrey V. Elsukov
2375563b5ab1SAlexander V. Chernikov /*
237674b22066SAlexander V. Chernikov * Marks every object index used in @rule with bit in @bmask.
237774b22066SAlexander V. Chernikov * Used to generate bitmask of referenced tables/objects for given ruleset
237874b22066SAlexander V. Chernikov * or its part.
237974b22066SAlexander V. Chernikov */
2380cefe3d67SAndrey V. Elsukov static void
mark_rule_objects(struct ip_fw_chain * ch,struct ip_fw * rule,struct dump_args * da)2381cefe3d67SAndrey V. Elsukov mark_rule_objects(struct ip_fw_chain *ch, struct ip_fw *rule,
2382cefe3d67SAndrey V. Elsukov struct dump_args *da)
238374b22066SAlexander V. Chernikov {
238474b22066SAlexander V. Chernikov struct opcode_obj_rewrite *rw;
2385b2df1f7eSAndrey V. Elsukov ipfw_insn *cmd;
2386cefe3d67SAndrey V. Elsukov int cmdlen, l;
2387b2df1f7eSAndrey V. Elsukov uint16_t kidx;
238874b22066SAlexander V. Chernikov uint8_t subtype;
238974b22066SAlexander V. Chernikov
239074b22066SAlexander V. Chernikov l = rule->cmd_len;
239174b22066SAlexander V. Chernikov cmd = rule->cmd;
239274b22066SAlexander V. Chernikov cmdlen = 0;
239374b22066SAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) {
239474b22066SAlexander V. Chernikov cmdlen = F_LEN(cmd);
239574b22066SAlexander V. Chernikov
2396b2df1f7eSAndrey V. Elsukov rw = find_op_rw(cmd, &kidx, &subtype);
239774b22066SAlexander V. Chernikov if (rw == NULL)
239874b22066SAlexander V. Chernikov continue;
239974b22066SAlexander V. Chernikov
2400cefe3d67SAndrey V. Elsukov if (ipfw_mark_object_kidx(da->bmask, rw->etlv, kidx))
2401cefe3d67SAndrey V. Elsukov da->tcount++;
240274b22066SAlexander V. Chernikov }
240374b22066SAlexander V. Chernikov }
240474b22066SAlexander V. Chernikov
240574b22066SAlexander V. Chernikov /*
2406563b5ab1SAlexander V. Chernikov * Dumps requested objects data
2407563b5ab1SAlexander V. Chernikov * Data layout (version 0)(current):
2408563b5ab1SAlexander V. Chernikov * Request: [ ipfw_cfg_lheader ] + IPFW_CFG_GET_* flags
2409563b5ab1SAlexander V. Chernikov * size = ipfw_cfg_lheader.size
2410ce575f53SAlexander V. Chernikov * Reply: [ ipfw_cfg_lheader
2411563b5ab1SAlexander V. Chernikov * [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional)
24127e767c79SAlexander V. Chernikov * [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST)
24137e767c79SAlexander V. Chernikov * ipfw_obj_tlv(IPFW_TLV_RULE_ENT) [ ip_fw_bcounter (optional) ip_fw_rule ]
24147e767c79SAlexander V. Chernikov * ] (optional)
24157e767c79SAlexander V. Chernikov * [ ipfw_obj_ctlv(IPFW_TLV_STATE_LIST) ipfw_obj_dyntlv x N ] (optional)
2416563b5ab1SAlexander V. Chernikov * ]
2417563b5ab1SAlexander V. Chernikov * * NOTE IPFW_TLV_STATE_LIST has the single valid field: objsize.
2418563b5ab1SAlexander V. Chernikov * The rest (size, count) are set to zero and needs to be ignored.
2419563b5ab1SAlexander V. Chernikov *
2420563b5ab1SAlexander V. Chernikov * Returns 0 on success.
2421563b5ab1SAlexander V. Chernikov */
2422563b5ab1SAlexander V. Chernikov static int
dump_config(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)2423e822d936SAlexander V. Chernikov dump_config(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
2424e822d936SAlexander V. Chernikov struct sockopt_data *sd)
2425563b5ab1SAlexander V. Chernikov {
2426cefe3d67SAndrey V. Elsukov struct dump_args da;
2427563b5ab1SAlexander V. Chernikov ipfw_cfg_lheader *hdr;
2428563b5ab1SAlexander V. Chernikov struct ip_fw *rule;
242918ad4197SAlexander V. Chernikov size_t sz, rnum;
2430cefe3d67SAndrey V. Elsukov uint32_t hdr_flags, *bmask;
2431563b5ab1SAlexander V. Chernikov int error, i;
2432563b5ab1SAlexander V. Chernikov
2433563b5ab1SAlexander V. Chernikov hdr = (ipfw_cfg_lheader *)ipfw_get_sopt_header(sd, sizeof(*hdr));
2434563b5ab1SAlexander V. Chernikov if (hdr == NULL)
2435563b5ab1SAlexander V. Chernikov return (EINVAL);
2436563b5ab1SAlexander V. Chernikov
2437563b5ab1SAlexander V. Chernikov error = 0;
2438563b5ab1SAlexander V. Chernikov bmask = NULL;
2439cefe3d67SAndrey V. Elsukov memset(&da, 0, sizeof(da));
2440cefe3d67SAndrey V. Elsukov /*
2441cefe3d67SAndrey V. Elsukov * Allocate needed state.
2442cefe3d67SAndrey V. Elsukov * Note we allocate 2xspace mask, for table & srv
2443cefe3d67SAndrey V. Elsukov */
2444cefe3d67SAndrey V. Elsukov if (hdr->flags & (IPFW_CFG_GET_STATIC | IPFW_CFG_GET_STATES))
2445cefe3d67SAndrey V. Elsukov da.bmask = bmask = malloc(
2446cefe3d67SAndrey V. Elsukov sizeof(uint32_t) * IPFW_TABLES_MAX * 2 / 32, M_TEMP,
2447cefe3d67SAndrey V. Elsukov M_WAITOK | M_ZERO);
2448563b5ab1SAlexander V. Chernikov IPFW_UH_RLOCK(chain);
2449563b5ab1SAlexander V. Chernikov
2450563b5ab1SAlexander V. Chernikov /*
2451563b5ab1SAlexander V. Chernikov * STAGE 1: Determine size/count for objects in range.
2452563b5ab1SAlexander V. Chernikov * Prepare used tables bitmask.
2453563b5ab1SAlexander V. Chernikov */
2454ce575f53SAlexander V. Chernikov sz = sizeof(ipfw_cfg_lheader);
2455563b5ab1SAlexander V. Chernikov da.e = chain->n_rules;
2456563b5ab1SAlexander V. Chernikov
24572aa75134SAlexander V. Chernikov if (hdr->end_rule != 0) {
24582aa75134SAlexander V. Chernikov /* Handle custom range */
24592aa75134SAlexander V. Chernikov if ((rnum = hdr->start_rule) > IPFW_DEFAULT_RULE)
24602aa75134SAlexander V. Chernikov rnum = IPFW_DEFAULT_RULE;
24612aa75134SAlexander V. Chernikov da.b = ipfw_find_rule(chain, rnum, 0);
2462288bf455SAndrey V. Elsukov rnum = (hdr->end_rule < IPFW_DEFAULT_RULE) ?
2463288bf455SAndrey V. Elsukov hdr->end_rule + 1: IPFW_DEFAULT_RULE;
2464288bf455SAndrey V. Elsukov da.e = ipfw_find_rule(chain, rnum, UINT32_MAX) + 1;
24652aa75134SAlexander V. Chernikov }
24662aa75134SAlexander V. Chernikov
2467563b5ab1SAlexander V. Chernikov if (hdr->flags & IPFW_CFG_GET_STATIC) {
2468563b5ab1SAlexander V. Chernikov for (i = da.b; i < da.e; i++) {
2469563b5ab1SAlexander V. Chernikov rule = chain->map[i];
24707e767c79SAlexander V. Chernikov da.rsize += RULEUSIZE1(rule) + sizeof(ipfw_obj_tlv);
2471563b5ab1SAlexander V. Chernikov da.rcount++;
247274b22066SAlexander V. Chernikov /* Update bitmask of used objects for given range */
2473cefe3d67SAndrey V. Elsukov mark_rule_objects(chain, rule, &da);
2474563b5ab1SAlexander V. Chernikov }
24757e767c79SAlexander V. Chernikov /* Add counters if requested */
24767e767c79SAlexander V. Chernikov if (hdr->flags & IPFW_CFG_GET_COUNTERS) {
24777e767c79SAlexander V. Chernikov da.rsize += sizeof(struct ip_fw_bcounter) * da.rcount;
24787e767c79SAlexander V. Chernikov da.rcounters = 1;
24797e767c79SAlexander V. Chernikov }
2480cefe3d67SAndrey V. Elsukov sz += da.rsize + sizeof(ipfw_obj_ctlv);
2481cefe3d67SAndrey V. Elsukov }
2482cefe3d67SAndrey V. Elsukov
2483cefe3d67SAndrey V. Elsukov if (hdr->flags & IPFW_CFG_GET_STATES) {
2484cefe3d67SAndrey V. Elsukov sz += sizeof(ipfw_obj_ctlv) +
2485cefe3d67SAndrey V. Elsukov ipfw_dyn_get_count(bmask, &i) * sizeof(ipfw_obj_dyntlv);
2486cefe3d67SAndrey V. Elsukov da.tcount += i;
2487cefe3d67SAndrey V. Elsukov }
2488563b5ab1SAlexander V. Chernikov
2489563b5ab1SAlexander V. Chernikov if (da.tcount > 0)
2490563b5ab1SAlexander V. Chernikov sz += da.tcount * sizeof(ipfw_obj_ntlv) +
2491563b5ab1SAlexander V. Chernikov sizeof(ipfw_obj_ctlv);
249218ad4197SAlexander V. Chernikov
249318ad4197SAlexander V. Chernikov /*
249418ad4197SAlexander V. Chernikov * Fill header anyway.
249518ad4197SAlexander V. Chernikov * Note we have to save header fields to stable storage
249618ad4197SAlexander V. Chernikov * buffer inside @sd can be flushed after dumping rules
249718ad4197SAlexander V. Chernikov */
2498563b5ab1SAlexander V. Chernikov hdr->size = sz;
2499ac35ff17SAlexander V. Chernikov hdr->set_mask = ~V_set_disable;
250018ad4197SAlexander V. Chernikov hdr_flags = hdr->flags;
250118ad4197SAlexander V. Chernikov hdr = NULL;
2502563b5ab1SAlexander V. Chernikov
2503563b5ab1SAlexander V. Chernikov if (sd->valsize < sz) {
250418ad4197SAlexander V. Chernikov error = ENOMEM;
250518ad4197SAlexander V. Chernikov goto cleanup;
2506563b5ab1SAlexander V. Chernikov }
2507563b5ab1SAlexander V. Chernikov
2508563b5ab1SAlexander V. Chernikov /* STAGE2: Store actual data */
2509cefe3d67SAndrey V. Elsukov if (da.tcount > 0) {
2510cefe3d67SAndrey V. Elsukov error = dump_named_objects(chain, &da, sd);
2511cefe3d67SAndrey V. Elsukov if (error != 0)
2512cefe3d67SAndrey V. Elsukov goto cleanup;
2513cefe3d67SAndrey V. Elsukov }
2514cefe3d67SAndrey V. Elsukov
251518ad4197SAlexander V. Chernikov if (hdr_flags & IPFW_CFG_GET_STATIC) {
2516cefe3d67SAndrey V. Elsukov error = dump_static_rules(chain, &da, sd);
251718ad4197SAlexander V. Chernikov if (error != 0)
251818ad4197SAlexander V. Chernikov goto cleanup;
2519563b5ab1SAlexander V. Chernikov }
2520563b5ab1SAlexander V. Chernikov
252118ad4197SAlexander V. Chernikov if (hdr_flags & IPFW_CFG_GET_STATES)
2522563b5ab1SAlexander V. Chernikov error = ipfw_dump_states(chain, sd);
2523563b5ab1SAlexander V. Chernikov
252418ad4197SAlexander V. Chernikov cleanup:
2525563b5ab1SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain);
2526563b5ab1SAlexander V. Chernikov
2527563b5ab1SAlexander V. Chernikov if (bmask != NULL)
2528563b5ab1SAlexander V. Chernikov free(bmask, M_TEMP);
2529563b5ab1SAlexander V. Chernikov
2530563b5ab1SAlexander V. Chernikov return (error);
2531563b5ab1SAlexander V. Chernikov }
2532563b5ab1SAlexander V. Chernikov
2533f81431ccSAndrey V. Elsukov int
ipfw_check_object_name_generic(const char * name)2534f81431ccSAndrey V. Elsukov ipfw_check_object_name_generic(const char *name)
25356c2997ffSAlexander V. Chernikov {
2536f81431ccSAndrey V. Elsukov int nsize;
25376c2997ffSAlexander V. Chernikov
2538f81431ccSAndrey V. Elsukov nsize = sizeof(((ipfw_obj_ntlv *)0)->name);
2539f81431ccSAndrey V. Elsukov if (strnlen(name, nsize) == nsize)
2540f81431ccSAndrey V. Elsukov return (EINVAL);
2541f81431ccSAndrey V. Elsukov if (name[0] == '\0')
2542f81431ccSAndrey V. Elsukov return (EINVAL);
25436c2997ffSAlexander V. Chernikov return (0);
25446c2997ffSAlexander V. Chernikov }
25456c2997ffSAlexander V. Chernikov
25466c2997ffSAlexander V. Chernikov /*
254774b22066SAlexander V. Chernikov * Creates non-existent objects referenced by rule.
254874b22066SAlexander V. Chernikov *
254974b22066SAlexander V. Chernikov * Return 0 on success.
255074b22066SAlexander V. Chernikov */
255174b22066SAlexander V. Chernikov int
create_objects_compat(struct ip_fw_chain * ch,ipfw_insn * cmd,struct obj_idx * oib,struct obj_idx * pidx,struct tid_info * ti)255274b22066SAlexander V. Chernikov create_objects_compat(struct ip_fw_chain *ch, ipfw_insn *cmd,
255374b22066SAlexander V. Chernikov struct obj_idx *oib, struct obj_idx *pidx, struct tid_info *ti)
255474b22066SAlexander V. Chernikov {
255574b22066SAlexander V. Chernikov struct opcode_obj_rewrite *rw;
255674b22066SAlexander V. Chernikov struct obj_idx *p;
255774b22066SAlexander V. Chernikov uint16_t kidx;
255874b22066SAlexander V. Chernikov int error;
255974b22066SAlexander V. Chernikov
256074b22066SAlexander V. Chernikov /*
256174b22066SAlexander V. Chernikov * Compatibility stuff: do actual creation for non-existing,
256274b22066SAlexander V. Chernikov * but referenced objects.
256374b22066SAlexander V. Chernikov */
256474b22066SAlexander V. Chernikov for (p = oib; p < pidx; p++) {
256574b22066SAlexander V. Chernikov if (p->kidx != 0)
256674b22066SAlexander V. Chernikov continue;
256774b22066SAlexander V. Chernikov
256874b22066SAlexander V. Chernikov ti->uidx = p->uidx;
256974b22066SAlexander V. Chernikov ti->type = p->type;
257074b22066SAlexander V. Chernikov ti->atype = 0;
257174b22066SAlexander V. Chernikov
2572b2df1f7eSAndrey V. Elsukov rw = find_op_rw(cmd + p->off, NULL, NULL);
257374b22066SAlexander V. Chernikov KASSERT(rw != NULL, ("Unable to find handler for op %d",
257474b22066SAlexander V. Chernikov (cmd + p->off)->opcode));
257574b22066SAlexander V. Chernikov
25769a5be809SAndrey V. Elsukov if (rw->create_object == NULL)
25779a5be809SAndrey V. Elsukov error = EOPNOTSUPP;
25789a5be809SAndrey V. Elsukov else
257974b22066SAlexander V. Chernikov error = rw->create_object(ch, ti, &kidx);
258074b22066SAlexander V. Chernikov if (error == 0) {
258174b22066SAlexander V. Chernikov p->kidx = kidx;
258274b22066SAlexander V. Chernikov continue;
258374b22066SAlexander V. Chernikov }
258474b22066SAlexander V. Chernikov
258574b22066SAlexander V. Chernikov /*
258674b22066SAlexander V. Chernikov * Error happened. We have to rollback everything.
258774b22066SAlexander V. Chernikov * Drop all already acquired references.
258874b22066SAlexander V. Chernikov */
258974b22066SAlexander V. Chernikov IPFW_UH_WLOCK(ch);
259074b22066SAlexander V. Chernikov unref_oib_objects(ch, cmd, oib, pidx);
259174b22066SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch);
259274b22066SAlexander V. Chernikov
259374b22066SAlexander V. Chernikov return (error);
259474b22066SAlexander V. Chernikov }
259574b22066SAlexander V. Chernikov
2596a1bddc75SAlexander V. Chernikov return (0);
259774b22066SAlexander V. Chernikov }
259874b22066SAlexander V. Chernikov
259974b22066SAlexander V. Chernikov /*
260074b22066SAlexander V. Chernikov * Compatibility function for old ipfw(8) binaries.
260174b22066SAlexander V. Chernikov * Rewrites table/nat kernel indices with userland ones.
260274b22066SAlexander V. Chernikov * Convert tables matching '/^\d+$/' to their atoi() value.
260374b22066SAlexander V. Chernikov * Use number 65535 for other tables.
260474b22066SAlexander V. Chernikov *
260574b22066SAlexander V. Chernikov * Returns 0 on success.
260674b22066SAlexander V. Chernikov */
260774b22066SAlexander V. Chernikov static int
set_legacy_obj_kidx(struct ip_fw_chain * ch,struct ip_fw_rule0 * rule)260874b22066SAlexander V. Chernikov set_legacy_obj_kidx(struct ip_fw_chain *ch, struct ip_fw_rule0 *rule)
260974b22066SAlexander V. Chernikov {
261074b22066SAlexander V. Chernikov struct opcode_obj_rewrite *rw;
2611b2df1f7eSAndrey V. Elsukov struct named_object *no;
2612b2df1f7eSAndrey V. Elsukov ipfw_insn *cmd;
261374b22066SAlexander V. Chernikov char *end;
261474b22066SAlexander V. Chernikov long val;
2615b2df1f7eSAndrey V. Elsukov int cmdlen, error, l;
2616b2df1f7eSAndrey V. Elsukov uint16_t kidx, uidx;
2617b2df1f7eSAndrey V. Elsukov uint8_t subtype;
261874b22066SAlexander V. Chernikov
261974b22066SAlexander V. Chernikov error = 0;
262074b22066SAlexander V. Chernikov
262174b22066SAlexander V. Chernikov l = rule->cmd_len;
262274b22066SAlexander V. Chernikov cmd = rule->cmd;
262374b22066SAlexander V. Chernikov cmdlen = 0;
262474b22066SAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) {
262574b22066SAlexander V. Chernikov cmdlen = F_LEN(cmd);
262674b22066SAlexander V. Chernikov
262774b22066SAlexander V. Chernikov /* Check if is index in given opcode */
2628b2df1f7eSAndrey V. Elsukov rw = find_op_rw(cmd, &kidx, &subtype);
2629b2df1f7eSAndrey V. Elsukov if (rw == NULL)
263074b22066SAlexander V. Chernikov continue;
263174b22066SAlexander V. Chernikov
263274b22066SAlexander V. Chernikov /* Try to find referenced kernel object */
263374b22066SAlexander V. Chernikov no = rw->find_bykidx(ch, kidx);
263474b22066SAlexander V. Chernikov if (no == NULL)
263574b22066SAlexander V. Chernikov continue;
263674b22066SAlexander V. Chernikov
263774b22066SAlexander V. Chernikov val = strtol(no->name, &end, 10);
263874b22066SAlexander V. Chernikov if (*end == '\0' && val < 65535) {
263974b22066SAlexander V. Chernikov uidx = val;
264074b22066SAlexander V. Chernikov } else {
264174b22066SAlexander V. Chernikov /*
264274b22066SAlexander V. Chernikov * We are called via legacy opcode.
264374b22066SAlexander V. Chernikov * Save error and show table as fake number
264474b22066SAlexander V. Chernikov * not to make ipfw(8) hang.
264574b22066SAlexander V. Chernikov */
264674b22066SAlexander V. Chernikov uidx = 65535;
264774b22066SAlexander V. Chernikov error = 2;
264874b22066SAlexander V. Chernikov }
264974b22066SAlexander V. Chernikov
265074b22066SAlexander V. Chernikov rw->update(cmd, uidx);
265174b22066SAlexander V. Chernikov }
265274b22066SAlexander V. Chernikov
265374b22066SAlexander V. Chernikov return (error);
265474b22066SAlexander V. Chernikov }
265574b22066SAlexander V. Chernikov
265674b22066SAlexander V. Chernikov /*
265774b22066SAlexander V. Chernikov * Unreferences all already-referenced objects in given @cmd rule,
265874b22066SAlexander V. Chernikov * using information in @oib.
265974b22066SAlexander V. Chernikov *
266074b22066SAlexander V. Chernikov * Used to rollback partially converted rule on error.
266174b22066SAlexander V. Chernikov */
2662f976a4edSAndrey V. Elsukov static void
unref_oib_objects(struct ip_fw_chain * ch,ipfw_insn * cmd,struct obj_idx * oib,struct obj_idx * end)266374b22066SAlexander V. Chernikov unref_oib_objects(struct ip_fw_chain *ch, ipfw_insn *cmd, struct obj_idx *oib,
266474b22066SAlexander V. Chernikov struct obj_idx *end)
266574b22066SAlexander V. Chernikov {
266674b22066SAlexander V. Chernikov struct opcode_obj_rewrite *rw;
266774b22066SAlexander V. Chernikov struct named_object *no;
266874b22066SAlexander V. Chernikov struct obj_idx *p;
266974b22066SAlexander V. Chernikov
267074b22066SAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(ch);
267174b22066SAlexander V. Chernikov
267274b22066SAlexander V. Chernikov for (p = oib; p < end; p++) {
267374b22066SAlexander V. Chernikov if (p->kidx == 0)
267474b22066SAlexander V. Chernikov continue;
267574b22066SAlexander V. Chernikov
2676b2df1f7eSAndrey V. Elsukov rw = find_op_rw(cmd + p->off, NULL, NULL);
267774b22066SAlexander V. Chernikov KASSERT(rw != NULL, ("Unable to find handler for op %d",
267874b22066SAlexander V. Chernikov (cmd + p->off)->opcode));
267974b22066SAlexander V. Chernikov
268074b22066SAlexander V. Chernikov /* Find & unref by existing idx */
268174b22066SAlexander V. Chernikov no = rw->find_bykidx(ch, p->kidx);
268274b22066SAlexander V. Chernikov KASSERT(no != NULL, ("Ref'd object %d disappeared", p->kidx));
268374b22066SAlexander V. Chernikov no->refcnt--;
268474b22066SAlexander V. Chernikov }
268574b22066SAlexander V. Chernikov }
268674b22066SAlexander V. Chernikov
268774b22066SAlexander V. Chernikov /*
268874b22066SAlexander V. Chernikov * Remove references from every object used in @rule.
268974b22066SAlexander V. Chernikov * Used at rule removal code.
269074b22066SAlexander V. Chernikov */
269174b22066SAlexander V. Chernikov static void
unref_rule_objects(struct ip_fw_chain * ch,struct ip_fw * rule)269274b22066SAlexander V. Chernikov unref_rule_objects(struct ip_fw_chain *ch, struct ip_fw *rule)
269374b22066SAlexander V. Chernikov {
269474b22066SAlexander V. Chernikov struct opcode_obj_rewrite *rw;
2695b2df1f7eSAndrey V. Elsukov struct named_object *no;
2696b2df1f7eSAndrey V. Elsukov ipfw_insn *cmd;
2697b2df1f7eSAndrey V. Elsukov int cmdlen, l;
2698b2df1f7eSAndrey V. Elsukov uint16_t kidx;
269974b22066SAlexander V. Chernikov uint8_t subtype;
270074b22066SAlexander V. Chernikov
270174b22066SAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(ch);
270274b22066SAlexander V. Chernikov
270374b22066SAlexander V. Chernikov l = rule->cmd_len;
270474b22066SAlexander V. Chernikov cmd = rule->cmd;
270574b22066SAlexander V. Chernikov cmdlen = 0;
270674b22066SAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) {
270774b22066SAlexander V. Chernikov cmdlen = F_LEN(cmd);
270874b22066SAlexander V. Chernikov
2709b2df1f7eSAndrey V. Elsukov rw = find_op_rw(cmd, &kidx, &subtype);
271074b22066SAlexander V. Chernikov if (rw == NULL)
271174b22066SAlexander V. Chernikov continue;
271274b22066SAlexander V. Chernikov no = rw->find_bykidx(ch, kidx);
271374b22066SAlexander V. Chernikov
271411c56650SAndrey V. Elsukov KASSERT(no != NULL, ("object id %d not found", kidx));
271574b22066SAlexander V. Chernikov KASSERT(no->subtype == subtype,
271611c56650SAndrey V. Elsukov ("wrong type %d (%d) for object id %d",
271774b22066SAlexander V. Chernikov no->subtype, subtype, kidx));
271811c56650SAndrey V. Elsukov KASSERT(no->refcnt > 0, ("refcount for object %d is %d",
271974b22066SAlexander V. Chernikov kidx, no->refcnt));
272074b22066SAlexander V. Chernikov
27211cf09efeSAndrey V. Elsukov if (no->refcnt == 1 && rw->destroy_object != NULL)
27221cf09efeSAndrey V. Elsukov rw->destroy_object(ch, no);
27231cf09efeSAndrey V. Elsukov else
272474b22066SAlexander V. Chernikov no->refcnt--;
272574b22066SAlexander V. Chernikov }
272674b22066SAlexander V. Chernikov }
272774b22066SAlexander V. Chernikov
272874b22066SAlexander V. Chernikov /*
272974b22066SAlexander V. Chernikov * Find and reference object (if any) stored in instruction @cmd.
273074b22066SAlexander V. Chernikov *
273174b22066SAlexander V. Chernikov * Saves object info in @pidx, sets
273274b22066SAlexander V. Chernikov * - @unresolved to 1 if object should exists but not found
273374b22066SAlexander V. Chernikov *
273474b22066SAlexander V. Chernikov * Returns non-zero value in case of error.
273574b22066SAlexander V. Chernikov */
2736f8e26ca3SAndrey V. Elsukov static int
ref_opcode_object(struct ip_fw_chain * ch,ipfw_insn * cmd,struct tid_info * ti,struct obj_idx * pidx,int * unresolved)273774b22066SAlexander V. Chernikov ref_opcode_object(struct ip_fw_chain *ch, ipfw_insn *cmd, struct tid_info *ti,
2738b2df1f7eSAndrey V. Elsukov struct obj_idx *pidx, int *unresolved)
273974b22066SAlexander V. Chernikov {
274074b22066SAlexander V. Chernikov struct named_object *no;
274174b22066SAlexander V. Chernikov struct opcode_obj_rewrite *rw;
274274b22066SAlexander V. Chernikov int error;
274374b22066SAlexander V. Chernikov
274474b22066SAlexander V. Chernikov /* Check if this opcode is candidate for rewrite */
2745b2df1f7eSAndrey V. Elsukov rw = find_op_rw(cmd, &ti->uidx, &ti->type);
274674b22066SAlexander V. Chernikov if (rw == NULL)
274774b22066SAlexander V. Chernikov return (0);
274874b22066SAlexander V. Chernikov
274974b22066SAlexander V. Chernikov /* Need to rewrite. Save necessary fields */
275074b22066SAlexander V. Chernikov pidx->uidx = ti->uidx;
275174b22066SAlexander V. Chernikov pidx->type = ti->type;
275274b22066SAlexander V. Chernikov
275374b22066SAlexander V. Chernikov /* Try to find referenced kernel object */
275474b22066SAlexander V. Chernikov error = rw->find_byname(ch, ti, &no);
275574b22066SAlexander V. Chernikov if (error != 0)
275674b22066SAlexander V. Chernikov return (error);
275774b22066SAlexander V. Chernikov if (no == NULL) {
2758b2df1f7eSAndrey V. Elsukov /*
2759b2df1f7eSAndrey V. Elsukov * Report about unresolved object for automaic
2760b2df1f7eSAndrey V. Elsukov * creation.
2761b2df1f7eSAndrey V. Elsukov */
276274b22066SAlexander V. Chernikov *unresolved = 1;
276374b22066SAlexander V. Chernikov return (0);
276474b22066SAlexander V. Chernikov }
276574b22066SAlexander V. Chernikov
2766c750a569SAndrey V. Elsukov /*
2767c750a569SAndrey V. Elsukov * Object is already exist.
2768c750a569SAndrey V. Elsukov * Its subtype should match with expected value.
2769c750a569SAndrey V. Elsukov */
2770c750a569SAndrey V. Elsukov if (ti->type != no->subtype)
2771c750a569SAndrey V. Elsukov return (EINVAL);
2772c750a569SAndrey V. Elsukov
2773c750a569SAndrey V. Elsukov /* Bump refcount and update kidx. */
277474b22066SAlexander V. Chernikov no->refcnt++;
2775b2df1f7eSAndrey V. Elsukov rw->update(cmd, no->kidx);
277674b22066SAlexander V. Chernikov return (0);
277774b22066SAlexander V. Chernikov }
277874b22066SAlexander V. Chernikov
277974b22066SAlexander V. Chernikov /*
2780f976a4edSAndrey V. Elsukov * Finds and bumps refcount for objects referenced by given @rule.
2781f976a4edSAndrey V. Elsukov * Auto-creates non-existing tables.
2782f976a4edSAndrey V. Elsukov * Fills in @oib array with userland/kernel indexes.
2783f976a4edSAndrey V. Elsukov *
2784f976a4edSAndrey V. Elsukov * Returns 0 on success.
2785f976a4edSAndrey V. Elsukov */
2786f976a4edSAndrey V. Elsukov static int
ref_rule_objects(struct ip_fw_chain * ch,struct ip_fw * rule,struct rule_check_info * ci,struct obj_idx * oib,struct tid_info * ti)2787f976a4edSAndrey V. Elsukov ref_rule_objects(struct ip_fw_chain *ch, struct ip_fw *rule,
2788f976a4edSAndrey V. Elsukov struct rule_check_info *ci, struct obj_idx *oib, struct tid_info *ti)
2789f976a4edSAndrey V. Elsukov {
2790f976a4edSAndrey V. Elsukov struct obj_idx *pidx;
2791b2df1f7eSAndrey V. Elsukov ipfw_insn *cmd;
2792b2df1f7eSAndrey V. Elsukov int cmdlen, error, l, unresolved;
2793f976a4edSAndrey V. Elsukov
2794f976a4edSAndrey V. Elsukov pidx = oib;
2795f976a4edSAndrey V. Elsukov l = rule->cmd_len;
2796f976a4edSAndrey V. Elsukov cmd = rule->cmd;
2797f976a4edSAndrey V. Elsukov cmdlen = 0;
2798f976a4edSAndrey V. Elsukov error = 0;
2799f976a4edSAndrey V. Elsukov
2800f976a4edSAndrey V. Elsukov IPFW_UH_WLOCK(ch);
2801f976a4edSAndrey V. Elsukov
2802f976a4edSAndrey V. Elsukov /* Increase refcount on each existing referenced table. */
2803f976a4edSAndrey V. Elsukov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) {
2804f976a4edSAndrey V. Elsukov cmdlen = F_LEN(cmd);
2805b2df1f7eSAndrey V. Elsukov unresolved = 0;
2806f976a4edSAndrey V. Elsukov
2807b2df1f7eSAndrey V. Elsukov error = ref_opcode_object(ch, cmd, ti, pidx, &unresolved);
2808f976a4edSAndrey V. Elsukov if (error != 0)
2809f976a4edSAndrey V. Elsukov break;
2810b2df1f7eSAndrey V. Elsukov /*
2811a4641f4eSPedro F. Giffuni * Compatibility stuff for old clients:
2812b2df1f7eSAndrey V. Elsukov * prepare to automaitcally create non-existing objects.
2813b2df1f7eSAndrey V. Elsukov */
2814b2df1f7eSAndrey V. Elsukov if (unresolved != 0) {
2815f976a4edSAndrey V. Elsukov pidx->off = rule->cmd_len - l;
2816f976a4edSAndrey V. Elsukov pidx++;
2817f976a4edSAndrey V. Elsukov }
2818f976a4edSAndrey V. Elsukov }
2819f976a4edSAndrey V. Elsukov
2820f976a4edSAndrey V. Elsukov if (error != 0) {
2821f976a4edSAndrey V. Elsukov /* Unref everything we have already done */
2822f976a4edSAndrey V. Elsukov unref_oib_objects(ch, rule->cmd, oib, pidx);
2823f976a4edSAndrey V. Elsukov IPFW_UH_WUNLOCK(ch);
2824f976a4edSAndrey V. Elsukov return (error);
2825f976a4edSAndrey V. Elsukov }
2826f976a4edSAndrey V. Elsukov IPFW_UH_WUNLOCK(ch);
2827f976a4edSAndrey V. Elsukov
2828f976a4edSAndrey V. Elsukov /* Perform auto-creation for non-existing objects */
2829b2df1f7eSAndrey V. Elsukov if (pidx != oib)
2830f976a4edSAndrey V. Elsukov error = create_objects_compat(ch, rule->cmd, oib, pidx, ti);
2831f976a4edSAndrey V. Elsukov
2832f976a4edSAndrey V. Elsukov /* Calculate real number of dynamic objects */
2833f976a4edSAndrey V. Elsukov ci->object_opcodes = (uint16_t)(pidx - oib);
2834f976a4edSAndrey V. Elsukov
2835f976a4edSAndrey V. Elsukov return (error);
2836f976a4edSAndrey V. Elsukov }
2837f976a4edSAndrey V. Elsukov
2838f976a4edSAndrey V. Elsukov /*
2839f976a4edSAndrey V. Elsukov * Checks is opcode is referencing table of appropriate type.
2840f976a4edSAndrey V. Elsukov * Adds reference count for found table if true.
2841f976a4edSAndrey V. Elsukov * Rewrites user-supplied opcode values with kernel ones.
2842f976a4edSAndrey V. Elsukov *
2843f976a4edSAndrey V. Elsukov * Returns 0 on success and appropriate error code otherwise.
2844f976a4edSAndrey V. Elsukov */
2845f976a4edSAndrey V. Elsukov static int
rewrite_rule_uidx(struct ip_fw_chain * chain,struct rule_check_info * ci)2846f976a4edSAndrey V. Elsukov rewrite_rule_uidx(struct ip_fw_chain *chain, struct rule_check_info *ci)
2847f976a4edSAndrey V. Elsukov {
2848f976a4edSAndrey V. Elsukov int error;
2849f976a4edSAndrey V. Elsukov ipfw_insn *cmd;
2850f976a4edSAndrey V. Elsukov struct obj_idx *p, *pidx_first, *pidx_last;
2851f976a4edSAndrey V. Elsukov struct tid_info ti;
2852f976a4edSAndrey V. Elsukov
2853f976a4edSAndrey V. Elsukov /*
2854f976a4edSAndrey V. Elsukov * Prepare an array for storing opcode indices.
2855f976a4edSAndrey V. Elsukov * Use stack allocation by default.
2856f976a4edSAndrey V. Elsukov */
2857f976a4edSAndrey V. Elsukov if (ci->object_opcodes <= (sizeof(ci->obuf)/sizeof(ci->obuf[0]))) {
2858f976a4edSAndrey V. Elsukov /* Stack */
2859f976a4edSAndrey V. Elsukov pidx_first = ci->obuf;
2860f976a4edSAndrey V. Elsukov } else
2861f976a4edSAndrey V. Elsukov pidx_first = malloc(
2862f976a4edSAndrey V. Elsukov ci->object_opcodes * sizeof(struct obj_idx),
2863f976a4edSAndrey V. Elsukov M_IPFW, M_WAITOK | M_ZERO);
2864f976a4edSAndrey V. Elsukov
2865f976a4edSAndrey V. Elsukov error = 0;
2866f976a4edSAndrey V. Elsukov memset(&ti, 0, sizeof(ti));
2867f976a4edSAndrey V. Elsukov
28682685841bSAndrey V. Elsukov /* Use set rule is assigned to. */
28692685841bSAndrey V. Elsukov ti.set = ci->krule->set;
2870f976a4edSAndrey V. Elsukov if (ci->ctlv != NULL) {
2871f976a4edSAndrey V. Elsukov ti.tlvs = (void *)(ci->ctlv + 1);
2872f976a4edSAndrey V. Elsukov ti.tlen = ci->ctlv->head.length - sizeof(ipfw_obj_ctlv);
2873f976a4edSAndrey V. Elsukov }
2874f976a4edSAndrey V. Elsukov
2875f976a4edSAndrey V. Elsukov /* Reference all used tables and other objects */
2876f976a4edSAndrey V. Elsukov error = ref_rule_objects(chain, ci->krule, ci, pidx_first, &ti);
2877f976a4edSAndrey V. Elsukov if (error != 0)
2878f976a4edSAndrey V. Elsukov goto free;
2879f976a4edSAndrey V. Elsukov /*
2880f976a4edSAndrey V. Elsukov * Note that ref_rule_objects() might have updated ci->object_opcodes
2881f976a4edSAndrey V. Elsukov * to reflect actual number of object opcodes.
2882f976a4edSAndrey V. Elsukov */
2883f976a4edSAndrey V. Elsukov
2884f8e26ca3SAndrey V. Elsukov /* Perform rewrite of remaining opcodes */
2885f976a4edSAndrey V. Elsukov p = pidx_first;
2886f976a4edSAndrey V. Elsukov pidx_last = pidx_first + ci->object_opcodes;
2887f976a4edSAndrey V. Elsukov for (p = pidx_first; p < pidx_last; p++) {
2888f976a4edSAndrey V. Elsukov cmd = ci->krule->cmd + p->off;
2889f976a4edSAndrey V. Elsukov update_opcode_kidx(cmd, p->kidx);
2890f976a4edSAndrey V. Elsukov }
2891f976a4edSAndrey V. Elsukov
2892f976a4edSAndrey V. Elsukov free:
2893f976a4edSAndrey V. Elsukov if (pidx_first != ci->obuf)
2894f976a4edSAndrey V. Elsukov free(pidx_first, M_IPFW);
2895f976a4edSAndrey V. Elsukov
2896f976a4edSAndrey V. Elsukov return (error);
2897f976a4edSAndrey V. Elsukov }
2898f976a4edSAndrey V. Elsukov
2899f976a4edSAndrey V. Elsukov /*
29006c2997ffSAlexander V. Chernikov * Adds one or more rules to ipfw @chain.
29016c2997ffSAlexander V. Chernikov * Data layout (version 0)(current):
29026c2997ffSAlexander V. Chernikov * Request:
29036c2997ffSAlexander V. Chernikov * [
29046c2997ffSAlexander V. Chernikov * ip_fw3_opheader
29056c2997ffSAlexander V. Chernikov * [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional *1)
29066c2997ffSAlexander V. Chernikov * [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) ip_fw x N ] (*2) (*3)
29076c2997ffSAlexander V. Chernikov * ]
29086c2997ffSAlexander V. Chernikov * Reply:
29096c2997ffSAlexander V. Chernikov * [
29106c2997ffSAlexander V. Chernikov * ip_fw3_opheader
29116c2997ffSAlexander V. Chernikov * [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional)
29126c2997ffSAlexander V. Chernikov * [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) ip_fw x N ]
29136c2997ffSAlexander V. Chernikov * ]
29146c2997ffSAlexander V. Chernikov *
29156c2997ffSAlexander V. Chernikov * Rules in reply are modified to store their actual ruleset number.
29166c2997ffSAlexander V. Chernikov *
29176c2997ffSAlexander V. Chernikov * (*1) TLVs inside IPFW_TLV_TBL_LIST needs to be sorted ascending
2918a4641f4eSPedro F. Giffuni * according to their idx field and there has to be no duplicates.
29196c2997ffSAlexander V. Chernikov * (*2) Numbered rules inside IPFW_TLV_RULE_LIST needs to be sorted ascending.
29206c2997ffSAlexander V. Chernikov * (*3) Each ip_fw structure needs to be aligned to u64 boundary.
29216c2997ffSAlexander V. Chernikov *
29226c2997ffSAlexander V. Chernikov * Returns 0 on success.
29236c2997ffSAlexander V. Chernikov */
29246c2997ffSAlexander V. Chernikov static int
add_rules(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)2925a73d728dSAlexander V. Chernikov add_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
2926a73d728dSAlexander V. Chernikov struct sockopt_data *sd)
29276c2997ffSAlexander V. Chernikov {
29286c2997ffSAlexander V. Chernikov ipfw_obj_ctlv *ctlv, *rtlv, *tstate;
29296c2997ffSAlexander V. Chernikov ipfw_obj_ntlv *ntlv;
29306c2997ffSAlexander V. Chernikov int clen, error, idx;
29316c2997ffSAlexander V. Chernikov uint32_t count, read;
29327e767c79SAlexander V. Chernikov struct ip_fw_rule *r;
29336c2997ffSAlexander V. Chernikov struct rule_check_info rci, *ci, *cbuf;
29346c2997ffSAlexander V. Chernikov int i, rsize;
29356c2997ffSAlexander V. Chernikov
29366c2997ffSAlexander V. Chernikov op3 = (ip_fw3_opheader *)ipfw_get_sopt_space(sd, sd->valsize);
29376c2997ffSAlexander V. Chernikov ctlv = (ipfw_obj_ctlv *)(op3 + 1);
29386c2997ffSAlexander V. Chernikov
29396c2997ffSAlexander V. Chernikov read = sizeof(ip_fw3_opheader);
29406c2997ffSAlexander V. Chernikov rtlv = NULL;
29416c2997ffSAlexander V. Chernikov tstate = NULL;
29426c2997ffSAlexander V. Chernikov cbuf = NULL;
29436c2997ffSAlexander V. Chernikov memset(&rci, 0, sizeof(struct rule_check_info));
29446c2997ffSAlexander V. Chernikov
29456c2997ffSAlexander V. Chernikov if (read + sizeof(*ctlv) > sd->valsize)
29466c2997ffSAlexander V. Chernikov return (EINVAL);
29476c2997ffSAlexander V. Chernikov
29486c2997ffSAlexander V. Chernikov if (ctlv->head.type == IPFW_TLV_TBLNAME_LIST) {
29496c2997ffSAlexander V. Chernikov clen = ctlv->head.length;
29507e767c79SAlexander V. Chernikov /* Check size and alignment */
29516c2997ffSAlexander V. Chernikov if (clen > sd->valsize || clen < sizeof(*ctlv))
29526c2997ffSAlexander V. Chernikov return (EINVAL);
29537e767c79SAlexander V. Chernikov if ((clen % sizeof(uint64_t)) != 0)
29547e767c79SAlexander V. Chernikov return (EINVAL);
29556c2997ffSAlexander V. Chernikov
29566c2997ffSAlexander V. Chernikov /*
29576c2997ffSAlexander V. Chernikov * Some table names or other named objects.
29586c2997ffSAlexander V. Chernikov * Check for validness.
29596c2997ffSAlexander V. Chernikov */
29606c2997ffSAlexander V. Chernikov count = (ctlv->head.length - sizeof(*ctlv)) / sizeof(*ntlv);
29616c2997ffSAlexander V. Chernikov if (ctlv->count != count || ctlv->objsize != sizeof(*ntlv))
29626c2997ffSAlexander V. Chernikov return (EINVAL);
29636c2997ffSAlexander V. Chernikov
29646c2997ffSAlexander V. Chernikov /*
29656c2997ffSAlexander V. Chernikov * Check each TLV.
29666c2997ffSAlexander V. Chernikov * Ensure TLVs are sorted ascending and
29676c2997ffSAlexander V. Chernikov * there are no duplicates.
29686c2997ffSAlexander V. Chernikov */
29696c2997ffSAlexander V. Chernikov idx = -1;
29706c2997ffSAlexander V. Chernikov ntlv = (ipfw_obj_ntlv *)(ctlv + 1);
29716c2997ffSAlexander V. Chernikov while (count > 0) {
29726c2997ffSAlexander V. Chernikov if (ntlv->head.length != sizeof(ipfw_obj_ntlv))
29736c2997ffSAlexander V. Chernikov return (EINVAL);
29746c2997ffSAlexander V. Chernikov
2975f81431ccSAndrey V. Elsukov error = ipfw_check_object_name_generic(ntlv->name);
29766c2997ffSAlexander V. Chernikov if (error != 0)
29776c2997ffSAlexander V. Chernikov return (error);
29786c2997ffSAlexander V. Chernikov
29796c2997ffSAlexander V. Chernikov if (ntlv->idx <= idx)
29806c2997ffSAlexander V. Chernikov return (EINVAL);
29816c2997ffSAlexander V. Chernikov
29826c2997ffSAlexander V. Chernikov idx = ntlv->idx;
29836c2997ffSAlexander V. Chernikov count--;
29846c2997ffSAlexander V. Chernikov ntlv++;
29856c2997ffSAlexander V. Chernikov }
29866c2997ffSAlexander V. Chernikov
29876c2997ffSAlexander V. Chernikov tstate = ctlv;
29886c2997ffSAlexander V. Chernikov read += ctlv->head.length;
29896c2997ffSAlexander V. Chernikov ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length);
29906c2997ffSAlexander V. Chernikov }
29916c2997ffSAlexander V. Chernikov
29926c2997ffSAlexander V. Chernikov if (read + sizeof(*ctlv) > sd->valsize)
29936c2997ffSAlexander V. Chernikov return (EINVAL);
29946c2997ffSAlexander V. Chernikov
29956c2997ffSAlexander V. Chernikov if (ctlv->head.type == IPFW_TLV_RULE_LIST) {
29966c2997ffSAlexander V. Chernikov clen = ctlv->head.length;
29976c2997ffSAlexander V. Chernikov if (clen + read > sd->valsize || clen < sizeof(*ctlv))
29986c2997ffSAlexander V. Chernikov return (EINVAL);
29997e767c79SAlexander V. Chernikov if ((clen % sizeof(uint64_t)) != 0)
30007e767c79SAlexander V. Chernikov return (EINVAL);
30016c2997ffSAlexander V. Chernikov
30026c2997ffSAlexander V. Chernikov /*
30036c2997ffSAlexander V. Chernikov * TODO: Permit adding multiple rules at once
30046c2997ffSAlexander V. Chernikov */
30056c2997ffSAlexander V. Chernikov if (ctlv->count != 1)
30066c2997ffSAlexander V. Chernikov return (ENOTSUP);
30076c2997ffSAlexander V. Chernikov
30086c2997ffSAlexander V. Chernikov clen -= sizeof(*ctlv);
30096c2997ffSAlexander V. Chernikov
30107e767c79SAlexander V. Chernikov if (ctlv->count > clen / sizeof(struct ip_fw_rule))
30116c2997ffSAlexander V. Chernikov return (EINVAL);
30126c2997ffSAlexander V. Chernikov
30136c2997ffSAlexander V. Chernikov /* Allocate state for each rule or use stack */
30146c2997ffSAlexander V. Chernikov if (ctlv->count == 1) {
30156c2997ffSAlexander V. Chernikov memset(&rci, 0, sizeof(struct rule_check_info));
30166c2997ffSAlexander V. Chernikov cbuf = &rci;
30176c2997ffSAlexander V. Chernikov } else
30186c2997ffSAlexander V. Chernikov cbuf = malloc(ctlv->count * sizeof(*ci), M_TEMP,
30196c2997ffSAlexander V. Chernikov M_WAITOK | M_ZERO);
30206c2997ffSAlexander V. Chernikov ci = cbuf;
30216c2997ffSAlexander V. Chernikov
30226c2997ffSAlexander V. Chernikov /*
30236c2997ffSAlexander V. Chernikov * Check each rule for validness.
30247e767c79SAlexander V. Chernikov * Ensure numbered rules are sorted ascending
30257e767c79SAlexander V. Chernikov * and properly aligned
30266c2997ffSAlexander V. Chernikov */
30277e767c79SAlexander V. Chernikov idx = 0;
30287e767c79SAlexander V. Chernikov r = (struct ip_fw_rule *)(ctlv + 1);
30296c2997ffSAlexander V. Chernikov count = 0;
30306c2997ffSAlexander V. Chernikov error = 0;
30316c2997ffSAlexander V. Chernikov while (clen > 0) {
30327e767c79SAlexander V. Chernikov rsize = roundup2(RULESIZE(r), sizeof(uint64_t));
30336c2997ffSAlexander V. Chernikov if (rsize > clen || ctlv->count <= count) {
30346c2997ffSAlexander V. Chernikov error = EINVAL;
30356c2997ffSAlexander V. Chernikov break;
30366c2997ffSAlexander V. Chernikov }
30376c2997ffSAlexander V. Chernikov
30386c2997ffSAlexander V. Chernikov ci->ctlv = tstate;
30397e767c79SAlexander V. Chernikov error = check_ipfw_rule1(r, rsize, ci);
30406c2997ffSAlexander V. Chernikov if (error != 0)
30416c2997ffSAlexander V. Chernikov break;
30426c2997ffSAlexander V. Chernikov
30436c2997ffSAlexander V. Chernikov /* Check sorting */
30446c2997ffSAlexander V. Chernikov if (r->rulenum != 0 && r->rulenum < idx) {
30457e767c79SAlexander V. Chernikov printf("rulenum %d idx %d\n", r->rulenum, idx);
30466c2997ffSAlexander V. Chernikov error = EINVAL;
30476c2997ffSAlexander V. Chernikov break;
30486c2997ffSAlexander V. Chernikov }
30496c2997ffSAlexander V. Chernikov idx = r->rulenum;
30506c2997ffSAlexander V. Chernikov
30517e767c79SAlexander V. Chernikov ci->urule = (caddr_t)r;
30526c2997ffSAlexander V. Chernikov
30536c2997ffSAlexander V. Chernikov rsize = roundup2(rsize, sizeof(uint64_t));
30546c2997ffSAlexander V. Chernikov clen -= rsize;
30557e767c79SAlexander V. Chernikov r = (struct ip_fw_rule *)((caddr_t)r + rsize);
30566c2997ffSAlexander V. Chernikov count++;
30576c2997ffSAlexander V. Chernikov ci++;
30586c2997ffSAlexander V. Chernikov }
30596c2997ffSAlexander V. Chernikov
30606c2997ffSAlexander V. Chernikov if (ctlv->count != count || error != 0) {
30616c2997ffSAlexander V. Chernikov if (cbuf != &rci)
30626c2997ffSAlexander V. Chernikov free(cbuf, M_TEMP);
30636c2997ffSAlexander V. Chernikov return (EINVAL);
30646c2997ffSAlexander V. Chernikov }
30656c2997ffSAlexander V. Chernikov
30666c2997ffSAlexander V. Chernikov rtlv = ctlv;
30676c2997ffSAlexander V. Chernikov read += ctlv->head.length;
30686c2997ffSAlexander V. Chernikov ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length);
30696c2997ffSAlexander V. Chernikov }
30706c2997ffSAlexander V. Chernikov
30716c2997ffSAlexander V. Chernikov if (read != sd->valsize || rtlv == NULL || rtlv->count == 0) {
30726c2997ffSAlexander V. Chernikov if (cbuf != NULL && cbuf != &rci)
30736c2997ffSAlexander V. Chernikov free(cbuf, M_TEMP);
30746c2997ffSAlexander V. Chernikov return (EINVAL);
30756c2997ffSAlexander V. Chernikov }
30766c2997ffSAlexander V. Chernikov
30776c2997ffSAlexander V. Chernikov /*
30786c2997ffSAlexander V. Chernikov * Passed rules seems to be valid.
30796c2997ffSAlexander V. Chernikov * Allocate storage and try to add them to chain.
30806c2997ffSAlexander V. Chernikov */
30816c2997ffSAlexander V. Chernikov for (i = 0, ci = cbuf; i < rtlv->count; i++, ci++) {
30827e767c79SAlexander V. Chernikov clen = RULEKSIZE1((struct ip_fw_rule *)ci->urule);
30837e767c79SAlexander V. Chernikov ci->krule = ipfw_alloc_rule(chain, clen);
30847e767c79SAlexander V. Chernikov import_rule1(ci);
30856c2997ffSAlexander V. Chernikov }
30866c2997ffSAlexander V. Chernikov
30876c2997ffSAlexander V. Chernikov if ((error = commit_rules(chain, cbuf, rtlv->count)) != 0) {
30886c2997ffSAlexander V. Chernikov /* Free allocate krules */
30896c2997ffSAlexander V. Chernikov for (i = 0, ci = cbuf; i < rtlv->count; i++, ci++)
3090cefe3d67SAndrey V. Elsukov ipfw_free_rule(ci->krule);
30916c2997ffSAlexander V. Chernikov }
30926c2997ffSAlexander V. Chernikov
30936c2997ffSAlexander V. Chernikov if (cbuf != NULL && cbuf != &rci)
30946c2997ffSAlexander V. Chernikov free(cbuf, M_TEMP);
30956c2997ffSAlexander V. Chernikov
30966c2997ffSAlexander V. Chernikov return (error);
30976c2997ffSAlexander V. Chernikov }
30986c2997ffSAlexander V. Chernikov
309991e721d7SAlexander V. Chernikov /*
3100be8bc457SAlexander V. Chernikov * Lists all sopts currently registered.
3101be8bc457SAlexander V. Chernikov * Data layout (v0)(current):
3102be8bc457SAlexander V. Chernikov * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size
3103be8bc457SAlexander V. Chernikov * Reply: [ ipfw_obj_lheader ipfw_sopt_info x N ]
3104be8bc457SAlexander V. Chernikov *
3105be8bc457SAlexander V. Chernikov * Returns 0 on success
3106be8bc457SAlexander V. Chernikov */
3107be8bc457SAlexander V. Chernikov static int
dump_soptcodes(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)3108be8bc457SAlexander V. Chernikov dump_soptcodes(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
3109be8bc457SAlexander V. Chernikov struct sockopt_data *sd)
3110be8bc457SAlexander V. Chernikov {
3111be8bc457SAlexander V. Chernikov struct _ipfw_obj_lheader *olh;
3112be8bc457SAlexander V. Chernikov ipfw_sopt_info *i;
3113be8bc457SAlexander V. Chernikov struct ipfw_sopt_handler *sh;
3114be8bc457SAlexander V. Chernikov uint32_t count, n, size;
3115be8bc457SAlexander V. Chernikov
3116be8bc457SAlexander V. Chernikov olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh));
3117be8bc457SAlexander V. Chernikov if (olh == NULL)
3118be8bc457SAlexander V. Chernikov return (EINVAL);
3119be8bc457SAlexander V. Chernikov if (sd->valsize < olh->size)
3120be8bc457SAlexander V. Chernikov return (EINVAL);
3121be8bc457SAlexander V. Chernikov
3122be8bc457SAlexander V. Chernikov CTL3_LOCK();
3123be8bc457SAlexander V. Chernikov count = ctl3_hsize;
3124be8bc457SAlexander V. Chernikov size = count * sizeof(ipfw_sopt_info) + sizeof(ipfw_obj_lheader);
3125be8bc457SAlexander V. Chernikov
3126be8bc457SAlexander V. Chernikov /* Fill in header regadless of buffer size */
3127be8bc457SAlexander V. Chernikov olh->count = count;
3128be8bc457SAlexander V. Chernikov olh->objsize = sizeof(ipfw_sopt_info);
3129be8bc457SAlexander V. Chernikov
3130be8bc457SAlexander V. Chernikov if (size > olh->size) {
3131be8bc457SAlexander V. Chernikov olh->size = size;
3132be8bc457SAlexander V. Chernikov CTL3_UNLOCK();
3133be8bc457SAlexander V. Chernikov return (ENOMEM);
3134be8bc457SAlexander V. Chernikov }
3135be8bc457SAlexander V. Chernikov olh->size = size;
3136be8bc457SAlexander V. Chernikov
3137be8bc457SAlexander V. Chernikov for (n = 1; n <= count; n++) {
3138be8bc457SAlexander V. Chernikov i = (ipfw_sopt_info *)ipfw_get_sopt_space(sd, sizeof(*i));
31397a6ab8f1SPedro F. Giffuni KASSERT(i != NULL, ("previously checked buffer is not enough"));
3140be8bc457SAlexander V. Chernikov sh = &ctl3_handlers[n];
3141be8bc457SAlexander V. Chernikov i->opcode = sh->opcode;
3142be8bc457SAlexander V. Chernikov i->version = sh->version;
3143be8bc457SAlexander V. Chernikov i->refcnt = sh->refcnt;
3144be8bc457SAlexander V. Chernikov }
3145be8bc457SAlexander V. Chernikov CTL3_UNLOCK();
3146be8bc457SAlexander V. Chernikov
3147be8bc457SAlexander V. Chernikov return (0);
3148be8bc457SAlexander V. Chernikov }
3149be8bc457SAlexander V. Chernikov
3150be8bc457SAlexander V. Chernikov /*
315174b22066SAlexander V. Chernikov * Compares two opcodes.
315274b22066SAlexander V. Chernikov * Used both in qsort() and bsearch().
315374b22066SAlexander V. Chernikov *
315474b22066SAlexander V. Chernikov * Returns 0 if match is found.
315574b22066SAlexander V. Chernikov */
315674b22066SAlexander V. Chernikov static int
compare_opcodes(const void * _a,const void * _b)315774b22066SAlexander V. Chernikov compare_opcodes(const void *_a, const void *_b)
315874b22066SAlexander V. Chernikov {
315974b22066SAlexander V. Chernikov const struct opcode_obj_rewrite *a, *b;
316074b22066SAlexander V. Chernikov
316174b22066SAlexander V. Chernikov a = (const struct opcode_obj_rewrite *)_a;
316274b22066SAlexander V. Chernikov b = (const struct opcode_obj_rewrite *)_b;
316374b22066SAlexander V. Chernikov
316474b22066SAlexander V. Chernikov if (a->opcode < b->opcode)
316574b22066SAlexander V. Chernikov return (-1);
316674b22066SAlexander V. Chernikov else if (a->opcode > b->opcode)
316774b22066SAlexander V. Chernikov return (1);
316874b22066SAlexander V. Chernikov
316974b22066SAlexander V. Chernikov return (0);
317074b22066SAlexander V. Chernikov }
317174b22066SAlexander V. Chernikov
317274b22066SAlexander V. Chernikov /*
3173b2df1f7eSAndrey V. Elsukov * XXX: Rewrite bsearch()
3174b2df1f7eSAndrey V. Elsukov */
3175b2df1f7eSAndrey V. Elsukov static int
find_op_rw_range(uint16_t op,struct opcode_obj_rewrite ** plo,struct opcode_obj_rewrite ** phi)3176b2df1f7eSAndrey V. Elsukov find_op_rw_range(uint16_t op, struct opcode_obj_rewrite **plo,
3177b2df1f7eSAndrey V. Elsukov struct opcode_obj_rewrite **phi)
3178b2df1f7eSAndrey V. Elsukov {
3179b2df1f7eSAndrey V. Elsukov struct opcode_obj_rewrite *ctl3_max, *lo, *hi, h, *rw;
3180b2df1f7eSAndrey V. Elsukov
3181b2df1f7eSAndrey V. Elsukov memset(&h, 0, sizeof(h));
3182b2df1f7eSAndrey V. Elsukov h.opcode = op;
3183b2df1f7eSAndrey V. Elsukov
3184b2df1f7eSAndrey V. Elsukov rw = (struct opcode_obj_rewrite *)bsearch(&h, ctl3_rewriters,
3185b2df1f7eSAndrey V. Elsukov ctl3_rsize, sizeof(h), compare_opcodes);
3186b2df1f7eSAndrey V. Elsukov if (rw == NULL)
3187b2df1f7eSAndrey V. Elsukov return (1);
3188b2df1f7eSAndrey V. Elsukov
3189b2df1f7eSAndrey V. Elsukov /* Find the first element matching the same opcode */
3190b2df1f7eSAndrey V. Elsukov lo = rw;
3191b2df1f7eSAndrey V. Elsukov for ( ; lo > ctl3_rewriters && (lo - 1)->opcode == op; lo--)
3192b2df1f7eSAndrey V. Elsukov ;
3193b2df1f7eSAndrey V. Elsukov
3194b2df1f7eSAndrey V. Elsukov /* Find the last element matching the same opcode */
3195b2df1f7eSAndrey V. Elsukov hi = rw;
3196b2df1f7eSAndrey V. Elsukov ctl3_max = ctl3_rewriters + ctl3_rsize;
3197b2df1f7eSAndrey V. Elsukov for ( ; (hi + 1) < ctl3_max && (hi + 1)->opcode == op; hi++)
3198b2df1f7eSAndrey V. Elsukov ;
3199b2df1f7eSAndrey V. Elsukov
3200b2df1f7eSAndrey V. Elsukov *plo = lo;
3201b2df1f7eSAndrey V. Elsukov *phi = hi;
3202b2df1f7eSAndrey V. Elsukov
3203b2df1f7eSAndrey V. Elsukov return (0);
3204b2df1f7eSAndrey V. Elsukov }
3205b2df1f7eSAndrey V. Elsukov
3206b2df1f7eSAndrey V. Elsukov /*
320774b22066SAlexander V. Chernikov * Finds opcode object rewriter based on @code.
320874b22066SAlexander V. Chernikov *
320974b22066SAlexander V. Chernikov * Returns pointer to handler or NULL.
321074b22066SAlexander V. Chernikov */
3211b2df1f7eSAndrey V. Elsukov static struct opcode_obj_rewrite *
find_op_rw(ipfw_insn * cmd,uint16_t * puidx,uint8_t * ptype)3212b2df1f7eSAndrey V. Elsukov find_op_rw(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype)
321374b22066SAlexander V. Chernikov {
3214b2df1f7eSAndrey V. Elsukov struct opcode_obj_rewrite *rw, *lo, *hi;
3215b2df1f7eSAndrey V. Elsukov uint16_t uidx;
3216b2df1f7eSAndrey V. Elsukov uint8_t subtype;
321774b22066SAlexander V. Chernikov
3218b2df1f7eSAndrey V. Elsukov if (find_op_rw_range(cmd->opcode, &lo, &hi) != 0)
3219b2df1f7eSAndrey V. Elsukov return (NULL);
322074b22066SAlexander V. Chernikov
3221b2df1f7eSAndrey V. Elsukov for (rw = lo; rw <= hi; rw++) {
3222b2df1f7eSAndrey V. Elsukov if (rw->classifier(cmd, &uidx, &subtype) == 0) {
3223b2df1f7eSAndrey V. Elsukov if (puidx != NULL)
3224b2df1f7eSAndrey V. Elsukov *puidx = uidx;
3225b2df1f7eSAndrey V. Elsukov if (ptype != NULL)
3226b2df1f7eSAndrey V. Elsukov *ptype = subtype;
322774b22066SAlexander V. Chernikov return (rw);
322874b22066SAlexander V. Chernikov }
3229b2df1f7eSAndrey V. Elsukov }
323074b22066SAlexander V. Chernikov
3231b2df1f7eSAndrey V. Elsukov return (NULL);
3232b2df1f7eSAndrey V. Elsukov }
323374b22066SAlexander V. Chernikov int
classify_opcode_kidx(ipfw_insn * cmd,uint16_t * puidx)323474b22066SAlexander V. Chernikov classify_opcode_kidx(ipfw_insn *cmd, uint16_t *puidx)
323574b22066SAlexander V. Chernikov {
323674b22066SAlexander V. Chernikov
3237e099b90bSPedro F. Giffuni if (find_op_rw(cmd, puidx, NULL) == NULL)
323874b22066SAlexander V. Chernikov return (1);
3239b2df1f7eSAndrey V. Elsukov return (0);
324074b22066SAlexander V. Chernikov }
324174b22066SAlexander V. Chernikov
324274b22066SAlexander V. Chernikov void
update_opcode_kidx(ipfw_insn * cmd,uint16_t idx)324374b22066SAlexander V. Chernikov update_opcode_kidx(ipfw_insn *cmd, uint16_t idx)
324474b22066SAlexander V. Chernikov {
324574b22066SAlexander V. Chernikov struct opcode_obj_rewrite *rw;
324674b22066SAlexander V. Chernikov
3247b2df1f7eSAndrey V. Elsukov rw = find_op_rw(cmd, NULL, NULL);
324874b22066SAlexander V. Chernikov KASSERT(rw != NULL, ("No handler to update opcode %d", cmd->opcode));
324974b22066SAlexander V. Chernikov rw->update(cmd, idx);
325074b22066SAlexander V. Chernikov }
325174b22066SAlexander V. Chernikov
325274b22066SAlexander V. Chernikov void
ipfw_init_obj_rewriter(void)325362030bb8SDimitry Andric ipfw_init_obj_rewriter(void)
325474b22066SAlexander V. Chernikov {
325574b22066SAlexander V. Chernikov
325674b22066SAlexander V. Chernikov ctl3_rewriters = NULL;
325774b22066SAlexander V. Chernikov ctl3_rsize = 0;
325874b22066SAlexander V. Chernikov }
325974b22066SAlexander V. Chernikov
326074b22066SAlexander V. Chernikov void
ipfw_destroy_obj_rewriter(void)326162030bb8SDimitry Andric ipfw_destroy_obj_rewriter(void)
326274b22066SAlexander V. Chernikov {
326374b22066SAlexander V. Chernikov
326474b22066SAlexander V. Chernikov if (ctl3_rewriters != NULL)
326574b22066SAlexander V. Chernikov free(ctl3_rewriters, M_IPFW);
326674b22066SAlexander V. Chernikov ctl3_rewriters = NULL;
326774b22066SAlexander V. Chernikov ctl3_rsize = 0;
326874b22066SAlexander V. Chernikov }
326974b22066SAlexander V. Chernikov
327074b22066SAlexander V. Chernikov /*
327174b22066SAlexander V. Chernikov * Adds one or more opcode object rewrite handlers to the global array.
327274b22066SAlexander V. Chernikov * Function may sleep.
327374b22066SAlexander V. Chernikov */
327474b22066SAlexander V. Chernikov void
ipfw_add_obj_rewriter(struct opcode_obj_rewrite * rw,size_t count)327574b22066SAlexander V. Chernikov ipfw_add_obj_rewriter(struct opcode_obj_rewrite *rw, size_t count)
327674b22066SAlexander V. Chernikov {
327774b22066SAlexander V. Chernikov size_t sz;
327874b22066SAlexander V. Chernikov struct opcode_obj_rewrite *tmp;
327974b22066SAlexander V. Chernikov
328074b22066SAlexander V. Chernikov CTL3_LOCK();
328174b22066SAlexander V. Chernikov
328274b22066SAlexander V. Chernikov for (;;) {
328374b22066SAlexander V. Chernikov sz = ctl3_rsize + count;
328474b22066SAlexander V. Chernikov CTL3_UNLOCK();
328574b22066SAlexander V. Chernikov tmp = malloc(sizeof(*rw) * sz, M_IPFW, M_WAITOK | M_ZERO);
328674b22066SAlexander V. Chernikov CTL3_LOCK();
328774b22066SAlexander V. Chernikov if (ctl3_rsize + count <= sz)
328874b22066SAlexander V. Chernikov break;
328974b22066SAlexander V. Chernikov
329074b22066SAlexander V. Chernikov /* Retry */
329174b22066SAlexander V. Chernikov free(tmp, M_IPFW);
329274b22066SAlexander V. Chernikov }
329374b22066SAlexander V. Chernikov
329474b22066SAlexander V. Chernikov /* Merge old & new arrays */
329574b22066SAlexander V. Chernikov sz = ctl3_rsize + count;
329674b22066SAlexander V. Chernikov memcpy(tmp, ctl3_rewriters, ctl3_rsize * sizeof(*rw));
329774b22066SAlexander V. Chernikov memcpy(&tmp[ctl3_rsize], rw, count * sizeof(*rw));
329874b22066SAlexander V. Chernikov qsort(tmp, sz, sizeof(*rw), compare_opcodes);
329974b22066SAlexander V. Chernikov /* Switch new and free old */
330074b22066SAlexander V. Chernikov if (ctl3_rewriters != NULL)
330174b22066SAlexander V. Chernikov free(ctl3_rewriters, M_IPFW);
330274b22066SAlexander V. Chernikov ctl3_rewriters = tmp;
330374b22066SAlexander V. Chernikov ctl3_rsize = sz;
330474b22066SAlexander V. Chernikov
330574b22066SAlexander V. Chernikov CTL3_UNLOCK();
330674b22066SAlexander V. Chernikov }
330774b22066SAlexander V. Chernikov
330874b22066SAlexander V. Chernikov /*
330974b22066SAlexander V. Chernikov * Removes one or more object rewrite handlers from the global array.
331074b22066SAlexander V. Chernikov */
331174b22066SAlexander V. Chernikov int
ipfw_del_obj_rewriter(struct opcode_obj_rewrite * rw,size_t count)331274b22066SAlexander V. Chernikov ipfw_del_obj_rewriter(struct opcode_obj_rewrite *rw, size_t count)
331374b22066SAlexander V. Chernikov {
331474b22066SAlexander V. Chernikov size_t sz;
3315b2df1f7eSAndrey V. Elsukov struct opcode_obj_rewrite *ctl3_max, *ktmp, *lo, *hi;
331674b22066SAlexander V. Chernikov int i;
331774b22066SAlexander V. Chernikov
331874b22066SAlexander V. Chernikov CTL3_LOCK();
331974b22066SAlexander V. Chernikov
332074b22066SAlexander V. Chernikov for (i = 0; i < count; i++) {
3321b2df1f7eSAndrey V. Elsukov if (find_op_rw_range(rw[i].opcode, &lo, &hi) != 0)
332274b22066SAlexander V. Chernikov continue;
332374b22066SAlexander V. Chernikov
3324b2df1f7eSAndrey V. Elsukov for (ktmp = lo; ktmp <= hi; ktmp++) {
3325b2df1f7eSAndrey V. Elsukov if (ktmp->classifier != rw[i].classifier)
3326b2df1f7eSAndrey V. Elsukov continue;
3327b2df1f7eSAndrey V. Elsukov
3328b2df1f7eSAndrey V. Elsukov ctl3_max = ctl3_rewriters + ctl3_rsize;
3329b2df1f7eSAndrey V. Elsukov sz = (ctl3_max - (ktmp + 1)) * sizeof(*ktmp);
3330b2df1f7eSAndrey V. Elsukov memmove(ktmp, ktmp + 1, sz);
333174b22066SAlexander V. Chernikov ctl3_rsize--;
3332b2df1f7eSAndrey V. Elsukov break;
3333b2df1f7eSAndrey V. Elsukov }
333474b22066SAlexander V. Chernikov }
333574b22066SAlexander V. Chernikov
333674b22066SAlexander V. Chernikov if (ctl3_rsize == 0) {
333774b22066SAlexander V. Chernikov if (ctl3_rewriters != NULL)
333874b22066SAlexander V. Chernikov free(ctl3_rewriters, M_IPFW);
333974b22066SAlexander V. Chernikov ctl3_rewriters = NULL;
334074b22066SAlexander V. Chernikov }
334174b22066SAlexander V. Chernikov
334274b22066SAlexander V. Chernikov CTL3_UNLOCK();
334374b22066SAlexander V. Chernikov
334474b22066SAlexander V. Chernikov return (0);
334574b22066SAlexander V. Chernikov }
334674b22066SAlexander V. Chernikov
3347b309f085SAndrey V. Elsukov static int
export_objhash_ntlv_internal(struct namedobj_instance * ni,struct named_object * no,void * arg)33485dc5a0e0SAndrey V. Elsukov export_objhash_ntlv_internal(struct namedobj_instance *ni,
33495dc5a0e0SAndrey V. Elsukov struct named_object *no, void *arg)
33505dc5a0e0SAndrey V. Elsukov {
33515dc5a0e0SAndrey V. Elsukov struct sockopt_data *sd;
33525dc5a0e0SAndrey V. Elsukov ipfw_obj_ntlv *ntlv;
33535dc5a0e0SAndrey V. Elsukov
33545dc5a0e0SAndrey V. Elsukov sd = (struct sockopt_data *)arg;
33555dc5a0e0SAndrey V. Elsukov ntlv = (ipfw_obj_ntlv *)ipfw_get_sopt_space(sd, sizeof(*ntlv));
33565dc5a0e0SAndrey V. Elsukov if (ntlv == NULL)
3357b309f085SAndrey V. Elsukov return (ENOMEM);
33585dc5a0e0SAndrey V. Elsukov ipfw_export_obj_ntlv(no, ntlv);
3359b309f085SAndrey V. Elsukov return (0);
33605dc5a0e0SAndrey V. Elsukov }
33615dc5a0e0SAndrey V. Elsukov
33625dc5a0e0SAndrey V. Elsukov /*
33635dc5a0e0SAndrey V. Elsukov * Lists all service objects.
33645dc5a0e0SAndrey V. Elsukov * Data layout (v0)(current):
3365f8e26ca3SAndrey V. Elsukov * Request: [ ipfw_obj_lheader ] size = ipfw_obj_lheader.size
33665dc5a0e0SAndrey V. Elsukov * Reply: [ ipfw_obj_lheader [ ipfw_obj_ntlv x N ] (optional) ]
33675dc5a0e0SAndrey V. Elsukov * Returns 0 on success
33685dc5a0e0SAndrey V. Elsukov */
33695dc5a0e0SAndrey V. Elsukov static int
dump_srvobjects(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)33705dc5a0e0SAndrey V. Elsukov dump_srvobjects(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
33715dc5a0e0SAndrey V. Elsukov struct sockopt_data *sd)
33725dc5a0e0SAndrey V. Elsukov {
33735dc5a0e0SAndrey V. Elsukov ipfw_obj_lheader *hdr;
33745dc5a0e0SAndrey V. Elsukov int count;
33755dc5a0e0SAndrey V. Elsukov
33765dc5a0e0SAndrey V. Elsukov hdr = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*hdr));
33775dc5a0e0SAndrey V. Elsukov if (hdr == NULL)
33785dc5a0e0SAndrey V. Elsukov return (EINVAL);
33795dc5a0e0SAndrey V. Elsukov
33805dc5a0e0SAndrey V. Elsukov IPFW_UH_RLOCK(chain);
33815dc5a0e0SAndrey V. Elsukov count = ipfw_objhash_count(CHAIN_TO_SRV(chain));
33825dc5a0e0SAndrey V. Elsukov hdr->size = sizeof(ipfw_obj_lheader) + count * sizeof(ipfw_obj_ntlv);
33835dc5a0e0SAndrey V. Elsukov if (sd->valsize < hdr->size) {
33845dc5a0e0SAndrey V. Elsukov IPFW_UH_RUNLOCK(chain);
33855dc5a0e0SAndrey V. Elsukov return (ENOMEM);
33865dc5a0e0SAndrey V. Elsukov }
33875dc5a0e0SAndrey V. Elsukov hdr->count = count;
33885dc5a0e0SAndrey V. Elsukov hdr->objsize = sizeof(ipfw_obj_ntlv);
33895dc5a0e0SAndrey V. Elsukov if (count > 0)
33905dc5a0e0SAndrey V. Elsukov ipfw_objhash_foreach(CHAIN_TO_SRV(chain),
33915dc5a0e0SAndrey V. Elsukov export_objhash_ntlv_internal, sd);
33925dc5a0e0SAndrey V. Elsukov IPFW_UH_RUNLOCK(chain);
33935dc5a0e0SAndrey V. Elsukov return (0);
33945dc5a0e0SAndrey V. Elsukov }
33955dc5a0e0SAndrey V. Elsukov
339674b22066SAlexander V. Chernikov /*
33976b988f3aSAlexander V. Chernikov * Compares two sopt handlers (code, version and handler ptr).
33986b988f3aSAlexander V. Chernikov * Used both as qsort() and bsearch().
33996b988f3aSAlexander V. Chernikov * Does not compare handler for latter case.
34006b988f3aSAlexander V. Chernikov *
34016b988f3aSAlexander V. Chernikov * Returns 0 if match is found.
34026b988f3aSAlexander V. Chernikov */
34036b988f3aSAlexander V. Chernikov static int
compare_sh(const void * _a,const void * _b)34046b988f3aSAlexander V. Chernikov compare_sh(const void *_a, const void *_b)
34056b988f3aSAlexander V. Chernikov {
3406e530ca73SAlexander V. Chernikov const struct ipfw_sopt_handler *a, *b;
34076b988f3aSAlexander V. Chernikov
3408e530ca73SAlexander V. Chernikov a = (const struct ipfw_sopt_handler *)_a;
3409e530ca73SAlexander V. Chernikov b = (const struct ipfw_sopt_handler *)_b;
34106b988f3aSAlexander V. Chernikov
34116b988f3aSAlexander V. Chernikov if (a->opcode < b->opcode)
34126b988f3aSAlexander V. Chernikov return (-1);
34136b988f3aSAlexander V. Chernikov else if (a->opcode > b->opcode)
34146b988f3aSAlexander V. Chernikov return (1);
34156b988f3aSAlexander V. Chernikov
34166b988f3aSAlexander V. Chernikov if (a->version < b->version)
34176b988f3aSAlexander V. Chernikov return (-1);
34186b988f3aSAlexander V. Chernikov else if (a->version > b->version)
34196b988f3aSAlexander V. Chernikov return (1);
34206b988f3aSAlexander V. Chernikov
34216b988f3aSAlexander V. Chernikov /* bsearch helper */
34226b988f3aSAlexander V. Chernikov if (a->handler == NULL)
34236b988f3aSAlexander V. Chernikov return (0);
34246b988f3aSAlexander V. Chernikov
34256b988f3aSAlexander V. Chernikov if ((uintptr_t)a->handler < (uintptr_t)b->handler)
34266b988f3aSAlexander V. Chernikov return (-1);
3427cd82d21bSGleb Smirnoff else if ((uintptr_t)a->handler > (uintptr_t)b->handler)
34286b988f3aSAlexander V. Chernikov return (1);
34296b988f3aSAlexander V. Chernikov
34306b988f3aSAlexander V. Chernikov return (0);
34316b988f3aSAlexander V. Chernikov }
34326b988f3aSAlexander V. Chernikov
34336b988f3aSAlexander V. Chernikov /*
34346b988f3aSAlexander V. Chernikov * Finds sopt handler based on @code and @version.
34356b988f3aSAlexander V. Chernikov *
34366b988f3aSAlexander V. Chernikov * Returns pointer to handler or NULL.
34376b988f3aSAlexander V. Chernikov */
34386b988f3aSAlexander V. Chernikov static struct ipfw_sopt_handler *
find_sh(uint16_t code,uint8_t version,sopt_handler_f * handler)343962f42cf8SLuigi Rizzo find_sh(uint16_t code, uint8_t version, sopt_handler_f *handler)
34406b988f3aSAlexander V. Chernikov {
34416b988f3aSAlexander V. Chernikov struct ipfw_sopt_handler *sh, h;
34426b988f3aSAlexander V. Chernikov
34436b988f3aSAlexander V. Chernikov memset(&h, 0, sizeof(h));
34446b988f3aSAlexander V. Chernikov h.opcode = code;
34456b988f3aSAlexander V. Chernikov h.version = version;
34466b988f3aSAlexander V. Chernikov h.handler = handler;
34476b988f3aSAlexander V. Chernikov
34486b988f3aSAlexander V. Chernikov sh = (struct ipfw_sopt_handler *)bsearch(&h, ctl3_handlers,
34496b988f3aSAlexander V. Chernikov ctl3_hsize, sizeof(h), compare_sh);
34506b988f3aSAlexander V. Chernikov
34516b988f3aSAlexander V. Chernikov return (sh);
34526b988f3aSAlexander V. Chernikov }
34536b988f3aSAlexander V. Chernikov
34546b988f3aSAlexander V. Chernikov static int
find_ref_sh(uint16_t opcode,uint8_t version,struct ipfw_sopt_handler * psh)34556b988f3aSAlexander V. Chernikov find_ref_sh(uint16_t opcode, uint8_t version, struct ipfw_sopt_handler *psh)
34566b988f3aSAlexander V. Chernikov {
34576b988f3aSAlexander V. Chernikov struct ipfw_sopt_handler *sh;
34586b988f3aSAlexander V. Chernikov
34596b988f3aSAlexander V. Chernikov CTL3_LOCK();
34606b988f3aSAlexander V. Chernikov if ((sh = find_sh(opcode, version, NULL)) == NULL) {
34616b988f3aSAlexander V. Chernikov CTL3_UNLOCK();
34626b988f3aSAlexander V. Chernikov printf("ipfw: ipfw_ctl3 invalid option %d""v""%d\n",
34636b988f3aSAlexander V. Chernikov opcode, version);
34646b988f3aSAlexander V. Chernikov return (EINVAL);
34656b988f3aSAlexander V. Chernikov }
34666b988f3aSAlexander V. Chernikov sh->refcnt++;
34676b988f3aSAlexander V. Chernikov ctl3_refct++;
34686b988f3aSAlexander V. Chernikov /* Copy handler data to requested buffer */
34696b988f3aSAlexander V. Chernikov *psh = *sh;
34706b988f3aSAlexander V. Chernikov CTL3_UNLOCK();
34716b988f3aSAlexander V. Chernikov
34726b988f3aSAlexander V. Chernikov return (0);
34736b988f3aSAlexander V. Chernikov }
34746b988f3aSAlexander V. Chernikov
34756b988f3aSAlexander V. Chernikov static void
find_unref_sh(struct ipfw_sopt_handler * psh)34766b988f3aSAlexander V. Chernikov find_unref_sh(struct ipfw_sopt_handler *psh)
34776b988f3aSAlexander V. Chernikov {
34786b988f3aSAlexander V. Chernikov struct ipfw_sopt_handler *sh;
34796b988f3aSAlexander V. Chernikov
34806b988f3aSAlexander V. Chernikov CTL3_LOCK();
34816b988f3aSAlexander V. Chernikov sh = find_sh(psh->opcode, psh->version, NULL);
34826b988f3aSAlexander V. Chernikov KASSERT(sh != NULL, ("ctl3 handler disappeared"));
34836b988f3aSAlexander V. Chernikov sh->refcnt--;
34846b988f3aSAlexander V. Chernikov ctl3_refct--;
34856b988f3aSAlexander V. Chernikov CTL3_UNLOCK();
34866b988f3aSAlexander V. Chernikov }
34876b988f3aSAlexander V. Chernikov
34886b988f3aSAlexander V. Chernikov void
ipfw_init_sopt_handler(void)3489d62830c5SDimitry Andric ipfw_init_sopt_handler(void)
34906b988f3aSAlexander V. Chernikov {
34916b988f3aSAlexander V. Chernikov
34926b988f3aSAlexander V. Chernikov CTL3_LOCK_INIT();
34936b988f3aSAlexander V. Chernikov IPFW_ADD_SOPT_HANDLER(1, scodes);
34946b988f3aSAlexander V. Chernikov }
34956b988f3aSAlexander V. Chernikov
34966b988f3aSAlexander V. Chernikov void
ipfw_destroy_sopt_handler(void)3497d62830c5SDimitry Andric ipfw_destroy_sopt_handler(void)
34986b988f3aSAlexander V. Chernikov {
34996b988f3aSAlexander V. Chernikov
35006b988f3aSAlexander V. Chernikov IPFW_DEL_SOPT_HANDLER(1, scodes);
35016b988f3aSAlexander V. Chernikov CTL3_LOCK_DESTROY();
35026b988f3aSAlexander V. Chernikov }
35036b988f3aSAlexander V. Chernikov
35046b988f3aSAlexander V. Chernikov /*
35056b988f3aSAlexander V. Chernikov * Adds one or more sockopt handlers to the global array.
35066b988f3aSAlexander V. Chernikov * Function may sleep.
35076b988f3aSAlexander V. Chernikov */
35086b988f3aSAlexander V. Chernikov void
ipfw_add_sopt_handler(struct ipfw_sopt_handler * sh,size_t count)35096b988f3aSAlexander V. Chernikov ipfw_add_sopt_handler(struct ipfw_sopt_handler *sh, size_t count)
35106b988f3aSAlexander V. Chernikov {
35116b988f3aSAlexander V. Chernikov size_t sz;
35126b988f3aSAlexander V. Chernikov struct ipfw_sopt_handler *tmp;
35136b988f3aSAlexander V. Chernikov
35146b988f3aSAlexander V. Chernikov CTL3_LOCK();
35156b988f3aSAlexander V. Chernikov
35166b988f3aSAlexander V. Chernikov for (;;) {
35176b988f3aSAlexander V. Chernikov sz = ctl3_hsize + count;
35186b988f3aSAlexander V. Chernikov CTL3_UNLOCK();
35196b988f3aSAlexander V. Chernikov tmp = malloc(sizeof(*sh) * sz, M_IPFW, M_WAITOK | M_ZERO);
35206b988f3aSAlexander V. Chernikov CTL3_LOCK();
35216b988f3aSAlexander V. Chernikov if (ctl3_hsize + count <= sz)
35226b988f3aSAlexander V. Chernikov break;
35236b988f3aSAlexander V. Chernikov
35246b988f3aSAlexander V. Chernikov /* Retry */
35256b988f3aSAlexander V. Chernikov free(tmp, M_IPFW);
35266b988f3aSAlexander V. Chernikov }
35276b988f3aSAlexander V. Chernikov
35286b988f3aSAlexander V. Chernikov /* Merge old & new arrays */
35296b988f3aSAlexander V. Chernikov sz = ctl3_hsize + count;
35306b988f3aSAlexander V. Chernikov memcpy(tmp, ctl3_handlers, ctl3_hsize * sizeof(*sh));
35316b988f3aSAlexander V. Chernikov memcpy(&tmp[ctl3_hsize], sh, count * sizeof(*sh));
35326b988f3aSAlexander V. Chernikov qsort(tmp, sz, sizeof(*sh), compare_sh);
35336b988f3aSAlexander V. Chernikov /* Switch new and free old */
35346b988f3aSAlexander V. Chernikov if (ctl3_handlers != NULL)
35356b988f3aSAlexander V. Chernikov free(ctl3_handlers, M_IPFW);
35366b988f3aSAlexander V. Chernikov ctl3_handlers = tmp;
35376b988f3aSAlexander V. Chernikov ctl3_hsize = sz;
35386b988f3aSAlexander V. Chernikov ctl3_gencnt++;
35396b988f3aSAlexander V. Chernikov
35406b988f3aSAlexander V. Chernikov CTL3_UNLOCK();
35416b988f3aSAlexander V. Chernikov }
35426b988f3aSAlexander V. Chernikov
35436b988f3aSAlexander V. Chernikov /*
35446b988f3aSAlexander V. Chernikov * Removes one or more sockopt handlers from the global array.
35456b988f3aSAlexander V. Chernikov */
35466b988f3aSAlexander V. Chernikov int
ipfw_del_sopt_handler(struct ipfw_sopt_handler * sh,size_t count)35476b988f3aSAlexander V. Chernikov ipfw_del_sopt_handler(struct ipfw_sopt_handler *sh, size_t count)
35486b988f3aSAlexander V. Chernikov {
35496b988f3aSAlexander V. Chernikov size_t sz;
35506b988f3aSAlexander V. Chernikov struct ipfw_sopt_handler *tmp, *h;
35516b988f3aSAlexander V. Chernikov int i;
35526b988f3aSAlexander V. Chernikov
35536b988f3aSAlexander V. Chernikov CTL3_LOCK();
35546b988f3aSAlexander V. Chernikov
35556b988f3aSAlexander V. Chernikov for (i = 0; i < count; i++) {
35566b988f3aSAlexander V. Chernikov tmp = &sh[i];
35576b988f3aSAlexander V. Chernikov h = find_sh(tmp->opcode, tmp->version, tmp->handler);
35586b988f3aSAlexander V. Chernikov if (h == NULL)
35596b988f3aSAlexander V. Chernikov continue;
35606b988f3aSAlexander V. Chernikov
35616b988f3aSAlexander V. Chernikov sz = (ctl3_handlers + ctl3_hsize - (h + 1)) * sizeof(*h);
35626b988f3aSAlexander V. Chernikov memmove(h, h + 1, sz);
35636b988f3aSAlexander V. Chernikov ctl3_hsize--;
35646b988f3aSAlexander V. Chernikov }
35656b988f3aSAlexander V. Chernikov
35666b988f3aSAlexander V. Chernikov if (ctl3_hsize == 0) {
35676b988f3aSAlexander V. Chernikov if (ctl3_handlers != NULL)
35686b988f3aSAlexander V. Chernikov free(ctl3_handlers, M_IPFW);
35696b988f3aSAlexander V. Chernikov ctl3_handlers = NULL;
35706b988f3aSAlexander V. Chernikov }
35716b988f3aSAlexander V. Chernikov
35726b988f3aSAlexander V. Chernikov ctl3_gencnt++;
35736b988f3aSAlexander V. Chernikov
35746b988f3aSAlexander V. Chernikov CTL3_UNLOCK();
35756b988f3aSAlexander V. Chernikov
35766b988f3aSAlexander V. Chernikov return (0);
35776b988f3aSAlexander V. Chernikov }
35786b988f3aSAlexander V. Chernikov
35796b988f3aSAlexander V. Chernikov /*
358091e721d7SAlexander V. Chernikov * Writes data accumulated in @sd to sockopt buffer.
358191e721d7SAlexander V. Chernikov * Zeroes internal @sd buffer.
358291e721d7SAlexander V. Chernikov */
358391e721d7SAlexander V. Chernikov static int
ipfw_flush_sopt_data(struct sockopt_data * sd)358491e721d7SAlexander V. Chernikov ipfw_flush_sopt_data(struct sockopt_data *sd)
358591e721d7SAlexander V. Chernikov {
358654b38fcfSAlexander V. Chernikov struct sockopt *sopt;
358791e721d7SAlexander V. Chernikov int error;
358891e721d7SAlexander V. Chernikov size_t sz;
358991e721d7SAlexander V. Chernikov
359054b38fcfSAlexander V. Chernikov sz = sd->koff;
359154b38fcfSAlexander V. Chernikov if (sz == 0)
359291e721d7SAlexander V. Chernikov return (0);
359391e721d7SAlexander V. Chernikov
359454b38fcfSAlexander V. Chernikov sopt = sd->sopt;
359554b38fcfSAlexander V. Chernikov
359654b38fcfSAlexander V. Chernikov if (sopt->sopt_dir == SOPT_GET) {
359754b38fcfSAlexander V. Chernikov error = copyout(sd->kbuf, sopt->sopt_val, sz);
359891e721d7SAlexander V. Chernikov if (error != 0)
359991e721d7SAlexander V. Chernikov return (error);
360091e721d7SAlexander V. Chernikov }
360191e721d7SAlexander V. Chernikov
360291e721d7SAlexander V. Chernikov memset(sd->kbuf, 0, sd->ksize);
360354b38fcfSAlexander V. Chernikov sd->ktotal += sz;
360491e721d7SAlexander V. Chernikov sd->koff = 0;
360591e721d7SAlexander V. Chernikov if (sd->ktotal + sd->ksize < sd->valsize)
360691e721d7SAlexander V. Chernikov sd->kavail = sd->ksize;
360791e721d7SAlexander V. Chernikov else
360891e721d7SAlexander V. Chernikov sd->kavail = sd->valsize - sd->ktotal;
360991e721d7SAlexander V. Chernikov
361054b38fcfSAlexander V. Chernikov /* Update sopt buffer data */
361154b38fcfSAlexander V. Chernikov sopt->sopt_valsize = sd->ktotal;
361254b38fcfSAlexander V. Chernikov sopt->sopt_val = sd->sopt_val + sd->ktotal;
361391e721d7SAlexander V. Chernikov
361491e721d7SAlexander V. Chernikov return (0);
361591e721d7SAlexander V. Chernikov }
361691e721d7SAlexander V. Chernikov
361791e721d7SAlexander V. Chernikov /*
3618a4641f4eSPedro F. Giffuni * Ensures that @sd buffer has contiguous @neeeded number of
361991e721d7SAlexander V. Chernikov * bytes.
362091e721d7SAlexander V. Chernikov *
362191e721d7SAlexander V. Chernikov * Returns pointer to requested space or NULL.
362291e721d7SAlexander V. Chernikov */
362391e721d7SAlexander V. Chernikov caddr_t
ipfw_get_sopt_space(struct sockopt_data * sd,size_t needed)362491e721d7SAlexander V. Chernikov ipfw_get_sopt_space(struct sockopt_data *sd, size_t needed)
362591e721d7SAlexander V. Chernikov {
362691e721d7SAlexander V. Chernikov int error;
362791e721d7SAlexander V. Chernikov caddr_t addr;
362891e721d7SAlexander V. Chernikov
362991e721d7SAlexander V. Chernikov if (sd->kavail < needed) {
363091e721d7SAlexander V. Chernikov /*
363191e721d7SAlexander V. Chernikov * Flush data and try another time.
363291e721d7SAlexander V. Chernikov */
363391e721d7SAlexander V. Chernikov error = ipfw_flush_sopt_data(sd);
363491e721d7SAlexander V. Chernikov
363591e721d7SAlexander V. Chernikov if (sd->kavail < needed || error != 0)
363691e721d7SAlexander V. Chernikov return (NULL);
363791e721d7SAlexander V. Chernikov }
363891e721d7SAlexander V. Chernikov
363991e721d7SAlexander V. Chernikov addr = sd->kbuf + sd->koff;
364091e721d7SAlexander V. Chernikov sd->koff += needed;
364191e721d7SAlexander V. Chernikov sd->kavail -= needed;
364291e721d7SAlexander V. Chernikov return (addr);
364391e721d7SAlexander V. Chernikov }
364491e721d7SAlexander V. Chernikov
364591e721d7SAlexander V. Chernikov /*
3646a4641f4eSPedro F. Giffuni * Requests @needed contiguous bytes from @sd buffer.
364791e721d7SAlexander V. Chernikov * Function is used to notify subsystem that we are
364891e721d7SAlexander V. Chernikov * interesed in first @needed bytes (request header)
364991e721d7SAlexander V. Chernikov * and the rest buffer can be safely zeroed.
365091e721d7SAlexander V. Chernikov *
365191e721d7SAlexander V. Chernikov * Returns pointer to requested space or NULL.
365291e721d7SAlexander V. Chernikov */
365391e721d7SAlexander V. Chernikov caddr_t
ipfw_get_sopt_header(struct sockopt_data * sd,size_t needed)365491e721d7SAlexander V. Chernikov ipfw_get_sopt_header(struct sockopt_data *sd, size_t needed)
365591e721d7SAlexander V. Chernikov {
365691e721d7SAlexander V. Chernikov caddr_t addr;
365791e721d7SAlexander V. Chernikov
365891e721d7SAlexander V. Chernikov if ((addr = ipfw_get_sopt_space(sd, needed)) == NULL)
365991e721d7SAlexander V. Chernikov return (NULL);
366091e721d7SAlexander V. Chernikov
366191e721d7SAlexander V. Chernikov if (sd->kavail > 0)
366291e721d7SAlexander V. Chernikov memset(sd->kbuf + sd->koff, 0, sd->kavail);
366391e721d7SAlexander V. Chernikov
366491e721d7SAlexander V. Chernikov return (addr);
366591e721d7SAlexander V. Chernikov }
366691e721d7SAlexander V. Chernikov
366791e721d7SAlexander V. Chernikov /*
366891e721d7SAlexander V. Chernikov * New sockopt handler.
366991e721d7SAlexander V. Chernikov */
36703b3a8eb9SGleb Smirnoff int
ipfw_ctl3(struct sockopt * sopt)3671b429d43cSAlexander V. Chernikov ipfw_ctl3(struct sockopt *sopt)
36723b3a8eb9SGleb Smirnoff {
3673ce575f53SAlexander V. Chernikov int error, locked;
36746b988f3aSAlexander V. Chernikov size_t size, valsize;
36753b3a8eb9SGleb Smirnoff struct ip_fw_chain *chain;
3676914bffb6SAlexander V. Chernikov char xbuf[256];
36772d99a349SAlexander V. Chernikov struct sockopt_data sdata;
36786b988f3aSAlexander V. Chernikov struct ipfw_sopt_handler h;
36793b3a8eb9SGleb Smirnoff ip_fw3_opheader *op3 = NULL;
36803b3a8eb9SGleb Smirnoff
36813b3a8eb9SGleb Smirnoff error = priv_check(sopt->sopt_td, PRIV_NETINET_IPFW);
3682b429d43cSAlexander V. Chernikov if (error != 0)
36833b3a8eb9SGleb Smirnoff return (error);
3684b429d43cSAlexander V. Chernikov
3685b429d43cSAlexander V. Chernikov if (sopt->sopt_name != IP_FW3)
368691e721d7SAlexander V. Chernikov return (ipfw_ctl(sopt));
36873b3a8eb9SGleb Smirnoff
36883b3a8eb9SGleb Smirnoff chain = &V_layer3_chain;
36893b3a8eb9SGleb Smirnoff error = 0;
36903b3a8eb9SGleb Smirnoff
36913b3a8eb9SGleb Smirnoff /* Save original valsize before it is altered via sooptcopyin() */
36923b3a8eb9SGleb Smirnoff valsize = sopt->sopt_valsize;
36932d99a349SAlexander V. Chernikov memset(&sdata, 0, sizeof(sdata));
36946c2997ffSAlexander V. Chernikov /* Read op3 header first to determine actual operation */
36956c2997ffSAlexander V. Chernikov op3 = (ip_fw3_opheader *)xbuf;
36966c2997ffSAlexander V. Chernikov error = sooptcopyin(sopt, op3, sizeof(*op3), sizeof(*op3));
36976c2997ffSAlexander V. Chernikov if (error != 0)
36986c2997ffSAlexander V. Chernikov return (error);
36996c2997ffSAlexander V. Chernikov sopt->sopt_valsize = valsize;
37006c2997ffSAlexander V. Chernikov
37016c2997ffSAlexander V. Chernikov /*
37026b988f3aSAlexander V. Chernikov * Find and reference command.
37036c2997ffSAlexander V. Chernikov */
37046b988f3aSAlexander V. Chernikov error = find_ref_sh(op3->opcode, op3->version, &h);
37056b988f3aSAlexander V. Chernikov if (error != 0)
37066b988f3aSAlexander V. Chernikov return (error);
37073a845e10SAlexander V. Chernikov
37083a845e10SAlexander V. Chernikov /*
37093a845e10SAlexander V. Chernikov * Disallow modifications in really-really secure mode, but still allow
37103a845e10SAlexander V. Chernikov * the logging counters to be reset.
37113a845e10SAlexander V. Chernikov */
37126b988f3aSAlexander V. Chernikov if ((h.dir & HDIR_SET) != 0 && h.opcode != IP_FW_XRESETLOG) {
37133a845e10SAlexander V. Chernikov error = securelevel_ge(sopt->sopt_td->td_ucred, 3);
37146b988f3aSAlexander V. Chernikov if (error != 0) {
37156b988f3aSAlexander V. Chernikov find_unref_sh(&h);
37163a845e10SAlexander V. Chernikov return (error);
37173a845e10SAlexander V. Chernikov }
37186b988f3aSAlexander V. Chernikov }
37196c2997ffSAlexander V. Chernikov
37203b3a8eb9SGleb Smirnoff /*
37212d99a349SAlexander V. Chernikov * Fill in sockopt_data structure that may be useful for
37226c2997ffSAlexander V. Chernikov * IP_FW3 get requests.
37233b3a8eb9SGleb Smirnoff */
3724ce575f53SAlexander V. Chernikov locked = 0;
37252d99a349SAlexander V. Chernikov if (valsize <= sizeof(xbuf)) {
37266b988f3aSAlexander V. Chernikov /* use on-stack buffer */
37272d99a349SAlexander V. Chernikov sdata.kbuf = xbuf;
37282d99a349SAlexander V. Chernikov sdata.ksize = sizeof(xbuf);
37292d99a349SAlexander V. Chernikov sdata.kavail = valsize;
37302d99a349SAlexander V. Chernikov } else {
37316b988f3aSAlexander V. Chernikov /*
37326b988f3aSAlexander V. Chernikov * Determine opcode type/buffer size:
37336b988f3aSAlexander V. Chernikov * allocate sliding-window buf for data export or
3734a4641f4eSPedro F. Giffuni * contiguous buffer for special ops.
37356b988f3aSAlexander V. Chernikov */
37366b988f3aSAlexander V. Chernikov if ((h.dir & HDIR_SET) != 0) {
37376b988f3aSAlexander V. Chernikov /* Set request. Allocate contigous buffer. */
37386b988f3aSAlexander V. Chernikov if (valsize > CTL3_LARGEBUF) {
37396b988f3aSAlexander V. Chernikov find_unref_sh(&h);
37406b988f3aSAlexander V. Chernikov return (EFBIG);
37416b988f3aSAlexander V. Chernikov }
37426b988f3aSAlexander V. Chernikov
37432d99a349SAlexander V. Chernikov size = valsize;
37446b988f3aSAlexander V. Chernikov } else {
37456b988f3aSAlexander V. Chernikov /* Get request. Allocate sliding window buffer */
37466b988f3aSAlexander V. Chernikov size = (valsize<CTL3_SMALLBUF) ? valsize:CTL3_SMALLBUF;
3747ce575f53SAlexander V. Chernikov
3748ce575f53SAlexander V. Chernikov if (size < valsize) {
3749ce575f53SAlexander V. Chernikov /* We have to wire user buffer */
3750ce575f53SAlexander V. Chernikov error = vslock(sopt->sopt_val, valsize);
3751ce575f53SAlexander V. Chernikov if (error != 0)
3752ce575f53SAlexander V. Chernikov return (error);
3753ce575f53SAlexander V. Chernikov locked = 1;
3754ce575f53SAlexander V. Chernikov }
37556b988f3aSAlexander V. Chernikov }
37562d99a349SAlexander V. Chernikov
37572d99a349SAlexander V. Chernikov sdata.kbuf = malloc(size, M_TEMP, M_WAITOK | M_ZERO);
37582d99a349SAlexander V. Chernikov sdata.ksize = size;
37592d99a349SAlexander V. Chernikov sdata.kavail = size;
37602d99a349SAlexander V. Chernikov }
37612d99a349SAlexander V. Chernikov
37622d99a349SAlexander V. Chernikov sdata.sopt = sopt;
3763b6ee846eSAlexander V. Chernikov sdata.sopt_val = sopt->sopt_val;
37642d99a349SAlexander V. Chernikov sdata.valsize = valsize;
37652d99a349SAlexander V. Chernikov
37662d99a349SAlexander V. Chernikov /*
37676c2997ffSAlexander V. Chernikov * Copy either all request (if valsize < bsize_max)
37686c2997ffSAlexander V. Chernikov * or first bsize_max bytes to guarantee most consumers
37692d99a349SAlexander V. Chernikov * that all necessary data has been copied).
37702d99a349SAlexander V. Chernikov * Anyway, copy not less than sizeof(ip_fw3_opheader).
37712d99a349SAlexander V. Chernikov */
37722d99a349SAlexander V. Chernikov if ((error = sooptcopyin(sopt, sdata.kbuf, sdata.ksize,
37733b3a8eb9SGleb Smirnoff sizeof(ip_fw3_opheader))) != 0)
37743b3a8eb9SGleb Smirnoff return (error);
37752d99a349SAlexander V. Chernikov op3 = (ip_fw3_opheader *)sdata.kbuf;
3776b429d43cSAlexander V. Chernikov
37776b988f3aSAlexander V. Chernikov /* Finally, run handler */
37786b988f3aSAlexander V. Chernikov error = h.handler(chain, op3, &sdata);
37796b988f3aSAlexander V. Chernikov find_unref_sh(&h);
3780b429d43cSAlexander V. Chernikov
3781b429d43cSAlexander V. Chernikov /* Flush state and free buffers */
3782b429d43cSAlexander V. Chernikov if (error == 0)
3783b429d43cSAlexander V. Chernikov error = ipfw_flush_sopt_data(&sdata);
3784b429d43cSAlexander V. Chernikov else
3785b429d43cSAlexander V. Chernikov ipfw_flush_sopt_data(&sdata);
3786b429d43cSAlexander V. Chernikov
3787ce575f53SAlexander V. Chernikov if (locked != 0)
3788ce575f53SAlexander V. Chernikov vsunlock(sdata.sopt_val, valsize);
3789ce575f53SAlexander V. Chernikov
3790b6ee846eSAlexander V. Chernikov /* Restore original pointer and set number of bytes written */
3791b6ee846eSAlexander V. Chernikov sopt->sopt_val = sdata.sopt_val;
3792b6ee846eSAlexander V. Chernikov sopt->sopt_valsize = sdata.ktotal;
3793b429d43cSAlexander V. Chernikov if (sdata.kbuf != xbuf)
3794b429d43cSAlexander V. Chernikov free(sdata.kbuf, M_TEMP);
3795b429d43cSAlexander V. Chernikov
3796b429d43cSAlexander V. Chernikov return (error);
3797b429d43cSAlexander V. Chernikov }
3798b429d43cSAlexander V. Chernikov
3799b429d43cSAlexander V. Chernikov /**
3800b429d43cSAlexander V. Chernikov * {set|get}sockopt parser.
3801b429d43cSAlexander V. Chernikov */
3802b429d43cSAlexander V. Chernikov int
ipfw_ctl(struct sockopt * sopt)3803b429d43cSAlexander V. Chernikov ipfw_ctl(struct sockopt *sopt)
3804b429d43cSAlexander V. Chernikov {
3805be3cc1b5SAlexander V. Chernikov #define RULE_MAXSIZE (512*sizeof(u_int32_t))
3806b429d43cSAlexander V. Chernikov int error;
380760a28b09SMateusz Guzik size_t size;
3808b429d43cSAlexander V. Chernikov struct ip_fw *buf;
3809b429d43cSAlexander V. Chernikov struct ip_fw_rule0 *rule;
3810b429d43cSAlexander V. Chernikov struct ip_fw_chain *chain;
3811b429d43cSAlexander V. Chernikov u_int32_t rulenum[2];
3812b429d43cSAlexander V. Chernikov uint32_t opt;
3813b429d43cSAlexander V. Chernikov struct rule_check_info ci;
3814ccba94b8SAlexander V. Chernikov IPFW_RLOCK_TRACKER;
3815b429d43cSAlexander V. Chernikov
3816b429d43cSAlexander V. Chernikov chain = &V_layer3_chain;
3817b429d43cSAlexander V. Chernikov error = 0;
3818b429d43cSAlexander V. Chernikov
3819b429d43cSAlexander V. Chernikov opt = sopt->sopt_name;
3820b429d43cSAlexander V. Chernikov
3821b429d43cSAlexander V. Chernikov /*
3822b429d43cSAlexander V. Chernikov * Disallow modifications in really-really secure mode, but still allow
3823b429d43cSAlexander V. Chernikov * the logging counters to be reset.
3824b429d43cSAlexander V. Chernikov */
3825b429d43cSAlexander V. Chernikov if (opt == IP_FW_ADD ||
3826b429d43cSAlexander V. Chernikov (sopt->sopt_dir == SOPT_SET && opt != IP_FW_RESETLOG)) {
3827b429d43cSAlexander V. Chernikov error = securelevel_ge(sopt->sopt_td->td_ucred, 3);
3828b429d43cSAlexander V. Chernikov if (error != 0)
3829b429d43cSAlexander V. Chernikov return (error);
38303b3a8eb9SGleb Smirnoff }
38313b3a8eb9SGleb Smirnoff
38323b3a8eb9SGleb Smirnoff switch (opt) {
38333b3a8eb9SGleb Smirnoff case IP_FW_GET:
38343b3a8eb9SGleb Smirnoff /*
38353b3a8eb9SGleb Smirnoff * pass up a copy of the current rules. Static rules
38363b3a8eb9SGleb Smirnoff * come first (the last of which has number IPFW_DEFAULT_RULE),
38373b3a8eb9SGleb Smirnoff * followed by a possibly empty list of dynamic rule.
38383b3a8eb9SGleb Smirnoff * The last dynamic rule has NULL in the "next" field.
38393b3a8eb9SGleb Smirnoff *
38403b3a8eb9SGleb Smirnoff * Note that the calculated size is used to bound the
38413b3a8eb9SGleb Smirnoff * amount of data returned to the user. The rule set may
38423b3a8eb9SGleb Smirnoff * change between calculating the size and returning the
38433b3a8eb9SGleb Smirnoff * data in which case we'll just return what fits.
38443b3a8eb9SGleb Smirnoff */
38453b3a8eb9SGleb Smirnoff for (;;) {
38463b3a8eb9SGleb Smirnoff int len = 0, want;
38473b3a8eb9SGleb Smirnoff
38483b3a8eb9SGleb Smirnoff size = chain->static_len;
38493b3a8eb9SGleb Smirnoff size += ipfw_dyn_len();
38503b3a8eb9SGleb Smirnoff if (size >= sopt->sopt_valsize)
38513b3a8eb9SGleb Smirnoff break;
38527e767c79SAlexander V. Chernikov buf = malloc(size, M_TEMP, M_WAITOK | M_ZERO);
38533b3a8eb9SGleb Smirnoff IPFW_UH_RLOCK(chain);
38543b3a8eb9SGleb Smirnoff /* check again how much space we need */
38553b3a8eb9SGleb Smirnoff want = chain->static_len + ipfw_dyn_len();
38563b3a8eb9SGleb Smirnoff if (size >= want)
38573b3a8eb9SGleb Smirnoff len = ipfw_getrules(chain, buf, size);
38583b3a8eb9SGleb Smirnoff IPFW_UH_RUNLOCK(chain);
38593b3a8eb9SGleb Smirnoff if (size >= want)
38603b3a8eb9SGleb Smirnoff error = sooptcopyout(sopt, buf, len);
38613b3a8eb9SGleb Smirnoff free(buf, M_TEMP);
38623b3a8eb9SGleb Smirnoff if (size >= want)
38633b3a8eb9SGleb Smirnoff break;
38643b3a8eb9SGleb Smirnoff }
38653b3a8eb9SGleb Smirnoff break;
38663b3a8eb9SGleb Smirnoff
38673b3a8eb9SGleb Smirnoff case IP_FW_FLUSH:
38683b3a8eb9SGleb Smirnoff /* locking is done within del_entry() */
38693b3a8eb9SGleb Smirnoff error = del_entry(chain, 0); /* special case, rule=0, cmd=0 means all */
38703b3a8eb9SGleb Smirnoff break;
38713b3a8eb9SGleb Smirnoff
38723b3a8eb9SGleb Smirnoff case IP_FW_ADD:
38733b3a8eb9SGleb Smirnoff rule = malloc(RULE_MAXSIZE, M_TEMP, M_WAITOK);
38743b3a8eb9SGleb Smirnoff error = sooptcopyin(sopt, rule, RULE_MAXSIZE,
38753b3a8eb9SGleb Smirnoff sizeof(struct ip_fw7) );
38763b3a8eb9SGleb Smirnoff
3877b074b7bbSAlexander V. Chernikov memset(&ci, 0, sizeof(struct rule_check_info));
3878b074b7bbSAlexander V. Chernikov
38793b3a8eb9SGleb Smirnoff /*
38803b3a8eb9SGleb Smirnoff * If the size of commands equals RULESIZE7 then we assume
38813b3a8eb9SGleb Smirnoff * a FreeBSD7.2 binary is talking to us (set is7=1).
38823b3a8eb9SGleb Smirnoff * is7 is persistent so the next 'ipfw list' command
38833b3a8eb9SGleb Smirnoff * will use this format.
38843b3a8eb9SGleb Smirnoff * NOTE: If wrong version is guessed (this can happen if
38853b3a8eb9SGleb Smirnoff * the first ipfw command is 'ipfw [pipe] list')
38863b3a8eb9SGleb Smirnoff * the ipfw binary may crash or loop infinitly...
38873b3a8eb9SGleb Smirnoff */
38887e767c79SAlexander V. Chernikov size = sopt->sopt_valsize;
38897e767c79SAlexander V. Chernikov if (size == RULESIZE7(rule)) {
38903b3a8eb9SGleb Smirnoff is7 = 1;
38913b3a8eb9SGleb Smirnoff error = convert_rule_to_8(rule);
38927946c49fSChristian Brueffer if (error) {
38937946c49fSChristian Brueffer free(rule, M_TEMP);
38943b3a8eb9SGleb Smirnoff return error;
38957946c49fSChristian Brueffer }
38967e767c79SAlexander V. Chernikov size = RULESIZE(rule);
38977e767c79SAlexander V. Chernikov } else
38983b3a8eb9SGleb Smirnoff is7 = 0;
38993b3a8eb9SGleb Smirnoff if (error == 0)
39007e767c79SAlexander V. Chernikov error = check_ipfw_rule0(rule, size, &ci);
39013b3a8eb9SGleb Smirnoff if (error == 0) {
3902b074b7bbSAlexander V. Chernikov /* locking is done within add_rule() */
39036c2997ffSAlexander V. Chernikov struct ip_fw *krule;
39047e767c79SAlexander V. Chernikov krule = ipfw_alloc_rule(chain, RULEKSIZE0(rule));
39057e767c79SAlexander V. Chernikov ci.urule = (caddr_t)rule;
39066c2997ffSAlexander V. Chernikov ci.krule = krule;
39077e767c79SAlexander V. Chernikov import_rule0(&ci);
39086c2997ffSAlexander V. Chernikov error = commit_rules(chain, &ci, 1);
39099f2e5ed3SAndrey V. Elsukov if (error != 0)
3910cefe3d67SAndrey V. Elsukov ipfw_free_rule(ci.krule);
39119f2e5ed3SAndrey V. Elsukov else if (sopt->sopt_dir == SOPT_GET) {
39123b3a8eb9SGleb Smirnoff if (is7) {
39133b3a8eb9SGleb Smirnoff error = convert_rule_to_7(rule);
39143b3a8eb9SGleb Smirnoff size = RULESIZE7(rule);
39157946c49fSChristian Brueffer if (error) {
39167946c49fSChristian Brueffer free(rule, M_TEMP);
39173b3a8eb9SGleb Smirnoff return error;
39183b3a8eb9SGleb Smirnoff }
39197946c49fSChristian Brueffer }
39203b3a8eb9SGleb Smirnoff error = sooptcopyout(sopt, rule, size);
39213b3a8eb9SGleb Smirnoff }
39223b3a8eb9SGleb Smirnoff }
39233b3a8eb9SGleb Smirnoff free(rule, M_TEMP);
39243b3a8eb9SGleb Smirnoff break;
39253b3a8eb9SGleb Smirnoff
39263b3a8eb9SGleb Smirnoff case IP_FW_DEL:
39273b3a8eb9SGleb Smirnoff /*
39283b3a8eb9SGleb Smirnoff * IP_FW_DEL is used for deleting single rules or sets,
39293b3a8eb9SGleb Smirnoff * and (ab)used to atomically manipulate sets. Argument size
39303b3a8eb9SGleb Smirnoff * is used to distinguish between the two:
39313b3a8eb9SGleb Smirnoff * sizeof(u_int32_t)
39323b3a8eb9SGleb Smirnoff * delete single rule or set of rules,
39333b3a8eb9SGleb Smirnoff * or reassign rules (or sets) to a different set.
39343b3a8eb9SGleb Smirnoff * 2*sizeof(u_int32_t)
39353b3a8eb9SGleb Smirnoff * atomic disable/enable sets.
39363b3a8eb9SGleb Smirnoff * first u_int32_t contains sets to be disabled,
39373b3a8eb9SGleb Smirnoff * second u_int32_t contains sets to be enabled.
39383b3a8eb9SGleb Smirnoff */
39393b3a8eb9SGleb Smirnoff error = sooptcopyin(sopt, rulenum,
39403b3a8eb9SGleb Smirnoff 2*sizeof(u_int32_t), sizeof(u_int32_t));
39413b3a8eb9SGleb Smirnoff if (error)
39423b3a8eb9SGleb Smirnoff break;
39433b3a8eb9SGleb Smirnoff size = sopt->sopt_valsize;
39443b3a8eb9SGleb Smirnoff if (size == sizeof(u_int32_t) && rulenum[0] != 0) {
39453b3a8eb9SGleb Smirnoff /* delete or reassign, locking done in del_entry() */
39463b3a8eb9SGleb Smirnoff error = del_entry(chain, rulenum[0]);
39473b3a8eb9SGleb Smirnoff } else if (size == 2*sizeof(u_int32_t)) { /* set enable/disable */
39483b3a8eb9SGleb Smirnoff IPFW_UH_WLOCK(chain);
39493b3a8eb9SGleb Smirnoff V_set_disable =
39503b3a8eb9SGleb Smirnoff (V_set_disable | rulenum[0]) & ~rulenum[1] &
39513b3a8eb9SGleb Smirnoff ~(1<<RESVD_SET); /* set RESVD_SET always enabled */
39523b3a8eb9SGleb Smirnoff IPFW_UH_WUNLOCK(chain);
39533b3a8eb9SGleb Smirnoff } else
39543b3a8eb9SGleb Smirnoff error = EINVAL;
39553b3a8eb9SGleb Smirnoff break;
39563b3a8eb9SGleb Smirnoff
39573b3a8eb9SGleb Smirnoff case IP_FW_ZERO:
39583b3a8eb9SGleb Smirnoff case IP_FW_RESETLOG: /* argument is an u_int_32, the rule number */
39593b3a8eb9SGleb Smirnoff rulenum[0] = 0;
39603b3a8eb9SGleb Smirnoff if (sopt->sopt_val != 0) {
39613b3a8eb9SGleb Smirnoff error = sooptcopyin(sopt, rulenum,
39623b3a8eb9SGleb Smirnoff sizeof(u_int32_t), sizeof(u_int32_t));
39633b3a8eb9SGleb Smirnoff if (error)
39643b3a8eb9SGleb Smirnoff break;
39653b3a8eb9SGleb Smirnoff }
39663b3a8eb9SGleb Smirnoff error = zero_entry(chain, rulenum[0],
39673b3a8eb9SGleb Smirnoff sopt->sopt_name == IP_FW_RESETLOG);
39683b3a8eb9SGleb Smirnoff break;
39693b3a8eb9SGleb Smirnoff
3970d3a4f924SAlexander V. Chernikov /*--- TABLE opcodes ---*/
3971d3a4f924SAlexander V. Chernikov case IP_FW_TABLE_ADD:
3972d3a4f924SAlexander V. Chernikov case IP_FW_TABLE_DEL:
3973d3a4f924SAlexander V. Chernikov {
3974d3a4f924SAlexander V. Chernikov ipfw_table_entry ent;
3975d3a4f924SAlexander V. Chernikov struct tentry_info tei;
3976d3a4f924SAlexander V. Chernikov struct tid_info ti;
39770cba2b28SAlexander V. Chernikov struct table_value v;
3978d3a4f924SAlexander V. Chernikov
3979d3a4f924SAlexander V. Chernikov error = sooptcopyin(sopt, &ent,
3980d3a4f924SAlexander V. Chernikov sizeof(ent), sizeof(ent));
3981d3a4f924SAlexander V. Chernikov if (error)
3982d3a4f924SAlexander V. Chernikov break;
3983d3a4f924SAlexander V. Chernikov
3984d3a4f924SAlexander V. Chernikov memset(&tei, 0, sizeof(tei));
3985d3a4f924SAlexander V. Chernikov tei.paddr = &ent.addr;
3986ac35ff17SAlexander V. Chernikov tei.subtype = AF_INET;
3987d3a4f924SAlexander V. Chernikov tei.masklen = ent.masklen;
39880cba2b28SAlexander V. Chernikov ipfw_import_table_value_legacy(ent.value, &v);
39890cba2b28SAlexander V. Chernikov tei.pvalue = &v;
3990d3a4f924SAlexander V. Chernikov memset(&ti, 0, sizeof(ti));
3991d3a4f924SAlexander V. Chernikov ti.uidx = ent.tbl;
3992d3a4f924SAlexander V. Chernikov ti.type = IPFW_TABLE_CIDR;
3993d3a4f924SAlexander V. Chernikov
3994d3a4f924SAlexander V. Chernikov error = (opt == IP_FW_TABLE_ADD) ?
39953a845e10SAlexander V. Chernikov add_table_entry(chain, &ti, &tei, 0, 1) :
39963a845e10SAlexander V. Chernikov del_table_entry(chain, &ti, &tei, 0, 1);
3997d3a4f924SAlexander V. Chernikov }
3998d3a4f924SAlexander V. Chernikov break;
3999d3a4f924SAlexander V. Chernikov
40003b3a8eb9SGleb Smirnoff case IP_FW_TABLE_FLUSH:
40013b3a8eb9SGleb Smirnoff {
40023b3a8eb9SGleb Smirnoff u_int16_t tbl;
4003b074b7bbSAlexander V. Chernikov struct tid_info ti;
40043b3a8eb9SGleb Smirnoff
40053b3a8eb9SGleb Smirnoff error = sooptcopyin(sopt, &tbl,
40063b3a8eb9SGleb Smirnoff sizeof(tbl), sizeof(tbl));
40073b3a8eb9SGleb Smirnoff if (error)
40083b3a8eb9SGleb Smirnoff break;
4009b074b7bbSAlexander V. Chernikov memset(&ti, 0, sizeof(ti));
4010b074b7bbSAlexander V. Chernikov ti.uidx = tbl;
40111832a7b3SAlexander V. Chernikov error = flush_table(chain, &ti);
40123b3a8eb9SGleb Smirnoff }
40133b3a8eb9SGleb Smirnoff break;
40143b3a8eb9SGleb Smirnoff
40153b3a8eb9SGleb Smirnoff case IP_FW_TABLE_GETSIZE:
40163b3a8eb9SGleb Smirnoff {
40173b3a8eb9SGleb Smirnoff u_int32_t tbl, cnt;
4018b074b7bbSAlexander V. Chernikov struct tid_info ti;
40193b3a8eb9SGleb Smirnoff
40203b3a8eb9SGleb Smirnoff if ((error = sooptcopyin(sopt, &tbl, sizeof(tbl),
40213b3a8eb9SGleb Smirnoff sizeof(tbl))))
40223b3a8eb9SGleb Smirnoff break;
4023b074b7bbSAlexander V. Chernikov memset(&ti, 0, sizeof(ti));
4024b074b7bbSAlexander V. Chernikov ti.uidx = tbl;
40253b3a8eb9SGleb Smirnoff IPFW_RLOCK(chain);
4026b074b7bbSAlexander V. Chernikov error = ipfw_count_table(chain, &ti, &cnt);
40273b3a8eb9SGleb Smirnoff IPFW_RUNLOCK(chain);
40283b3a8eb9SGleb Smirnoff if (error)
40293b3a8eb9SGleb Smirnoff break;
40303b3a8eb9SGleb Smirnoff error = sooptcopyout(sopt, &cnt, sizeof(cnt));
40313b3a8eb9SGleb Smirnoff }
40323b3a8eb9SGleb Smirnoff break;
40333b3a8eb9SGleb Smirnoff
40343b3a8eb9SGleb Smirnoff case IP_FW_TABLE_LIST:
40353b3a8eb9SGleb Smirnoff {
40363b3a8eb9SGleb Smirnoff ipfw_table *tbl;
4037b074b7bbSAlexander V. Chernikov struct tid_info ti;
40383b3a8eb9SGleb Smirnoff
40393b3a8eb9SGleb Smirnoff if (sopt->sopt_valsize < sizeof(*tbl)) {
40403b3a8eb9SGleb Smirnoff error = EINVAL;
40413b3a8eb9SGleb Smirnoff break;
40423b3a8eb9SGleb Smirnoff }
40433b3a8eb9SGleb Smirnoff size = sopt->sopt_valsize;
40443b3a8eb9SGleb Smirnoff tbl = malloc(size, M_TEMP, M_WAITOK);
40453b3a8eb9SGleb Smirnoff error = sooptcopyin(sopt, tbl, size, sizeof(*tbl));
40463b3a8eb9SGleb Smirnoff if (error) {
40473b3a8eb9SGleb Smirnoff free(tbl, M_TEMP);
40483b3a8eb9SGleb Smirnoff break;
40493b3a8eb9SGleb Smirnoff }
40503b3a8eb9SGleb Smirnoff tbl->size = (size - sizeof(*tbl)) /
40513b3a8eb9SGleb Smirnoff sizeof(ipfw_table_entry);
4052b074b7bbSAlexander V. Chernikov memset(&ti, 0, sizeof(ti));
4053b074b7bbSAlexander V. Chernikov ti.uidx = tbl->tbl;
40543b3a8eb9SGleb Smirnoff IPFW_RLOCK(chain);
4055f1220db8SAlexander V. Chernikov error = ipfw_dump_table_legacy(chain, &ti, tbl);
40563b3a8eb9SGleb Smirnoff IPFW_RUNLOCK(chain);
40573b3a8eb9SGleb Smirnoff if (error) {
40583b3a8eb9SGleb Smirnoff free(tbl, M_TEMP);
40593b3a8eb9SGleb Smirnoff break;
40603b3a8eb9SGleb Smirnoff }
40613b3a8eb9SGleb Smirnoff error = sooptcopyout(sopt, tbl, size);
40623b3a8eb9SGleb Smirnoff free(tbl, M_TEMP);
40633b3a8eb9SGleb Smirnoff }
40643b3a8eb9SGleb Smirnoff break;
40653b3a8eb9SGleb Smirnoff
40663b3a8eb9SGleb Smirnoff /*--- NAT operations are protected by the IPFW_LOCK ---*/
40673b3a8eb9SGleb Smirnoff case IP_FW_NAT_CFG:
40683b3a8eb9SGleb Smirnoff if (IPFW_NAT_LOADED)
40693b3a8eb9SGleb Smirnoff error = ipfw_nat_cfg_ptr(sopt);
40703b3a8eb9SGleb Smirnoff else {
40713b3a8eb9SGleb Smirnoff printf("IP_FW_NAT_CFG: %s\n",
40723b3a8eb9SGleb Smirnoff "ipfw_nat not present, please load it");
40733b3a8eb9SGleb Smirnoff error = EINVAL;
40743b3a8eb9SGleb Smirnoff }
40753b3a8eb9SGleb Smirnoff break;
40763b3a8eb9SGleb Smirnoff
40773b3a8eb9SGleb Smirnoff case IP_FW_NAT_DEL:
40783b3a8eb9SGleb Smirnoff if (IPFW_NAT_LOADED)
40793b3a8eb9SGleb Smirnoff error = ipfw_nat_del_ptr(sopt);
40803b3a8eb9SGleb Smirnoff else {
40813b3a8eb9SGleb Smirnoff printf("IP_FW_NAT_DEL: %s\n",
40823b3a8eb9SGleb Smirnoff "ipfw_nat not present, please load it");
40833b3a8eb9SGleb Smirnoff error = EINVAL;
40843b3a8eb9SGleb Smirnoff }
40853b3a8eb9SGleb Smirnoff break;
40863b3a8eb9SGleb Smirnoff
40873b3a8eb9SGleb Smirnoff case IP_FW_NAT_GET_CONFIG:
40883b3a8eb9SGleb Smirnoff if (IPFW_NAT_LOADED)
40893b3a8eb9SGleb Smirnoff error = ipfw_nat_get_cfg_ptr(sopt);
40903b3a8eb9SGleb Smirnoff else {
40913b3a8eb9SGleb Smirnoff printf("IP_FW_NAT_GET_CFG: %s\n",
40923b3a8eb9SGleb Smirnoff "ipfw_nat not present, please load it");
40933b3a8eb9SGleb Smirnoff error = EINVAL;
40943b3a8eb9SGleb Smirnoff }
40953b3a8eb9SGleb Smirnoff break;
40963b3a8eb9SGleb Smirnoff
40973b3a8eb9SGleb Smirnoff case IP_FW_NAT_GET_LOG:
40983b3a8eb9SGleb Smirnoff if (IPFW_NAT_LOADED)
40993b3a8eb9SGleb Smirnoff error = ipfw_nat_get_log_ptr(sopt);
41003b3a8eb9SGleb Smirnoff else {
41013b3a8eb9SGleb Smirnoff printf("IP_FW_NAT_GET_LOG: %s\n",
41023b3a8eb9SGleb Smirnoff "ipfw_nat not present, please load it");
41033b3a8eb9SGleb Smirnoff error = EINVAL;
41043b3a8eb9SGleb Smirnoff }
41053b3a8eb9SGleb Smirnoff break;
41063b3a8eb9SGleb Smirnoff
41073b3a8eb9SGleb Smirnoff default:
41083b3a8eb9SGleb Smirnoff printf("ipfw: ipfw_ctl invalid option %d\n", sopt->sopt_name);
41093b3a8eb9SGleb Smirnoff error = EINVAL;
41103b3a8eb9SGleb Smirnoff }
41113b3a8eb9SGleb Smirnoff
41123b3a8eb9SGleb Smirnoff return (error);
41133b3a8eb9SGleb Smirnoff #undef RULE_MAXSIZE
41143b3a8eb9SGleb Smirnoff }
41153b3a8eb9SGleb Smirnoff #define RULE_MAXSIZE (256*sizeof(u_int32_t))
41163b3a8eb9SGleb Smirnoff
41173b3a8eb9SGleb Smirnoff /* Functions to convert rules 7.2 <==> 8.0 */
41187e767c79SAlexander V. Chernikov static int
convert_rule_to_7(struct ip_fw_rule0 * rule)41197e767c79SAlexander V. Chernikov convert_rule_to_7(struct ip_fw_rule0 *rule)
41203b3a8eb9SGleb Smirnoff {
41213b3a8eb9SGleb Smirnoff /* Used to modify original rule */
41223b3a8eb9SGleb Smirnoff struct ip_fw7 *rule7 = (struct ip_fw7 *)rule;
41233b3a8eb9SGleb Smirnoff /* copy of original rule, version 8 */
4124030b184fSAlexander V. Chernikov struct ip_fw_rule0 *tmp;
41253b3a8eb9SGleb Smirnoff
41263b3a8eb9SGleb Smirnoff /* Used to copy commands */
41273b3a8eb9SGleb Smirnoff ipfw_insn *ccmd, *dst;
41283b3a8eb9SGleb Smirnoff int ll = 0, ccmdlen = 0;
41293b3a8eb9SGleb Smirnoff
41303b3a8eb9SGleb Smirnoff tmp = malloc(RULE_MAXSIZE, M_TEMP, M_NOWAIT | M_ZERO);
41313b3a8eb9SGleb Smirnoff if (tmp == NULL) {
41323b3a8eb9SGleb Smirnoff return 1; //XXX error
41333b3a8eb9SGleb Smirnoff }
41343b3a8eb9SGleb Smirnoff bcopy(rule, tmp, RULE_MAXSIZE);
41353b3a8eb9SGleb Smirnoff
41363b3a8eb9SGleb Smirnoff /* Copy fields */
41377e767c79SAlexander V. Chernikov //rule7->_pad = tmp->_pad;
41383b3a8eb9SGleb Smirnoff rule7->set = tmp->set;
41393b3a8eb9SGleb Smirnoff rule7->rulenum = tmp->rulenum;
41403b3a8eb9SGleb Smirnoff rule7->cmd_len = tmp->cmd_len;
41413b3a8eb9SGleb Smirnoff rule7->act_ofs = tmp->act_ofs;
41423b3a8eb9SGleb Smirnoff rule7->next_rule = (struct ip_fw7 *)tmp->next_rule;
41433b3a8eb9SGleb Smirnoff rule7->cmd_len = tmp->cmd_len;
4144030b184fSAlexander V. Chernikov rule7->pcnt = tmp->pcnt;
4145030b184fSAlexander V. Chernikov rule7->bcnt = tmp->bcnt;
4146030b184fSAlexander V. Chernikov rule7->timestamp = tmp->timestamp;
41473b3a8eb9SGleb Smirnoff
41483b3a8eb9SGleb Smirnoff /* Copy commands */
41493b3a8eb9SGleb Smirnoff for (ll = tmp->cmd_len, ccmd = tmp->cmd, dst = rule7->cmd ;
41503b3a8eb9SGleb Smirnoff ll > 0 ; ll -= ccmdlen, ccmd += ccmdlen, dst += ccmdlen) {
41513b3a8eb9SGleb Smirnoff ccmdlen = F_LEN(ccmd);
41523b3a8eb9SGleb Smirnoff
41533b3a8eb9SGleb Smirnoff bcopy(ccmd, dst, F_LEN(ccmd)*sizeof(uint32_t));
41543b3a8eb9SGleb Smirnoff
41553b3a8eb9SGleb Smirnoff if (dst->opcode > O_NAT)
41563b3a8eb9SGleb Smirnoff /* O_REASS doesn't exists in 7.2 version, so
41573b3a8eb9SGleb Smirnoff * decrement opcode if it is after O_REASS
41583b3a8eb9SGleb Smirnoff */
41593b3a8eb9SGleb Smirnoff dst->opcode--;
41603b3a8eb9SGleb Smirnoff
41613b3a8eb9SGleb Smirnoff if (ccmdlen > ll) {
41623b3a8eb9SGleb Smirnoff printf("ipfw: opcode %d size truncated\n",
41633b3a8eb9SGleb Smirnoff ccmd->opcode);
41643b3a8eb9SGleb Smirnoff return EINVAL;
41653b3a8eb9SGleb Smirnoff }
41663b3a8eb9SGleb Smirnoff }
41673b3a8eb9SGleb Smirnoff free(tmp, M_TEMP);
41683b3a8eb9SGleb Smirnoff
41693b3a8eb9SGleb Smirnoff return 0;
41703b3a8eb9SGleb Smirnoff }
41713b3a8eb9SGleb Smirnoff
41727e767c79SAlexander V. Chernikov static int
convert_rule_to_8(struct ip_fw_rule0 * rule)41737e767c79SAlexander V. Chernikov convert_rule_to_8(struct ip_fw_rule0 *rule)
41743b3a8eb9SGleb Smirnoff {
41753b3a8eb9SGleb Smirnoff /* Used to modify original rule */
41763b3a8eb9SGleb Smirnoff struct ip_fw7 *rule7 = (struct ip_fw7 *) rule;
41773b3a8eb9SGleb Smirnoff
41783b3a8eb9SGleb Smirnoff /* Used to copy commands */
41793b3a8eb9SGleb Smirnoff ipfw_insn *ccmd, *dst;
41803b3a8eb9SGleb Smirnoff int ll = 0, ccmdlen = 0;
41813b3a8eb9SGleb Smirnoff
41823b3a8eb9SGleb Smirnoff /* Copy of original rule */
41833b3a8eb9SGleb Smirnoff struct ip_fw7 *tmp = malloc(RULE_MAXSIZE, M_TEMP, M_NOWAIT | M_ZERO);
41843b3a8eb9SGleb Smirnoff if (tmp == NULL) {
41853b3a8eb9SGleb Smirnoff return 1; //XXX error
41863b3a8eb9SGleb Smirnoff }
41873b3a8eb9SGleb Smirnoff
41883b3a8eb9SGleb Smirnoff bcopy(rule7, tmp, RULE_MAXSIZE);
41893b3a8eb9SGleb Smirnoff
41903b3a8eb9SGleb Smirnoff for (ll = tmp->cmd_len, ccmd = tmp->cmd, dst = rule->cmd ;
41913b3a8eb9SGleb Smirnoff ll > 0 ; ll -= ccmdlen, ccmd += ccmdlen, dst += ccmdlen) {
41923b3a8eb9SGleb Smirnoff ccmdlen = F_LEN(ccmd);
41933b3a8eb9SGleb Smirnoff
41943b3a8eb9SGleb Smirnoff bcopy(ccmd, dst, F_LEN(ccmd)*sizeof(uint32_t));
41953b3a8eb9SGleb Smirnoff
41963b3a8eb9SGleb Smirnoff if (dst->opcode > O_NAT)
41973b3a8eb9SGleb Smirnoff /* O_REASS doesn't exists in 7.2 version, so
41983b3a8eb9SGleb Smirnoff * increment opcode if it is after O_REASS
41993b3a8eb9SGleb Smirnoff */
42003b3a8eb9SGleb Smirnoff dst->opcode++;
42013b3a8eb9SGleb Smirnoff
42023b3a8eb9SGleb Smirnoff if (ccmdlen > ll) {
42033b3a8eb9SGleb Smirnoff printf("ipfw: opcode %d size truncated\n",
42043b3a8eb9SGleb Smirnoff ccmd->opcode);
42053b3a8eb9SGleb Smirnoff return EINVAL;
42063b3a8eb9SGleb Smirnoff }
42073b3a8eb9SGleb Smirnoff }
42083b3a8eb9SGleb Smirnoff
42093b3a8eb9SGleb Smirnoff rule->_pad = tmp->_pad;
42103b3a8eb9SGleb Smirnoff rule->set = tmp->set;
42113b3a8eb9SGleb Smirnoff rule->rulenum = tmp->rulenum;
42123b3a8eb9SGleb Smirnoff rule->cmd_len = tmp->cmd_len;
42133b3a8eb9SGleb Smirnoff rule->act_ofs = tmp->act_ofs;
42143b3a8eb9SGleb Smirnoff rule->next_rule = (struct ip_fw *)tmp->next_rule;
42153b3a8eb9SGleb Smirnoff rule->cmd_len = tmp->cmd_len;
42163b3a8eb9SGleb Smirnoff rule->id = 0; /* XXX see if is ok = 0 */
42173b3a8eb9SGleb Smirnoff rule->pcnt = tmp->pcnt;
42183b3a8eb9SGleb Smirnoff rule->bcnt = tmp->bcnt;
42193b3a8eb9SGleb Smirnoff rule->timestamp = tmp->timestamp;
42203b3a8eb9SGleb Smirnoff
42213b3a8eb9SGleb Smirnoff free (tmp, M_TEMP);
42223b3a8eb9SGleb Smirnoff return 0;
42233b3a8eb9SGleb Smirnoff }
42243b3a8eb9SGleb Smirnoff
4225b074b7bbSAlexander V. Chernikov /*
4226b074b7bbSAlexander V. Chernikov * Named object api
4227b074b7bbSAlexander V. Chernikov *
4228b074b7bbSAlexander V. Chernikov */
4229b074b7bbSAlexander V. Chernikov
423074b22066SAlexander V. Chernikov void
ipfw_init_srv(struct ip_fw_chain * ch)423174b22066SAlexander V. Chernikov ipfw_init_srv(struct ip_fw_chain *ch)
423274b22066SAlexander V. Chernikov {
423374b22066SAlexander V. Chernikov
423474b22066SAlexander V. Chernikov ch->srvmap = ipfw_objhash_create(IPFW_OBJECTS_DEFAULT);
423574b22066SAlexander V. Chernikov ch->srvstate = malloc(sizeof(void *) * IPFW_OBJECTS_DEFAULT,
423674b22066SAlexander V. Chernikov M_IPFW, M_WAITOK | M_ZERO);
423774b22066SAlexander V. Chernikov }
423874b22066SAlexander V. Chernikov
423974b22066SAlexander V. Chernikov void
ipfw_destroy_srv(struct ip_fw_chain * ch)424074b22066SAlexander V. Chernikov ipfw_destroy_srv(struct ip_fw_chain *ch)
424174b22066SAlexander V. Chernikov {
424274b22066SAlexander V. Chernikov
424374b22066SAlexander V. Chernikov free(ch->srvstate, M_IPFW);
424474b22066SAlexander V. Chernikov ipfw_objhash_destroy(ch->srvmap);
424574b22066SAlexander V. Chernikov }
424674b22066SAlexander V. Chernikov
42479f7d47b0SAlexander V. Chernikov /*
42489f7d47b0SAlexander V. Chernikov * Allocate new bitmask which can be used to enlarge/shrink
42499f7d47b0SAlexander V. Chernikov * named instance index.
42509f7d47b0SAlexander V. Chernikov */
4251b074b7bbSAlexander V. Chernikov void
ipfw_objhash_bitmap_alloc(uint32_t items,void ** idx,int * pblocks)4252b074b7bbSAlexander V. Chernikov ipfw_objhash_bitmap_alloc(uint32_t items, void **idx, int *pblocks)
4253b074b7bbSAlexander V. Chernikov {
4254b074b7bbSAlexander V. Chernikov size_t size;
4255b074b7bbSAlexander V. Chernikov int max_blocks;
425656f43a5eSAlexander V. Chernikov u_long *idx_mask;
4257b074b7bbSAlexander V. Chernikov
42580cba2b28SAlexander V. Chernikov KASSERT((items % BLOCK_ITEMS) == 0,
42595f8ad2bdSAlexander V. Chernikov ("bitmask size needs to power of 2 and greater or equal to %zu",
42600cba2b28SAlexander V. Chernikov BLOCK_ITEMS));
42610cba2b28SAlexander V. Chernikov
4262b074b7bbSAlexander V. Chernikov max_blocks = items / BLOCK_ITEMS;
4263b074b7bbSAlexander V. Chernikov size = items / 8;
4264b074b7bbSAlexander V. Chernikov idx_mask = malloc(size * IPFW_MAX_SETS, M_IPFW, M_WAITOK);
4265b074b7bbSAlexander V. Chernikov /* Mark all as free */
4266b074b7bbSAlexander V. Chernikov memset(idx_mask, 0xFF, size * IPFW_MAX_SETS);
426756f43a5eSAlexander V. Chernikov *idx_mask &= ~(u_long)1; /* Skip index 0 */
4268b074b7bbSAlexander V. Chernikov
4269b074b7bbSAlexander V. Chernikov *idx = idx_mask;
4270b074b7bbSAlexander V. Chernikov *pblocks = max_blocks;
4271b074b7bbSAlexander V. Chernikov }
4272b074b7bbSAlexander V. Chernikov
42739f7d47b0SAlexander V. Chernikov /*
42749f7d47b0SAlexander V. Chernikov * Copy current bitmask index to new one.
42759f7d47b0SAlexander V. Chernikov */
42769f7d47b0SAlexander V. Chernikov void
ipfw_objhash_bitmap_merge(struct namedobj_instance * ni,void ** idx,int * blocks)4277b074b7bbSAlexander V. Chernikov ipfw_objhash_bitmap_merge(struct namedobj_instance *ni, void **idx, int *blocks)
4278b074b7bbSAlexander V. Chernikov {
4279b074b7bbSAlexander V. Chernikov int old_blocks, new_blocks;
4280b074b7bbSAlexander V. Chernikov u_long *old_idx, *new_idx;
4281b074b7bbSAlexander V. Chernikov int i;
4282b074b7bbSAlexander V. Chernikov
4283b074b7bbSAlexander V. Chernikov old_idx = ni->idx_mask;
4284b074b7bbSAlexander V. Chernikov old_blocks = ni->max_blocks;
4285b074b7bbSAlexander V. Chernikov new_idx = *idx;
4286b074b7bbSAlexander V. Chernikov new_blocks = *blocks;
4287b074b7bbSAlexander V. Chernikov
4288b074b7bbSAlexander V. Chernikov for (i = 0; i < IPFW_MAX_SETS; i++) {
4289b074b7bbSAlexander V. Chernikov memcpy(&new_idx[new_blocks * i], &old_idx[old_blocks * i],
4290b074b7bbSAlexander V. Chernikov old_blocks * sizeof(u_long));
4291b074b7bbSAlexander V. Chernikov }
42929f7d47b0SAlexander V. Chernikov }
4293b074b7bbSAlexander V. Chernikov
42949f7d47b0SAlexander V. Chernikov /*
42959f7d47b0SAlexander V. Chernikov * Swaps current @ni index with new one.
42969f7d47b0SAlexander V. Chernikov */
42979f7d47b0SAlexander V. Chernikov void
ipfw_objhash_bitmap_swap(struct namedobj_instance * ni,void ** idx,int * blocks)42989f7d47b0SAlexander V. Chernikov ipfw_objhash_bitmap_swap(struct namedobj_instance *ni, void **idx, int *blocks)
42999f7d47b0SAlexander V. Chernikov {
43009f7d47b0SAlexander V. Chernikov int old_blocks;
43019f7d47b0SAlexander V. Chernikov u_long *old_idx;
43029f7d47b0SAlexander V. Chernikov
43039f7d47b0SAlexander V. Chernikov old_idx = ni->idx_mask;
43049f7d47b0SAlexander V. Chernikov old_blocks = ni->max_blocks;
43059f7d47b0SAlexander V. Chernikov
43069f7d47b0SAlexander V. Chernikov ni->idx_mask = *idx;
43079f7d47b0SAlexander V. Chernikov ni->max_blocks = *blocks;
4308b074b7bbSAlexander V. Chernikov
4309b074b7bbSAlexander V. Chernikov /* Save old values */
4310b074b7bbSAlexander V. Chernikov *idx = old_idx;
4311b074b7bbSAlexander V. Chernikov *blocks = old_blocks;
4312b074b7bbSAlexander V. Chernikov }
4313b074b7bbSAlexander V. Chernikov
4314b074b7bbSAlexander V. Chernikov void
ipfw_objhash_bitmap_free(void * idx,int blocks)4315b074b7bbSAlexander V. Chernikov ipfw_objhash_bitmap_free(void *idx, int blocks)
4316b074b7bbSAlexander V. Chernikov {
4317b074b7bbSAlexander V. Chernikov
4318b074b7bbSAlexander V. Chernikov free(idx, M_IPFW);
4319b074b7bbSAlexander V. Chernikov }
4320b074b7bbSAlexander V. Chernikov
4321b074b7bbSAlexander V. Chernikov /*
4322b074b7bbSAlexander V. Chernikov * Creates named hash instance.
4323b074b7bbSAlexander V. Chernikov * Must be called without holding any locks.
4324b074b7bbSAlexander V. Chernikov * Return pointer to new instance.
4325b074b7bbSAlexander V. Chernikov */
4326b074b7bbSAlexander V. Chernikov struct namedobj_instance *
ipfw_objhash_create(uint32_t items)4327b074b7bbSAlexander V. Chernikov ipfw_objhash_create(uint32_t items)
4328b074b7bbSAlexander V. Chernikov {
4329b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni;
4330b074b7bbSAlexander V. Chernikov int i;
4331b074b7bbSAlexander V. Chernikov size_t size;
4332b074b7bbSAlexander V. Chernikov
4333b074b7bbSAlexander V. Chernikov size = sizeof(struct namedobj_instance) +
4334b074b7bbSAlexander V. Chernikov sizeof(struct namedobjects_head) * NAMEDOBJ_HASH_SIZE +
4335b074b7bbSAlexander V. Chernikov sizeof(struct namedobjects_head) * NAMEDOBJ_HASH_SIZE;
4336b074b7bbSAlexander V. Chernikov
4337b074b7bbSAlexander V. Chernikov ni = malloc(size, M_IPFW, M_WAITOK | M_ZERO);
4338b074b7bbSAlexander V. Chernikov ni->nn_size = NAMEDOBJ_HASH_SIZE;
4339b074b7bbSAlexander V. Chernikov ni->nv_size = NAMEDOBJ_HASH_SIZE;
4340b074b7bbSAlexander V. Chernikov
4341b074b7bbSAlexander V. Chernikov ni->names = (struct namedobjects_head *)(ni +1);
4342b074b7bbSAlexander V. Chernikov ni->values = &ni->names[ni->nn_size];
4343b074b7bbSAlexander V. Chernikov
4344b074b7bbSAlexander V. Chernikov for (i = 0; i < ni->nn_size; i++)
4345b074b7bbSAlexander V. Chernikov TAILQ_INIT(&ni->names[i]);
4346b074b7bbSAlexander V. Chernikov
4347b074b7bbSAlexander V. Chernikov for (i = 0; i < ni->nv_size; i++)
4348b074b7bbSAlexander V. Chernikov TAILQ_INIT(&ni->values[i]);
4349b074b7bbSAlexander V. Chernikov
435013263632SAlexander V. Chernikov /* Set default hashing/comparison functions */
435113263632SAlexander V. Chernikov ni->hash_f = objhash_hash_name;
435213263632SAlexander V. Chernikov ni->cmp_f = objhash_cmp_name;
435313263632SAlexander V. Chernikov
4354b074b7bbSAlexander V. Chernikov /* Allocate bitmask separately due to possible resize */
4355b074b7bbSAlexander V. Chernikov ipfw_objhash_bitmap_alloc(items, (void*)&ni->idx_mask, &ni->max_blocks);
4356b074b7bbSAlexander V. Chernikov
4357b074b7bbSAlexander V. Chernikov return (ni);
4358b074b7bbSAlexander V. Chernikov }
4359b074b7bbSAlexander V. Chernikov
4360b074b7bbSAlexander V. Chernikov void
ipfw_objhash_destroy(struct namedobj_instance * ni)4361b074b7bbSAlexander V. Chernikov ipfw_objhash_destroy(struct namedobj_instance *ni)
4362b074b7bbSAlexander V. Chernikov {
4363b074b7bbSAlexander V. Chernikov
4364b074b7bbSAlexander V. Chernikov free(ni->idx_mask, M_IPFW);
4365b074b7bbSAlexander V. Chernikov free(ni, M_IPFW);
4366b074b7bbSAlexander V. Chernikov }
4367b074b7bbSAlexander V. Chernikov
436813263632SAlexander V. Chernikov void
ipfw_objhash_set_funcs(struct namedobj_instance * ni,objhash_hash_f * hash_f,objhash_cmp_f * cmp_f)436913263632SAlexander V. Chernikov ipfw_objhash_set_funcs(struct namedobj_instance *ni, objhash_hash_f *hash_f,
437013263632SAlexander V. Chernikov objhash_cmp_f *cmp_f)
437113263632SAlexander V. Chernikov {
437213263632SAlexander V. Chernikov
437313263632SAlexander V. Chernikov ni->hash_f = hash_f;
437413263632SAlexander V. Chernikov ni->cmp_f = cmp_f;
437513263632SAlexander V. Chernikov }
437613263632SAlexander V. Chernikov
4377b074b7bbSAlexander V. Chernikov static uint32_t
objhash_hash_name(struct namedobj_instance * ni,const void * name,uint32_t set)43782acdf79fSAndrey V. Elsukov objhash_hash_name(struct namedobj_instance *ni, const void *name, uint32_t set)
4379b074b7bbSAlexander V. Chernikov {
4380b074b7bbSAlexander V. Chernikov
43812acdf79fSAndrey V. Elsukov return (fnv_32_str((const char *)name, FNV1_32_INIT));
4382b074b7bbSAlexander V. Chernikov }
4383b074b7bbSAlexander V. Chernikov
438413263632SAlexander V. Chernikov static int
objhash_cmp_name(struct named_object * no,const void * name,uint32_t set)43852acdf79fSAndrey V. Elsukov objhash_cmp_name(struct named_object *no, const void *name, uint32_t set)
438613263632SAlexander V. Chernikov {
438713263632SAlexander V. Chernikov
43882acdf79fSAndrey V. Elsukov if ((strcmp(no->name, (const char *)name) == 0) && (no->set == set))
438913263632SAlexander V. Chernikov return (0);
439013263632SAlexander V. Chernikov
439113263632SAlexander V. Chernikov return (1);
439213263632SAlexander V. Chernikov }
439313263632SAlexander V. Chernikov
4394b074b7bbSAlexander V. Chernikov static uint32_t
objhash_hash_idx(struct namedobj_instance * ni,uint32_t val)439513263632SAlexander V. Chernikov objhash_hash_idx(struct namedobj_instance *ni, uint32_t val)
4396b074b7bbSAlexander V. Chernikov {
4397b074b7bbSAlexander V. Chernikov uint32_t v;
4398b074b7bbSAlexander V. Chernikov
4399b074b7bbSAlexander V. Chernikov v = val % (ni->nv_size - 1);
4400b074b7bbSAlexander V. Chernikov
4401b074b7bbSAlexander V. Chernikov return (v);
4402b074b7bbSAlexander V. Chernikov }
4403b074b7bbSAlexander V. Chernikov
4404b074b7bbSAlexander V. Chernikov struct named_object *
ipfw_objhash_lookup_name(struct namedobj_instance * ni,uint32_t set,const char * name)4405e81fcbecSJustin Hibbits ipfw_objhash_lookup_name(struct namedobj_instance *ni, uint32_t set,
4406e81fcbecSJustin Hibbits const char *name)
4407b074b7bbSAlexander V. Chernikov {
4408b074b7bbSAlexander V. Chernikov struct named_object *no;
4409b074b7bbSAlexander V. Chernikov uint32_t hash;
4410b074b7bbSAlexander V. Chernikov
44110cba2b28SAlexander V. Chernikov hash = ni->hash_f(ni, name, set) % ni->nn_size;
4412b074b7bbSAlexander V. Chernikov
4413b074b7bbSAlexander V. Chernikov TAILQ_FOREACH(no, &ni->names[hash], nn_next) {
441413263632SAlexander V. Chernikov if (ni->cmp_f(no, name, set) == 0)
4415b074b7bbSAlexander V. Chernikov return (no);
4416b074b7bbSAlexander V. Chernikov }
4417b074b7bbSAlexander V. Chernikov
4418b074b7bbSAlexander V. Chernikov return (NULL);
4419b074b7bbSAlexander V. Chernikov }
4420b074b7bbSAlexander V. Chernikov
442174b22066SAlexander V. Chernikov /*
44222acdf79fSAndrey V. Elsukov * Find named object by @uid.
44232acdf79fSAndrey V. Elsukov * Check @tlvs for valid data inside.
44242acdf79fSAndrey V. Elsukov *
44252acdf79fSAndrey V. Elsukov * Returns pointer to found TLV or NULL.
44262acdf79fSAndrey V. Elsukov */
44272df1a11fSAndrey V. Elsukov ipfw_obj_ntlv *
ipfw_find_name_tlv_type(void * tlvs,int len,uint16_t uidx,uint32_t etlv)44282df1a11fSAndrey V. Elsukov ipfw_find_name_tlv_type(void *tlvs, int len, uint16_t uidx, uint32_t etlv)
44292acdf79fSAndrey V. Elsukov {
44302acdf79fSAndrey V. Elsukov ipfw_obj_ntlv *ntlv;
44312acdf79fSAndrey V. Elsukov uintptr_t pa, pe;
44322acdf79fSAndrey V. Elsukov int l;
44332acdf79fSAndrey V. Elsukov
44342acdf79fSAndrey V. Elsukov pa = (uintptr_t)tlvs;
44352acdf79fSAndrey V. Elsukov pe = pa + len;
44362acdf79fSAndrey V. Elsukov l = 0;
44372acdf79fSAndrey V. Elsukov for (; pa < pe; pa += l) {
44382acdf79fSAndrey V. Elsukov ntlv = (ipfw_obj_ntlv *)pa;
44392acdf79fSAndrey V. Elsukov l = ntlv->head.length;
44402acdf79fSAndrey V. Elsukov
44412acdf79fSAndrey V. Elsukov if (l != sizeof(*ntlv))
44422acdf79fSAndrey V. Elsukov return (NULL);
44432acdf79fSAndrey V. Elsukov
44442acdf79fSAndrey V. Elsukov if (ntlv->idx != uidx)
44452acdf79fSAndrey V. Elsukov continue;
44462acdf79fSAndrey V. Elsukov /*
44472acdf79fSAndrey V. Elsukov * When userland has specified zero TLV type, do
44482acdf79fSAndrey V. Elsukov * not compare it with eltv. In some cases userland
44492acdf79fSAndrey V. Elsukov * doesn't know what type should it have. Use only
44502acdf79fSAndrey V. Elsukov * uidx and name for search named_object.
44512acdf79fSAndrey V. Elsukov */
44522acdf79fSAndrey V. Elsukov if (ntlv->head.type != 0 &&
44532acdf79fSAndrey V. Elsukov ntlv->head.type != (uint16_t)etlv)
44542acdf79fSAndrey V. Elsukov continue;
44552acdf79fSAndrey V. Elsukov
44562acdf79fSAndrey V. Elsukov if (ipfw_check_object_name_generic(ntlv->name) != 0)
44572acdf79fSAndrey V. Elsukov return (NULL);
44582acdf79fSAndrey V. Elsukov
44592acdf79fSAndrey V. Elsukov return (ntlv);
44602acdf79fSAndrey V. Elsukov }
44612acdf79fSAndrey V. Elsukov
44622acdf79fSAndrey V. Elsukov return (NULL);
44632acdf79fSAndrey V. Elsukov }
44642acdf79fSAndrey V. Elsukov
44652acdf79fSAndrey V. Elsukov /*
44662acdf79fSAndrey V. Elsukov * Finds object config based on either legacy index
44672acdf79fSAndrey V. Elsukov * or name in ntlv.
44682acdf79fSAndrey V. Elsukov * Note @ti structure contains unchecked data from userland.
44692acdf79fSAndrey V. Elsukov *
44702acdf79fSAndrey V. Elsukov * Returns 0 in success and fills in @pno with found config
44712acdf79fSAndrey V. Elsukov */
44722acdf79fSAndrey V. Elsukov int
ipfw_objhash_find_type(struct namedobj_instance * ni,struct tid_info * ti,uint32_t etlv,struct named_object ** pno)44732acdf79fSAndrey V. Elsukov ipfw_objhash_find_type(struct namedobj_instance *ni, struct tid_info *ti,
44742acdf79fSAndrey V. Elsukov uint32_t etlv, struct named_object **pno)
44752acdf79fSAndrey V. Elsukov {
44762acdf79fSAndrey V. Elsukov char *name;
44772acdf79fSAndrey V. Elsukov ipfw_obj_ntlv *ntlv;
44782acdf79fSAndrey V. Elsukov uint32_t set;
44792acdf79fSAndrey V. Elsukov
44802acdf79fSAndrey V. Elsukov if (ti->tlvs == NULL)
44812acdf79fSAndrey V. Elsukov return (EINVAL);
44822acdf79fSAndrey V. Elsukov
44832df1a11fSAndrey V. Elsukov ntlv = ipfw_find_name_tlv_type(ti->tlvs, ti->tlen, ti->uidx, etlv);
44842acdf79fSAndrey V. Elsukov if (ntlv == NULL)
44852acdf79fSAndrey V. Elsukov return (EINVAL);
44862acdf79fSAndrey V. Elsukov name = ntlv->name;
44872acdf79fSAndrey V. Elsukov
44882acdf79fSAndrey V. Elsukov /*
44892acdf79fSAndrey V. Elsukov * Use set provided by @ti instead of @ntlv one.
44902acdf79fSAndrey V. Elsukov * This is needed due to different sets behavior
44912acdf79fSAndrey V. Elsukov * controlled by V_fw_tables_sets.
44922acdf79fSAndrey V. Elsukov */
44932acdf79fSAndrey V. Elsukov set = ti->set;
44942acdf79fSAndrey V. Elsukov *pno = ipfw_objhash_lookup_name(ni, set, name);
44952acdf79fSAndrey V. Elsukov if (*pno == NULL)
44962acdf79fSAndrey V. Elsukov return (ESRCH);
44972acdf79fSAndrey V. Elsukov return (0);
44982acdf79fSAndrey V. Elsukov }
44992acdf79fSAndrey V. Elsukov
45002acdf79fSAndrey V. Elsukov /*
450174b22066SAlexander V. Chernikov * Find named object by name, considering also its TLV type.
450274b22066SAlexander V. Chernikov */
450374b22066SAlexander V. Chernikov struct named_object *
ipfw_objhash_lookup_name_type(struct namedobj_instance * ni,uint32_t set,uint32_t type,const char * name)450474b22066SAlexander V. Chernikov ipfw_objhash_lookup_name_type(struct namedobj_instance *ni, uint32_t set,
45052acdf79fSAndrey V. Elsukov uint32_t type, const char *name)
450674b22066SAlexander V. Chernikov {
450774b22066SAlexander V. Chernikov struct named_object *no;
450874b22066SAlexander V. Chernikov uint32_t hash;
450974b22066SAlexander V. Chernikov
451074b22066SAlexander V. Chernikov hash = ni->hash_f(ni, name, set) % ni->nn_size;
451174b22066SAlexander V. Chernikov
451274b22066SAlexander V. Chernikov TAILQ_FOREACH(no, &ni->names[hash], nn_next) {
45134bd91656SAndrey V. Elsukov if (ni->cmp_f(no, name, set) == 0 &&
45144bd91656SAndrey V. Elsukov no->etlv == (uint16_t)type)
451574b22066SAlexander V. Chernikov return (no);
451674b22066SAlexander V. Chernikov }
451774b22066SAlexander V. Chernikov
451874b22066SAlexander V. Chernikov return (NULL);
451974b22066SAlexander V. Chernikov }
452074b22066SAlexander V. Chernikov
4521b074b7bbSAlexander V. Chernikov struct named_object *
ipfw_objhash_lookup_kidx(struct namedobj_instance * ni,uint16_t kidx)4522ac35ff17SAlexander V. Chernikov ipfw_objhash_lookup_kidx(struct namedobj_instance *ni, uint16_t kidx)
4523b074b7bbSAlexander V. Chernikov {
4524b074b7bbSAlexander V. Chernikov struct named_object *no;
4525b074b7bbSAlexander V. Chernikov uint32_t hash;
4526b074b7bbSAlexander V. Chernikov
452713263632SAlexander V. Chernikov hash = objhash_hash_idx(ni, kidx);
4528b074b7bbSAlexander V. Chernikov
4529b074b7bbSAlexander V. Chernikov TAILQ_FOREACH(no, &ni->values[hash], nv_next) {
4530ac35ff17SAlexander V. Chernikov if (no->kidx == kidx)
4531b074b7bbSAlexander V. Chernikov return (no);
4532b074b7bbSAlexander V. Chernikov }
4533b074b7bbSAlexander V. Chernikov
4534b074b7bbSAlexander V. Chernikov return (NULL);
4535b074b7bbSAlexander V. Chernikov }
4536b074b7bbSAlexander V. Chernikov
45379490a627SAlexander V. Chernikov int
ipfw_objhash_same_name(struct namedobj_instance * ni,struct named_object * a,struct named_object * b)45389490a627SAlexander V. Chernikov ipfw_objhash_same_name(struct namedobj_instance *ni, struct named_object *a,
45399490a627SAlexander V. Chernikov struct named_object *b)
45409490a627SAlexander V. Chernikov {
45419490a627SAlexander V. Chernikov
45429490a627SAlexander V. Chernikov if ((strcmp(a->name, b->name) == 0) && a->set == b->set)
45439490a627SAlexander V. Chernikov return (1);
45449490a627SAlexander V. Chernikov
45459490a627SAlexander V. Chernikov return (0);
45469490a627SAlexander V. Chernikov }
45479490a627SAlexander V. Chernikov
4548b074b7bbSAlexander V. Chernikov void
ipfw_objhash_add(struct namedobj_instance * ni,struct named_object * no)4549b074b7bbSAlexander V. Chernikov ipfw_objhash_add(struct namedobj_instance *ni, struct named_object *no)
4550b074b7bbSAlexander V. Chernikov {
4551b074b7bbSAlexander V. Chernikov uint32_t hash;
4552b074b7bbSAlexander V. Chernikov
45530cba2b28SAlexander V. Chernikov hash = ni->hash_f(ni, no->name, no->set) % ni->nn_size;
4554b074b7bbSAlexander V. Chernikov TAILQ_INSERT_HEAD(&ni->names[hash], no, nn_next);
4555b074b7bbSAlexander V. Chernikov
455613263632SAlexander V. Chernikov hash = objhash_hash_idx(ni, no->kidx);
4557b074b7bbSAlexander V. Chernikov TAILQ_INSERT_HEAD(&ni->values[hash], no, nv_next);
45589f7d47b0SAlexander V. Chernikov
45599f7d47b0SAlexander V. Chernikov ni->count++;
4560b074b7bbSAlexander V. Chernikov }
4561b074b7bbSAlexander V. Chernikov
4562b074b7bbSAlexander V. Chernikov void
ipfw_objhash_del(struct namedobj_instance * ni,struct named_object * no)4563b074b7bbSAlexander V. Chernikov ipfw_objhash_del(struct namedobj_instance *ni, struct named_object *no)
4564b074b7bbSAlexander V. Chernikov {
4565b074b7bbSAlexander V. Chernikov uint32_t hash;
4566b074b7bbSAlexander V. Chernikov
45670cba2b28SAlexander V. Chernikov hash = ni->hash_f(ni, no->name, no->set) % ni->nn_size;
4568b074b7bbSAlexander V. Chernikov TAILQ_REMOVE(&ni->names[hash], no, nn_next);
4569b074b7bbSAlexander V. Chernikov
457013263632SAlexander V. Chernikov hash = objhash_hash_idx(ni, no->kidx);
4571b074b7bbSAlexander V. Chernikov TAILQ_REMOVE(&ni->values[hash], no, nv_next);
45729f7d47b0SAlexander V. Chernikov
45739f7d47b0SAlexander V. Chernikov ni->count--;
45749f7d47b0SAlexander V. Chernikov }
45759f7d47b0SAlexander V. Chernikov
45769f7d47b0SAlexander V. Chernikov uint32_t
ipfw_objhash_count(struct namedobj_instance * ni)45779f7d47b0SAlexander V. Chernikov ipfw_objhash_count(struct namedobj_instance *ni)
45789f7d47b0SAlexander V. Chernikov {
45799f7d47b0SAlexander V. Chernikov
45809f7d47b0SAlexander V. Chernikov return (ni->count);
4581b074b7bbSAlexander V. Chernikov }
4582b074b7bbSAlexander V. Chernikov
45832685841bSAndrey V. Elsukov uint32_t
ipfw_objhash_count_type(struct namedobj_instance * ni,uint16_t type)45842685841bSAndrey V. Elsukov ipfw_objhash_count_type(struct namedobj_instance *ni, uint16_t type)
45852685841bSAndrey V. Elsukov {
45862685841bSAndrey V. Elsukov struct named_object *no;
45872685841bSAndrey V. Elsukov uint32_t count;
45882685841bSAndrey V. Elsukov int i;
45892685841bSAndrey V. Elsukov
45902685841bSAndrey V. Elsukov count = 0;
45912685841bSAndrey V. Elsukov for (i = 0; i < ni->nn_size; i++) {
45922685841bSAndrey V. Elsukov TAILQ_FOREACH(no, &ni->names[i], nn_next) {
45932685841bSAndrey V. Elsukov if (no->etlv == type)
45942685841bSAndrey V. Elsukov count++;
45952685841bSAndrey V. Elsukov }
45962685841bSAndrey V. Elsukov }
45972685841bSAndrey V. Elsukov return (count);
45982685841bSAndrey V. Elsukov }
45992685841bSAndrey V. Elsukov
4600b074b7bbSAlexander V. Chernikov /*
4601b074b7bbSAlexander V. Chernikov * Runs @func for each found named object.
4602b074b7bbSAlexander V. Chernikov * It is safe to delete objects from callback
4603b074b7bbSAlexander V. Chernikov */
4604b309f085SAndrey V. Elsukov int
ipfw_objhash_foreach(struct namedobj_instance * ni,objhash_cb_t * f,void * arg)4605b074b7bbSAlexander V. Chernikov ipfw_objhash_foreach(struct namedobj_instance *ni, objhash_cb_t *f, void *arg)
4606b074b7bbSAlexander V. Chernikov {
4607b074b7bbSAlexander V. Chernikov struct named_object *no, *no_tmp;
4608b309f085SAndrey V. Elsukov int i, ret;
4609b074b7bbSAlexander V. Chernikov
4610b074b7bbSAlexander V. Chernikov for (i = 0; i < ni->nn_size; i++) {
4611b309f085SAndrey V. Elsukov TAILQ_FOREACH_SAFE(no, &ni->names[i], nn_next, no_tmp) {
4612b309f085SAndrey V. Elsukov ret = f(ni, no, arg);
4613b309f085SAndrey V. Elsukov if (ret != 0)
4614b309f085SAndrey V. Elsukov return (ret);
4615b074b7bbSAlexander V. Chernikov }
4616b074b7bbSAlexander V. Chernikov }
4617b309f085SAndrey V. Elsukov return (0);
4618b309f085SAndrey V. Elsukov }
4619b074b7bbSAlexander V. Chernikov
4620b074b7bbSAlexander V. Chernikov /*
46212685841bSAndrey V. Elsukov * Runs @f for each found named object with type @type.
46222685841bSAndrey V. Elsukov * It is safe to delete objects from callback
46232685841bSAndrey V. Elsukov */
46242685841bSAndrey V. Elsukov int
ipfw_objhash_foreach_type(struct namedobj_instance * ni,objhash_cb_t * f,void * arg,uint16_t type)46252685841bSAndrey V. Elsukov ipfw_objhash_foreach_type(struct namedobj_instance *ni, objhash_cb_t *f,
46262685841bSAndrey V. Elsukov void *arg, uint16_t type)
46272685841bSAndrey V. Elsukov {
46282685841bSAndrey V. Elsukov struct named_object *no, *no_tmp;
46292685841bSAndrey V. Elsukov int i, ret;
46302685841bSAndrey V. Elsukov
46312685841bSAndrey V. Elsukov for (i = 0; i < ni->nn_size; i++) {
46322685841bSAndrey V. Elsukov TAILQ_FOREACH_SAFE(no, &ni->names[i], nn_next, no_tmp) {
46332685841bSAndrey V. Elsukov if (no->etlv != type)
46342685841bSAndrey V. Elsukov continue;
46352685841bSAndrey V. Elsukov ret = f(ni, no, arg);
46362685841bSAndrey V. Elsukov if (ret != 0)
46372685841bSAndrey V. Elsukov return (ret);
46382685841bSAndrey V. Elsukov }
46392685841bSAndrey V. Elsukov }
46402685841bSAndrey V. Elsukov return (0);
46412685841bSAndrey V. Elsukov }
46422685841bSAndrey V. Elsukov
46432685841bSAndrey V. Elsukov /*
4644b074b7bbSAlexander V. Chernikov * Removes index from given set.
4645b074b7bbSAlexander V. Chernikov * Returns 0 on success.
4646b074b7bbSAlexander V. Chernikov */
4647b074b7bbSAlexander V. Chernikov int
ipfw_objhash_free_idx(struct namedobj_instance * ni,uint16_t idx)4648ac35ff17SAlexander V. Chernikov ipfw_objhash_free_idx(struct namedobj_instance *ni, uint16_t idx)
4649b074b7bbSAlexander V. Chernikov {
4650b074b7bbSAlexander V. Chernikov u_long *mask;
4651b074b7bbSAlexander V. Chernikov int i, v;
4652b074b7bbSAlexander V. Chernikov
4653b074b7bbSAlexander V. Chernikov i = idx / BLOCK_ITEMS;
4654b074b7bbSAlexander V. Chernikov v = idx % BLOCK_ITEMS;
4655b074b7bbSAlexander V. Chernikov
4656ac35ff17SAlexander V. Chernikov if (i >= ni->max_blocks)
4657b074b7bbSAlexander V. Chernikov return (1);
4658b074b7bbSAlexander V. Chernikov
4659ac35ff17SAlexander V. Chernikov mask = &ni->idx_mask[i];
4660b074b7bbSAlexander V. Chernikov
4661b074b7bbSAlexander V. Chernikov if ((*mask & ((u_long)1 << v)) != 0)
4662b074b7bbSAlexander V. Chernikov return (1);
4663b074b7bbSAlexander V. Chernikov
4664b074b7bbSAlexander V. Chernikov /* Mark as free */
4665b074b7bbSAlexander V. Chernikov *mask |= (u_long)1 << v;
4666b074b7bbSAlexander V. Chernikov
4667b074b7bbSAlexander V. Chernikov /* Update free offset */
4668ac35ff17SAlexander V. Chernikov if (ni->free_off[0] > i)
4669ac35ff17SAlexander V. Chernikov ni->free_off[0] = i;
4670b074b7bbSAlexander V. Chernikov
4671b074b7bbSAlexander V. Chernikov return (0);
4672b074b7bbSAlexander V. Chernikov }
4673b074b7bbSAlexander V. Chernikov
4674b074b7bbSAlexander V. Chernikov /*
467513263632SAlexander V. Chernikov * Allocate new index in given instance and stores in in @pidx.
4676b074b7bbSAlexander V. Chernikov * Returns 0 on success.
4677b074b7bbSAlexander V. Chernikov */
4678b074b7bbSAlexander V. Chernikov int
ipfw_objhash_alloc_idx(void * n,uint16_t * pidx)4679ac35ff17SAlexander V. Chernikov ipfw_objhash_alloc_idx(void *n, uint16_t *pidx)
4680b074b7bbSAlexander V. Chernikov {
4681b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni;
4682b074b7bbSAlexander V. Chernikov u_long *mask;
4683b074b7bbSAlexander V. Chernikov int i, off, v;
4684b074b7bbSAlexander V. Chernikov
4685b074b7bbSAlexander V. Chernikov ni = (struct namedobj_instance *)n;
4686b074b7bbSAlexander V. Chernikov
4687ac35ff17SAlexander V. Chernikov off = ni->free_off[0];
4688ac35ff17SAlexander V. Chernikov mask = &ni->idx_mask[off];
4689b074b7bbSAlexander V. Chernikov
4690b074b7bbSAlexander V. Chernikov for (i = off; i < ni->max_blocks; i++, mask++) {
4691b074b7bbSAlexander V. Chernikov if ((v = ffsl(*mask)) == 0)
4692b074b7bbSAlexander V. Chernikov continue;
4693b074b7bbSAlexander V. Chernikov
4694b074b7bbSAlexander V. Chernikov /* Mark as busy */
4695b074b7bbSAlexander V. Chernikov *mask &= ~ ((u_long)1 << (v - 1));
4696b074b7bbSAlexander V. Chernikov
4697ac35ff17SAlexander V. Chernikov ni->free_off[0] = i;
4698b074b7bbSAlexander V. Chernikov
4699b074b7bbSAlexander V. Chernikov v = BLOCK_ITEMS * i + v - 1;
4700b074b7bbSAlexander V. Chernikov
4701b074b7bbSAlexander V. Chernikov *pidx = v;
4702b074b7bbSAlexander V. Chernikov return (0);
4703b074b7bbSAlexander V. Chernikov }
4704b074b7bbSAlexander V. Chernikov
4705b074b7bbSAlexander V. Chernikov return (1);
4706b074b7bbSAlexander V. Chernikov }
4707b074b7bbSAlexander V. Chernikov
47083b3a8eb9SGleb Smirnoff /* end of file */
4709