13b3a8eb9SGleb Smirnoff /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3fe267a55SPedro F. Giffuni *
43b3a8eb9SGleb Smirnoff * Copyright (c) 2002-2009 Luigi Rizzo, Universita` di Pisa
54a77657cSAndrey V. Elsukov * Copyright (c) 2014-2025 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
774a77657cSAndrey V. Elsukov static enum ipfw_opcheck_result
check_opcode_compat_nop(ipfw_insn ** pcmd,int * plen,struct rule_check_info * ci)784a77657cSAndrey V. Elsukov check_opcode_compat_nop(ipfw_insn **pcmd, int *plen,
794a77657cSAndrey V. Elsukov struct rule_check_info *ci)
804a77657cSAndrey V. Elsukov {
814a77657cSAndrey V. Elsukov /* Compatibility code is not registered */
824a77657cSAndrey V. Elsukov return (FAILED);
834a77657cSAndrey V. Elsukov }
844a77657cSAndrey V. Elsukov
854a77657cSAndrey V. Elsukov static ipfw_check_opcode_t check_opcode_f = check_opcode_compat_nop;
864a77657cSAndrey V. Elsukov
877e767c79SAlexander V. Chernikov static int check_ipfw_rule_body(ipfw_insn *cmd, int cmd_len,
887e767c79SAlexander V. Chernikov struct rule_check_info *ci);
89f976a4edSAndrey V. Elsukov static int rewrite_rule_uidx(struct ip_fw_chain *chain,
90f976a4edSAndrey V. Elsukov struct rule_check_info *ci);
917e767c79SAlexander V. Chernikov
92b074b7bbSAlexander V. Chernikov struct namedobj_instance {
93b074b7bbSAlexander V. Chernikov struct namedobjects_head *names;
94b074b7bbSAlexander V. Chernikov struct namedobjects_head *values;
95b074b7bbSAlexander V. Chernikov uint32_t nn_size; /* names hash size */
96b074b7bbSAlexander V. Chernikov uint32_t nv_size; /* number hash size */
97b074b7bbSAlexander V. Chernikov u_long *idx_mask; /* used items bitmask */
98b074b7bbSAlexander V. Chernikov uint32_t max_blocks; /* number of "long" blocks in bitmask */
999f7d47b0SAlexander V. Chernikov uint32_t count; /* number of items */
100b074b7bbSAlexander V. Chernikov uint16_t free_off[IPFW_MAX_SETS]; /* first possible free offset */
10113263632SAlexander V. Chernikov objhash_hash_f *hash_f;
10213263632SAlexander V. Chernikov objhash_cmp_f *cmp_f;
103b074b7bbSAlexander V. Chernikov };
104b074b7bbSAlexander V. Chernikov #define BLOCK_ITEMS (8 * sizeof(u_long)) /* Number of items for ffsl() */
105b074b7bbSAlexander V. Chernikov
1062acdf79fSAndrey V. Elsukov static uint32_t objhash_hash_name(struct namedobj_instance *ni,
1072acdf79fSAndrey V. Elsukov const void *key, uint32_t kopt);
10813263632SAlexander V. Chernikov static uint32_t objhash_hash_idx(struct namedobj_instance *ni, uint32_t val);
1092acdf79fSAndrey V. Elsukov static int objhash_cmp_name(struct named_object *no, const void *name,
1102acdf79fSAndrey V. Elsukov uint32_t set);
111b074b7bbSAlexander V. Chernikov
1126b988f3aSAlexander V. Chernikov MALLOC_DEFINE(M_IPFW, "IpFw/IpAcct", "IpFw/IpAcct chain's");
1136b988f3aSAlexander V. Chernikov
1146b988f3aSAlexander V. Chernikov /* ctl3 handler data */
1154a77657cSAndrey V. Elsukov static struct mtx ctl3_lock;
1166b988f3aSAlexander V. Chernikov #define CTL3_LOCK_INIT() mtx_init(&ctl3_lock, "ctl3_lock", NULL, MTX_DEF)
1176b988f3aSAlexander V. Chernikov #define CTL3_LOCK_DESTROY() mtx_destroy(&ctl3_lock)
1186b988f3aSAlexander V. Chernikov #define CTL3_LOCK() mtx_lock(&ctl3_lock)
1196b988f3aSAlexander V. Chernikov #define CTL3_UNLOCK() mtx_unlock(&ctl3_lock)
1206b988f3aSAlexander V. Chernikov
1216b988f3aSAlexander V. Chernikov static struct ipfw_sopt_handler *ctl3_handlers;
1226b988f3aSAlexander V. Chernikov static size_t ctl3_hsize;
1236b988f3aSAlexander V. Chernikov static uint64_t ctl3_refct, ctl3_gencnt;
1246b988f3aSAlexander V. Chernikov #define CTL3_SMALLBUF 4096 /* small page-size write buffer */
1254a77657cSAndrey V. Elsukov #define CTL3_LARGEBUF (16 * 1024 * 1024) /* handle large rulesets */
1266b988f3aSAlexander V. Chernikov
1272d99a349SAlexander V. Chernikov static int ipfw_flush_sopt_data(struct sockopt_data *sd);
128b074b7bbSAlexander V. Chernikov
1294a77657cSAndrey V. Elsukov static sopt_handler_f dump_config, add_rules, del_rules, clear_rules,
1304a77657cSAndrey V. Elsukov move_rules, manage_sets, dump_soptcodes, dump_srvobjects,
1314a77657cSAndrey V. Elsukov manage_skiptocache;
1324a77657cSAndrey V. Elsukov
1336b988f3aSAlexander V. Chernikov static struct ipfw_sopt_handler scodes[] = {
1344a77657cSAndrey V. Elsukov { IP_FW_XGET, IP_FW3_OPVER, HDIR_GET, dump_config },
1354a77657cSAndrey V. Elsukov { IP_FW_XADD, IP_FW3_OPVER, HDIR_BOTH, add_rules },
1364a77657cSAndrey V. Elsukov { IP_FW_XDEL, IP_FW3_OPVER, HDIR_BOTH, del_rules },
1374a77657cSAndrey V. Elsukov { IP_FW_XZERO, IP_FW3_OPVER, HDIR_SET, clear_rules },
1384a77657cSAndrey V. Elsukov { IP_FW_XRESETLOG, IP_FW3_OPVER, HDIR_SET, clear_rules },
1394a77657cSAndrey V. Elsukov { IP_FW_XMOVE, IP_FW3_OPVER, HDIR_SET, move_rules },
1404a77657cSAndrey V. Elsukov { IP_FW_SET_SWAP, IP_FW3_OPVER, HDIR_SET, manage_sets },
1414a77657cSAndrey V. Elsukov { IP_FW_SET_MOVE, IP_FW3_OPVER, HDIR_SET, manage_sets },
1424a77657cSAndrey V. Elsukov { IP_FW_SET_ENABLE, IP_FW3_OPVER, HDIR_SET, manage_sets },
1434a77657cSAndrey V. Elsukov { IP_FW_DUMP_SOPTCODES, IP_FW3_OPVER, HDIR_GET, dump_soptcodes },
1444a77657cSAndrey V. Elsukov { IP_FW_DUMP_SRVOBJECTS, IP_FW3_OPVER, HDIR_GET, dump_srvobjects },
1454a77657cSAndrey V. Elsukov { IP_FW_SKIPTO_CACHE, IP_FW3_OPVER, HDIR_BOTH, manage_skiptocache },
1466b988f3aSAlexander V. Chernikov };
1473b3a8eb9SGleb Smirnoff
148b2df1f7eSAndrey V. Elsukov static struct opcode_obj_rewrite *find_op_rw(ipfw_insn *cmd,
1494a77657cSAndrey V. Elsukov uint32_t *puidx, uint8_t *ptype);
150f976a4edSAndrey V. Elsukov static int ref_rule_objects(struct ip_fw_chain *ch, struct ip_fw *rule,
151f976a4edSAndrey V. Elsukov struct rule_check_info *ci, struct obj_idx *oib, struct tid_info *ti);
152f976a4edSAndrey V. Elsukov static int ref_opcode_object(struct ip_fw_chain *ch, ipfw_insn *cmd,
153b2df1f7eSAndrey V. Elsukov struct tid_info *ti, struct obj_idx *pidx, int *unresolved);
15474b22066SAlexander V. Chernikov static void unref_rule_objects(struct ip_fw_chain *chain, struct ip_fw *rule);
155f976a4edSAndrey V. Elsukov static void unref_oib_objects(struct ip_fw_chain *ch, ipfw_insn *cmd,
156f976a4edSAndrey V. Elsukov struct obj_idx *oib, struct obj_idx *end);
1574a77657cSAndrey V. Elsukov static int export_objhash_ntlv(struct namedobj_instance *ni, uint32_t kidx,
15874b22066SAlexander V. Chernikov struct sockopt_data *sd);
15974b22066SAlexander V. Chernikov
16074b22066SAlexander V. Chernikov /*
16174b22066SAlexander V. Chernikov * Opcode object rewriter variables
16274b22066SAlexander V. Chernikov */
16374b22066SAlexander V. Chernikov struct opcode_obj_rewrite *ctl3_rewriters;
16474b22066SAlexander V. Chernikov static size_t ctl3_rsize;
16574b22066SAlexander V. Chernikov
1663b3a8eb9SGleb Smirnoff /*
1677e767c79SAlexander V. Chernikov * static variables followed by global ones
1683b3a8eb9SGleb Smirnoff */
1693b3a8eb9SGleb Smirnoff
1705f901c92SAndrew Turner VNET_DEFINE_STATIC(uma_zone_t, ipfw_cntr_zone);
1717e767c79SAlexander V. Chernikov #define V_ipfw_cntr_zone VNET(ipfw_cntr_zone)
1727e767c79SAlexander V. Chernikov
1737e767c79SAlexander V. Chernikov void
ipfw_init_counters(void)17462030bb8SDimitry Andric ipfw_init_counters(void)
1757e767c79SAlexander V. Chernikov {
1767e767c79SAlexander V. Chernikov
1777e767c79SAlexander V. Chernikov V_ipfw_cntr_zone = uma_zcreate("IPFW counters",
1780d90989bSAlexander V. Chernikov IPFW_RULE_CNTR_SIZE, NULL, NULL, NULL, NULL,
1797e767c79SAlexander V. Chernikov UMA_ALIGN_PTR, UMA_ZONE_PCPU);
1807e767c79SAlexander V. Chernikov }
1817e767c79SAlexander V. Chernikov
1827e767c79SAlexander V. Chernikov void
ipfw_destroy_counters(void)18362030bb8SDimitry Andric ipfw_destroy_counters(void)
1847e767c79SAlexander V. Chernikov {
1857e767c79SAlexander V. Chernikov
1867e767c79SAlexander V. Chernikov uma_zdestroy(V_ipfw_cntr_zone);
1877e767c79SAlexander V. Chernikov }
1887e767c79SAlexander V. Chernikov
1897e767c79SAlexander V. Chernikov struct ip_fw *
ipfw_alloc_rule(struct ip_fw_chain * chain,size_t rulesize)1907e767c79SAlexander V. Chernikov ipfw_alloc_rule(struct ip_fw_chain *chain, size_t rulesize)
1917e767c79SAlexander V. Chernikov {
1927e767c79SAlexander V. Chernikov struct ip_fw *rule;
1937e767c79SAlexander V. Chernikov
1947e767c79SAlexander V. Chernikov rule = malloc(rulesize, M_IPFW, M_WAITOK | M_ZERO);
1954e180881SMateusz Guzik rule->cntr = uma_zalloc_pcpu(V_ipfw_cntr_zone, M_WAITOK | M_ZERO);
196cefe3d67SAndrey V. Elsukov rule->refcnt = 1;
1977e767c79SAlexander V. Chernikov
1987e767c79SAlexander V. Chernikov return (rule);
1997e767c79SAlexander V. Chernikov }
2007e767c79SAlexander V. Chernikov
201cefe3d67SAndrey V. Elsukov void
ipfw_free_rule(struct ip_fw * rule)202cefe3d67SAndrey V. Elsukov ipfw_free_rule(struct ip_fw *rule)
2037e767c79SAlexander V. Chernikov {
2047e767c79SAlexander V. Chernikov
205cefe3d67SAndrey V. Elsukov /*
206cefe3d67SAndrey V. Elsukov * We don't release refcnt here, since this function
207cefe3d67SAndrey V. Elsukov * can be called without any locks held. The caller
208cefe3d67SAndrey V. Elsukov * must release reference under IPFW_UH_WLOCK, and then
209cefe3d67SAndrey V. Elsukov * call this function if refcount becomes 1.
210cefe3d67SAndrey V. Elsukov */
211cefe3d67SAndrey V. Elsukov if (rule->refcnt > 1)
212cefe3d67SAndrey V. Elsukov return;
2134e180881SMateusz Guzik uma_zfree_pcpu(V_ipfw_cntr_zone, rule->cntr);
2147e767c79SAlexander V. Chernikov free(rule, M_IPFW);
2157e767c79SAlexander V. Chernikov }
2167e767c79SAlexander V. Chernikov
2173b3a8eb9SGleb Smirnoff /*
2183b3a8eb9SGleb Smirnoff * Find the smallest rule >= key, id.
2193b3a8eb9SGleb Smirnoff * We could use bsearch but it is so simple that we code it directly
2203b3a8eb9SGleb Smirnoff */
2213b3a8eb9SGleb Smirnoff int
ipfw_find_rule(struct ip_fw_chain * chain,uint32_t key,uint32_t id)2223b3a8eb9SGleb Smirnoff ipfw_find_rule(struct ip_fw_chain *chain, uint32_t key, uint32_t id)
2233b3a8eb9SGleb Smirnoff {
2243b3a8eb9SGleb Smirnoff int i, lo, hi;
2253b3a8eb9SGleb Smirnoff struct ip_fw *r;
2263b3a8eb9SGleb Smirnoff
2273b3a8eb9SGleb Smirnoff for (lo = 0, hi = chain->n_rules - 1; lo < hi;) {
2283b3a8eb9SGleb Smirnoff i = (lo + hi) / 2;
2293b3a8eb9SGleb Smirnoff r = chain->map[i];
2303b3a8eb9SGleb Smirnoff if (r->rulenum < key)
2313b3a8eb9SGleb Smirnoff lo = i + 1; /* continue from the next one */
2323b3a8eb9SGleb Smirnoff else if (r->rulenum > key)
2333b3a8eb9SGleb Smirnoff hi = i; /* this might be good */
2343b3a8eb9SGleb Smirnoff else if (r->id < id)
2353b3a8eb9SGleb Smirnoff lo = i + 1; /* continue from the next one */
2363b3a8eb9SGleb Smirnoff else /* r->id >= id */
2373b3a8eb9SGleb Smirnoff hi = i; /* this might be good */
23874b8d63dSPedro F. Giffuni }
2393b3a8eb9SGleb Smirnoff return hi;
2403b3a8eb9SGleb Smirnoff }
2413b3a8eb9SGleb Smirnoff
2423b3a8eb9SGleb Smirnoff /*
243d5eb80cbSAlexander V. Chernikov * Builds skipto cache on rule set @map.
244d5eb80cbSAlexander V. Chernikov */
245d5eb80cbSAlexander V. Chernikov static void
update_skipto_cache(struct ip_fw_chain * chain,struct ip_fw ** map)246d5eb80cbSAlexander V. Chernikov update_skipto_cache(struct ip_fw_chain *chain, struct ip_fw **map)
247d5eb80cbSAlexander V. Chernikov {
2484a77657cSAndrey V. Elsukov uint32_t *smap, rulenum;
249d5eb80cbSAlexander V. Chernikov int i, mi;
250d5eb80cbSAlexander V. Chernikov
251d5eb80cbSAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(chain);
252d5eb80cbSAlexander V. Chernikov
253d5eb80cbSAlexander V. Chernikov mi = 0;
254d5eb80cbSAlexander V. Chernikov rulenum = map[mi]->rulenum;
255d5eb80cbSAlexander V. Chernikov smap = chain->idxmap_back;
256d5eb80cbSAlexander V. Chernikov
257d5eb80cbSAlexander V. Chernikov if (smap == NULL)
258d5eb80cbSAlexander V. Chernikov return;
259d5eb80cbSAlexander V. Chernikov
2604a77657cSAndrey V. Elsukov for (i = 0; i <= IPFW_DEFAULT_RULE; i++) {
261d5eb80cbSAlexander V. Chernikov smap[i] = mi;
262d5eb80cbSAlexander V. Chernikov /* Use the same rule index until i < rulenum */
2634a77657cSAndrey V. Elsukov if (i != rulenum || i == IPFW_DEFAULT_RULE)
264d5eb80cbSAlexander V. Chernikov continue;
265d5eb80cbSAlexander V. Chernikov /* Find next rule with num > i */
266d5eb80cbSAlexander V. Chernikov rulenum = map[++mi]->rulenum;
267d5eb80cbSAlexander V. Chernikov while (rulenum == i)
268d5eb80cbSAlexander V. Chernikov rulenum = map[++mi]->rulenum;
269d5eb80cbSAlexander V. Chernikov }
270d5eb80cbSAlexander V. Chernikov }
271d5eb80cbSAlexander V. Chernikov
272d5eb80cbSAlexander V. Chernikov /*
273d5eb80cbSAlexander V. Chernikov * Swaps prepared (backup) index with current one.
274d5eb80cbSAlexander V. Chernikov */
275d5eb80cbSAlexander V. Chernikov static void
swap_skipto_cache(struct ip_fw_chain * chain)276d5eb80cbSAlexander V. Chernikov swap_skipto_cache(struct ip_fw_chain *chain)
277d5eb80cbSAlexander V. Chernikov {
2784a77657cSAndrey V. Elsukov uint32_t *map;
279d5eb80cbSAlexander V. Chernikov
280d5eb80cbSAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(chain);
281d5eb80cbSAlexander V. Chernikov IPFW_WLOCK_ASSERT(chain);
282d5eb80cbSAlexander V. Chernikov
283d5eb80cbSAlexander V. Chernikov map = chain->idxmap;
284d5eb80cbSAlexander V. Chernikov chain->idxmap = chain->idxmap_back;
285d5eb80cbSAlexander V. Chernikov chain->idxmap_back = map;
286d5eb80cbSAlexander V. Chernikov }
287d5eb80cbSAlexander V. Chernikov
288d5eb80cbSAlexander V. Chernikov /*
289d5eb80cbSAlexander V. Chernikov * Allocate and initialize skipto cache.
290d5eb80cbSAlexander V. Chernikov */
291d5eb80cbSAlexander V. Chernikov void
ipfw_init_skipto_cache(struct ip_fw_chain * chain)292d5eb80cbSAlexander V. Chernikov ipfw_init_skipto_cache(struct ip_fw_chain *chain)
293d5eb80cbSAlexander V. Chernikov {
2944a77657cSAndrey V. Elsukov uint32_t *idxmap, *idxmap_back;
295d5eb80cbSAlexander V. Chernikov
2964a77657cSAndrey V. Elsukov idxmap = malloc((IPFW_DEFAULT_RULE + 1) * sizeof(uint32_t),
2974a77657cSAndrey V. Elsukov M_IPFW, M_WAITOK | M_ZERO);
2984a77657cSAndrey V. Elsukov idxmap_back = malloc((IPFW_DEFAULT_RULE + 1) * sizeof(uint32_t),
2994a77657cSAndrey V. Elsukov M_IPFW, M_WAITOK | M_ZERO);
300d5eb80cbSAlexander V. Chernikov
301d5eb80cbSAlexander V. Chernikov /*
302d5eb80cbSAlexander V. Chernikov * Note we may be called at any time after initialization,
303d5eb80cbSAlexander V. Chernikov * for example, on first skipto rule, so we need to
304d5eb80cbSAlexander V. Chernikov * provide valid chain->idxmap on return
305d5eb80cbSAlexander V. Chernikov */
306d5eb80cbSAlexander V. Chernikov
307d5eb80cbSAlexander V. Chernikov IPFW_UH_WLOCK(chain);
308d5eb80cbSAlexander V. Chernikov if (chain->idxmap != NULL) {
309d5eb80cbSAlexander V. Chernikov IPFW_UH_WUNLOCK(chain);
310d5eb80cbSAlexander V. Chernikov free(idxmap, M_IPFW);
311d5eb80cbSAlexander V. Chernikov free(idxmap_back, M_IPFW);
312d5eb80cbSAlexander V. Chernikov return;
313d5eb80cbSAlexander V. Chernikov }
314d5eb80cbSAlexander V. Chernikov
315d5eb80cbSAlexander V. Chernikov /* Set backup pointer first to permit building cache */
316d5eb80cbSAlexander V. Chernikov chain->idxmap_back = idxmap_back;
3174a77657cSAndrey V. Elsukov if (V_skipto_cache != 0)
318d5eb80cbSAlexander V. Chernikov update_skipto_cache(chain, chain->map);
319d5eb80cbSAlexander V. Chernikov IPFW_WLOCK(chain);
320d5eb80cbSAlexander V. Chernikov /* It is now safe to set chain->idxmap ptr */
321d5eb80cbSAlexander V. Chernikov chain->idxmap = idxmap;
322d5eb80cbSAlexander V. Chernikov swap_skipto_cache(chain);
323d5eb80cbSAlexander V. Chernikov IPFW_WUNLOCK(chain);
324d5eb80cbSAlexander V. Chernikov IPFW_UH_WUNLOCK(chain);
325d5eb80cbSAlexander V. Chernikov }
326d5eb80cbSAlexander V. Chernikov
327d5eb80cbSAlexander V. Chernikov /*
328d5eb80cbSAlexander V. Chernikov * Destroys skipto cache.
329d5eb80cbSAlexander V. Chernikov */
330d5eb80cbSAlexander V. Chernikov void
ipfw_destroy_skipto_cache(struct ip_fw_chain * chain)331d5eb80cbSAlexander V. Chernikov ipfw_destroy_skipto_cache(struct ip_fw_chain *chain)
332d5eb80cbSAlexander V. Chernikov {
333d5eb80cbSAlexander V. Chernikov free(chain->idxmap, M_IPFW);
334d5eb80cbSAlexander V. Chernikov free(chain->idxmap_back, M_IPFW);
335d5eb80cbSAlexander V. Chernikov }
336d5eb80cbSAlexander V. Chernikov
337d5eb80cbSAlexander V. Chernikov /*
3383b3a8eb9SGleb Smirnoff * allocate a new map, returns the chain locked. extra is the number
3393b3a8eb9SGleb Smirnoff * of entries to add or delete.
3403b3a8eb9SGleb Smirnoff */
3413b3a8eb9SGleb Smirnoff static struct ip_fw **
get_map(struct ip_fw_chain * chain,int extra,int locked)3423b3a8eb9SGleb Smirnoff get_map(struct ip_fw_chain *chain, int extra, int locked)
3433b3a8eb9SGleb Smirnoff {
3443b3a8eb9SGleb Smirnoff
3453b3a8eb9SGleb Smirnoff for (;;) {
3463b3a8eb9SGleb Smirnoff struct ip_fw **map;
347d821d364SPedro F. Giffuni u_int i, mflags;
348a73d728dSAlexander V. Chernikov
349a73d728dSAlexander V. Chernikov mflags = M_ZERO | ((locked != 0) ? M_NOWAIT : M_WAITOK);
3503b3a8eb9SGleb Smirnoff
3513b3a8eb9SGleb Smirnoff i = chain->n_rules + extra;
352a73d728dSAlexander V. Chernikov map = malloc(i * sizeof(struct ip_fw *), M_IPFW, mflags);
3533b3a8eb9SGleb Smirnoff if (map == NULL) {
3543b3a8eb9SGleb Smirnoff printf("%s: cannot allocate map\n", __FUNCTION__);
3553b3a8eb9SGleb Smirnoff return NULL;
3563b3a8eb9SGleb Smirnoff }
3573b3a8eb9SGleb Smirnoff if (!locked)
3583b3a8eb9SGleb Smirnoff IPFW_UH_WLOCK(chain);
3593b3a8eb9SGleb Smirnoff if (i >= chain->n_rules + extra) /* good */
3603b3a8eb9SGleb Smirnoff return map;
3613b3a8eb9SGleb Smirnoff /* otherwise we lost the race, free and retry */
3623b3a8eb9SGleb Smirnoff if (!locked)
3633b3a8eb9SGleb Smirnoff IPFW_UH_WUNLOCK(chain);
3643b3a8eb9SGleb Smirnoff free(map, M_IPFW);
3653b3a8eb9SGleb Smirnoff }
3663b3a8eb9SGleb Smirnoff }
3673b3a8eb9SGleb Smirnoff
3683b3a8eb9SGleb Smirnoff /*
3693b3a8eb9SGleb Smirnoff * swap the maps. It is supposed to be called with IPFW_UH_WLOCK
3703b3a8eb9SGleb Smirnoff */
3713b3a8eb9SGleb Smirnoff static struct ip_fw **
swap_map(struct ip_fw_chain * chain,struct ip_fw ** new_map,int new_len)3723b3a8eb9SGleb Smirnoff swap_map(struct ip_fw_chain *chain, struct ip_fw **new_map, int new_len)
3733b3a8eb9SGleb Smirnoff {
3743b3a8eb9SGleb Smirnoff struct ip_fw **old_map;
3753b3a8eb9SGleb Smirnoff
3763b3a8eb9SGleb Smirnoff IPFW_WLOCK(chain);
3773b3a8eb9SGleb Smirnoff chain->id++;
3783b3a8eb9SGleb Smirnoff chain->n_rules = new_len;
3793b3a8eb9SGleb Smirnoff old_map = chain->map;
3803b3a8eb9SGleb Smirnoff chain->map = new_map;
381d5eb80cbSAlexander V. Chernikov swap_skipto_cache(chain);
3823b3a8eb9SGleb Smirnoff IPFW_WUNLOCK(chain);
3833b3a8eb9SGleb Smirnoff return old_map;
3843b3a8eb9SGleb Smirnoff }
3853b3a8eb9SGleb Smirnoff
3867e767c79SAlexander V. Chernikov static void
export_cntr1_base(struct ip_fw * krule,struct ip_fw_bcounter * cntr)3877e767c79SAlexander V. Chernikov export_cntr1_base(struct ip_fw *krule, struct ip_fw_bcounter *cntr)
3887e767c79SAlexander V. Chernikov {
389584b675eSKonstantin Belousov struct timeval boottime;
3907e767c79SAlexander V. Chernikov
3917e767c79SAlexander V. Chernikov cntr->size = sizeof(*cntr);
3927e767c79SAlexander V. Chernikov
3937e767c79SAlexander V. Chernikov if (krule->cntr != NULL) {
3947e767c79SAlexander V. Chernikov cntr->pcnt = counter_u64_fetch(krule->cntr);
3957e767c79SAlexander V. Chernikov cntr->bcnt = counter_u64_fetch(krule->cntr + 1);
3967e767c79SAlexander V. Chernikov cntr->timestamp = krule->timestamp;
3977e767c79SAlexander V. Chernikov }
398584b675eSKonstantin Belousov if (cntr->timestamp > 0) {
399584b675eSKonstantin Belousov getboottime(&boottime);
4007e767c79SAlexander V. Chernikov cntr->timestamp += boottime.tv_sec;
4017e767c79SAlexander V. Chernikov }
402584b675eSKonstantin Belousov }
4037e767c79SAlexander V. Chernikov
4047e767c79SAlexander V. Chernikov /*
4057e767c79SAlexander V. Chernikov * Export rule into v1 format (Current).
4067e767c79SAlexander V. Chernikov * Layout:
4077e767c79SAlexander V. Chernikov * [ ipfw_obj_tlv(IPFW_TLV_RULE_ENT)
4087e767c79SAlexander V. Chernikov * [ ip_fw_rule ] OR
4097e767c79SAlexander V. Chernikov * [ ip_fw_bcounter ip_fw_rule] (depends on rcntrs).
4107e767c79SAlexander V. Chernikov * ]
4117e767c79SAlexander V. Chernikov * Assume @data is zeroed.
4127e767c79SAlexander V. Chernikov */
4137e767c79SAlexander V. Chernikov static void
export_rule1(struct ip_fw * krule,caddr_t data,int len,int rcntrs)4147e767c79SAlexander V. Chernikov export_rule1(struct ip_fw *krule, caddr_t data, int len, int rcntrs)
4157e767c79SAlexander V. Chernikov {
4167e767c79SAlexander V. Chernikov struct ip_fw_bcounter *cntr;
4177e767c79SAlexander V. Chernikov struct ip_fw_rule *urule;
4187e767c79SAlexander V. Chernikov ipfw_obj_tlv *tlv;
4197e767c79SAlexander V. Chernikov
4207e767c79SAlexander V. Chernikov /* Fill in TLV header */
4217e767c79SAlexander V. Chernikov tlv = (ipfw_obj_tlv *)data;
4227e767c79SAlexander V. Chernikov tlv->type = IPFW_TLV_RULE_ENT;
4237e767c79SAlexander V. Chernikov tlv->length = len;
4247e767c79SAlexander V. Chernikov
4257e767c79SAlexander V. Chernikov if (rcntrs != 0) {
4267e767c79SAlexander V. Chernikov /* Copy counters */
4277e767c79SAlexander V. Chernikov cntr = (struct ip_fw_bcounter *)(tlv + 1);
4287e767c79SAlexander V. Chernikov urule = (struct ip_fw_rule *)(cntr + 1);
4297e767c79SAlexander V. Chernikov export_cntr1_base(krule, cntr);
4307e767c79SAlexander V. Chernikov } else
4317e767c79SAlexander V. Chernikov urule = (struct ip_fw_rule *)(tlv + 1);
4327e767c79SAlexander V. Chernikov
4337e767c79SAlexander V. Chernikov /* copy header */
4347e767c79SAlexander V. Chernikov urule->act_ofs = krule->act_ofs;
4357e767c79SAlexander V. Chernikov urule->cmd_len = krule->cmd_len;
4367e767c79SAlexander V. Chernikov urule->rulenum = krule->rulenum;
4377e767c79SAlexander V. Chernikov urule->set = krule->set;
4387e767c79SAlexander V. Chernikov urule->flags = krule->flags;
4397e767c79SAlexander V. Chernikov urule->id = krule->id;
4407e767c79SAlexander V. Chernikov
4417e767c79SAlexander V. Chernikov /* Copy opcodes */
4427e767c79SAlexander V. Chernikov memcpy(urule->cmd, krule->cmd, krule->cmd_len * sizeof(uint32_t));
4437e767c79SAlexander V. Chernikov }
4447e767c79SAlexander V. Chernikov
4457e767c79SAlexander V. Chernikov /*
4466c2997ffSAlexander V. Chernikov * Add new rule(s) to the list possibly creating rule number for each.
4473b3a8eb9SGleb Smirnoff * Update the rule_number in the input struct so the caller knows it as well.
4483b3a8eb9SGleb Smirnoff * Must be called without IPFW_UH held
4493b3a8eb9SGleb Smirnoff */
4504a77657cSAndrey V. Elsukov int
ipfw_commit_rules(struct ip_fw_chain * chain,struct rule_check_info * rci,int count)4514a77657cSAndrey V. Elsukov ipfw_commit_rules(struct ip_fw_chain *chain, struct rule_check_info *rci,
4524a77657cSAndrey V. Elsukov int count)
4533b3a8eb9SGleb Smirnoff {
4544a77657cSAndrey V. Elsukov int error, i, insert_before, tcount, rule_idx, last_rule_idx;
4554a77657cSAndrey V. Elsukov uint32_t rulenum;
4566c2997ffSAlexander V. Chernikov struct rule_check_info *ci;
4577e767c79SAlexander V. Chernikov struct ip_fw *krule;
4583b3a8eb9SGleb Smirnoff struct ip_fw **map; /* the new array of pointers */
4593b3a8eb9SGleb Smirnoff
46074b22066SAlexander V. Chernikov /* Check if we need to do table/obj index remap */
4616c2997ffSAlexander V. Chernikov tcount = 0;
4626c2997ffSAlexander V. Chernikov for (ci = rci, i = 0; i < count; ci++, i++) {
46374b22066SAlexander V. Chernikov if (ci->object_opcodes == 0)
4646c2997ffSAlexander V. Chernikov continue;
4656c2997ffSAlexander V. Chernikov
4666c2997ffSAlexander V. Chernikov /*
46774b22066SAlexander V. Chernikov * Rule has some object opcodes.
46874b22066SAlexander V. Chernikov * We need to find (and create non-existing)
46974b22066SAlexander V. Chernikov * kernel objects, and reference existing ones.
4706c2997ffSAlexander V. Chernikov */
471f976a4edSAndrey V. Elsukov error = rewrite_rule_uidx(chain, ci);
4726c2997ffSAlexander V. Chernikov if (error != 0) {
4734a77657cSAndrey V. Elsukov
4746c2997ffSAlexander V. Chernikov /*
4756c2997ffSAlexander V. Chernikov * rewrite failed, state for current rule
4766c2997ffSAlexander V. Chernikov * has been reverted. Check if we need to
4776c2997ffSAlexander V. Chernikov * revert more.
4786c2997ffSAlexander V. Chernikov */
4796c2997ffSAlexander V. Chernikov if (tcount > 0) {
4804a77657cSAndrey V. Elsukov
4816c2997ffSAlexander V. Chernikov /*
4826c2997ffSAlexander V. Chernikov * We have some more table rules
4836c2997ffSAlexander V. Chernikov * we need to rollback.
4846c2997ffSAlexander V. Chernikov */
4856c2997ffSAlexander V. Chernikov
4866c2997ffSAlexander V. Chernikov IPFW_UH_WLOCK(chain);
4876c2997ffSAlexander V. Chernikov while (ci != rci) {
4886c2997ffSAlexander V. Chernikov ci--;
48974b22066SAlexander V. Chernikov if (ci->object_opcodes == 0)
4906c2997ffSAlexander V. Chernikov continue;
49174b22066SAlexander V. Chernikov unref_rule_objects(chain,ci->krule);
4924a77657cSAndrey V. Elsukov
493b074b7bbSAlexander V. Chernikov }
4946c2997ffSAlexander V. Chernikov IPFW_UH_WUNLOCK(chain);
4954a77657cSAndrey V. Elsukov
4966c2997ffSAlexander V. Chernikov }
4976c2997ffSAlexander V. Chernikov
4986c2997ffSAlexander V. Chernikov return (error);
4996c2997ffSAlexander V. Chernikov }
5006c2997ffSAlexander V. Chernikov
5016c2997ffSAlexander V. Chernikov tcount++;
502b074b7bbSAlexander V. Chernikov }
503b074b7bbSAlexander V. Chernikov
504b074b7bbSAlexander V. Chernikov /* get_map returns with IPFW_UH_WLOCK if successful */
5056c2997ffSAlexander V. Chernikov map = get_map(chain, count, 0 /* not locked */);
506b074b7bbSAlexander V. Chernikov if (map == NULL) {
5076c2997ffSAlexander V. Chernikov if (tcount > 0) {
5086c2997ffSAlexander V. Chernikov /* Unbind tables */
509b074b7bbSAlexander V. Chernikov IPFW_UH_WLOCK(chain);
5106c2997ffSAlexander V. Chernikov for (ci = rci, i = 0; i < count; ci++, i++) {
51174b22066SAlexander V. Chernikov if (ci->object_opcodes == 0)
5126c2997ffSAlexander V. Chernikov continue;
5136c2997ffSAlexander V. Chernikov
51474b22066SAlexander V. Chernikov unref_rule_objects(chain, ci->krule);
5156c2997ffSAlexander V. Chernikov }
516b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(chain);
517b074b7bbSAlexander V. Chernikov }
518b074b7bbSAlexander V. Chernikov
519b074b7bbSAlexander V. Chernikov return (ENOSPC);
520b074b7bbSAlexander V. Chernikov }
521b074b7bbSAlexander V. Chernikov
5223b3a8eb9SGleb Smirnoff if (V_autoinc_step < 1)
5233b3a8eb9SGleb Smirnoff V_autoinc_step = 1;
5243b3a8eb9SGleb Smirnoff else if (V_autoinc_step > 1000)
5253b3a8eb9SGleb Smirnoff V_autoinc_step = 1000;
5266c2997ffSAlexander V. Chernikov
5274a77657cSAndrey V. Elsukov last_rule_idx = 0;
5284a77657cSAndrey V. Elsukov for (ci = rci, i = 0; i < count; ci++, i++) {
5297e767c79SAlexander V. Chernikov krule = ci->krule;
5307e767c79SAlexander V. Chernikov rulenum = krule->rulenum;
5316c2997ffSAlexander V. Chernikov
5324a77657cSAndrey V. Elsukov krule->id = chain->id + 1;
5334a77657cSAndrey V. Elsukov
5343b3a8eb9SGleb Smirnoff /* find the insertion point, we will insert before */
5357e767c79SAlexander V. Chernikov insert_before = rulenum ? rulenum + 1 : IPFW_DEFAULT_RULE;
5364a77657cSAndrey V. Elsukov rule_idx = ipfw_find_rule(chain, insert_before, 0);
5374a77657cSAndrey V. Elsukov /* duplicate the previous part */
5384a77657cSAndrey V. Elsukov if (last_rule_idx < rule_idx)
5394a77657cSAndrey V. Elsukov bcopy(chain->map + last_rule_idx, map + last_rule_idx + i,
5404a77657cSAndrey V. Elsukov (rule_idx - last_rule_idx) * sizeof(struct ip_fw *));
5414a77657cSAndrey V. Elsukov last_rule_idx = rule_idx;
5424a77657cSAndrey V. Elsukov map[rule_idx + i] = krule;
5437e767c79SAlexander V. Chernikov if (rulenum == 0) {
5447e767c79SAlexander V. Chernikov /* Compute rule number and write it back */
5454a77657cSAndrey V. Elsukov rulenum = rule_idx + i > 0 ? map[rule_idx + i - 1]->rulenum : 0;
5467e767c79SAlexander V. Chernikov if (rulenum < IPFW_DEFAULT_RULE - V_autoinc_step)
5477e767c79SAlexander V. Chernikov rulenum += V_autoinc_step;
5487e767c79SAlexander V. Chernikov krule->rulenum = rulenum;
5497e767c79SAlexander V. Chernikov /* Save number to userland rule */
5504a77657cSAndrey V. Elsukov memcpy((char *)ci->urule + ci->urule_numoff, &rulenum,
5514a77657cSAndrey V. Elsukov sizeof(rulenum));
5524a77657cSAndrey V. Elsukov }
5533b3a8eb9SGleb Smirnoff }
5543b3a8eb9SGleb Smirnoff
5554a77657cSAndrey V. Elsukov /* duplicate the remaining part, we always have the default rule */
5564a77657cSAndrey V. Elsukov bcopy(chain->map + last_rule_idx, map + last_rule_idx + count,
5574a77657cSAndrey V. Elsukov (chain->n_rules - last_rule_idx) * sizeof(struct ip_fw *));
5584a77657cSAndrey V. Elsukov
5594a77657cSAndrey V. Elsukov if (V_skipto_cache != 0)
560d5eb80cbSAlexander V. Chernikov update_skipto_cache(chain, map);
5614a77657cSAndrey V. Elsukov map = swap_map(chain, map, chain->n_rules + count);
5623b3a8eb9SGleb Smirnoff IPFW_UH_WUNLOCK(chain);
5633b3a8eb9SGleb Smirnoff if (map)
5643b3a8eb9SGleb Smirnoff free(map, M_IPFW);
5653b3a8eb9SGleb Smirnoff return (0);
5663b3a8eb9SGleb Smirnoff }
5673b3a8eb9SGleb Smirnoff
5687143bb76SAndrey V. Elsukov int
ipfw_add_protected_rule(struct ip_fw_chain * chain,struct ip_fw * rule,int locked)5697143bb76SAndrey V. Elsukov ipfw_add_protected_rule(struct ip_fw_chain *chain, struct ip_fw *rule,
5707143bb76SAndrey V. Elsukov int locked)
5717143bb76SAndrey V. Elsukov {
5727143bb76SAndrey V. Elsukov struct ip_fw **map;
5737143bb76SAndrey V. Elsukov
5747143bb76SAndrey V. Elsukov map = get_map(chain, 1, locked);
5757143bb76SAndrey V. Elsukov if (map == NULL)
5767143bb76SAndrey V. Elsukov return (ENOMEM);
5777143bb76SAndrey V. Elsukov if (chain->n_rules > 0)
5787143bb76SAndrey V. Elsukov bcopy(chain->map, map,
5797143bb76SAndrey V. Elsukov chain->n_rules * sizeof(struct ip_fw *));
5807143bb76SAndrey V. Elsukov map[chain->n_rules] = rule;
5817143bb76SAndrey V. Elsukov rule->rulenum = IPFW_DEFAULT_RULE;
5827143bb76SAndrey V. Elsukov rule->set = RESVD_SET;
5837143bb76SAndrey V. Elsukov rule->id = chain->id + 1;
5847143bb76SAndrey V. Elsukov /* We add rule in the end of chain, no need to update skipto cache */
5857143bb76SAndrey V. Elsukov map = swap_map(chain, map, chain->n_rules + 1);
5867143bb76SAndrey V. Elsukov IPFW_UH_WUNLOCK(chain);
5877143bb76SAndrey V. Elsukov free(map, M_IPFW);
5887143bb76SAndrey V. Elsukov return (0);
5897143bb76SAndrey V. Elsukov }
5907143bb76SAndrey V. Elsukov
5913b3a8eb9SGleb Smirnoff /*
592030b184fSAlexander V. Chernikov * Adds @rule to the list of rules to reap
593030b184fSAlexander V. Chernikov */
594030b184fSAlexander V. Chernikov void
ipfw_reap_add(struct ip_fw_chain * chain,struct ip_fw ** head,struct ip_fw * rule)595030b184fSAlexander V. Chernikov ipfw_reap_add(struct ip_fw_chain *chain, struct ip_fw **head,
596030b184fSAlexander V. Chernikov struct ip_fw *rule)
597030b184fSAlexander V. Chernikov {
598030b184fSAlexander V. Chernikov
599030b184fSAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(chain);
600030b184fSAlexander V. Chernikov
601030b184fSAlexander V. Chernikov /* Unlink rule from everywhere */
60274b22066SAlexander V. Chernikov unref_rule_objects(chain, rule);
603030b184fSAlexander V. Chernikov
604cefe3d67SAndrey V. Elsukov rule->next = *head;
605030b184fSAlexander V. Chernikov *head = rule;
606030b184fSAlexander V. Chernikov }
607030b184fSAlexander V. Chernikov
608030b184fSAlexander V. Chernikov /*
6093b3a8eb9SGleb Smirnoff * Reclaim storage associated with a list of rules. This is
6103b3a8eb9SGleb Smirnoff * typically the list created using remove_rule.
6113b3a8eb9SGleb Smirnoff * A NULL pointer on input is handled correctly.
6123b3a8eb9SGleb Smirnoff */
6133b3a8eb9SGleb Smirnoff void
ipfw_reap_rules(struct ip_fw * head)6143b3a8eb9SGleb Smirnoff ipfw_reap_rules(struct ip_fw *head)
6153b3a8eb9SGleb Smirnoff {
6163b3a8eb9SGleb Smirnoff struct ip_fw *rule;
6173b3a8eb9SGleb Smirnoff
6183b3a8eb9SGleb Smirnoff while ((rule = head) != NULL) {
619cefe3d67SAndrey V. Elsukov head = head->next;
620cefe3d67SAndrey V. Elsukov ipfw_free_rule(rule);
6213b3a8eb9SGleb Smirnoff }
6223b3a8eb9SGleb Smirnoff }
6233b3a8eb9SGleb Smirnoff
6243b3a8eb9SGleb Smirnoff /*
6253b3a8eb9SGleb Smirnoff * Rules to keep are
6263b3a8eb9SGleb Smirnoff * (default || reserved || !match_set || !match_number)
6273b3a8eb9SGleb Smirnoff * where
6283b3a8eb9SGleb Smirnoff * default ::= (rule->rulenum == IPFW_DEFAULT_RULE)
6293b3a8eb9SGleb Smirnoff * // the default rule is always protected
6303b3a8eb9SGleb Smirnoff *
6313b3a8eb9SGleb Smirnoff * reserved ::= (cmd == 0 && n == 0 && rule->set == RESVD_SET)
6323b3a8eb9SGleb Smirnoff * // RESVD_SET is protected only if cmd == 0 and n == 0 ("ipfw flush")
6333b3a8eb9SGleb Smirnoff *
6343b3a8eb9SGleb Smirnoff * match_set ::= (cmd == 0 || rule->set == set)
6353b3a8eb9SGleb Smirnoff * // set number is ignored for cmd == 0
6363b3a8eb9SGleb Smirnoff *
6373b3a8eb9SGleb Smirnoff * match_number ::= (cmd == 1 || n == 0 || n == rule->rulenum)
6383b3a8eb9SGleb Smirnoff * // number is ignored for cmd == 1 or n == 0
6393b3a8eb9SGleb Smirnoff *
6403b3a8eb9SGleb Smirnoff */
641a73d728dSAlexander V. Chernikov int
ipfw_match_range(struct ip_fw * rule,ipfw_range_tlv * rt)642a73d728dSAlexander V. Chernikov ipfw_match_range(struct ip_fw *rule, ipfw_range_tlv *rt)
6433b3a8eb9SGleb Smirnoff {
644a73d728dSAlexander V. Chernikov
6452930362fSAlexander V. Chernikov /* Don't match default rule for modification queries */
6462930362fSAlexander V. Chernikov if (rule->rulenum == IPFW_DEFAULT_RULE &&
6472930362fSAlexander V. Chernikov (rt->flags & IPFW_RCFLAG_DEFAULT) == 0)
648a73d728dSAlexander V. Chernikov return (0);
649a73d728dSAlexander V. Chernikov
650a73d728dSAlexander V. Chernikov /* Don't match rules in reserved set for flush requests */
651a73d728dSAlexander V. Chernikov if ((rt->flags & IPFW_RCFLAG_ALL) != 0 && rule->set == RESVD_SET)
652a73d728dSAlexander V. Chernikov return (0);
653a73d728dSAlexander V. Chernikov
654a73d728dSAlexander V. Chernikov /* If we're filtering by set, don't match other sets */
655a73d728dSAlexander V. Chernikov if ((rt->flags & IPFW_RCFLAG_SET) != 0 && rule->set != rt->set)
656a73d728dSAlexander V. Chernikov return (0);
657a73d728dSAlexander V. Chernikov
658a73d728dSAlexander V. Chernikov if ((rt->flags & IPFW_RCFLAG_RANGE) != 0 &&
659a73d728dSAlexander V. Chernikov (rule->rulenum < rt->start_rule || rule->rulenum > rt->end_rule))
660a73d728dSAlexander V. Chernikov return (0);
661a73d728dSAlexander V. Chernikov
662a73d728dSAlexander V. Chernikov return (1);
663a73d728dSAlexander V. Chernikov }
664a73d728dSAlexander V. Chernikov
6652685841bSAndrey V. Elsukov struct manage_sets_args {
6664a77657cSAndrey V. Elsukov uint32_t set;
6672685841bSAndrey V. Elsukov uint8_t new_set;
6682685841bSAndrey V. Elsukov };
6692685841bSAndrey V. Elsukov
6702685841bSAndrey V. Elsukov static int
swap_sets_cb(struct namedobj_instance * ni,struct named_object * no,void * arg)6712685841bSAndrey V. Elsukov swap_sets_cb(struct namedobj_instance *ni, struct named_object *no,
6722685841bSAndrey V. Elsukov void *arg)
6732685841bSAndrey V. Elsukov {
6742685841bSAndrey V. Elsukov struct manage_sets_args *args;
6752685841bSAndrey V. Elsukov
6762685841bSAndrey V. Elsukov args = (struct manage_sets_args *)arg;
6772685841bSAndrey V. Elsukov if (no->set == (uint8_t)args->set)
6782685841bSAndrey V. Elsukov no->set = args->new_set;
6792685841bSAndrey V. Elsukov else if (no->set == args->new_set)
6802685841bSAndrey V. Elsukov no->set = (uint8_t)args->set;
6812685841bSAndrey V. Elsukov return (0);
6822685841bSAndrey V. Elsukov }
6832685841bSAndrey V. Elsukov
6842685841bSAndrey V. Elsukov static int
move_sets_cb(struct namedobj_instance * ni,struct named_object * no,void * arg)6852685841bSAndrey V. Elsukov move_sets_cb(struct namedobj_instance *ni, struct named_object *no,
6862685841bSAndrey V. Elsukov void *arg)
6872685841bSAndrey V. Elsukov {
6882685841bSAndrey V. Elsukov struct manage_sets_args *args;
6892685841bSAndrey V. Elsukov
6902685841bSAndrey V. Elsukov args = (struct manage_sets_args *)arg;
6912685841bSAndrey V. Elsukov if (no->set == (uint8_t)args->set)
6922685841bSAndrey V. Elsukov no->set = args->new_set;
6932685841bSAndrey V. Elsukov return (0);
6942685841bSAndrey V. Elsukov }
6952685841bSAndrey V. Elsukov
6962685841bSAndrey V. Elsukov static int
test_sets_cb(struct namedobj_instance * ni,struct named_object * no,void * arg)6972685841bSAndrey V. Elsukov test_sets_cb(struct namedobj_instance *ni, struct named_object *no,
6982685841bSAndrey V. Elsukov void *arg)
6992685841bSAndrey V. Elsukov {
7002685841bSAndrey V. Elsukov struct manage_sets_args *args;
7012685841bSAndrey V. Elsukov
7022685841bSAndrey V. Elsukov args = (struct manage_sets_args *)arg;
7032685841bSAndrey V. Elsukov if (no->set != (uint8_t)args->set)
7042685841bSAndrey V. Elsukov return (0);
7052685841bSAndrey V. Elsukov if (ipfw_objhash_lookup_name_type(ni, args->new_set,
7062685841bSAndrey V. Elsukov no->etlv, no->name) != NULL)
7072685841bSAndrey V. Elsukov return (EEXIST);
7082685841bSAndrey V. Elsukov return (0);
7092685841bSAndrey V. Elsukov }
7102685841bSAndrey V. Elsukov
7112685841bSAndrey V. Elsukov /*
7122685841bSAndrey V. Elsukov * Generic function to handler moving and swapping sets.
7132685841bSAndrey V. Elsukov */
7142685841bSAndrey V. Elsukov int
ipfw_obj_manage_sets(struct namedobj_instance * ni,uint16_t type,uint32_t set,uint8_t new_set,enum ipfw_sets_cmd cmd)7152685841bSAndrey V. Elsukov ipfw_obj_manage_sets(struct namedobj_instance *ni, uint16_t type,
7164a77657cSAndrey V. Elsukov uint32_t set, uint8_t new_set, enum ipfw_sets_cmd cmd)
7172685841bSAndrey V. Elsukov {
7182685841bSAndrey V. Elsukov struct manage_sets_args args;
7192685841bSAndrey V. Elsukov struct named_object *no;
7202685841bSAndrey V. Elsukov
7212685841bSAndrey V. Elsukov args.set = set;
7222685841bSAndrey V. Elsukov args.new_set = new_set;
7232685841bSAndrey V. Elsukov switch (cmd) {
7242685841bSAndrey V. Elsukov case SWAP_ALL:
7252685841bSAndrey V. Elsukov return (ipfw_objhash_foreach_type(ni, swap_sets_cb,
7262685841bSAndrey V. Elsukov &args, type));
7272685841bSAndrey V. Elsukov case TEST_ALL:
7282685841bSAndrey V. Elsukov return (ipfw_objhash_foreach_type(ni, test_sets_cb,
7292685841bSAndrey V. Elsukov &args, type));
7302685841bSAndrey V. Elsukov case MOVE_ALL:
7312685841bSAndrey V. Elsukov return (ipfw_objhash_foreach_type(ni, move_sets_cb,
7322685841bSAndrey V. Elsukov &args, type));
7332685841bSAndrey V. Elsukov case COUNT_ONE:
7342685841bSAndrey V. Elsukov /*
7352685841bSAndrey V. Elsukov * @set used to pass kidx.
7362685841bSAndrey V. Elsukov * When @new_set is zero - reset object counter,
7372685841bSAndrey V. Elsukov * otherwise increment it.
7382685841bSAndrey V. Elsukov */
7392685841bSAndrey V. Elsukov no = ipfw_objhash_lookup_kidx(ni, set);
7402685841bSAndrey V. Elsukov if (new_set != 0)
7412685841bSAndrey V. Elsukov no->ocnt++;
7422685841bSAndrey V. Elsukov else
7432685841bSAndrey V. Elsukov no->ocnt = 0;
7442685841bSAndrey V. Elsukov return (0);
7452685841bSAndrey V. Elsukov case TEST_ONE:
7462685841bSAndrey V. Elsukov /* @set used to pass kidx */
7472685841bSAndrey V. Elsukov no = ipfw_objhash_lookup_kidx(ni, set);
7482685841bSAndrey V. Elsukov /*
7492685841bSAndrey V. Elsukov * First check number of references:
7502685841bSAndrey V. Elsukov * when it differs, this mean other rules are holding
7512685841bSAndrey V. Elsukov * reference to given object, so it is not possible to
7522685841bSAndrey V. Elsukov * change its set. Note that refcnt may account references
7532685841bSAndrey V. Elsukov * to some going-to-be-added rules. Since we don't know
7542685841bSAndrey V. Elsukov * their numbers (and even if they will be added) it is
7552685841bSAndrey V. Elsukov * perfectly OK to return error here.
7562685841bSAndrey V. Elsukov */
7572685841bSAndrey V. Elsukov if (no->ocnt != no->refcnt)
7582685841bSAndrey V. Elsukov return (EBUSY);
7592685841bSAndrey V. Elsukov if (ipfw_objhash_lookup_name_type(ni, new_set, type,
7602685841bSAndrey V. Elsukov no->name) != NULL)
7612685841bSAndrey V. Elsukov return (EEXIST);
7622685841bSAndrey V. Elsukov return (0);
7632685841bSAndrey V. Elsukov case MOVE_ONE:
7642685841bSAndrey V. Elsukov /* @set used to pass kidx */
7652685841bSAndrey V. Elsukov no = ipfw_objhash_lookup_kidx(ni, set);
7662685841bSAndrey V. Elsukov no->set = new_set;
7672685841bSAndrey V. Elsukov return (0);
7682685841bSAndrey V. Elsukov }
7692685841bSAndrey V. Elsukov return (EINVAL);
7702685841bSAndrey V. Elsukov }
7712685841bSAndrey V. Elsukov
772a73d728dSAlexander V. Chernikov /*
773a73d728dSAlexander V. Chernikov * Delete rules matching range @rt.
774a73d728dSAlexander V. Chernikov * Saves number of deleted rules in @ndel.
775a73d728dSAlexander V. Chernikov *
776a73d728dSAlexander V. Chernikov * Returns 0 on success.
777a73d728dSAlexander V. Chernikov */
7784a77657cSAndrey V. Elsukov int
delete_range(struct ip_fw_chain * chain,ipfw_range_tlv * rt,int * ndel)779a73d728dSAlexander V. Chernikov delete_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int *ndel)
780a73d728dSAlexander V. Chernikov {
781a73d728dSAlexander V. Chernikov struct ip_fw *reap, *rule, **map;
7824a77657cSAndrey V. Elsukov uint32_t end, start;
783a73d728dSAlexander V. Chernikov int i, n, ndyn, ofs;
784a73d728dSAlexander V. Chernikov
785a73d728dSAlexander V. Chernikov reap = NULL;
786a73d728dSAlexander V. Chernikov IPFW_UH_WLOCK(chain); /* arbitrate writers */
787a73d728dSAlexander V. Chernikov
788a73d728dSAlexander V. Chernikov /*
789a73d728dSAlexander V. Chernikov * Stage 1: Determine range to inspect.
790a73d728dSAlexander V. Chernikov * Range is half-inclusive, e.g [start, end).
791a73d728dSAlexander V. Chernikov */
792a73d728dSAlexander V. Chernikov start = 0;
793a73d728dSAlexander V. Chernikov end = chain->n_rules - 1;
794a73d728dSAlexander V. Chernikov
795a73d728dSAlexander V. Chernikov if ((rt->flags & IPFW_RCFLAG_RANGE) != 0) {
796a73d728dSAlexander V. Chernikov start = ipfw_find_rule(chain, rt->start_rule, 0);
797a73d728dSAlexander V. Chernikov
798288bf455SAndrey V. Elsukov if (rt->end_rule >= IPFW_DEFAULT_RULE)
799288bf455SAndrey V. Elsukov rt->end_rule = IPFW_DEFAULT_RULE - 1;
800288bf455SAndrey V. Elsukov end = ipfw_find_rule(chain, rt->end_rule, UINT32_MAX);
801a73d728dSAlexander V. Chernikov }
802a73d728dSAlexander V. Chernikov
803d66f9c86SAndrey V. Elsukov if (rt->flags & IPFW_RCFLAG_DYNAMIC) {
804d66f9c86SAndrey V. Elsukov /*
805d66f9c86SAndrey V. Elsukov * Requested deleting only for dynamic states.
806d66f9c86SAndrey V. Elsukov */
807d66f9c86SAndrey V. Elsukov *ndel = 0;
808d66f9c86SAndrey V. Elsukov ipfw_expire_dyn_states(chain, rt);
809d66f9c86SAndrey V. Elsukov IPFW_UH_WUNLOCK(chain);
810d66f9c86SAndrey V. Elsukov return (0);
811d66f9c86SAndrey V. Elsukov }
812d66f9c86SAndrey V. Elsukov
813a73d728dSAlexander V. Chernikov /* Allocate new map of the same size */
814a73d728dSAlexander V. Chernikov map = get_map(chain, 0, 1 /* locked */);
815a73d728dSAlexander V. Chernikov if (map == NULL) {
816a73d728dSAlexander V. Chernikov IPFW_UH_WUNLOCK(chain);
817a73d728dSAlexander V. Chernikov return (ENOMEM);
818a73d728dSAlexander V. Chernikov }
819a73d728dSAlexander V. Chernikov
820a73d728dSAlexander V. Chernikov n = 0;
821a73d728dSAlexander V. Chernikov ndyn = 0;
822a73d728dSAlexander V. Chernikov ofs = start;
823a73d728dSAlexander V. Chernikov /* 1. bcopy the initial part of the map */
824a73d728dSAlexander V. Chernikov if (start > 0)
825a73d728dSAlexander V. Chernikov bcopy(chain->map, map, start * sizeof(struct ip_fw *));
826a73d728dSAlexander V. Chernikov /* 2. copy active rules between start and end */
827a73d728dSAlexander V. Chernikov for (i = start; i < end; i++) {
828a73d728dSAlexander V. Chernikov rule = chain->map[i];
829a73d728dSAlexander V. Chernikov if (ipfw_match_range(rule, rt) == 0) {
830a73d728dSAlexander V. Chernikov map[ofs++] = rule;
831a73d728dSAlexander V. Chernikov continue;
832a73d728dSAlexander V. Chernikov }
833a73d728dSAlexander V. Chernikov
834a73d728dSAlexander V. Chernikov n++;
835a73d728dSAlexander V. Chernikov if (ipfw_is_dyn_rule(rule) != 0)
836a73d728dSAlexander V. Chernikov ndyn++;
837a73d728dSAlexander V. Chernikov }
838a73d728dSAlexander V. Chernikov /* 3. copy the final part of the map */
839a73d728dSAlexander V. Chernikov bcopy(chain->map + end, map + ofs,
840a73d728dSAlexander V. Chernikov (chain->n_rules - end) * sizeof(struct ip_fw *));
841a73d728dSAlexander V. Chernikov /* 4. recalculate skipto cache */
842a73d728dSAlexander V. Chernikov update_skipto_cache(chain, map);
843a73d728dSAlexander V. Chernikov /* 5. swap the maps (under UH_WLOCK + WHLOCK) */
844a73d728dSAlexander V. Chernikov map = swap_map(chain, map, chain->n_rules - n);
845a73d728dSAlexander V. Chernikov /* 6. Remove all dynamic states originated by deleted rules */
846a73d728dSAlexander V. Chernikov if (ndyn > 0)
847b99a6823SAndrey V. Elsukov ipfw_expire_dyn_states(chain, rt);
848a73d728dSAlexander V. Chernikov /* 7. now remove the rules deleted from the old map */
849a73d728dSAlexander V. Chernikov for (i = start; i < end; i++) {
850a73d728dSAlexander V. Chernikov rule = map[i];
851a73d728dSAlexander V. Chernikov if (ipfw_match_range(rule, rt) == 0)
852a73d728dSAlexander V. Chernikov continue;
853030b184fSAlexander V. Chernikov ipfw_reap_add(chain, &reap, rule);
854a73d728dSAlexander V. Chernikov }
855a73d728dSAlexander V. Chernikov IPFW_UH_WUNLOCK(chain);
856030b184fSAlexander V. Chernikov
857a73d728dSAlexander V. Chernikov ipfw_reap_rules(reap);
858a73d728dSAlexander V. Chernikov if (map != NULL)
859a73d728dSAlexander V. Chernikov free(map, M_IPFW);
860a73d728dSAlexander V. Chernikov *ndel = n;
861a73d728dSAlexander V. Chernikov return (0);
862a73d728dSAlexander V. Chernikov }
863a73d728dSAlexander V. Chernikov
8642685841bSAndrey V. Elsukov static int
move_objects(struct ip_fw_chain * ch,ipfw_range_tlv * rt)8652685841bSAndrey V. Elsukov move_objects(struct ip_fw_chain *ch, ipfw_range_tlv *rt)
8662685841bSAndrey V. Elsukov {
8672685841bSAndrey V. Elsukov struct opcode_obj_rewrite *rw;
8682685841bSAndrey V. Elsukov struct ip_fw *rule;
8692685841bSAndrey V. Elsukov ipfw_insn *cmd;
8704a77657cSAndrey V. Elsukov uint32_t kidx;
8712685841bSAndrey V. Elsukov int cmdlen, i, l, c;
8722685841bSAndrey V. Elsukov
8732685841bSAndrey V. Elsukov IPFW_UH_WLOCK_ASSERT(ch);
8742685841bSAndrey V. Elsukov
8752685841bSAndrey V. Elsukov /* Stage 1: count number of references by given rules */
8762685841bSAndrey V. Elsukov for (c = 0, i = 0; i < ch->n_rules - 1; i++) {
8772685841bSAndrey V. Elsukov rule = ch->map[i];
8782685841bSAndrey V. Elsukov if (ipfw_match_range(rule, rt) == 0)
8792685841bSAndrey V. Elsukov continue;
8802685841bSAndrey V. Elsukov if (rule->set == rt->new_set) /* nothing to do */
8812685841bSAndrey V. Elsukov continue;
8822685841bSAndrey V. Elsukov /* Search opcodes with named objects */
8832685841bSAndrey V. Elsukov for (l = rule->cmd_len, cmdlen = 0, cmd = rule->cmd;
8842685841bSAndrey V. Elsukov l > 0; l -= cmdlen, cmd += cmdlen) {
8852685841bSAndrey V. Elsukov cmdlen = F_LEN(cmd);
8862685841bSAndrey V. Elsukov rw = find_op_rw(cmd, &kidx, NULL);
8872685841bSAndrey V. Elsukov if (rw == NULL || rw->manage_sets == NULL)
8882685841bSAndrey V. Elsukov continue;
889a73d728dSAlexander V. Chernikov /*
8902685841bSAndrey V. Elsukov * When manage_sets() returns non-zero value to
8912685841bSAndrey V. Elsukov * COUNT_ONE command, consider this as an object
8922685841bSAndrey V. Elsukov * doesn't support sets (e.g. disabled with sysctl).
8932685841bSAndrey V. Elsukov * So, skip checks for this object.
8942685841bSAndrey V. Elsukov */
8952685841bSAndrey V. Elsukov if (rw->manage_sets(ch, kidx, 1, COUNT_ONE) != 0)
8962685841bSAndrey V. Elsukov continue;
8972685841bSAndrey V. Elsukov c++;
8982685841bSAndrey V. Elsukov }
8992685841bSAndrey V. Elsukov }
9002685841bSAndrey V. Elsukov if (c == 0) /* No objects found */
9012685841bSAndrey V. Elsukov return (0);
9022685841bSAndrey V. Elsukov /* Stage 2: verify "ownership" */
9032685841bSAndrey V. Elsukov for (c = 0, i = 0; (i < ch->n_rules - 1) && c == 0; i++) {
9042685841bSAndrey V. Elsukov rule = ch->map[i];
9052685841bSAndrey V. Elsukov if (ipfw_match_range(rule, rt) == 0)
9062685841bSAndrey V. Elsukov continue;
9072685841bSAndrey V. Elsukov if (rule->set == rt->new_set) /* nothing to do */
9082685841bSAndrey V. Elsukov continue;
9092685841bSAndrey V. Elsukov /* Search opcodes with named objects */
9102685841bSAndrey V. Elsukov for (l = rule->cmd_len, cmdlen = 0, cmd = rule->cmd;
9112685841bSAndrey V. Elsukov l > 0 && c == 0; l -= cmdlen, cmd += cmdlen) {
9122685841bSAndrey V. Elsukov cmdlen = F_LEN(cmd);
9132685841bSAndrey V. Elsukov rw = find_op_rw(cmd, &kidx, NULL);
9142685841bSAndrey V. Elsukov if (rw == NULL || rw->manage_sets == NULL)
9152685841bSAndrey V. Elsukov continue;
9162685841bSAndrey V. Elsukov /* Test for ownership and conflicting names */
9172685841bSAndrey V. Elsukov c = rw->manage_sets(ch, kidx,
9182685841bSAndrey V. Elsukov (uint8_t)rt->new_set, TEST_ONE);
9192685841bSAndrey V. Elsukov }
9202685841bSAndrey V. Elsukov }
9212685841bSAndrey V. Elsukov /* Stage 3: change set and cleanup */
9222685841bSAndrey V. Elsukov for (i = 0; i < ch->n_rules - 1; i++) {
9232685841bSAndrey V. Elsukov rule = ch->map[i];
9242685841bSAndrey V. Elsukov if (ipfw_match_range(rule, rt) == 0)
9252685841bSAndrey V. Elsukov continue;
9262685841bSAndrey V. Elsukov if (rule->set == rt->new_set) /* nothing to do */
9272685841bSAndrey V. Elsukov continue;
9282685841bSAndrey V. Elsukov /* Search opcodes with named objects */
9292685841bSAndrey V. Elsukov for (l = rule->cmd_len, cmdlen = 0, cmd = rule->cmd;
9302685841bSAndrey V. Elsukov l > 0; l -= cmdlen, cmd += cmdlen) {
9312685841bSAndrey V. Elsukov cmdlen = F_LEN(cmd);
9322685841bSAndrey V. Elsukov rw = find_op_rw(cmd, &kidx, NULL);
9332685841bSAndrey V. Elsukov if (rw == NULL || rw->manage_sets == NULL)
9342685841bSAndrey V. Elsukov continue;
9352685841bSAndrey V. Elsukov /* cleanup object counter */
9362685841bSAndrey V. Elsukov rw->manage_sets(ch, kidx,
9372685841bSAndrey V. Elsukov 0 /* reset counter */, COUNT_ONE);
9382685841bSAndrey V. Elsukov if (c != 0)
9392685841bSAndrey V. Elsukov continue;
9402685841bSAndrey V. Elsukov /* change set */
9412685841bSAndrey V. Elsukov rw->manage_sets(ch, kidx,
9422685841bSAndrey V. Elsukov (uint8_t)rt->new_set, MOVE_ONE);
9432685841bSAndrey V. Elsukov }
9442685841bSAndrey V. Elsukov }
9452685841bSAndrey V. Elsukov return (c);
946978f2d17SAndrey V. Elsukov }
947978f2d17SAndrey V. Elsukov
948978f2d17SAndrey V. Elsukov /*
949a73d728dSAlexander V. Chernikov * Changes set of given rule rannge @rt
950a73d728dSAlexander V. Chernikov * with each other.
951a73d728dSAlexander V. Chernikov *
952a73d728dSAlexander V. Chernikov * Returns 0 on success.
953a73d728dSAlexander V. Chernikov */
954a73d728dSAlexander V. Chernikov static int
move_range(struct ip_fw_chain * chain,ipfw_range_tlv * rt)955a73d728dSAlexander V. Chernikov move_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt)
956a73d728dSAlexander V. Chernikov {
957a73d728dSAlexander V. Chernikov struct ip_fw *rule;
958a73d728dSAlexander V. Chernikov int i;
959a73d728dSAlexander V. Chernikov
960a73d728dSAlexander V. Chernikov IPFW_UH_WLOCK(chain);
961a73d728dSAlexander V. Chernikov
962a73d728dSAlexander V. Chernikov /*
963a73d728dSAlexander V. Chernikov * Move rules with matching paramenerts to a new set.
964a73d728dSAlexander V. Chernikov * This one is much more complex. We have to ensure
965a73d728dSAlexander V. Chernikov * that all referenced tables (if any) are referenced
966a73d728dSAlexander V. Chernikov * by given rule subset only. Otherwise, we can't move
967a73d728dSAlexander V. Chernikov * them to new set and have to return error.
968a73d728dSAlexander V. Chernikov */
9692685841bSAndrey V. Elsukov if ((i = move_objects(chain, rt)) != 0) {
970a73d728dSAlexander V. Chernikov IPFW_UH_WUNLOCK(chain);
9712685841bSAndrey V. Elsukov return (i);
972a73d728dSAlexander V. Chernikov }
973a73d728dSAlexander V. Chernikov
974a73d728dSAlexander V. Chernikov /* XXX: We have to do swap holding WLOCK */
9752930362fSAlexander V. Chernikov for (i = 0; i < chain->n_rules; i++) {
976a73d728dSAlexander V. Chernikov rule = chain->map[i];
977a73d728dSAlexander V. Chernikov if (ipfw_match_range(rule, rt) == 0)
978a73d728dSAlexander V. Chernikov continue;
979a73d728dSAlexander V. Chernikov rule->set = rt->new_set;
980a73d728dSAlexander V. Chernikov }
981a73d728dSAlexander V. Chernikov
982a73d728dSAlexander V. Chernikov IPFW_UH_WUNLOCK(chain);
983a73d728dSAlexander V. Chernikov
984a73d728dSAlexander V. Chernikov return (0);
985a73d728dSAlexander V. Chernikov }
986a73d728dSAlexander V. Chernikov
987a73d728dSAlexander V. Chernikov /*
988e758846cSAndrey V. Elsukov * Returns pointer to action instruction, skips all possible rule
989e758846cSAndrey V. Elsukov * modifiers like O_LOG, O_TAG, O_ALTQ.
990e758846cSAndrey V. Elsukov */
991e758846cSAndrey V. Elsukov ipfw_insn *
ipfw_get_action(struct ip_fw * rule)992e758846cSAndrey V. Elsukov ipfw_get_action(struct ip_fw *rule)
993e758846cSAndrey V. Elsukov {
994e758846cSAndrey V. Elsukov ipfw_insn *cmd;
995e758846cSAndrey V. Elsukov int l, cmdlen;
996e758846cSAndrey V. Elsukov
997e758846cSAndrey V. Elsukov cmd = ACTION_PTR(rule);
998e758846cSAndrey V. Elsukov l = rule->cmd_len - rule->act_ofs;
999e758846cSAndrey V. Elsukov while (l > 0) {
1000e758846cSAndrey V. Elsukov switch (cmd->opcode) {
1001e758846cSAndrey V. Elsukov case O_ALTQ:
1002e758846cSAndrey V. Elsukov case O_LOG:
1003e758846cSAndrey V. Elsukov case O_TAG:
1004e758846cSAndrey V. Elsukov break;
1005e758846cSAndrey V. Elsukov default:
1006e758846cSAndrey V. Elsukov return (cmd);
1007e758846cSAndrey V. Elsukov }
1008e758846cSAndrey V. Elsukov cmdlen = F_LEN(cmd);
1009e758846cSAndrey V. Elsukov l -= cmdlen;
1010e758846cSAndrey V. Elsukov cmd += cmdlen;
1011e758846cSAndrey V. Elsukov }
1012e758846cSAndrey V. Elsukov panic("%s: rule (%p) has not action opcode", __func__, rule);
1013e758846cSAndrey V. Elsukov return (NULL);
1014e758846cSAndrey V. Elsukov }
1015e758846cSAndrey V. Elsukov
1016e758846cSAndrey V. Elsukov /*
1017a73d728dSAlexander V. Chernikov * Clear counters for a specific rule.
1018a73d728dSAlexander V. Chernikov * Normally run under IPFW_UH_RLOCK, but these are idempotent ops
1019a73d728dSAlexander V. Chernikov * so we only care that rules do not disappear.
1020a73d728dSAlexander V. Chernikov */
1021a73d728dSAlexander V. Chernikov static void
clear_counters(struct ip_fw * rule,int log_only)1022a73d728dSAlexander V. Chernikov clear_counters(struct ip_fw *rule, int log_only)
1023a73d728dSAlexander V. Chernikov {
1024a73d728dSAlexander V. Chernikov ipfw_insn_log *l = (ipfw_insn_log *)ACTION_PTR(rule);
1025a73d728dSAlexander V. Chernikov
1026a73d728dSAlexander V. Chernikov if (log_only == 0)
1027a73d728dSAlexander V. Chernikov IPFW_ZERO_RULE_COUNTER(rule);
1028a73d728dSAlexander V. Chernikov if (l->o.opcode == O_LOG)
1029a73d728dSAlexander V. Chernikov l->log_left = l->max_log;
1030a73d728dSAlexander V. Chernikov }
1031a73d728dSAlexander V. Chernikov
1032a73d728dSAlexander V. Chernikov /*
1033a73d728dSAlexander V. Chernikov * Flushes rules counters and/or log values on matching range.
1034a73d728dSAlexander V. Chernikov *
1035a73d728dSAlexander V. Chernikov * Returns number of items cleared.
1036a73d728dSAlexander V. Chernikov */
1037a73d728dSAlexander V. Chernikov static int
clear_range(struct ip_fw_chain * chain,ipfw_range_tlv * rt,int log_only)1038a73d728dSAlexander V. Chernikov clear_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int log_only)
1039a73d728dSAlexander V. Chernikov {
1040a73d728dSAlexander V. Chernikov struct ip_fw *rule;
1041a73d728dSAlexander V. Chernikov int num;
1042a73d728dSAlexander V. Chernikov int i;
1043a73d728dSAlexander V. Chernikov
1044a73d728dSAlexander V. Chernikov num = 0;
10452930362fSAlexander V. Chernikov rt->flags |= IPFW_RCFLAG_DEFAULT;
1046a73d728dSAlexander V. Chernikov
1047a73d728dSAlexander V. Chernikov IPFW_UH_WLOCK(chain); /* arbitrate writers */
10482930362fSAlexander V. Chernikov for (i = 0; i < chain->n_rules; i++) {
1049a73d728dSAlexander V. Chernikov rule = chain->map[i];
1050a73d728dSAlexander V. Chernikov if (ipfw_match_range(rule, rt) == 0)
1051a73d728dSAlexander V. Chernikov continue;
1052a73d728dSAlexander V. Chernikov clear_counters(rule, log_only);
1053a73d728dSAlexander V. Chernikov num++;
1054a73d728dSAlexander V. Chernikov }
1055a73d728dSAlexander V. Chernikov IPFW_UH_WUNLOCK(chain);
1056a73d728dSAlexander V. Chernikov
1057a73d728dSAlexander V. Chernikov return (num);
1058a73d728dSAlexander V. Chernikov }
1059a73d728dSAlexander V. Chernikov
1060a73d728dSAlexander V. Chernikov static int
check_range_tlv(ipfw_range_tlv * rt)1061a73d728dSAlexander V. Chernikov check_range_tlv(ipfw_range_tlv *rt)
1062a73d728dSAlexander V. Chernikov {
1063a73d728dSAlexander V. Chernikov
1064a73d728dSAlexander V. Chernikov if (rt->head.length != sizeof(*rt))
1065a73d728dSAlexander V. Chernikov return (1);
1066a73d728dSAlexander V. Chernikov if (rt->start_rule > rt->end_rule)
1067a73d728dSAlexander V. Chernikov return (1);
1068a73d728dSAlexander V. Chernikov if (rt->set >= IPFW_MAX_SETS || rt->new_set >= IPFW_MAX_SETS)
1069a73d728dSAlexander V. Chernikov return (1);
1070a73d728dSAlexander V. Chernikov
10712930362fSAlexander V. Chernikov if ((rt->flags & IPFW_RCFLAG_USER) != rt->flags)
10722930362fSAlexander V. Chernikov return (1);
10732930362fSAlexander V. Chernikov
1074a73d728dSAlexander V. Chernikov return (0);
1075a73d728dSAlexander V. Chernikov }
1076a73d728dSAlexander V. Chernikov
1077a73d728dSAlexander V. Chernikov /*
1078a73d728dSAlexander V. Chernikov * Delete rules matching specified parameters
1079a73d728dSAlexander V. Chernikov * Data layout (v0)(current):
1080a73d728dSAlexander V. Chernikov * Request: [ ipfw_obj_header ipfw_range_tlv ]
1081a73d728dSAlexander V. Chernikov * Reply: [ ipfw_obj_header ipfw_range_tlv ]
1082a73d728dSAlexander V. Chernikov *
1083a73d728dSAlexander V. Chernikov * Saves number of deleted rules in ipfw_range_tlv->new_set.
1084a73d728dSAlexander V. Chernikov *
1085a73d728dSAlexander V. Chernikov * Returns 0 on success.
1086a73d728dSAlexander V. Chernikov */
1087a73d728dSAlexander V. Chernikov static int
del_rules(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)1088a73d728dSAlexander V. Chernikov del_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
1089a73d728dSAlexander V. Chernikov struct sockopt_data *sd)
1090a73d728dSAlexander V. Chernikov {
1091a73d728dSAlexander V. Chernikov ipfw_range_header *rh;
1092a73d728dSAlexander V. Chernikov int error, ndel;
1093a73d728dSAlexander V. Chernikov
1094a73d728dSAlexander V. Chernikov if (sd->valsize != sizeof(*rh))
1095a73d728dSAlexander V. Chernikov return (EINVAL);
1096a73d728dSAlexander V. Chernikov
1097a73d728dSAlexander V. Chernikov rh = (ipfw_range_header *)ipfw_get_sopt_space(sd, sd->valsize);
1098a73d728dSAlexander V. Chernikov
1099a73d728dSAlexander V. Chernikov if (check_range_tlv(&rh->range) != 0)
1100a73d728dSAlexander V. Chernikov return (EINVAL);
1101a73d728dSAlexander V. Chernikov
1102a73d728dSAlexander V. Chernikov ndel = 0;
1103a73d728dSAlexander V. Chernikov if ((error = delete_range(chain, &rh->range, &ndel)) != 0)
1104a73d728dSAlexander V. Chernikov return (error);
1105a73d728dSAlexander V. Chernikov
1106a73d728dSAlexander V. Chernikov /* Save number of rules deleted */
1107a73d728dSAlexander V. Chernikov rh->range.new_set = ndel;
1108a73d728dSAlexander V. Chernikov return (0);
1109a73d728dSAlexander V. Chernikov }
1110a73d728dSAlexander V. Chernikov
1111a73d728dSAlexander V. Chernikov /*
1112a73d728dSAlexander V. Chernikov * Move rules/sets matching specified parameters
1113a73d728dSAlexander V. Chernikov * Data layout (v0)(current):
1114a73d728dSAlexander V. Chernikov * Request: [ ipfw_obj_header ipfw_range_tlv ]
1115a73d728dSAlexander V. Chernikov *
1116a73d728dSAlexander V. Chernikov * Returns 0 on success.
1117a73d728dSAlexander V. Chernikov */
1118a73d728dSAlexander V. Chernikov static int
move_rules(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)1119a73d728dSAlexander V. Chernikov move_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
1120a73d728dSAlexander V. Chernikov struct sockopt_data *sd)
1121a73d728dSAlexander V. Chernikov {
1122a73d728dSAlexander V. Chernikov ipfw_range_header *rh;
1123a73d728dSAlexander V. Chernikov
1124a73d728dSAlexander V. Chernikov if (sd->valsize != sizeof(*rh))
1125a73d728dSAlexander V. Chernikov return (EINVAL);
1126a73d728dSAlexander V. Chernikov
1127a73d728dSAlexander V. Chernikov rh = (ipfw_range_header *)ipfw_get_sopt_space(sd, sd->valsize);
1128a73d728dSAlexander V. Chernikov
1129a73d728dSAlexander V. Chernikov if (check_range_tlv(&rh->range) != 0)
1130a73d728dSAlexander V. Chernikov return (EINVAL);
1131a73d728dSAlexander V. Chernikov
1132a73d728dSAlexander V. Chernikov return (move_range(chain, &rh->range));
1133a73d728dSAlexander V. Chernikov }
1134a73d728dSAlexander V. Chernikov
1135a73d728dSAlexander V. Chernikov /*
1136a73d728dSAlexander V. Chernikov * Clear rule accounting data matching specified parameters
1137a73d728dSAlexander V. Chernikov * Data layout (v0)(current):
1138a73d728dSAlexander V. Chernikov * Request: [ ipfw_obj_header ipfw_range_tlv ]
1139a73d728dSAlexander V. Chernikov * Reply: [ ipfw_obj_header ipfw_range_tlv ]
1140a73d728dSAlexander V. Chernikov *
1141a73d728dSAlexander V. Chernikov * Saves number of cleared rules in ipfw_range_tlv->new_set.
1142a73d728dSAlexander V. Chernikov *
1143a73d728dSAlexander V. Chernikov * Returns 0 on success.
1144a73d728dSAlexander V. Chernikov */
1145a73d728dSAlexander V. Chernikov static int
clear_rules(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)1146a73d728dSAlexander V. Chernikov clear_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
1147a73d728dSAlexander V. Chernikov struct sockopt_data *sd)
1148a73d728dSAlexander V. Chernikov {
1149a73d728dSAlexander V. Chernikov ipfw_range_header *rh;
1150a73d728dSAlexander V. Chernikov int log_only, num;
1151a73d728dSAlexander V. Chernikov char *msg;
1152a73d728dSAlexander V. Chernikov
1153a73d728dSAlexander V. Chernikov if (sd->valsize != sizeof(*rh))
1154a73d728dSAlexander V. Chernikov return (EINVAL);
1155a73d728dSAlexander V. Chernikov
1156a73d728dSAlexander V. Chernikov rh = (ipfw_range_header *)ipfw_get_sopt_space(sd, sd->valsize);
1157a73d728dSAlexander V. Chernikov
1158a73d728dSAlexander V. Chernikov if (check_range_tlv(&rh->range) != 0)
1159a73d728dSAlexander V. Chernikov return (EINVAL);
1160a73d728dSAlexander V. Chernikov
1161a73d728dSAlexander V. Chernikov log_only = (op3->opcode == IP_FW_XRESETLOG);
1162a73d728dSAlexander V. Chernikov
1163a73d728dSAlexander V. Chernikov num = clear_range(chain, &rh->range, log_only);
1164a73d728dSAlexander V. Chernikov
1165a73d728dSAlexander V. Chernikov if (rh->range.flags & IPFW_RCFLAG_ALL)
1166a73d728dSAlexander V. Chernikov msg = log_only ? "All logging counts reset" :
1167a73d728dSAlexander V. Chernikov "Accounting cleared";
1168a73d728dSAlexander V. Chernikov else
1169a73d728dSAlexander V. Chernikov msg = log_only ? "logging count reset" : "cleared";
1170a73d728dSAlexander V. Chernikov
1171a73d728dSAlexander V. Chernikov if (V_fw_verbose) {
1172a73d728dSAlexander V. Chernikov int lev = LOG_SECURITY | LOG_NOTICE;
1173a73d728dSAlexander V. Chernikov log(lev, "ipfw: %s.\n", msg);
1174a73d728dSAlexander V. Chernikov }
1175a73d728dSAlexander V. Chernikov
1176a73d728dSAlexander V. Chernikov /* Save number of rules cleared */
1177a73d728dSAlexander V. Chernikov rh->range.new_set = num;
1178a73d728dSAlexander V. Chernikov return (0);
1179a73d728dSAlexander V. Chernikov }
1180a73d728dSAlexander V. Chernikov
1181a73d728dSAlexander V. Chernikov static void
enable_sets(struct ip_fw_chain * chain,ipfw_range_tlv * rt)1182a73d728dSAlexander V. Chernikov enable_sets(struct ip_fw_chain *chain, ipfw_range_tlv *rt)
1183a73d728dSAlexander V. Chernikov {
1184a73d728dSAlexander V. Chernikov uint32_t v_set;
1185a73d728dSAlexander V. Chernikov
1186a73d728dSAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(chain);
1187a73d728dSAlexander V. Chernikov
1188a73d728dSAlexander V. Chernikov /* Change enabled/disabled sets mask */
1189a73d728dSAlexander V. Chernikov v_set = (V_set_disable | rt->set) & ~rt->new_set;
1190a73d728dSAlexander V. Chernikov v_set &= ~(1 << RESVD_SET); /* set RESVD_SET always enabled */
1191a73d728dSAlexander V. Chernikov IPFW_WLOCK(chain);
1192a73d728dSAlexander V. Chernikov V_set_disable = v_set;
1193a73d728dSAlexander V. Chernikov IPFW_WUNLOCK(chain);
1194a73d728dSAlexander V. Chernikov }
1195a73d728dSAlexander V. Chernikov
11962685841bSAndrey V. Elsukov static int
swap_sets(struct ip_fw_chain * chain,ipfw_range_tlv * rt,int mv)1197a73d728dSAlexander V. Chernikov swap_sets(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int mv)
1198a73d728dSAlexander V. Chernikov {
11992685841bSAndrey V. Elsukov struct opcode_obj_rewrite *rw;
1200a73d728dSAlexander V. Chernikov struct ip_fw *rule;
1201a73d728dSAlexander V. Chernikov int i;
1202a73d728dSAlexander V. Chernikov
1203a73d728dSAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(chain);
1204a73d728dSAlexander V. Chernikov
12052685841bSAndrey V. Elsukov if (rt->set == rt->new_set) /* nothing to do */
12062685841bSAndrey V. Elsukov return (0);
12072685841bSAndrey V. Elsukov
12082685841bSAndrey V. Elsukov if (mv != 0) {
12092685841bSAndrey V. Elsukov /*
12102685841bSAndrey V. Elsukov * Berfore moving the rules we need to check that
12112685841bSAndrey V. Elsukov * there aren't any conflicting named objects.
12122685841bSAndrey V. Elsukov */
12132685841bSAndrey V. Elsukov for (rw = ctl3_rewriters;
12142685841bSAndrey V. Elsukov rw < ctl3_rewriters + ctl3_rsize; rw++) {
12152685841bSAndrey V. Elsukov if (rw->manage_sets == NULL)
12162685841bSAndrey V. Elsukov continue;
12172685841bSAndrey V. Elsukov i = rw->manage_sets(chain, (uint8_t)rt->set,
12182685841bSAndrey V. Elsukov (uint8_t)rt->new_set, TEST_ALL);
12192685841bSAndrey V. Elsukov if (i != 0)
12202685841bSAndrey V. Elsukov return (EEXIST);
12212685841bSAndrey V. Elsukov }
12222685841bSAndrey V. Elsukov }
1223a73d728dSAlexander V. Chernikov /* Swap or move two sets */
1224a73d728dSAlexander V. Chernikov for (i = 0; i < chain->n_rules - 1; i++) {
1225a73d728dSAlexander V. Chernikov rule = chain->map[i];
12262685841bSAndrey V. Elsukov if (rule->set == (uint8_t)rt->set)
12272685841bSAndrey V. Elsukov rule->set = (uint8_t)rt->new_set;
12282685841bSAndrey V. Elsukov else if (rule->set == (uint8_t)rt->new_set && mv == 0)
12292685841bSAndrey V. Elsukov rule->set = (uint8_t)rt->set;
1230a73d728dSAlexander V. Chernikov }
12312685841bSAndrey V. Elsukov for (rw = ctl3_rewriters; rw < ctl3_rewriters + ctl3_rsize; rw++) {
12322685841bSAndrey V. Elsukov if (rw->manage_sets == NULL)
12332685841bSAndrey V. Elsukov continue;
12342685841bSAndrey V. Elsukov rw->manage_sets(chain, (uint8_t)rt->set,
12352685841bSAndrey V. Elsukov (uint8_t)rt->new_set, mv != 0 ? MOVE_ALL: SWAP_ALL);
12362685841bSAndrey V. Elsukov }
12372685841bSAndrey V. Elsukov return (0);
1238a73d728dSAlexander V. Chernikov }
1239a73d728dSAlexander V. Chernikov
1240a73d728dSAlexander V. Chernikov /*
1241a73d728dSAlexander V. Chernikov * Swaps or moves set
1242a73d728dSAlexander V. Chernikov * Data layout (v0)(current):
1243a73d728dSAlexander V. Chernikov * Request: [ ipfw_obj_header ipfw_range_tlv ]
1244a73d728dSAlexander V. Chernikov *
1245a73d728dSAlexander V. Chernikov * Returns 0 on success.
1246a73d728dSAlexander V. Chernikov */
1247a73d728dSAlexander V. Chernikov static int
manage_sets(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)1248a73d728dSAlexander V. Chernikov manage_sets(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
1249a73d728dSAlexander V. Chernikov struct sockopt_data *sd)
1250a73d728dSAlexander V. Chernikov {
1251a73d728dSAlexander V. Chernikov ipfw_range_header *rh;
12522685841bSAndrey V. Elsukov int ret;
1253a73d728dSAlexander V. Chernikov
1254a73d728dSAlexander V. Chernikov if (sd->valsize != sizeof(*rh))
1255a73d728dSAlexander V. Chernikov return (EINVAL);
1256a73d728dSAlexander V. Chernikov
1257a73d728dSAlexander V. Chernikov rh = (ipfw_range_header *)ipfw_get_sopt_space(sd, sd->valsize);
1258a73d728dSAlexander V. Chernikov
1259a73d728dSAlexander V. Chernikov if (rh->range.head.length != sizeof(ipfw_range_tlv))
1260a73d728dSAlexander V. Chernikov return (1);
1261e7560c83SOleg Bulyzhin /* enable_sets() expects bitmasks. */
1262e7560c83SOleg Bulyzhin if (op3->opcode != IP_FW_SET_ENABLE &&
1263e7560c83SOleg Bulyzhin (rh->range.set >= IPFW_MAX_SETS ||
1264e7560c83SOleg Bulyzhin rh->range.new_set >= IPFW_MAX_SETS))
12652685841bSAndrey V. Elsukov return (EINVAL);
1266a73d728dSAlexander V. Chernikov
12672685841bSAndrey V. Elsukov ret = 0;
1268a73d728dSAlexander V. Chernikov IPFW_UH_WLOCK(chain);
1269a73d728dSAlexander V. Chernikov switch (op3->opcode) {
1270a73d728dSAlexander V. Chernikov case IP_FW_SET_SWAP:
1271a73d728dSAlexander V. Chernikov case IP_FW_SET_MOVE:
12722685841bSAndrey V. Elsukov ret = swap_sets(chain, &rh->range,
12732685841bSAndrey V. Elsukov op3->opcode == IP_FW_SET_MOVE);
1274a73d728dSAlexander V. Chernikov break;
1275a73d728dSAlexander V. Chernikov case IP_FW_SET_ENABLE:
1276a73d728dSAlexander V. Chernikov enable_sets(chain, &rh->range);
1277a73d728dSAlexander V. Chernikov break;
1278a73d728dSAlexander V. Chernikov }
1279a73d728dSAlexander V. Chernikov IPFW_UH_WUNLOCK(chain);
1280a73d728dSAlexander V. Chernikov
12812685841bSAndrey V. Elsukov return (ret);
12823b3a8eb9SGleb Smirnoff }
12833b3a8eb9SGleb Smirnoff
12844a77657cSAndrey V. Elsukov /* Check rule format */
12854a77657cSAndrey V. Elsukov int
ipfw_check_rule(struct ip_fw_rule * rule,size_t size,struct rule_check_info * ci)12864a77657cSAndrey V. Elsukov ipfw_check_rule(struct ip_fw_rule *rule, size_t size,
12877e767c79SAlexander V. Chernikov struct rule_check_info *ci)
12883b3a8eb9SGleb Smirnoff {
12897e767c79SAlexander V. Chernikov int l;
12903b3a8eb9SGleb Smirnoff
12913b3a8eb9SGleb Smirnoff if (size < sizeof(*rule)) {
12923b3a8eb9SGleb Smirnoff printf("ipfw: rule too short\n");
12933b3a8eb9SGleb Smirnoff return (EINVAL);
12943b3a8eb9SGleb Smirnoff }
12957e767c79SAlexander V. Chernikov
12967e767c79SAlexander V. Chernikov /* Check for valid cmd_len */
12977e767c79SAlexander V. Chernikov l = roundup2(RULESIZE(rule), sizeof(uint64_t));
12983b3a8eb9SGleb Smirnoff if (l != size) {
12994a77657cSAndrey V. Elsukov printf("ipfw: size mismatch (have %zu want %d)\n", size, l);
13003b3a8eb9SGleb Smirnoff return (EINVAL);
13013b3a8eb9SGleb Smirnoff }
13023b3a8eb9SGleb Smirnoff if (rule->act_ofs >= rule->cmd_len) {
13033b3a8eb9SGleb Smirnoff printf("ipfw: bogus action offset (%u > %u)\n",
13043b3a8eb9SGleb Smirnoff rule->act_ofs, rule->cmd_len - 1);
13053b3a8eb9SGleb Smirnoff return (EINVAL);
13063b3a8eb9SGleb Smirnoff }
13076c2997ffSAlexander V. Chernikov
13086c2997ffSAlexander V. Chernikov if (rule->rulenum > IPFW_DEFAULT_RULE - 1)
13096c2997ffSAlexander V. Chernikov return (EINVAL);
13106c2997ffSAlexander V. Chernikov
13117e767c79SAlexander V. Chernikov return (check_ipfw_rule_body(rule->cmd, rule->cmd_len, ci));
13127e767c79SAlexander V. Chernikov }
13137e767c79SAlexander V. Chernikov
13144a77657cSAndrey V. Elsukov enum ipfw_opcheck_result
ipfw_check_opcode(ipfw_insn ** pcmd,int * plen,struct rule_check_info * ci)13154a77657cSAndrey V. Elsukov ipfw_check_opcode(ipfw_insn **pcmd, int *plen, struct rule_check_info *ci)
13167e767c79SAlexander V. Chernikov {
13174a77657cSAndrey V. Elsukov ipfw_insn *cmd;
13184a77657cSAndrey V. Elsukov size_t cmdlen;
13197e767c79SAlexander V. Chernikov
13204a77657cSAndrey V. Elsukov cmd = *pcmd;
13213b3a8eb9SGleb Smirnoff cmdlen = F_LEN(cmd);
13224a77657cSAndrey V. Elsukov
13233b3a8eb9SGleb Smirnoff switch (cmd->opcode) {
13243b3a8eb9SGleb Smirnoff case O_PROBE_STATE:
13253b3a8eb9SGleb Smirnoff case O_KEEP_STATE:
13264a77657cSAndrey V. Elsukov if (cmdlen != F_INSN_SIZE(ipfw_insn_kidx))
13274a77657cSAndrey V. Elsukov return (BAD_SIZE);
1328ed22e564SAndrey V. Elsukov ci->object_opcodes++;
1329ed22e564SAndrey V. Elsukov break;
13303b3a8eb9SGleb Smirnoff case O_PROTO:
13313b3a8eb9SGleb Smirnoff case O_IP_SRC_ME:
13323b3a8eb9SGleb Smirnoff case O_IP_DST_ME:
13333b3a8eb9SGleb Smirnoff case O_LAYER2:
13343b3a8eb9SGleb Smirnoff case O_IN:
13353b3a8eb9SGleb Smirnoff case O_FRAG:
13363b3a8eb9SGleb Smirnoff case O_DIVERTED:
13373b3a8eb9SGleb Smirnoff case O_IPOPT:
13383b3a8eb9SGleb Smirnoff case O_IPTOS:
13393b3a8eb9SGleb Smirnoff case O_IPPRECEDENCE:
13403b3a8eb9SGleb Smirnoff case O_IPVER:
13413b3a8eb9SGleb Smirnoff case O_SOCKARG:
13423b3a8eb9SGleb Smirnoff case O_TCPFLAGS:
13433b3a8eb9SGleb Smirnoff case O_TCPOPTS:
13443b3a8eb9SGleb Smirnoff case O_ESTAB:
13453b3a8eb9SGleb Smirnoff case O_VERREVPATH:
13463b3a8eb9SGleb Smirnoff case O_VERSRCREACH:
13473b3a8eb9SGleb Smirnoff case O_ANTISPOOF:
13483b3a8eb9SGleb Smirnoff case O_IPSEC:
13493b3a8eb9SGleb Smirnoff #ifdef INET6
13503b3a8eb9SGleb Smirnoff case O_IP6_SRC_ME:
13513b3a8eb9SGleb Smirnoff case O_IP6_DST_ME:
13523b3a8eb9SGleb Smirnoff case O_EXT_HDR:
13533b3a8eb9SGleb Smirnoff case O_IP6:
13543b3a8eb9SGleb Smirnoff #endif
13553b3a8eb9SGleb Smirnoff case O_IP4:
13563b3a8eb9SGleb Smirnoff case O_TAG:
1357f7c4fdeeSAndrey V. Elsukov case O_SKIP_ACTION:
13583b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn))
13594a77657cSAndrey V. Elsukov return (BAD_SIZE);
13603b3a8eb9SGleb Smirnoff break;
13613b3a8eb9SGleb Smirnoff
13622acdf79fSAndrey V. Elsukov case O_EXTERNAL_ACTION:
13634a77657cSAndrey V. Elsukov if (cmdlen != F_INSN_SIZE(ipfw_insn_kidx))
13644a77657cSAndrey V. Elsukov return (BAD_SIZE);
13654a77657cSAndrey V. Elsukov
13664a77657cSAndrey V. Elsukov if (insntod(cmd, kidx)->kidx == 0)
13674a77657cSAndrey V. Elsukov return (FAILED);
13682acdf79fSAndrey V. Elsukov ci->object_opcodes++;
136911c56650SAndrey V. Elsukov /*
137011c56650SAndrey V. Elsukov * Do we have O_EXTERNAL_INSTANCE or O_EXTERNAL_DATA
137111c56650SAndrey V. Elsukov * opcode?
137211c56650SAndrey V. Elsukov */
13734a77657cSAndrey V. Elsukov if (*plen != cmdlen) {
13744a77657cSAndrey V. Elsukov *plen -= cmdlen;
13752acdf79fSAndrey V. Elsukov cmd += cmdlen;
13764a77657cSAndrey V. Elsukov *pcmd = cmd;
13772acdf79fSAndrey V. Elsukov cmdlen = F_LEN(cmd);
137811c56650SAndrey V. Elsukov if (cmd->opcode == O_EXTERNAL_DATA)
13794a77657cSAndrey V. Elsukov return (CHECK_ACTION);
13802acdf79fSAndrey V. Elsukov if (cmd->opcode != O_EXTERNAL_INSTANCE) {
13812acdf79fSAndrey V. Elsukov printf("ipfw: invalid opcode "
13822acdf79fSAndrey V. Elsukov "next to external action %u\n",
13832acdf79fSAndrey V. Elsukov cmd->opcode);
13844a77657cSAndrey V. Elsukov return (FAILED);
13852acdf79fSAndrey V. Elsukov }
13864a77657cSAndrey V. Elsukov if (cmdlen != F_INSN_SIZE(ipfw_insn_kidx))
13874a77657cSAndrey V. Elsukov return (BAD_SIZE);
13884a77657cSAndrey V. Elsukov if (insntod(cmd, kidx)->kidx == 0)
13894a77657cSAndrey V. Elsukov return (FAILED);
13902acdf79fSAndrey V. Elsukov ci->object_opcodes++;
13912acdf79fSAndrey V. Elsukov }
13924a77657cSAndrey V. Elsukov return (CHECK_ACTION);
13932acdf79fSAndrey V. Elsukov
13943b3a8eb9SGleb Smirnoff case O_FIB:
13953b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn))
13964a77657cSAndrey V. Elsukov return (BAD_SIZE);
13973b3a8eb9SGleb Smirnoff if (cmd->arg1 >= rt_numfibs) {
13983b3a8eb9SGleb Smirnoff printf("ipfw: invalid fib number %d\n",
13993b3a8eb9SGleb Smirnoff cmd->arg1);
14004a77657cSAndrey V. Elsukov return (FAILED);
14013b3a8eb9SGleb Smirnoff }
14023b3a8eb9SGleb Smirnoff break;
14033b3a8eb9SGleb Smirnoff
14043b3a8eb9SGleb Smirnoff case O_SETFIB:
14053b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn))
14064a77657cSAndrey V. Elsukov return (BAD_SIZE);
14071940fa77SAlexander V. Chernikov if ((cmd->arg1 != IP_FW_TARG) &&
1408b554a278SAlexander V. Chernikov ((cmd->arg1 & 0x7FFF) >= rt_numfibs)) {
14093b3a8eb9SGleb Smirnoff printf("ipfw: invalid fib number %d\n",
1410b554a278SAlexander V. Chernikov cmd->arg1 & 0x7FFF);
14114a77657cSAndrey V. Elsukov return (FAILED);
14123b3a8eb9SGleb Smirnoff }
14134a77657cSAndrey V. Elsukov return (CHECK_ACTION);
14143b3a8eb9SGleb Smirnoff
14153b3a8eb9SGleb Smirnoff case O_UID:
14163b3a8eb9SGleb Smirnoff case O_GID:
14173b3a8eb9SGleb Smirnoff case O_JAIL:
14183b3a8eb9SGleb Smirnoff case O_IP_SRC:
14193b3a8eb9SGleb Smirnoff case O_IP_DST:
14203b3a8eb9SGleb Smirnoff case O_TCPSEQ:
14213b3a8eb9SGleb Smirnoff case O_TCPACK:
14223b3a8eb9SGleb Smirnoff case O_PROB:
14233b3a8eb9SGleb Smirnoff case O_ICMPTYPE:
14243b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn_u32))
14254a77657cSAndrey V. Elsukov return (BAD_SIZE);
14263b3a8eb9SGleb Smirnoff break;
14273b3a8eb9SGleb Smirnoff
14283b3a8eb9SGleb Smirnoff case O_LIMIT:
14293b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn_limit))
14304a77657cSAndrey V. Elsukov return (BAD_SIZE);
1431ed22e564SAndrey V. Elsukov ci->object_opcodes++;
14323b3a8eb9SGleb Smirnoff break;
14333b3a8eb9SGleb Smirnoff
14343b3a8eb9SGleb Smirnoff case O_LOG:
14353b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn_log))
14364a77657cSAndrey V. Elsukov return (BAD_SIZE);
14374a77657cSAndrey V. Elsukov insntod(cmd, log)->log_left = insntod(cmd, log)->max_log;
14383b3a8eb9SGleb Smirnoff break;
14393b3a8eb9SGleb Smirnoff
14403b3a8eb9SGleb Smirnoff case O_IP_SRC_MASK:
14413b3a8eb9SGleb Smirnoff case O_IP_DST_MASK:
14423b3a8eb9SGleb Smirnoff /* only odd command lengths */
1443c6fb65b1SAlexander V. Chernikov if ((cmdlen & 1) == 0)
14444a77657cSAndrey V. Elsukov return (BAD_SIZE);
14453b3a8eb9SGleb Smirnoff break;
14463b3a8eb9SGleb Smirnoff
14473b3a8eb9SGleb Smirnoff case O_IP_SRC_SET:
14483b3a8eb9SGleb Smirnoff case O_IP_DST_SET:
14493b3a8eb9SGleb Smirnoff if (cmd->arg1 == 0 || cmd->arg1 > 256) {
14503b3a8eb9SGleb Smirnoff printf("ipfw: invalid set size %d\n",
14513b3a8eb9SGleb Smirnoff cmd->arg1);
14524a77657cSAndrey V. Elsukov return (FAILED);
14533b3a8eb9SGleb Smirnoff }
14543b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) +
14553b3a8eb9SGleb Smirnoff (cmd->arg1+31)/32 )
14564a77657cSAndrey V. Elsukov return (BAD_SIZE);
14573b3a8eb9SGleb Smirnoff break;
14583b3a8eb9SGleb Smirnoff
14593b3a8eb9SGleb Smirnoff case O_IP_SRC_LOOKUP:
14603b3a8eb9SGleb Smirnoff case O_IP_DST_LOOKUP:
1461914bffb6SAlexander V. Chernikov case O_IP_FLOW_LOOKUP:
146281cac390SArseny Smalyuk case O_MAC_SRC_LOOKUP:
14634a77657cSAndrey V. Elsukov case O_MAC_DST_LOOKUP:
14644a77657cSAndrey V. Elsukov if (cmdlen != F_INSN_SIZE(ipfw_insn_kidx) &&
14654a77657cSAndrey V. Elsukov cmdlen != F_INSN_SIZE(ipfw_insn_table))
14664a77657cSAndrey V. Elsukov return (BAD_SIZE);
14674a77657cSAndrey V. Elsukov if (insntod(cmd, kidx)->kidx >= V_fw_tables_max) {
14684a77657cSAndrey V. Elsukov printf("ipfw: invalid table index %u\n",
14694a77657cSAndrey V. Elsukov insntod(cmd, kidx)->kidx);
14704a77657cSAndrey V. Elsukov return (FAILED);
1471914bffb6SAlexander V. Chernikov }
147274b22066SAlexander V. Chernikov ci->object_opcodes++;
1473914bffb6SAlexander V. Chernikov break;
14743b3a8eb9SGleb Smirnoff case O_MACADDR2:
14753b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn_mac))
14764a77657cSAndrey V. Elsukov return (BAD_SIZE);
14773b3a8eb9SGleb Smirnoff break;
14783b3a8eb9SGleb Smirnoff
14793b3a8eb9SGleb Smirnoff case O_NOP:
14803b3a8eb9SGleb Smirnoff case O_IPID:
14813b3a8eb9SGleb Smirnoff case O_IPTTL:
14823b3a8eb9SGleb Smirnoff case O_IPLEN:
14833b3a8eb9SGleb Smirnoff case O_TCPDATALEN:
1484978f2d17SAndrey V. Elsukov case O_TCPMSS:
14853b3a8eb9SGleb Smirnoff case O_TCPWIN:
14863b3a8eb9SGleb Smirnoff case O_TAGGED:
14873b3a8eb9SGleb Smirnoff if (cmdlen < 1 || cmdlen > 31)
14884a77657cSAndrey V. Elsukov return (BAD_SIZE);
14893b3a8eb9SGleb Smirnoff break;
14903b3a8eb9SGleb Smirnoff
14914037b828SAlexander V. Chernikov case O_DSCP:
1492fc727ad6SBoris Lytochkin case O_MARK:
14934037b828SAlexander V. Chernikov if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 1)
14944a77657cSAndrey V. Elsukov return (BAD_SIZE);
14954037b828SAlexander V. Chernikov break;
14964037b828SAlexander V. Chernikov
14973b3a8eb9SGleb Smirnoff case O_MAC_TYPE:
14983b3a8eb9SGleb Smirnoff case O_IP_SRCPORT:
14993b3a8eb9SGleb Smirnoff case O_IP_DSTPORT: /* XXX artificial limit, 30 port pairs */
15003b3a8eb9SGleb Smirnoff if (cmdlen < 2 || cmdlen > 31)
15014a77657cSAndrey V. Elsukov return (BAD_SIZE);
15023b3a8eb9SGleb Smirnoff break;
15033b3a8eb9SGleb Smirnoff
15043b3a8eb9SGleb Smirnoff case O_RECV:
15053b3a8eb9SGleb Smirnoff case O_XMIT:
15063b3a8eb9SGleb Smirnoff case O_VIA:
15073b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn_if))
15084a77657cSAndrey V. Elsukov return (BAD_SIZE);
1509748c9559SAndrey V. Elsukov ci->object_opcodes++;
15103b3a8eb9SGleb Smirnoff break;
15113b3a8eb9SGleb Smirnoff
15123b3a8eb9SGleb Smirnoff case O_ALTQ:
15133b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn_altq))
15144a77657cSAndrey V. Elsukov return (BAD_SIZE);
15153b3a8eb9SGleb Smirnoff break;
15163b3a8eb9SGleb Smirnoff
15173b3a8eb9SGleb Smirnoff case O_PIPE:
15183b3a8eb9SGleb Smirnoff case O_QUEUE:
15193b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn))
15204a77657cSAndrey V. Elsukov return (BAD_SIZE);
15214a77657cSAndrey V. Elsukov return (CHECK_ACTION);
15223b3a8eb9SGleb Smirnoff
15233b3a8eb9SGleb Smirnoff case O_FORWARD_IP:
15243b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn_sa))
15254a77657cSAndrey V. Elsukov return (BAD_SIZE);
15264a77657cSAndrey V. Elsukov return (CHECK_ACTION);
15273b3a8eb9SGleb Smirnoff #ifdef INET6
15283b3a8eb9SGleb Smirnoff case O_FORWARD_IP6:
15293b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn_sa6))
15304a77657cSAndrey V. Elsukov return (BAD_SIZE);
15314a77657cSAndrey V. Elsukov return (CHECK_ACTION);
15323b3a8eb9SGleb Smirnoff #endif /* INET6 */
15333b3a8eb9SGleb Smirnoff
15343b3a8eb9SGleb Smirnoff case O_DIVERT:
15353b3a8eb9SGleb Smirnoff case O_TEE:
15363b3a8eb9SGleb Smirnoff if (ip_divert_ptr == NULL)
15374a77657cSAndrey V. Elsukov return (FAILED);
15384a77657cSAndrey V. Elsukov if (cmdlen != F_INSN_SIZE(ipfw_insn))
15394a77657cSAndrey V. Elsukov return (BAD_SIZE);
15404a77657cSAndrey V. Elsukov return (CHECK_ACTION);
15413b3a8eb9SGleb Smirnoff case O_NETGRAPH:
15423b3a8eb9SGleb Smirnoff case O_NGTEE:
15433b3a8eb9SGleb Smirnoff if (ng_ipfw_input_p == NULL)
15444a77657cSAndrey V. Elsukov return (FAILED);
15454a77657cSAndrey V. Elsukov if (cmdlen != F_INSN_SIZE(ipfw_insn))
15464a77657cSAndrey V. Elsukov return (BAD_SIZE);
15474a77657cSAndrey V. Elsukov return (CHECK_ACTION);
15483b3a8eb9SGleb Smirnoff case O_NAT:
15493b3a8eb9SGleb Smirnoff if (!IPFW_NAT_LOADED)
15504a77657cSAndrey V. Elsukov return (FAILED);
15513b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn_nat))
15524a77657cSAndrey V. Elsukov return (BAD_SIZE);
15534a77657cSAndrey V. Elsukov return (CHECK_ACTION);
15544a77657cSAndrey V. Elsukov
15554a77657cSAndrey V. Elsukov case O_SKIPTO:
15564a77657cSAndrey V. Elsukov case O_CALLRETURN:
1557fc727ad6SBoris Lytochkin case O_SETMARK:
1558fc727ad6SBoris Lytochkin if (cmdlen != F_INSN_SIZE(ipfw_insn_u32))
15594a77657cSAndrey V. Elsukov return (BAD_SIZE);
15604a77657cSAndrey V. Elsukov return (CHECK_ACTION);
15614a77657cSAndrey V. Elsukov
15624a77657cSAndrey V. Elsukov case O_CHECK_STATE:
15634a77657cSAndrey V. Elsukov if (cmdlen != F_INSN_SIZE(ipfw_insn_kidx))
15644a77657cSAndrey V. Elsukov return (BAD_SIZE);
15654a77657cSAndrey V. Elsukov ci->object_opcodes++;
15664a77657cSAndrey V. Elsukov return (CHECK_ACTION);
15674a77657cSAndrey V. Elsukov
1568ed22e564SAndrey V. Elsukov case O_FORWARD_MAC: /* XXX not implemented yet */
15693b3a8eb9SGleb Smirnoff case O_COUNT:
15703b3a8eb9SGleb Smirnoff case O_ACCEPT:
15713b3a8eb9SGleb Smirnoff case O_DENY:
15724a77657cSAndrey V. Elsukov case O_REJECT:
1573ae01d73cSAlexander V. Chernikov case O_SETDSCP:
15743b3a8eb9SGleb Smirnoff #ifdef INET6
15753b3a8eb9SGleb Smirnoff case O_UNREACH6:
15763b3a8eb9SGleb Smirnoff #endif
15773b3a8eb9SGleb Smirnoff case O_REASS:
15783b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn))
15794a77657cSAndrey V. Elsukov return (BAD_SIZE);
15804a77657cSAndrey V. Elsukov return (CHECK_ACTION);
15813b3a8eb9SGleb Smirnoff #ifdef INET6
15823b3a8eb9SGleb Smirnoff case O_IP6_SRC:
15833b3a8eb9SGleb Smirnoff case O_IP6_DST:
15843b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(struct in6_addr) +
15853b3a8eb9SGleb Smirnoff F_INSN_SIZE(ipfw_insn))
15864a77657cSAndrey V. Elsukov return (BAD_SIZE);
15873b3a8eb9SGleb Smirnoff break;
15883b3a8eb9SGleb Smirnoff
15893b3a8eb9SGleb Smirnoff case O_FLOW6ID:
15903b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) +
15913b3a8eb9SGleb Smirnoff ((ipfw_insn_u32 *)cmd)->o.arg1)
15924a77657cSAndrey V. Elsukov return (BAD_SIZE);
15933b3a8eb9SGleb Smirnoff break;
15943b3a8eb9SGleb Smirnoff
15953b3a8eb9SGleb Smirnoff case O_IP6_SRC_MASK:
15963b3a8eb9SGleb Smirnoff case O_IP6_DST_MASK:
15973b3a8eb9SGleb Smirnoff if ( !(cmdlen & 1) || cmdlen > 127)
15984a77657cSAndrey V. Elsukov return (BAD_SIZE);
15993b3a8eb9SGleb Smirnoff break;
16003b3a8eb9SGleb Smirnoff case O_ICMP6TYPE:
16013b3a8eb9SGleb Smirnoff if( cmdlen != F_INSN_SIZE( ipfw_insn_icmp6 ) )
16024a77657cSAndrey V. Elsukov return (BAD_SIZE);
16033b3a8eb9SGleb Smirnoff break;
16043b3a8eb9SGleb Smirnoff #endif
16053b3a8eb9SGleb Smirnoff
16063b3a8eb9SGleb Smirnoff default:
16073b3a8eb9SGleb Smirnoff switch (cmd->opcode) {
16083b3a8eb9SGleb Smirnoff #ifndef INET6
16093b3a8eb9SGleb Smirnoff case O_IP6_SRC_ME:
16103b3a8eb9SGleb Smirnoff case O_IP6_DST_ME:
16113b3a8eb9SGleb Smirnoff case O_EXT_HDR:
16123b3a8eb9SGleb Smirnoff case O_IP6:
16133b3a8eb9SGleb Smirnoff case O_UNREACH6:
16143b3a8eb9SGleb Smirnoff case O_IP6_SRC:
16153b3a8eb9SGleb Smirnoff case O_IP6_DST:
16163b3a8eb9SGleb Smirnoff case O_FLOW6ID:
16173b3a8eb9SGleb Smirnoff case O_IP6_SRC_MASK:
16183b3a8eb9SGleb Smirnoff case O_IP6_DST_MASK:
16193b3a8eb9SGleb Smirnoff case O_ICMP6TYPE:
16203b3a8eb9SGleb Smirnoff printf("ipfw: no IPv6 support in kernel\n");
16214a77657cSAndrey V. Elsukov return (FAILED);
16223b3a8eb9SGleb Smirnoff #endif
16233b3a8eb9SGleb Smirnoff default:
16244a77657cSAndrey V. Elsukov printf("ipfw: opcode %d: unknown opcode\n",
16254a77657cSAndrey V. Elsukov cmd->opcode);
16264a77657cSAndrey V. Elsukov return (FAILED);
16274a77657cSAndrey V. Elsukov }
16284a77657cSAndrey V. Elsukov }
16294a77657cSAndrey V. Elsukov return (SUCCESS);
16304a77657cSAndrey V. Elsukov }
16314a77657cSAndrey V. Elsukov
16324a77657cSAndrey V. Elsukov static __noinline int
check_ipfw_rule_body(ipfw_insn * cmd,int cmd_len,struct rule_check_info * ci)16334a77657cSAndrey V. Elsukov check_ipfw_rule_body(ipfw_insn *cmd, int cmd_len, struct rule_check_info *ci)
16344a77657cSAndrey V. Elsukov {
16354a77657cSAndrey V. Elsukov int cmdlen, l;
16364a77657cSAndrey V. Elsukov int have_action, ret;
16374a77657cSAndrey V. Elsukov
16384a77657cSAndrey V. Elsukov /*
16394a77657cSAndrey V. Elsukov * Now go for the individual checks. Very simple ones, basically only
16404a77657cSAndrey V. Elsukov * instruction sizes.
16414a77657cSAndrey V. Elsukov */
16424a77657cSAndrey V. Elsukov have_action = 0;
16434a77657cSAndrey V. Elsukov for (l = cmd_len; l > 0 ; l -= cmdlen, cmd += cmdlen) {
16444a77657cSAndrey V. Elsukov cmdlen = F_LEN(cmd);
16454a77657cSAndrey V. Elsukov if (cmdlen > l) {
16464a77657cSAndrey V. Elsukov printf("ipfw: opcode %d: size truncated\n",
16473b3a8eb9SGleb Smirnoff cmd->opcode);
16487e767c79SAlexander V. Chernikov return (EINVAL);
16493b3a8eb9SGleb Smirnoff }
16504a77657cSAndrey V. Elsukov if (ci->version != IP_FW3_OPVER)
16514a77657cSAndrey V. Elsukov ret = (*check_opcode_f)(&cmd, &l, ci);
16524a77657cSAndrey V. Elsukov else
16534a77657cSAndrey V. Elsukov ret = ipfw_check_opcode(&cmd, &l, ci);
16544a77657cSAndrey V. Elsukov
16554a77657cSAndrey V. Elsukov if (ret == CHECK_ACTION) {
16564a77657cSAndrey V. Elsukov if (have_action != 0) {
16574a77657cSAndrey V. Elsukov printf("ipfw: opcode %d: multiple actions"
16584a77657cSAndrey V. Elsukov " not allowed\n", cmd->opcode);
16594a77657cSAndrey V. Elsukov ret = FAILED;
16604a77657cSAndrey V. Elsukov } else
16614a77657cSAndrey V. Elsukov have_action = 1;
16624a77657cSAndrey V. Elsukov
16634a77657cSAndrey V. Elsukov if (l != F_LEN(cmd)) {
16644a77657cSAndrey V. Elsukov printf("ipfw: opcode %d: action must be"
16654a77657cSAndrey V. Elsukov " last opcode\n", cmd->opcode);
16664a77657cSAndrey V. Elsukov ret = FAILED;
16674a77657cSAndrey V. Elsukov }
16684a77657cSAndrey V. Elsukov }
16694a77657cSAndrey V. Elsukov switch (ret) {
16704a77657cSAndrey V. Elsukov case SUCCESS:
16714a77657cSAndrey V. Elsukov continue;
16724a77657cSAndrey V. Elsukov case BAD_SIZE:
16734a77657cSAndrey V. Elsukov printf("ipfw: opcode %d: wrong size %d\n",
16744a77657cSAndrey V. Elsukov cmd->opcode, cmdlen);
16754a77657cSAndrey V. Elsukov /* FALLTHROUGH */
16764a77657cSAndrey V. Elsukov case FAILED:
16774a77657cSAndrey V. Elsukov return (EINVAL);
16783b3a8eb9SGleb Smirnoff }
16793b3a8eb9SGleb Smirnoff }
16803b3a8eb9SGleb Smirnoff if (have_action == 0) {
16813b3a8eb9SGleb Smirnoff printf("ipfw: missing action\n");
16827e767c79SAlexander V. Chernikov return (EINVAL);
16833b3a8eb9SGleb Smirnoff }
1684b074b7bbSAlexander V. Chernikov return (0);
16853b3a8eb9SGleb Smirnoff }
16863b3a8eb9SGleb Smirnoff
1687563b5ab1SAlexander V. Chernikov struct dump_args {
1688563b5ab1SAlexander V. Chernikov uint32_t b; /* start rule */
1689563b5ab1SAlexander V. Chernikov uint32_t e; /* end rule */
1690563b5ab1SAlexander V. Chernikov uint32_t rcount; /* number of rules */
1691563b5ab1SAlexander V. Chernikov uint32_t rsize; /* rules size */
1692563b5ab1SAlexander V. Chernikov uint32_t tcount; /* number of tables */
16937e767c79SAlexander V. Chernikov int rcounters; /* counters */
1694cefe3d67SAndrey V. Elsukov uint32_t *bmask; /* index bitmask of used named objects */
1695563b5ab1SAlexander V. Chernikov };
1696563b5ab1SAlexander V. Chernikov
16975dc5a0e0SAndrey V. Elsukov void
ipfw_export_obj_ntlv(struct named_object * no,ipfw_obj_ntlv * ntlv)16985dc5a0e0SAndrey V. Elsukov ipfw_export_obj_ntlv(struct named_object *no, ipfw_obj_ntlv *ntlv)
16995dc5a0e0SAndrey V. Elsukov {
17005dc5a0e0SAndrey V. Elsukov
17015dc5a0e0SAndrey V. Elsukov ntlv->head.type = no->etlv;
17025dc5a0e0SAndrey V. Elsukov ntlv->head.length = sizeof(*ntlv);
17035dc5a0e0SAndrey V. Elsukov ntlv->idx = no->kidx;
17045dc5a0e0SAndrey V. Elsukov strlcpy(ntlv->name, no->name, sizeof(ntlv->name));
17055dc5a0e0SAndrey V. Elsukov }
17065dc5a0e0SAndrey V. Elsukov
1707563b5ab1SAlexander V. Chernikov /*
170874b22066SAlexander V. Chernikov * Export named object info in instance @ni, identified by @kidx
170974b22066SAlexander V. Chernikov * to ipfw_obj_ntlv. TLV is allocated from @sd space.
171074b22066SAlexander V. Chernikov *
171174b22066SAlexander V. Chernikov * Returns 0 on success.
171274b22066SAlexander V. Chernikov */
171374b22066SAlexander V. Chernikov static int
export_objhash_ntlv(struct namedobj_instance * ni,uint32_t kidx,struct sockopt_data * sd)17144a77657cSAndrey V. Elsukov export_objhash_ntlv(struct namedobj_instance *ni, uint32_t kidx,
171574b22066SAlexander V. Chernikov struct sockopt_data *sd)
171674b22066SAlexander V. Chernikov {
171774b22066SAlexander V. Chernikov struct named_object *no;
171874b22066SAlexander V. Chernikov ipfw_obj_ntlv *ntlv;
171974b22066SAlexander V. Chernikov
172074b22066SAlexander V. Chernikov no = ipfw_objhash_lookup_kidx(ni, kidx);
172174b22066SAlexander V. Chernikov KASSERT(no != NULL, ("invalid object kernel index passed"));
172274b22066SAlexander V. Chernikov
172374b22066SAlexander V. Chernikov ntlv = (ipfw_obj_ntlv *)ipfw_get_sopt_space(sd, sizeof(*ntlv));
172474b22066SAlexander V. Chernikov if (ntlv == NULL)
172574b22066SAlexander V. Chernikov return (ENOMEM);
172674b22066SAlexander V. Chernikov
17275dc5a0e0SAndrey V. Elsukov ipfw_export_obj_ntlv(no, ntlv);
172874b22066SAlexander V. Chernikov return (0);
172974b22066SAlexander V. Chernikov }
173074b22066SAlexander V. Chernikov
1731563b5ab1SAlexander V. Chernikov static int
export_named_objects(struct namedobj_instance * ni,struct dump_args * da,struct sockopt_data * sd)1732cefe3d67SAndrey V. Elsukov export_named_objects(struct namedobj_instance *ni, struct dump_args *da,
1733cefe3d67SAndrey V. Elsukov struct sockopt_data *sd)
1734563b5ab1SAlexander V. Chernikov {
17354a77657cSAndrey V. Elsukov uint32_t i;
17364a77657cSAndrey V. Elsukov int error;
1737563b5ab1SAlexander V. Chernikov
1738cefe3d67SAndrey V. Elsukov for (i = 0; i < IPFW_TABLES_MAX && da->tcount > 0; i++) {
1739cefe3d67SAndrey V. Elsukov if ((da->bmask[i / 32] & (1 << (i % 32))) == 0)
1740cefe3d67SAndrey V. Elsukov continue;
1741cefe3d67SAndrey V. Elsukov if ((error = export_objhash_ntlv(ni, i, sd)) != 0)
1742cefe3d67SAndrey V. Elsukov return (error);
1743cefe3d67SAndrey V. Elsukov da->tcount--;
1744cefe3d67SAndrey V. Elsukov }
1745cefe3d67SAndrey V. Elsukov return (0);
1746cefe3d67SAndrey V. Elsukov }
1747cefe3d67SAndrey V. Elsukov
1748cefe3d67SAndrey V. Elsukov static int
dump_named_objects(struct ip_fw_chain * ch,struct dump_args * da,struct sockopt_data * sd)1749cefe3d67SAndrey V. Elsukov dump_named_objects(struct ip_fw_chain *ch, struct dump_args *da,
1750cefe3d67SAndrey V. Elsukov struct sockopt_data *sd)
1751cefe3d67SAndrey V. Elsukov {
1752cefe3d67SAndrey V. Elsukov ipfw_obj_ctlv *ctlv;
1753cefe3d67SAndrey V. Elsukov int error;
1754cefe3d67SAndrey V. Elsukov
1755cefe3d67SAndrey V. Elsukov MPASS(da->tcount > 0);
1756563b5ab1SAlexander V. Chernikov /* Header first */
1757563b5ab1SAlexander V. Chernikov ctlv = (ipfw_obj_ctlv *)ipfw_get_sopt_space(sd, sizeof(*ctlv));
1758563b5ab1SAlexander V. Chernikov if (ctlv == NULL)
1759563b5ab1SAlexander V. Chernikov return (ENOMEM);
1760563b5ab1SAlexander V. Chernikov ctlv->head.type = IPFW_TLV_TBLNAME_LIST;
1761563b5ab1SAlexander V. Chernikov ctlv->head.length = da->tcount * sizeof(ipfw_obj_ntlv) +
1762563b5ab1SAlexander V. Chernikov sizeof(*ctlv);
1763563b5ab1SAlexander V. Chernikov ctlv->count = da->tcount;
1764563b5ab1SAlexander V. Chernikov ctlv->objsize = sizeof(ipfw_obj_ntlv);
1765563b5ab1SAlexander V. Chernikov
1766cefe3d67SAndrey V. Elsukov /* Dump table names first (if any) */
1767cefe3d67SAndrey V. Elsukov error = export_named_objects(ipfw_get_table_objhash(ch), da, sd);
1768cefe3d67SAndrey V. Elsukov if (error != 0)
1769563b5ab1SAlexander V. Chernikov return (error);
1770cefe3d67SAndrey V. Elsukov /* Then dump another named objects */
1771cefe3d67SAndrey V. Elsukov da->bmask += IPFW_TABLES_MAX / 32;
1772cefe3d67SAndrey V. Elsukov return (export_named_objects(CHAIN_TO_SRV(ch), da, sd));
1773563b5ab1SAlexander V. Chernikov }
1774563b5ab1SAlexander V. Chernikov
1775cefe3d67SAndrey V. Elsukov /*
1776cefe3d67SAndrey V. Elsukov * Dumps static rules with table TLVs in buffer @sd.
1777cefe3d67SAndrey V. Elsukov *
1778cefe3d67SAndrey V. Elsukov * Returns 0 on success.
1779cefe3d67SAndrey V. Elsukov */
1780cefe3d67SAndrey V. Elsukov static int
dump_static_rules(struct ip_fw_chain * chain,struct dump_args * da,struct sockopt_data * sd)1781cefe3d67SAndrey V. Elsukov dump_static_rules(struct ip_fw_chain *chain, struct dump_args *da,
1782cefe3d67SAndrey V. Elsukov struct sockopt_data *sd)
1783cefe3d67SAndrey V. Elsukov {
1784cefe3d67SAndrey V. Elsukov ipfw_obj_ctlv *ctlv;
1785cefe3d67SAndrey V. Elsukov struct ip_fw *krule;
1786cefe3d67SAndrey V. Elsukov caddr_t dst;
1787cefe3d67SAndrey V. Elsukov int i, l;
1788cefe3d67SAndrey V. Elsukov
1789563b5ab1SAlexander V. Chernikov /* Dump rules */
1790563b5ab1SAlexander V. Chernikov ctlv = (ipfw_obj_ctlv *)ipfw_get_sopt_space(sd, sizeof(*ctlv));
1791563b5ab1SAlexander V. Chernikov if (ctlv == NULL)
1792563b5ab1SAlexander V. Chernikov return (ENOMEM);
1793563b5ab1SAlexander V. Chernikov ctlv->head.type = IPFW_TLV_RULE_LIST;
1794563b5ab1SAlexander V. Chernikov ctlv->head.length = da->rsize + sizeof(*ctlv);
1795563b5ab1SAlexander V. Chernikov ctlv->count = da->rcount;
1796563b5ab1SAlexander V. Chernikov
1797563b5ab1SAlexander V. Chernikov for (i = da->b; i < da->e; i++) {
17987e767c79SAlexander V. Chernikov krule = chain->map[i];
1799563b5ab1SAlexander V. Chernikov
18007e767c79SAlexander V. Chernikov l = RULEUSIZE1(krule) + sizeof(ipfw_obj_tlv);
18017e767c79SAlexander V. Chernikov if (da->rcounters != 0)
18027e767c79SAlexander V. Chernikov l += sizeof(struct ip_fw_bcounter);
18037e767c79SAlexander V. Chernikov dst = (caddr_t)ipfw_get_sopt_space(sd, l);
18047e767c79SAlexander V. Chernikov if (dst == NULL)
1805563b5ab1SAlexander V. Chernikov return (ENOMEM);
1806563b5ab1SAlexander V. Chernikov
18077e767c79SAlexander V. Chernikov export_rule1(krule, dst, l, da->rcounters);
1808563b5ab1SAlexander V. Chernikov }
1809563b5ab1SAlexander V. Chernikov
1810563b5ab1SAlexander V. Chernikov return (0);
1811563b5ab1SAlexander V. Chernikov }
1812563b5ab1SAlexander V. Chernikov
1813cefe3d67SAndrey V. Elsukov int
ipfw_mark_object_kidx(uint32_t * bmask,uint16_t etlv,uint32_t kidx)18144a77657cSAndrey V. Elsukov ipfw_mark_object_kidx(uint32_t *bmask, uint16_t etlv, uint32_t kidx)
1815cefe3d67SAndrey V. Elsukov {
1816cefe3d67SAndrey V. Elsukov uint32_t bidx;
1817cefe3d67SAndrey V. Elsukov
1818cefe3d67SAndrey V. Elsukov /*
1819cefe3d67SAndrey V. Elsukov * Maintain separate bitmasks for table and non-table objects.
1820cefe3d67SAndrey V. Elsukov */
1821cefe3d67SAndrey V. Elsukov bidx = (etlv == IPFW_TLV_TBL_NAME) ? 0: IPFW_TABLES_MAX / 32;
1822cefe3d67SAndrey V. Elsukov bidx += kidx / 32;
1823cefe3d67SAndrey V. Elsukov if ((bmask[bidx] & (1 << (kidx % 32))) != 0)
1824cefe3d67SAndrey V. Elsukov return (0);
1825cefe3d67SAndrey V. Elsukov
1826cefe3d67SAndrey V. Elsukov bmask[bidx] |= 1 << (kidx % 32);
1827cefe3d67SAndrey V. Elsukov return (1);
1828cefe3d67SAndrey V. Elsukov }
1829cefe3d67SAndrey V. Elsukov
1830563b5ab1SAlexander V. Chernikov /*
183174b22066SAlexander V. Chernikov * Marks every object index used in @rule with bit in @bmask.
183274b22066SAlexander V. Chernikov * Used to generate bitmask of referenced tables/objects for given ruleset
183374b22066SAlexander V. Chernikov * or its part.
183474b22066SAlexander V. Chernikov */
1835cefe3d67SAndrey V. Elsukov static void
mark_rule_objects(struct ip_fw_chain * ch,struct ip_fw * rule,struct dump_args * da)1836cefe3d67SAndrey V. Elsukov mark_rule_objects(struct ip_fw_chain *ch, struct ip_fw *rule,
1837cefe3d67SAndrey V. Elsukov struct dump_args *da)
183874b22066SAlexander V. Chernikov {
183974b22066SAlexander V. Chernikov struct opcode_obj_rewrite *rw;
1840b2df1f7eSAndrey V. Elsukov ipfw_insn *cmd;
18414a77657cSAndrey V. Elsukov uint32_t kidx;
1842cefe3d67SAndrey V. Elsukov int cmdlen, l;
184374b22066SAlexander V. Chernikov uint8_t subtype;
184474b22066SAlexander V. Chernikov
184574b22066SAlexander V. Chernikov l = rule->cmd_len;
184674b22066SAlexander V. Chernikov cmd = rule->cmd;
184774b22066SAlexander V. Chernikov cmdlen = 0;
184874b22066SAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) {
184974b22066SAlexander V. Chernikov cmdlen = F_LEN(cmd);
185074b22066SAlexander V. Chernikov
1851b2df1f7eSAndrey V. Elsukov rw = find_op_rw(cmd, &kidx, &subtype);
185274b22066SAlexander V. Chernikov if (rw == NULL)
185374b22066SAlexander V. Chernikov continue;
185474b22066SAlexander V. Chernikov
1855cefe3d67SAndrey V. Elsukov if (ipfw_mark_object_kidx(da->bmask, rw->etlv, kidx))
1856cefe3d67SAndrey V. Elsukov da->tcount++;
185774b22066SAlexander V. Chernikov }
185874b22066SAlexander V. Chernikov }
185974b22066SAlexander V. Chernikov
186074b22066SAlexander V. Chernikov /*
1861563b5ab1SAlexander V. Chernikov * Dumps requested objects data
1862563b5ab1SAlexander V. Chernikov * Data layout (version 0)(current):
1863563b5ab1SAlexander V. Chernikov * Request: [ ipfw_cfg_lheader ] + IPFW_CFG_GET_* flags
1864563b5ab1SAlexander V. Chernikov * size = ipfw_cfg_lheader.size
1865ce575f53SAlexander V. Chernikov * Reply: [ ipfw_cfg_lheader
1866563b5ab1SAlexander V. Chernikov * [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional)
18677e767c79SAlexander V. Chernikov * [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST)
18687e767c79SAlexander V. Chernikov * ipfw_obj_tlv(IPFW_TLV_RULE_ENT) [ ip_fw_bcounter (optional) ip_fw_rule ]
18697e767c79SAlexander V. Chernikov * ] (optional)
18707e767c79SAlexander V. Chernikov * [ ipfw_obj_ctlv(IPFW_TLV_STATE_LIST) ipfw_obj_dyntlv x N ] (optional)
1871563b5ab1SAlexander V. Chernikov * ]
1872563b5ab1SAlexander V. Chernikov * * NOTE IPFW_TLV_STATE_LIST has the single valid field: objsize.
1873563b5ab1SAlexander V. Chernikov * The rest (size, count) are set to zero and needs to be ignored.
1874563b5ab1SAlexander V. Chernikov *
1875563b5ab1SAlexander V. Chernikov * Returns 0 on success.
1876563b5ab1SAlexander V. Chernikov */
1877563b5ab1SAlexander V. Chernikov static int
dump_config(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)1878e822d936SAlexander V. Chernikov dump_config(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
1879e822d936SAlexander V. Chernikov struct sockopt_data *sd)
1880563b5ab1SAlexander V. Chernikov {
1881cefe3d67SAndrey V. Elsukov struct dump_args da;
1882563b5ab1SAlexander V. Chernikov ipfw_cfg_lheader *hdr;
1883563b5ab1SAlexander V. Chernikov struct ip_fw *rule;
188418ad4197SAlexander V. Chernikov size_t sz, rnum;
1885cefe3d67SAndrey V. Elsukov uint32_t hdr_flags, *bmask;
1886563b5ab1SAlexander V. Chernikov int error, i;
1887563b5ab1SAlexander V. Chernikov
1888563b5ab1SAlexander V. Chernikov hdr = (ipfw_cfg_lheader *)ipfw_get_sopt_header(sd, sizeof(*hdr));
1889563b5ab1SAlexander V. Chernikov if (hdr == NULL)
1890563b5ab1SAlexander V. Chernikov return (EINVAL);
1891563b5ab1SAlexander V. Chernikov
1892563b5ab1SAlexander V. Chernikov error = 0;
1893563b5ab1SAlexander V. Chernikov bmask = NULL;
1894cefe3d67SAndrey V. Elsukov memset(&da, 0, sizeof(da));
1895cefe3d67SAndrey V. Elsukov /*
1896cefe3d67SAndrey V. Elsukov * Allocate needed state.
1897cefe3d67SAndrey V. Elsukov * Note we allocate 2xspace mask, for table & srv
1898cefe3d67SAndrey V. Elsukov */
1899cefe3d67SAndrey V. Elsukov if (hdr->flags & (IPFW_CFG_GET_STATIC | IPFW_CFG_GET_STATES))
1900cefe3d67SAndrey V. Elsukov da.bmask = bmask = malloc(
1901cefe3d67SAndrey V. Elsukov sizeof(uint32_t) * IPFW_TABLES_MAX * 2 / 32, M_TEMP,
1902cefe3d67SAndrey V. Elsukov M_WAITOK | M_ZERO);
1903563b5ab1SAlexander V. Chernikov IPFW_UH_RLOCK(chain);
1904563b5ab1SAlexander V. Chernikov
1905563b5ab1SAlexander V. Chernikov /*
1906563b5ab1SAlexander V. Chernikov * STAGE 1: Determine size/count for objects in range.
1907563b5ab1SAlexander V. Chernikov * Prepare used tables bitmask.
1908563b5ab1SAlexander V. Chernikov */
1909ce575f53SAlexander V. Chernikov sz = sizeof(ipfw_cfg_lheader);
1910563b5ab1SAlexander V. Chernikov da.e = chain->n_rules;
1911563b5ab1SAlexander V. Chernikov
19122aa75134SAlexander V. Chernikov if (hdr->end_rule != 0) {
19132aa75134SAlexander V. Chernikov /* Handle custom range */
19142aa75134SAlexander V. Chernikov if ((rnum = hdr->start_rule) > IPFW_DEFAULT_RULE)
19152aa75134SAlexander V. Chernikov rnum = IPFW_DEFAULT_RULE;
19162aa75134SAlexander V. Chernikov da.b = ipfw_find_rule(chain, rnum, 0);
1917288bf455SAndrey V. Elsukov rnum = (hdr->end_rule < IPFW_DEFAULT_RULE) ?
1918288bf455SAndrey V. Elsukov hdr->end_rule + 1: IPFW_DEFAULT_RULE;
1919288bf455SAndrey V. Elsukov da.e = ipfw_find_rule(chain, rnum, UINT32_MAX) + 1;
19202aa75134SAlexander V. Chernikov }
19212aa75134SAlexander V. Chernikov
1922563b5ab1SAlexander V. Chernikov if (hdr->flags & IPFW_CFG_GET_STATIC) {
1923563b5ab1SAlexander V. Chernikov for (i = da.b; i < da.e; i++) {
1924563b5ab1SAlexander V. Chernikov rule = chain->map[i];
19257e767c79SAlexander V. Chernikov da.rsize += RULEUSIZE1(rule) + sizeof(ipfw_obj_tlv);
1926563b5ab1SAlexander V. Chernikov da.rcount++;
192774b22066SAlexander V. Chernikov /* Update bitmask of used objects for given range */
1928cefe3d67SAndrey V. Elsukov mark_rule_objects(chain, rule, &da);
1929563b5ab1SAlexander V. Chernikov }
19307e767c79SAlexander V. Chernikov /* Add counters if requested */
19317e767c79SAlexander V. Chernikov if (hdr->flags & IPFW_CFG_GET_COUNTERS) {
19327e767c79SAlexander V. Chernikov da.rsize += sizeof(struct ip_fw_bcounter) * da.rcount;
19337e767c79SAlexander V. Chernikov da.rcounters = 1;
19347e767c79SAlexander V. Chernikov }
1935cefe3d67SAndrey V. Elsukov sz += da.rsize + sizeof(ipfw_obj_ctlv);
1936cefe3d67SAndrey V. Elsukov }
1937cefe3d67SAndrey V. Elsukov
1938cefe3d67SAndrey V. Elsukov if (hdr->flags & IPFW_CFG_GET_STATES) {
1939cefe3d67SAndrey V. Elsukov sz += sizeof(ipfw_obj_ctlv) +
1940cefe3d67SAndrey V. Elsukov ipfw_dyn_get_count(bmask, &i) * sizeof(ipfw_obj_dyntlv);
1941cefe3d67SAndrey V. Elsukov da.tcount += i;
1942cefe3d67SAndrey V. Elsukov }
1943563b5ab1SAlexander V. Chernikov
1944563b5ab1SAlexander V. Chernikov if (da.tcount > 0)
1945563b5ab1SAlexander V. Chernikov sz += da.tcount * sizeof(ipfw_obj_ntlv) +
1946563b5ab1SAlexander V. Chernikov sizeof(ipfw_obj_ctlv);
194718ad4197SAlexander V. Chernikov
194818ad4197SAlexander V. Chernikov /*
194918ad4197SAlexander V. Chernikov * Fill header anyway.
195018ad4197SAlexander V. Chernikov * Note we have to save header fields to stable storage
195118ad4197SAlexander V. Chernikov * buffer inside @sd can be flushed after dumping rules
195218ad4197SAlexander V. Chernikov */
1953563b5ab1SAlexander V. Chernikov hdr->size = sz;
1954ac35ff17SAlexander V. Chernikov hdr->set_mask = ~V_set_disable;
195518ad4197SAlexander V. Chernikov hdr_flags = hdr->flags;
195618ad4197SAlexander V. Chernikov hdr = NULL;
1957563b5ab1SAlexander V. Chernikov
1958563b5ab1SAlexander V. Chernikov if (sd->valsize < sz) {
195918ad4197SAlexander V. Chernikov error = ENOMEM;
196018ad4197SAlexander V. Chernikov goto cleanup;
1961563b5ab1SAlexander V. Chernikov }
1962563b5ab1SAlexander V. Chernikov
1963563b5ab1SAlexander V. Chernikov /* STAGE2: Store actual data */
1964cefe3d67SAndrey V. Elsukov if (da.tcount > 0) {
1965cefe3d67SAndrey V. Elsukov error = dump_named_objects(chain, &da, sd);
1966cefe3d67SAndrey V. Elsukov if (error != 0)
1967cefe3d67SAndrey V. Elsukov goto cleanup;
1968cefe3d67SAndrey V. Elsukov }
1969cefe3d67SAndrey V. Elsukov
197018ad4197SAlexander V. Chernikov if (hdr_flags & IPFW_CFG_GET_STATIC) {
1971cefe3d67SAndrey V. Elsukov error = dump_static_rules(chain, &da, sd);
197218ad4197SAlexander V. Chernikov if (error != 0)
197318ad4197SAlexander V. Chernikov goto cleanup;
1974563b5ab1SAlexander V. Chernikov }
1975563b5ab1SAlexander V. Chernikov
197618ad4197SAlexander V. Chernikov if (hdr_flags & IPFW_CFG_GET_STATES)
1977563b5ab1SAlexander V. Chernikov error = ipfw_dump_states(chain, sd);
1978563b5ab1SAlexander V. Chernikov
197918ad4197SAlexander V. Chernikov cleanup:
1980563b5ab1SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain);
1981563b5ab1SAlexander V. Chernikov
1982563b5ab1SAlexander V. Chernikov if (bmask != NULL)
1983563b5ab1SAlexander V. Chernikov free(bmask, M_TEMP);
1984563b5ab1SAlexander V. Chernikov
1985563b5ab1SAlexander V. Chernikov return (error);
1986563b5ab1SAlexander V. Chernikov }
1987563b5ab1SAlexander V. Chernikov
1988f81431ccSAndrey V. Elsukov int
ipfw_check_object_name_generic(const char * name)1989f81431ccSAndrey V. Elsukov ipfw_check_object_name_generic(const char *name)
19906c2997ffSAlexander V. Chernikov {
1991f81431ccSAndrey V. Elsukov int nsize;
19926c2997ffSAlexander V. Chernikov
1993f81431ccSAndrey V. Elsukov nsize = sizeof(((ipfw_obj_ntlv *)0)->name);
1994f81431ccSAndrey V. Elsukov if (strnlen(name, nsize) == nsize)
1995f81431ccSAndrey V. Elsukov return (EINVAL);
1996f81431ccSAndrey V. Elsukov if (name[0] == '\0')
1997f81431ccSAndrey V. Elsukov return (EINVAL);
19986c2997ffSAlexander V. Chernikov return (0);
19996c2997ffSAlexander V. Chernikov }
20006c2997ffSAlexander V. Chernikov
20016c2997ffSAlexander V. Chernikov /*
200274b22066SAlexander V. Chernikov * Creates non-existent objects referenced by rule.
200374b22066SAlexander V. Chernikov *
200474b22066SAlexander V. Chernikov * Return 0 on success.
200574b22066SAlexander V. Chernikov */
200674b22066SAlexander 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)200774b22066SAlexander V. Chernikov create_objects_compat(struct ip_fw_chain *ch, ipfw_insn *cmd,
200874b22066SAlexander V. Chernikov struct obj_idx *oib, struct obj_idx *pidx, struct tid_info *ti)
200974b22066SAlexander V. Chernikov {
201074b22066SAlexander V. Chernikov struct opcode_obj_rewrite *rw;
201174b22066SAlexander V. Chernikov struct obj_idx *p;
20124a77657cSAndrey V. Elsukov uint32_t kidx;
201374b22066SAlexander V. Chernikov int error;
201474b22066SAlexander V. Chernikov
201574b22066SAlexander V. Chernikov /*
201674b22066SAlexander V. Chernikov * Compatibility stuff: do actual creation for non-existing,
201774b22066SAlexander V. Chernikov * but referenced objects.
201874b22066SAlexander V. Chernikov */
201974b22066SAlexander V. Chernikov for (p = oib; p < pidx; p++) {
202074b22066SAlexander V. Chernikov if (p->kidx != 0)
202174b22066SAlexander V. Chernikov continue;
202274b22066SAlexander V. Chernikov
202374b22066SAlexander V. Chernikov ti->uidx = p->uidx;
202474b22066SAlexander V. Chernikov ti->type = p->type;
202574b22066SAlexander V. Chernikov ti->atype = 0;
202674b22066SAlexander V. Chernikov
2027b2df1f7eSAndrey V. Elsukov rw = find_op_rw(cmd + p->off, NULL, NULL);
202874b22066SAlexander V. Chernikov KASSERT(rw != NULL, ("Unable to find handler for op %d",
202974b22066SAlexander V. Chernikov (cmd + p->off)->opcode));
203074b22066SAlexander V. Chernikov
20319a5be809SAndrey V. Elsukov if (rw->create_object == NULL)
20329a5be809SAndrey V. Elsukov error = EOPNOTSUPP;
20339a5be809SAndrey V. Elsukov else
203474b22066SAlexander V. Chernikov error = rw->create_object(ch, ti, &kidx);
203574b22066SAlexander V. Chernikov if (error == 0) {
203674b22066SAlexander V. Chernikov p->kidx = kidx;
203774b22066SAlexander V. Chernikov continue;
203874b22066SAlexander V. Chernikov }
203974b22066SAlexander V. Chernikov
204074b22066SAlexander V. Chernikov /*
204174b22066SAlexander V. Chernikov * Error happened. We have to rollback everything.
204274b22066SAlexander V. Chernikov * Drop all already acquired references.
204374b22066SAlexander V. Chernikov */
204474b22066SAlexander V. Chernikov IPFW_UH_WLOCK(ch);
204574b22066SAlexander V. Chernikov unref_oib_objects(ch, cmd, oib, pidx);
204674b22066SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch);
204774b22066SAlexander V. Chernikov
204874b22066SAlexander V. Chernikov return (error);
204974b22066SAlexander V. Chernikov }
205074b22066SAlexander V. Chernikov
2051a1bddc75SAlexander V. Chernikov return (0);
205274b22066SAlexander V. Chernikov }
205374b22066SAlexander V. Chernikov
205474b22066SAlexander V. Chernikov /*
205574b22066SAlexander V. Chernikov * Unreferences all already-referenced objects in given @cmd rule,
205674b22066SAlexander V. Chernikov * using information in @oib.
205774b22066SAlexander V. Chernikov *
205874b22066SAlexander V. Chernikov * Used to rollback partially converted rule on error.
205974b22066SAlexander V. Chernikov */
2060f976a4edSAndrey V. Elsukov static void
unref_oib_objects(struct ip_fw_chain * ch,ipfw_insn * cmd,struct obj_idx * oib,struct obj_idx * end)206174b22066SAlexander V. Chernikov unref_oib_objects(struct ip_fw_chain *ch, ipfw_insn *cmd, struct obj_idx *oib,
206274b22066SAlexander V. Chernikov struct obj_idx *end)
206374b22066SAlexander V. Chernikov {
206474b22066SAlexander V. Chernikov struct opcode_obj_rewrite *rw;
206574b22066SAlexander V. Chernikov struct named_object *no;
206674b22066SAlexander V. Chernikov struct obj_idx *p;
206774b22066SAlexander V. Chernikov
206874b22066SAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(ch);
206974b22066SAlexander V. Chernikov
207074b22066SAlexander V. Chernikov for (p = oib; p < end; p++) {
207174b22066SAlexander V. Chernikov if (p->kidx == 0)
207274b22066SAlexander V. Chernikov continue;
207374b22066SAlexander V. Chernikov
2074b2df1f7eSAndrey V. Elsukov rw = find_op_rw(cmd + p->off, NULL, NULL);
207574b22066SAlexander V. Chernikov KASSERT(rw != NULL, ("Unable to find handler for op %d",
207674b22066SAlexander V. Chernikov (cmd + p->off)->opcode));
207774b22066SAlexander V. Chernikov
207874b22066SAlexander V. Chernikov /* Find & unref by existing idx */
207974b22066SAlexander V. Chernikov no = rw->find_bykidx(ch, p->kidx);
208074b22066SAlexander V. Chernikov KASSERT(no != NULL, ("Ref'd object %d disappeared", p->kidx));
208174b22066SAlexander V. Chernikov no->refcnt--;
208274b22066SAlexander V. Chernikov }
208374b22066SAlexander V. Chernikov }
208474b22066SAlexander V. Chernikov
208574b22066SAlexander V. Chernikov /*
208674b22066SAlexander V. Chernikov * Remove references from every object used in @rule.
208774b22066SAlexander V. Chernikov * Used at rule removal code.
208874b22066SAlexander V. Chernikov */
208974b22066SAlexander V. Chernikov static void
unref_rule_objects(struct ip_fw_chain * ch,struct ip_fw * rule)209074b22066SAlexander V. Chernikov unref_rule_objects(struct ip_fw_chain *ch, struct ip_fw *rule)
209174b22066SAlexander V. Chernikov {
209274b22066SAlexander V. Chernikov struct opcode_obj_rewrite *rw;
2093b2df1f7eSAndrey V. Elsukov struct named_object *no;
2094b2df1f7eSAndrey V. Elsukov ipfw_insn *cmd;
20954a77657cSAndrey V. Elsukov uint32_t kidx;
2096b2df1f7eSAndrey V. Elsukov int cmdlen, l;
209774b22066SAlexander V. Chernikov uint8_t subtype;
209874b22066SAlexander V. Chernikov
209974b22066SAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(ch);
210074b22066SAlexander V. Chernikov
210174b22066SAlexander V. Chernikov l = rule->cmd_len;
210274b22066SAlexander V. Chernikov cmd = rule->cmd;
210374b22066SAlexander V. Chernikov cmdlen = 0;
210474b22066SAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) {
210574b22066SAlexander V. Chernikov cmdlen = F_LEN(cmd);
210674b22066SAlexander V. Chernikov
2107b2df1f7eSAndrey V. Elsukov rw = find_op_rw(cmd, &kidx, &subtype);
210874b22066SAlexander V. Chernikov if (rw == NULL)
210974b22066SAlexander V. Chernikov continue;
211074b22066SAlexander V. Chernikov no = rw->find_bykidx(ch, kidx);
211174b22066SAlexander V. Chernikov
211211c56650SAndrey V. Elsukov KASSERT(no != NULL, ("object id %d not found", kidx));
211374b22066SAlexander V. Chernikov KASSERT(no->subtype == subtype,
211411c56650SAndrey V. Elsukov ("wrong type %d (%d) for object id %d",
211574b22066SAlexander V. Chernikov no->subtype, subtype, kidx));
211611c56650SAndrey V. Elsukov KASSERT(no->refcnt > 0, ("refcount for object %d is %d",
211774b22066SAlexander V. Chernikov kidx, no->refcnt));
211874b22066SAlexander V. Chernikov
21191cf09efeSAndrey V. Elsukov if (no->refcnt == 1 && rw->destroy_object != NULL)
21201cf09efeSAndrey V. Elsukov rw->destroy_object(ch, no);
21211cf09efeSAndrey V. Elsukov else
212274b22066SAlexander V. Chernikov no->refcnt--;
212374b22066SAlexander V. Chernikov }
212474b22066SAlexander V. Chernikov }
212574b22066SAlexander V. Chernikov
212674b22066SAlexander V. Chernikov /*
212774b22066SAlexander V. Chernikov * Find and reference object (if any) stored in instruction @cmd.
212874b22066SAlexander V. Chernikov *
212974b22066SAlexander V. Chernikov * Saves object info in @pidx, sets
213074b22066SAlexander V. Chernikov * - @unresolved to 1 if object should exists but not found
213174b22066SAlexander V. Chernikov *
213274b22066SAlexander V. Chernikov * Returns non-zero value in case of error.
213374b22066SAlexander V. Chernikov */
2134f8e26ca3SAndrey 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)213574b22066SAlexander V. Chernikov ref_opcode_object(struct ip_fw_chain *ch, ipfw_insn *cmd, struct tid_info *ti,
2136b2df1f7eSAndrey V. Elsukov struct obj_idx *pidx, int *unresolved)
213774b22066SAlexander V. Chernikov {
213874b22066SAlexander V. Chernikov struct named_object *no;
213974b22066SAlexander V. Chernikov struct opcode_obj_rewrite *rw;
214074b22066SAlexander V. Chernikov int error;
214174b22066SAlexander V. Chernikov
214274b22066SAlexander V. Chernikov /* Check if this opcode is candidate for rewrite */
2143b2df1f7eSAndrey V. Elsukov rw = find_op_rw(cmd, &ti->uidx, &ti->type);
214474b22066SAlexander V. Chernikov if (rw == NULL)
214574b22066SAlexander V. Chernikov return (0);
214674b22066SAlexander V. Chernikov
214774b22066SAlexander V. Chernikov /* Need to rewrite. Save necessary fields */
214874b22066SAlexander V. Chernikov pidx->uidx = ti->uidx;
214974b22066SAlexander V. Chernikov pidx->type = ti->type;
215074b22066SAlexander V. Chernikov
215174b22066SAlexander V. Chernikov /* Try to find referenced kernel object */
215274b22066SAlexander V. Chernikov error = rw->find_byname(ch, ti, &no);
215374b22066SAlexander V. Chernikov if (error != 0)
215474b22066SAlexander V. Chernikov return (error);
215574b22066SAlexander V. Chernikov if (no == NULL) {
2156b2df1f7eSAndrey V. Elsukov /*
2157b2df1f7eSAndrey V. Elsukov * Report about unresolved object for automaic
2158b2df1f7eSAndrey V. Elsukov * creation.
2159b2df1f7eSAndrey V. Elsukov */
216074b22066SAlexander V. Chernikov *unresolved = 1;
216174b22066SAlexander V. Chernikov return (0);
216274b22066SAlexander V. Chernikov }
216374b22066SAlexander V. Chernikov
2164c750a569SAndrey V. Elsukov /*
2165c750a569SAndrey V. Elsukov * Object is already exist.
2166c750a569SAndrey V. Elsukov * Its subtype should match with expected value.
2167c750a569SAndrey V. Elsukov */
2168c750a569SAndrey V. Elsukov if (ti->type != no->subtype)
2169c750a569SAndrey V. Elsukov return (EINVAL);
2170c750a569SAndrey V. Elsukov
2171c750a569SAndrey V. Elsukov /* Bump refcount and update kidx. */
217274b22066SAlexander V. Chernikov no->refcnt++;
2173b2df1f7eSAndrey V. Elsukov rw->update(cmd, no->kidx);
217474b22066SAlexander V. Chernikov return (0);
217574b22066SAlexander V. Chernikov }
217674b22066SAlexander V. Chernikov
217774b22066SAlexander V. Chernikov /*
2178f976a4edSAndrey V. Elsukov * Finds and bumps refcount for objects referenced by given @rule.
2179f976a4edSAndrey V. Elsukov * Auto-creates non-existing tables.
2180f976a4edSAndrey V. Elsukov * Fills in @oib array with userland/kernel indexes.
2181f976a4edSAndrey V. Elsukov *
2182f976a4edSAndrey V. Elsukov * Returns 0 on success.
2183f976a4edSAndrey V. Elsukov */
2184f976a4edSAndrey 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)2185f976a4edSAndrey V. Elsukov ref_rule_objects(struct ip_fw_chain *ch, struct ip_fw *rule,
2186f976a4edSAndrey V. Elsukov struct rule_check_info *ci, struct obj_idx *oib, struct tid_info *ti)
2187f976a4edSAndrey V. Elsukov {
2188f976a4edSAndrey V. Elsukov struct obj_idx *pidx;
2189b2df1f7eSAndrey V. Elsukov ipfw_insn *cmd;
2190b2df1f7eSAndrey V. Elsukov int cmdlen, error, l, unresolved;
2191f976a4edSAndrey V. Elsukov
2192f976a4edSAndrey V. Elsukov pidx = oib;
2193f976a4edSAndrey V. Elsukov l = rule->cmd_len;
2194f976a4edSAndrey V. Elsukov cmd = rule->cmd;
2195f976a4edSAndrey V. Elsukov cmdlen = 0;
2196f976a4edSAndrey V. Elsukov error = 0;
2197f976a4edSAndrey V. Elsukov
2198f976a4edSAndrey V. Elsukov IPFW_UH_WLOCK(ch);
2199f976a4edSAndrey V. Elsukov
2200f976a4edSAndrey V. Elsukov /* Increase refcount on each existing referenced table. */
2201f976a4edSAndrey V. Elsukov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) {
2202f976a4edSAndrey V. Elsukov cmdlen = F_LEN(cmd);
2203b2df1f7eSAndrey V. Elsukov unresolved = 0;
2204f976a4edSAndrey V. Elsukov
2205b2df1f7eSAndrey V. Elsukov error = ref_opcode_object(ch, cmd, ti, pidx, &unresolved);
2206f976a4edSAndrey V. Elsukov if (error != 0)
2207f976a4edSAndrey V. Elsukov break;
2208b2df1f7eSAndrey V. Elsukov /*
2209a4641f4eSPedro F. Giffuni * Compatibility stuff for old clients:
2210b2df1f7eSAndrey V. Elsukov * prepare to automaitcally create non-existing objects.
2211b2df1f7eSAndrey V. Elsukov */
2212b2df1f7eSAndrey V. Elsukov if (unresolved != 0) {
2213f976a4edSAndrey V. Elsukov pidx->off = rule->cmd_len - l;
2214f976a4edSAndrey V. Elsukov pidx++;
2215f976a4edSAndrey V. Elsukov }
2216f976a4edSAndrey V. Elsukov }
2217f976a4edSAndrey V. Elsukov
2218f976a4edSAndrey V. Elsukov if (error != 0) {
2219f976a4edSAndrey V. Elsukov /* Unref everything we have already done */
2220f976a4edSAndrey V. Elsukov unref_oib_objects(ch, rule->cmd, oib, pidx);
2221f976a4edSAndrey V. Elsukov IPFW_UH_WUNLOCK(ch);
2222f976a4edSAndrey V. Elsukov return (error);
2223f976a4edSAndrey V. Elsukov }
2224f976a4edSAndrey V. Elsukov IPFW_UH_WUNLOCK(ch);
2225f976a4edSAndrey V. Elsukov
2226f976a4edSAndrey V. Elsukov /* Perform auto-creation for non-existing objects */
2227b2df1f7eSAndrey V. Elsukov if (pidx != oib)
2228f976a4edSAndrey V. Elsukov error = create_objects_compat(ch, rule->cmd, oib, pidx, ti);
2229f976a4edSAndrey V. Elsukov
2230f976a4edSAndrey V. Elsukov /* Calculate real number of dynamic objects */
2231f976a4edSAndrey V. Elsukov ci->object_opcodes = (uint16_t)(pidx - oib);
2232f976a4edSAndrey V. Elsukov
2233f976a4edSAndrey V. Elsukov return (error);
2234f976a4edSAndrey V. Elsukov }
2235f976a4edSAndrey V. Elsukov
2236f976a4edSAndrey V. Elsukov /*
2237f976a4edSAndrey V. Elsukov * Checks is opcode is referencing table of appropriate type.
2238f976a4edSAndrey V. Elsukov * Adds reference count for found table if true.
2239f976a4edSAndrey V. Elsukov * Rewrites user-supplied opcode values with kernel ones.
2240f976a4edSAndrey V. Elsukov *
2241f976a4edSAndrey V. Elsukov * Returns 0 on success and appropriate error code otherwise.
2242f976a4edSAndrey V. Elsukov */
2243f976a4edSAndrey V. Elsukov static int
rewrite_rule_uidx(struct ip_fw_chain * chain,struct rule_check_info * ci)2244f976a4edSAndrey V. Elsukov rewrite_rule_uidx(struct ip_fw_chain *chain, struct rule_check_info *ci)
2245f976a4edSAndrey V. Elsukov {
2246f976a4edSAndrey V. Elsukov int error;
2247f976a4edSAndrey V. Elsukov ipfw_insn *cmd;
2248f976a4edSAndrey V. Elsukov struct obj_idx *p, *pidx_first, *pidx_last;
2249f976a4edSAndrey V. Elsukov struct tid_info ti;
2250f976a4edSAndrey V. Elsukov
2251f976a4edSAndrey V. Elsukov /*
2252f976a4edSAndrey V. Elsukov * Prepare an array for storing opcode indices.
2253f976a4edSAndrey V. Elsukov * Use stack allocation by default.
2254f976a4edSAndrey V. Elsukov */
2255f976a4edSAndrey V. Elsukov if (ci->object_opcodes <= (sizeof(ci->obuf)/sizeof(ci->obuf[0]))) {
2256f976a4edSAndrey V. Elsukov /* Stack */
2257f976a4edSAndrey V. Elsukov pidx_first = ci->obuf;
2258f976a4edSAndrey V. Elsukov } else
2259f976a4edSAndrey V. Elsukov pidx_first = malloc(
2260f976a4edSAndrey V. Elsukov ci->object_opcodes * sizeof(struct obj_idx),
2261f976a4edSAndrey V. Elsukov M_IPFW, M_WAITOK | M_ZERO);
2262f976a4edSAndrey V. Elsukov
2263f976a4edSAndrey V. Elsukov error = 0;
2264f976a4edSAndrey V. Elsukov memset(&ti, 0, sizeof(ti));
2265f976a4edSAndrey V. Elsukov
22662685841bSAndrey V. Elsukov /* Use set rule is assigned to. */
22672685841bSAndrey V. Elsukov ti.set = ci->krule->set;
2268f976a4edSAndrey V. Elsukov if (ci->ctlv != NULL) {
2269f976a4edSAndrey V. Elsukov ti.tlvs = (void *)(ci->ctlv + 1);
2270f976a4edSAndrey V. Elsukov ti.tlen = ci->ctlv->head.length - sizeof(ipfw_obj_ctlv);
2271f976a4edSAndrey V. Elsukov }
2272f976a4edSAndrey V. Elsukov
2273f976a4edSAndrey V. Elsukov /* Reference all used tables and other objects */
2274f976a4edSAndrey V. Elsukov error = ref_rule_objects(chain, ci->krule, ci, pidx_first, &ti);
2275f976a4edSAndrey V. Elsukov if (error != 0)
2276f976a4edSAndrey V. Elsukov goto free;
2277f976a4edSAndrey V. Elsukov /*
2278f976a4edSAndrey V. Elsukov * Note that ref_rule_objects() might have updated ci->object_opcodes
2279f976a4edSAndrey V. Elsukov * to reflect actual number of object opcodes.
2280f976a4edSAndrey V. Elsukov */
2281f976a4edSAndrey V. Elsukov
2282f8e26ca3SAndrey V. Elsukov /* Perform rewrite of remaining opcodes */
2283f976a4edSAndrey V. Elsukov p = pidx_first;
2284f976a4edSAndrey V. Elsukov pidx_last = pidx_first + ci->object_opcodes;
2285f976a4edSAndrey V. Elsukov for (p = pidx_first; p < pidx_last; p++) {
2286f976a4edSAndrey V. Elsukov cmd = ci->krule->cmd + p->off;
2287f976a4edSAndrey V. Elsukov update_opcode_kidx(cmd, p->kidx);
2288f976a4edSAndrey V. Elsukov }
2289f976a4edSAndrey V. Elsukov
2290f976a4edSAndrey V. Elsukov free:
2291f976a4edSAndrey V. Elsukov if (pidx_first != ci->obuf)
2292f976a4edSAndrey V. Elsukov free(pidx_first, M_IPFW);
2293f976a4edSAndrey V. Elsukov
2294f976a4edSAndrey V. Elsukov return (error);
2295f976a4edSAndrey V. Elsukov }
2296f976a4edSAndrey V. Elsukov
2297f976a4edSAndrey V. Elsukov /*
22984a77657cSAndrey V. Elsukov * Parses one or more rules from userland.
22994a77657cSAndrey V. Elsukov * Data layout (version 1)(current):
23006c2997ffSAlexander V. Chernikov * Request:
23016c2997ffSAlexander V. Chernikov * [
23026c2997ffSAlexander V. Chernikov * ip_fw3_opheader
23036c2997ffSAlexander V. Chernikov * [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional *1)
23046c2997ffSAlexander V. Chernikov * [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) ip_fw x N ] (*2) (*3)
23056c2997ffSAlexander V. Chernikov * ]
23066c2997ffSAlexander V. Chernikov * Reply:
23076c2997ffSAlexander V. Chernikov * [
23086c2997ffSAlexander V. Chernikov * ip_fw3_opheader
23096c2997ffSAlexander V. Chernikov * [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional)
23106c2997ffSAlexander V. Chernikov * [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) ip_fw x N ]
23116c2997ffSAlexander V. Chernikov * ]
23126c2997ffSAlexander V. Chernikov *
23136c2997ffSAlexander V. Chernikov * Rules in reply are modified to store their actual ruleset number.
23146c2997ffSAlexander V. Chernikov *
23156c2997ffSAlexander V. Chernikov * (*1) TLVs inside IPFW_TLV_TBL_LIST needs to be sorted ascending
2316a4641f4eSPedro F. Giffuni * according to their idx field and there has to be no duplicates.
23176c2997ffSAlexander V. Chernikov * (*2) Numbered rules inside IPFW_TLV_RULE_LIST needs to be sorted ascending.
23186c2997ffSAlexander V. Chernikov * (*3) Each ip_fw structure needs to be aligned to u64 boundary.
23196c2997ffSAlexander V. Chernikov *
23206c2997ffSAlexander V. Chernikov * Returns 0 on success.
23216c2997ffSAlexander V. Chernikov */
23224a77657cSAndrey V. Elsukov static __noinline int
parse_rules_v1(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd,ipfw_obj_ctlv ** prtlv,struct rule_check_info ** pci)23234a77657cSAndrey V. Elsukov parse_rules_v1(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
23244a77657cSAndrey V. Elsukov struct sockopt_data *sd, ipfw_obj_ctlv **prtlv,
23254a77657cSAndrey V. Elsukov struct rule_check_info **pci)
23266c2997ffSAlexander V. Chernikov {
23276c2997ffSAlexander V. Chernikov ipfw_obj_ctlv *ctlv, *rtlv, *tstate;
23286c2997ffSAlexander V. Chernikov ipfw_obj_ntlv *ntlv;
23294a77657cSAndrey V. Elsukov struct rule_check_info *ci, *cbuf;
23307e767c79SAlexander V. Chernikov struct ip_fw_rule *r;
23314a77657cSAndrey V. Elsukov size_t count, clen, read, rsize;
23324a77657cSAndrey V. Elsukov uint32_t idx, rulenum;
23334a77657cSAndrey V. Elsukov int error;
23346c2997ffSAlexander V. Chernikov
23356c2997ffSAlexander V. Chernikov op3 = (ip_fw3_opheader *)ipfw_get_sopt_space(sd, sd->valsize);
23366c2997ffSAlexander V. Chernikov ctlv = (ipfw_obj_ctlv *)(op3 + 1);
23376c2997ffSAlexander V. Chernikov read = sizeof(ip_fw3_opheader);
23386c2997ffSAlexander V. Chernikov if (read + sizeof(*ctlv) > sd->valsize)
23396c2997ffSAlexander V. Chernikov return (EINVAL);
23406c2997ffSAlexander V. Chernikov
23414a77657cSAndrey V. Elsukov rtlv = NULL;
23424a77657cSAndrey V. Elsukov tstate = NULL;
23434a77657cSAndrey V. Elsukov cbuf = NULL;
23444a77657cSAndrey V. Elsukov /* Table names or other named objects. */
23456c2997ffSAlexander V. Chernikov if (ctlv->head.type == IPFW_TLV_TBLNAME_LIST) {
23464a77657cSAndrey V. Elsukov /* Check size and alignment. */
23476c2997ffSAlexander V. Chernikov clen = ctlv->head.length;
23484a77657cSAndrey V. Elsukov if (read + clen > sd->valsize || clen < sizeof(*ctlv) ||
23494a77657cSAndrey V. Elsukov (clen % sizeof(uint64_t)) != 0)
23506c2997ffSAlexander V. Chernikov return (EINVAL);
23514a77657cSAndrey V. Elsukov /* Check for validness. */
23526c2997ffSAlexander V. Chernikov count = (ctlv->head.length - sizeof(*ctlv)) / sizeof(*ntlv);
23536c2997ffSAlexander V. Chernikov if (ctlv->count != count || ctlv->objsize != sizeof(*ntlv))
23546c2997ffSAlexander V. Chernikov return (EINVAL);
23556c2997ffSAlexander V. Chernikov /*
23566c2997ffSAlexander V. Chernikov * Check each TLV.
23576c2997ffSAlexander V. Chernikov * Ensure TLVs are sorted ascending and
23586c2997ffSAlexander V. Chernikov * there are no duplicates.
23596c2997ffSAlexander V. Chernikov */
23604a77657cSAndrey V. Elsukov idx = 0;
23616c2997ffSAlexander V. Chernikov ntlv = (ipfw_obj_ntlv *)(ctlv + 1);
23626c2997ffSAlexander V. Chernikov while (count > 0) {
23636c2997ffSAlexander V. Chernikov if (ntlv->head.length != sizeof(ipfw_obj_ntlv))
23646c2997ffSAlexander V. Chernikov return (EINVAL);
23656c2997ffSAlexander V. Chernikov
2366f81431ccSAndrey V. Elsukov error = ipfw_check_object_name_generic(ntlv->name);
23676c2997ffSAlexander V. Chernikov if (error != 0)
23686c2997ffSAlexander V. Chernikov return (error);
23696c2997ffSAlexander V. Chernikov
23706c2997ffSAlexander V. Chernikov if (ntlv->idx <= idx)
23716c2997ffSAlexander V. Chernikov return (EINVAL);
23726c2997ffSAlexander V. Chernikov
23736c2997ffSAlexander V. Chernikov idx = ntlv->idx;
23746c2997ffSAlexander V. Chernikov count--;
23756c2997ffSAlexander V. Chernikov ntlv++;
23766c2997ffSAlexander V. Chernikov }
23776c2997ffSAlexander V. Chernikov
23786c2997ffSAlexander V. Chernikov tstate = ctlv;
23796c2997ffSAlexander V. Chernikov read += ctlv->head.length;
23806c2997ffSAlexander V. Chernikov ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length);
23816c2997ffSAlexander V. Chernikov
23826c2997ffSAlexander V. Chernikov if (read + sizeof(*ctlv) > sd->valsize)
23836c2997ffSAlexander V. Chernikov return (EINVAL);
23844a77657cSAndrey V. Elsukov }
23856c2997ffSAlexander V. Chernikov
23864a77657cSAndrey V. Elsukov /* List of rules. */
23876c2997ffSAlexander V. Chernikov if (ctlv->head.type == IPFW_TLV_RULE_LIST) {
23886c2997ffSAlexander V. Chernikov clen = ctlv->head.length;
23894a77657cSAndrey V. Elsukov if (read + clen > sd->valsize || clen < sizeof(*ctlv) ||
23904a77657cSAndrey V. Elsukov (clen % sizeof(uint64_t)) != 0)
23916c2997ffSAlexander V. Chernikov return (EINVAL);
23926c2997ffSAlexander V. Chernikov
23936c2997ffSAlexander V. Chernikov clen -= sizeof(*ctlv);
23944a77657cSAndrey V. Elsukov if (ctlv->count == 0 ||
23954a77657cSAndrey V. Elsukov ctlv->count > clen / sizeof(struct ip_fw_rule))
23966c2997ffSAlexander V. Chernikov return (EINVAL);
23976c2997ffSAlexander V. Chernikov
23984a77657cSAndrey V. Elsukov /* Allocate state for each rule */
23994a77657cSAndrey V. Elsukov cbuf = malloc(ctlv->count * sizeof(struct rule_check_info),
24004a77657cSAndrey V. Elsukov M_TEMP, M_WAITOK | M_ZERO);
24016c2997ffSAlexander V. Chernikov
24026c2997ffSAlexander V. Chernikov /*
24036c2997ffSAlexander V. Chernikov * Check each rule for validness.
24047e767c79SAlexander V. Chernikov * Ensure numbered rules are sorted ascending
24057e767c79SAlexander V. Chernikov * and properly aligned
24066c2997ffSAlexander V. Chernikov */
24074a77657cSAndrey V. Elsukov rulenum = 0;
24086c2997ffSAlexander V. Chernikov count = 0;
24096c2997ffSAlexander V. Chernikov error = 0;
24104a77657cSAndrey V. Elsukov ci = cbuf;
24114a77657cSAndrey V. Elsukov r = (struct ip_fw_rule *)(ctlv + 1);
24126c2997ffSAlexander V. Chernikov while (clen > 0) {
24134a77657cSAndrey V. Elsukov rsize = RULEUSIZE1(r);
24144a77657cSAndrey V. Elsukov if (rsize > clen || count > ctlv->count) {
24156c2997ffSAlexander V. Chernikov error = EINVAL;
24166c2997ffSAlexander V. Chernikov break;
24176c2997ffSAlexander V. Chernikov }
24186c2997ffSAlexander V. Chernikov ci->ctlv = tstate;
24194a77657cSAndrey V. Elsukov ci->version = IP_FW3_OPVER;
24204a77657cSAndrey V. Elsukov error = ipfw_check_rule(r, rsize, ci);
24216c2997ffSAlexander V. Chernikov if (error != 0)
24226c2997ffSAlexander V. Chernikov break;
24236c2997ffSAlexander V. Chernikov
24246c2997ffSAlexander V. Chernikov /* Check sorting */
24254a77657cSAndrey V. Elsukov if (count != 0 && ((rulenum == 0) != (r->rulenum == 0) ||
24264a77657cSAndrey V. Elsukov r->rulenum < rulenum)) {
24274a77657cSAndrey V. Elsukov printf("ipfw: wrong order: rulenum %u"
24284a77657cSAndrey V. Elsukov " vs %u\n", r->rulenum, rulenum);
24296c2997ffSAlexander V. Chernikov error = EINVAL;
24306c2997ffSAlexander V. Chernikov break;
24316c2997ffSAlexander V. Chernikov }
24324a77657cSAndrey V. Elsukov rulenum = r->rulenum;
24337e767c79SAlexander V. Chernikov ci->urule = (caddr_t)r;
24346c2997ffSAlexander V. Chernikov clen -= rsize;
24357e767c79SAlexander V. Chernikov r = (struct ip_fw_rule *)((caddr_t)r + rsize);
24366c2997ffSAlexander V. Chernikov count++;
24376c2997ffSAlexander V. Chernikov ci++;
24386c2997ffSAlexander V. Chernikov }
24396c2997ffSAlexander V. Chernikov
24406c2997ffSAlexander V. Chernikov if (ctlv->count != count || error != 0) {
24416c2997ffSAlexander V. Chernikov free(cbuf, M_TEMP);
24426c2997ffSAlexander V. Chernikov return (EINVAL);
24436c2997ffSAlexander V. Chernikov }
24446c2997ffSAlexander V. Chernikov
24456c2997ffSAlexander V. Chernikov rtlv = ctlv;
24466c2997ffSAlexander V. Chernikov read += ctlv->head.length;
24476c2997ffSAlexander V. Chernikov ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length);
24486c2997ffSAlexander V. Chernikov }
24496c2997ffSAlexander V. Chernikov
24504a77657cSAndrey V. Elsukov if (read != sd->valsize || rtlv == NULL) {
24516c2997ffSAlexander V. Chernikov free(cbuf, M_TEMP);
24526c2997ffSAlexander V. Chernikov return (EINVAL);
24536c2997ffSAlexander V. Chernikov }
24546c2997ffSAlexander V. Chernikov
24554a77657cSAndrey V. Elsukov *prtlv = rtlv;
24564a77657cSAndrey V. Elsukov *pci = cbuf;
24574a77657cSAndrey V. Elsukov return (0);
24586c2997ffSAlexander V. Chernikov }
24596c2997ffSAlexander V. Chernikov
24604a77657cSAndrey V. Elsukov /*
24614a77657cSAndrey V. Elsukov * Copy rule @urule from v1 userland format (current) to kernel @krule.
24624a77657cSAndrey V. Elsukov */
24634a77657cSAndrey V. Elsukov static void
import_rule_v1(struct ip_fw_chain * chain,struct rule_check_info * ci)24644a77657cSAndrey V. Elsukov import_rule_v1(struct ip_fw_chain *chain, struct rule_check_info *ci)
24654a77657cSAndrey V. Elsukov {
24664a77657cSAndrey V. Elsukov struct ip_fw_rule *urule;
24674a77657cSAndrey V. Elsukov struct ip_fw *krule;
24684a77657cSAndrey V. Elsukov
24694a77657cSAndrey V. Elsukov urule = (struct ip_fw_rule *)ci->urule;
24704a77657cSAndrey V. Elsukov krule = ci->krule = ipfw_alloc_rule(chain, RULEKSIZE1(urule));
24714a77657cSAndrey V. Elsukov
24724a77657cSAndrey V. Elsukov krule->act_ofs = urule->act_ofs;
24734a77657cSAndrey V. Elsukov krule->cmd_len = urule->cmd_len;
24744a77657cSAndrey V. Elsukov krule->rulenum = urule->rulenum;
24754a77657cSAndrey V. Elsukov krule->set = urule->set;
24764a77657cSAndrey V. Elsukov krule->flags = urule->flags;
24774a77657cSAndrey V. Elsukov
24784a77657cSAndrey V. Elsukov /* Save rulenum offset */
24794a77657cSAndrey V. Elsukov ci->urule_numoff = offsetof(struct ip_fw_rule, rulenum);
24804a77657cSAndrey V. Elsukov
24814a77657cSAndrey V. Elsukov /* Copy opcodes */
24824a77657cSAndrey V. Elsukov memcpy(krule->cmd, urule->cmd, krule->cmd_len * sizeof(uint32_t));
24834a77657cSAndrey V. Elsukov }
24844a77657cSAndrey V. Elsukov
24854a77657cSAndrey V. Elsukov /*
24864a77657cSAndrey V. Elsukov * Adds one or more rules to ipfw @chain.
24874a77657cSAndrey V. Elsukov */
24884a77657cSAndrey V. Elsukov static int
add_rules(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)24894a77657cSAndrey V. Elsukov add_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
24904a77657cSAndrey V. Elsukov struct sockopt_data *sd)
24914a77657cSAndrey V. Elsukov {
24924a77657cSAndrey V. Elsukov ipfw_obj_ctlv *rtlv;
24934a77657cSAndrey V. Elsukov struct rule_check_info *ci, *nci;
24944a77657cSAndrey V. Elsukov int i, ret;
24954a77657cSAndrey V. Elsukov
24964a77657cSAndrey V. Elsukov /*
24974a77657cSAndrey V. Elsukov * Check rules buffer for validness.
24984a77657cSAndrey V. Elsukov */
24994a77657cSAndrey V. Elsukov ret = parse_rules_v1(chain, op3, sd, &rtlv, &nci);
25004a77657cSAndrey V. Elsukov if (ret != 0)
25014a77657cSAndrey V. Elsukov return (ret);
25024a77657cSAndrey V. Elsukov /*
25034a77657cSAndrey V. Elsukov * Allocate storage for the kernel representation of rules.
25044a77657cSAndrey V. Elsukov */
25054a77657cSAndrey V. Elsukov for (i = 0, ci = nci; i < rtlv->count; i++, ci++)
25064a77657cSAndrey V. Elsukov import_rule_v1(chain, ci);
25074a77657cSAndrey V. Elsukov /*
25084a77657cSAndrey V. Elsukov * Try to add new rules to the chain.
25094a77657cSAndrey V. Elsukov */
25104a77657cSAndrey V. Elsukov if ((ret = ipfw_commit_rules(chain, nci, rtlv->count)) != 0) {
25114a77657cSAndrey V. Elsukov for (i = 0, ci = nci; i < rtlv->count; i++, ci++)
2512cefe3d67SAndrey V. Elsukov ipfw_free_rule(ci->krule);
25136c2997ffSAlexander V. Chernikov }
25144a77657cSAndrey V. Elsukov /* Cleanup after parse_rules() */
25154a77657cSAndrey V. Elsukov free(nci, M_TEMP);
25164a77657cSAndrey V. Elsukov return (ret);
25176c2997ffSAlexander V. Chernikov }
25186c2997ffSAlexander V. Chernikov
251991e721d7SAlexander V. Chernikov /*
2520be8bc457SAlexander V. Chernikov * Lists all sopts currently registered.
25214a77657cSAndrey V. Elsukov * Data layout (v1)(current):
2522be8bc457SAlexander V. Chernikov * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size
2523be8bc457SAlexander V. Chernikov * Reply: [ ipfw_obj_lheader ipfw_sopt_info x N ]
2524be8bc457SAlexander V. Chernikov *
2525be8bc457SAlexander V. Chernikov * Returns 0 on success
2526be8bc457SAlexander V. Chernikov */
2527be8bc457SAlexander V. Chernikov static int
dump_soptcodes(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)2528be8bc457SAlexander V. Chernikov dump_soptcodes(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
2529be8bc457SAlexander V. Chernikov struct sockopt_data *sd)
2530be8bc457SAlexander V. Chernikov {
2531be8bc457SAlexander V. Chernikov struct _ipfw_obj_lheader *olh;
2532be8bc457SAlexander V. Chernikov ipfw_sopt_info *i;
2533be8bc457SAlexander V. Chernikov struct ipfw_sopt_handler *sh;
2534be8bc457SAlexander V. Chernikov uint32_t count, n, size;
2535be8bc457SAlexander V. Chernikov
25364a77657cSAndrey V. Elsukov olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,
25374a77657cSAndrey V. Elsukov sizeof(*olh));
2538be8bc457SAlexander V. Chernikov if (olh == NULL)
2539be8bc457SAlexander V. Chernikov return (EINVAL);
2540be8bc457SAlexander V. Chernikov if (sd->valsize < olh->size)
2541be8bc457SAlexander V. Chernikov return (EINVAL);
2542be8bc457SAlexander V. Chernikov
2543be8bc457SAlexander V. Chernikov CTL3_LOCK();
2544be8bc457SAlexander V. Chernikov count = ctl3_hsize;
2545be8bc457SAlexander V. Chernikov size = count * sizeof(ipfw_sopt_info) + sizeof(ipfw_obj_lheader);
2546be8bc457SAlexander V. Chernikov
2547be8bc457SAlexander V. Chernikov /* Fill in header regadless of buffer size */
2548be8bc457SAlexander V. Chernikov olh->count = count;
2549be8bc457SAlexander V. Chernikov olh->objsize = sizeof(ipfw_sopt_info);
2550be8bc457SAlexander V. Chernikov
2551be8bc457SAlexander V. Chernikov if (size > olh->size) {
2552be8bc457SAlexander V. Chernikov olh->size = size;
2553be8bc457SAlexander V. Chernikov CTL3_UNLOCK();
2554be8bc457SAlexander V. Chernikov return (ENOMEM);
2555be8bc457SAlexander V. Chernikov }
2556be8bc457SAlexander V. Chernikov olh->size = size;
2557be8bc457SAlexander V. Chernikov
2558*b405250cSAndrey V. Elsukov for (n = 0; n < count; n++) {
2559be8bc457SAlexander V. Chernikov i = (ipfw_sopt_info *)ipfw_get_sopt_space(sd, sizeof(*i));
25607a6ab8f1SPedro F. Giffuni KASSERT(i != NULL, ("previously checked buffer is not enough"));
2561be8bc457SAlexander V. Chernikov sh = &ctl3_handlers[n];
2562be8bc457SAlexander V. Chernikov i->opcode = sh->opcode;
2563be8bc457SAlexander V. Chernikov i->version = sh->version;
2564be8bc457SAlexander V. Chernikov i->refcnt = sh->refcnt;
2565be8bc457SAlexander V. Chernikov }
2566be8bc457SAlexander V. Chernikov CTL3_UNLOCK();
2567be8bc457SAlexander V. Chernikov
2568be8bc457SAlexander V. Chernikov return (0);
2569be8bc457SAlexander V. Chernikov }
2570be8bc457SAlexander V. Chernikov
2571be8bc457SAlexander V. Chernikov /*
257274b22066SAlexander V. Chernikov * Compares two opcodes.
257374b22066SAlexander V. Chernikov * Used both in qsort() and bsearch().
257474b22066SAlexander V. Chernikov *
257574b22066SAlexander V. Chernikov * Returns 0 if match is found.
257674b22066SAlexander V. Chernikov */
257774b22066SAlexander V. Chernikov static int
compare_opcodes(const void * _a,const void * _b)257874b22066SAlexander V. Chernikov compare_opcodes(const void *_a, const void *_b)
257974b22066SAlexander V. Chernikov {
258074b22066SAlexander V. Chernikov const struct opcode_obj_rewrite *a, *b;
258174b22066SAlexander V. Chernikov
258274b22066SAlexander V. Chernikov a = (const struct opcode_obj_rewrite *)_a;
258374b22066SAlexander V. Chernikov b = (const struct opcode_obj_rewrite *)_b;
258474b22066SAlexander V. Chernikov
258574b22066SAlexander V. Chernikov if (a->opcode < b->opcode)
258674b22066SAlexander V. Chernikov return (-1);
258774b22066SAlexander V. Chernikov else if (a->opcode > b->opcode)
258874b22066SAlexander V. Chernikov return (1);
258974b22066SAlexander V. Chernikov
259074b22066SAlexander V. Chernikov return (0);
259174b22066SAlexander V. Chernikov }
259274b22066SAlexander V. Chernikov
259374b22066SAlexander V. Chernikov /*
2594b2df1f7eSAndrey V. Elsukov * XXX: Rewrite bsearch()
2595b2df1f7eSAndrey V. Elsukov */
2596b2df1f7eSAndrey V. Elsukov static int
find_op_rw_range(uint16_t op,struct opcode_obj_rewrite ** plo,struct opcode_obj_rewrite ** phi)2597b2df1f7eSAndrey V. Elsukov find_op_rw_range(uint16_t op, struct opcode_obj_rewrite **plo,
2598b2df1f7eSAndrey V. Elsukov struct opcode_obj_rewrite **phi)
2599b2df1f7eSAndrey V. Elsukov {
2600b2df1f7eSAndrey V. Elsukov struct opcode_obj_rewrite *ctl3_max, *lo, *hi, h, *rw;
2601b2df1f7eSAndrey V. Elsukov
2602b2df1f7eSAndrey V. Elsukov memset(&h, 0, sizeof(h));
2603b2df1f7eSAndrey V. Elsukov h.opcode = op;
2604b2df1f7eSAndrey V. Elsukov
2605b2df1f7eSAndrey V. Elsukov rw = (struct opcode_obj_rewrite *)bsearch(&h, ctl3_rewriters,
2606b2df1f7eSAndrey V. Elsukov ctl3_rsize, sizeof(h), compare_opcodes);
2607b2df1f7eSAndrey V. Elsukov if (rw == NULL)
2608b2df1f7eSAndrey V. Elsukov return (1);
2609b2df1f7eSAndrey V. Elsukov
2610b2df1f7eSAndrey V. Elsukov /* Find the first element matching the same opcode */
2611b2df1f7eSAndrey V. Elsukov lo = rw;
2612b2df1f7eSAndrey V. Elsukov for ( ; lo > ctl3_rewriters && (lo - 1)->opcode == op; lo--)
2613b2df1f7eSAndrey V. Elsukov ;
2614b2df1f7eSAndrey V. Elsukov
2615b2df1f7eSAndrey V. Elsukov /* Find the last element matching the same opcode */
2616b2df1f7eSAndrey V. Elsukov hi = rw;
2617b2df1f7eSAndrey V. Elsukov ctl3_max = ctl3_rewriters + ctl3_rsize;
2618b2df1f7eSAndrey V. Elsukov for ( ; (hi + 1) < ctl3_max && (hi + 1)->opcode == op; hi++)
2619b2df1f7eSAndrey V. Elsukov ;
2620b2df1f7eSAndrey V. Elsukov
2621b2df1f7eSAndrey V. Elsukov *plo = lo;
2622b2df1f7eSAndrey V. Elsukov *phi = hi;
2623b2df1f7eSAndrey V. Elsukov
2624b2df1f7eSAndrey V. Elsukov return (0);
2625b2df1f7eSAndrey V. Elsukov }
2626b2df1f7eSAndrey V. Elsukov
2627b2df1f7eSAndrey V. Elsukov /*
262874b22066SAlexander V. Chernikov * Finds opcode object rewriter based on @code.
262974b22066SAlexander V. Chernikov *
263074b22066SAlexander V. Chernikov * Returns pointer to handler or NULL.
263174b22066SAlexander V. Chernikov */
2632b2df1f7eSAndrey V. Elsukov static struct opcode_obj_rewrite *
find_op_rw(ipfw_insn * cmd,uint32_t * puidx,uint8_t * ptype)26334a77657cSAndrey V. Elsukov find_op_rw(ipfw_insn *cmd, uint32_t *puidx, uint8_t *ptype)
263474b22066SAlexander V. Chernikov {
2635b2df1f7eSAndrey V. Elsukov struct opcode_obj_rewrite *rw, *lo, *hi;
26364a77657cSAndrey V. Elsukov uint32_t uidx;
2637b2df1f7eSAndrey V. Elsukov uint8_t subtype;
263874b22066SAlexander V. Chernikov
2639b2df1f7eSAndrey V. Elsukov if (find_op_rw_range(cmd->opcode, &lo, &hi) != 0)
2640b2df1f7eSAndrey V. Elsukov return (NULL);
264174b22066SAlexander V. Chernikov
2642b2df1f7eSAndrey V. Elsukov for (rw = lo; rw <= hi; rw++) {
2643b2df1f7eSAndrey V. Elsukov if (rw->classifier(cmd, &uidx, &subtype) == 0) {
2644b2df1f7eSAndrey V. Elsukov if (puidx != NULL)
2645b2df1f7eSAndrey V. Elsukov *puidx = uidx;
2646b2df1f7eSAndrey V. Elsukov if (ptype != NULL)
2647b2df1f7eSAndrey V. Elsukov *ptype = subtype;
264874b22066SAlexander V. Chernikov return (rw);
264974b22066SAlexander V. Chernikov }
2650b2df1f7eSAndrey V. Elsukov }
265174b22066SAlexander V. Chernikov
2652b2df1f7eSAndrey V. Elsukov return (NULL);
2653b2df1f7eSAndrey V. Elsukov }
265474b22066SAlexander V. Chernikov int
classify_opcode_kidx(ipfw_insn * cmd,uint32_t * puidx)26554a77657cSAndrey V. Elsukov classify_opcode_kidx(ipfw_insn *cmd, uint32_t *puidx)
265674b22066SAlexander V. Chernikov {
265774b22066SAlexander V. Chernikov
2658e099b90bSPedro F. Giffuni if (find_op_rw(cmd, puidx, NULL) == NULL)
265974b22066SAlexander V. Chernikov return (1);
2660b2df1f7eSAndrey V. Elsukov return (0);
266174b22066SAlexander V. Chernikov }
266274b22066SAlexander V. Chernikov
266374b22066SAlexander V. Chernikov void
update_opcode_kidx(ipfw_insn * cmd,uint32_t idx)26644a77657cSAndrey V. Elsukov update_opcode_kidx(ipfw_insn *cmd, uint32_t idx)
266574b22066SAlexander V. Chernikov {
266674b22066SAlexander V. Chernikov struct opcode_obj_rewrite *rw;
266774b22066SAlexander V. Chernikov
2668b2df1f7eSAndrey V. Elsukov rw = find_op_rw(cmd, NULL, NULL);
266974b22066SAlexander V. Chernikov KASSERT(rw != NULL, ("No handler to update opcode %d", cmd->opcode));
267074b22066SAlexander V. Chernikov rw->update(cmd, idx);
267174b22066SAlexander V. Chernikov }
267274b22066SAlexander V. Chernikov
267374b22066SAlexander V. Chernikov void
ipfw_init_obj_rewriter(void)267462030bb8SDimitry Andric ipfw_init_obj_rewriter(void)
267574b22066SAlexander V. Chernikov {
267674b22066SAlexander V. Chernikov ctl3_rewriters = NULL;
267774b22066SAlexander V. Chernikov ctl3_rsize = 0;
267874b22066SAlexander V. Chernikov }
267974b22066SAlexander V. Chernikov
268074b22066SAlexander V. Chernikov void
ipfw_destroy_obj_rewriter(void)268162030bb8SDimitry Andric ipfw_destroy_obj_rewriter(void)
268274b22066SAlexander V. Chernikov {
268374b22066SAlexander V. Chernikov if (ctl3_rewriters != NULL)
268474b22066SAlexander V. Chernikov free(ctl3_rewriters, M_IPFW);
268574b22066SAlexander V. Chernikov ctl3_rewriters = NULL;
268674b22066SAlexander V. Chernikov ctl3_rsize = 0;
268774b22066SAlexander V. Chernikov }
268874b22066SAlexander V. Chernikov
268974b22066SAlexander V. Chernikov /*
269074b22066SAlexander V. Chernikov * Adds one or more opcode object rewrite handlers to the global array.
269174b22066SAlexander V. Chernikov * Function may sleep.
269274b22066SAlexander V. Chernikov */
269374b22066SAlexander V. Chernikov void
ipfw_add_obj_rewriter(struct opcode_obj_rewrite * rw,size_t count)269474b22066SAlexander V. Chernikov ipfw_add_obj_rewriter(struct opcode_obj_rewrite *rw, size_t count)
269574b22066SAlexander V. Chernikov {
269674b22066SAlexander V. Chernikov size_t sz;
269774b22066SAlexander V. Chernikov struct opcode_obj_rewrite *tmp;
269874b22066SAlexander V. Chernikov
269974b22066SAlexander V. Chernikov CTL3_LOCK();
270074b22066SAlexander V. Chernikov
270174b22066SAlexander V. Chernikov for (;;) {
270274b22066SAlexander V. Chernikov sz = ctl3_rsize + count;
270374b22066SAlexander V. Chernikov CTL3_UNLOCK();
270474b22066SAlexander V. Chernikov tmp = malloc(sizeof(*rw) * sz, M_IPFW, M_WAITOK | M_ZERO);
270574b22066SAlexander V. Chernikov CTL3_LOCK();
270674b22066SAlexander V. Chernikov if (ctl3_rsize + count <= sz)
270774b22066SAlexander V. Chernikov break;
270874b22066SAlexander V. Chernikov
270974b22066SAlexander V. Chernikov /* Retry */
271074b22066SAlexander V. Chernikov free(tmp, M_IPFW);
271174b22066SAlexander V. Chernikov }
271274b22066SAlexander V. Chernikov
271374b22066SAlexander V. Chernikov /* Merge old & new arrays */
271474b22066SAlexander V. Chernikov sz = ctl3_rsize + count;
271574b22066SAlexander V. Chernikov memcpy(tmp, ctl3_rewriters, ctl3_rsize * sizeof(*rw));
271674b22066SAlexander V. Chernikov memcpy(&tmp[ctl3_rsize], rw, count * sizeof(*rw));
271774b22066SAlexander V. Chernikov qsort(tmp, sz, sizeof(*rw), compare_opcodes);
271874b22066SAlexander V. Chernikov /* Switch new and free old */
271974b22066SAlexander V. Chernikov if (ctl3_rewriters != NULL)
272074b22066SAlexander V. Chernikov free(ctl3_rewriters, M_IPFW);
272174b22066SAlexander V. Chernikov ctl3_rewriters = tmp;
272274b22066SAlexander V. Chernikov ctl3_rsize = sz;
272374b22066SAlexander V. Chernikov
272474b22066SAlexander V. Chernikov CTL3_UNLOCK();
272574b22066SAlexander V. Chernikov }
272674b22066SAlexander V. Chernikov
272774b22066SAlexander V. Chernikov /*
272874b22066SAlexander V. Chernikov * Removes one or more object rewrite handlers from the global array.
272974b22066SAlexander V. Chernikov */
273074b22066SAlexander V. Chernikov int
ipfw_del_obj_rewriter(struct opcode_obj_rewrite * rw,size_t count)273174b22066SAlexander V. Chernikov ipfw_del_obj_rewriter(struct opcode_obj_rewrite *rw, size_t count)
273274b22066SAlexander V. Chernikov {
273374b22066SAlexander V. Chernikov size_t sz;
2734b2df1f7eSAndrey V. Elsukov struct opcode_obj_rewrite *ctl3_max, *ktmp, *lo, *hi;
273574b22066SAlexander V. Chernikov int i;
273674b22066SAlexander V. Chernikov
273774b22066SAlexander V. Chernikov CTL3_LOCK();
273874b22066SAlexander V. Chernikov
273974b22066SAlexander V. Chernikov for (i = 0; i < count; i++) {
2740b2df1f7eSAndrey V. Elsukov if (find_op_rw_range(rw[i].opcode, &lo, &hi) != 0)
274174b22066SAlexander V. Chernikov continue;
274274b22066SAlexander V. Chernikov
2743b2df1f7eSAndrey V. Elsukov for (ktmp = lo; ktmp <= hi; ktmp++) {
2744b2df1f7eSAndrey V. Elsukov if (ktmp->classifier != rw[i].classifier)
2745b2df1f7eSAndrey V. Elsukov continue;
2746b2df1f7eSAndrey V. Elsukov
2747b2df1f7eSAndrey V. Elsukov ctl3_max = ctl3_rewriters + ctl3_rsize;
2748b2df1f7eSAndrey V. Elsukov sz = (ctl3_max - (ktmp + 1)) * sizeof(*ktmp);
2749b2df1f7eSAndrey V. Elsukov memmove(ktmp, ktmp + 1, sz);
275074b22066SAlexander V. Chernikov ctl3_rsize--;
2751b2df1f7eSAndrey V. Elsukov break;
2752b2df1f7eSAndrey V. Elsukov }
275374b22066SAlexander V. Chernikov }
275474b22066SAlexander V. Chernikov
275574b22066SAlexander V. Chernikov if (ctl3_rsize == 0) {
275674b22066SAlexander V. Chernikov if (ctl3_rewriters != NULL)
275774b22066SAlexander V. Chernikov free(ctl3_rewriters, M_IPFW);
275874b22066SAlexander V. Chernikov ctl3_rewriters = NULL;
275974b22066SAlexander V. Chernikov }
276074b22066SAlexander V. Chernikov
276174b22066SAlexander V. Chernikov CTL3_UNLOCK();
276274b22066SAlexander V. Chernikov
276374b22066SAlexander V. Chernikov return (0);
276474b22066SAlexander V. Chernikov }
276574b22066SAlexander V. Chernikov
2766b309f085SAndrey V. Elsukov static int
export_objhash_ntlv_internal(struct namedobj_instance * ni,struct named_object * no,void * arg)27675dc5a0e0SAndrey V. Elsukov export_objhash_ntlv_internal(struct namedobj_instance *ni,
27685dc5a0e0SAndrey V. Elsukov struct named_object *no, void *arg)
27695dc5a0e0SAndrey V. Elsukov {
27705dc5a0e0SAndrey V. Elsukov struct sockopt_data *sd;
27715dc5a0e0SAndrey V. Elsukov ipfw_obj_ntlv *ntlv;
27725dc5a0e0SAndrey V. Elsukov
27735dc5a0e0SAndrey V. Elsukov sd = (struct sockopt_data *)arg;
27745dc5a0e0SAndrey V. Elsukov ntlv = (ipfw_obj_ntlv *)ipfw_get_sopt_space(sd, sizeof(*ntlv));
27755dc5a0e0SAndrey V. Elsukov if (ntlv == NULL)
2776b309f085SAndrey V. Elsukov return (ENOMEM);
27775dc5a0e0SAndrey V. Elsukov ipfw_export_obj_ntlv(no, ntlv);
2778b309f085SAndrey V. Elsukov return (0);
27795dc5a0e0SAndrey V. Elsukov }
27805dc5a0e0SAndrey V. Elsukov
27815dc5a0e0SAndrey V. Elsukov /*
27825dc5a0e0SAndrey V. Elsukov * Lists all service objects.
27835dc5a0e0SAndrey V. Elsukov * Data layout (v0)(current):
2784f8e26ca3SAndrey V. Elsukov * Request: [ ipfw_obj_lheader ] size = ipfw_obj_lheader.size
27855dc5a0e0SAndrey V. Elsukov * Reply: [ ipfw_obj_lheader [ ipfw_obj_ntlv x N ] (optional) ]
27865dc5a0e0SAndrey V. Elsukov * Returns 0 on success
27875dc5a0e0SAndrey V. Elsukov */
27885dc5a0e0SAndrey V. Elsukov static int
dump_srvobjects(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)27895dc5a0e0SAndrey V. Elsukov dump_srvobjects(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
27905dc5a0e0SAndrey V. Elsukov struct sockopt_data *sd)
27915dc5a0e0SAndrey V. Elsukov {
27925dc5a0e0SAndrey V. Elsukov ipfw_obj_lheader *hdr;
27935dc5a0e0SAndrey V. Elsukov int count;
27945dc5a0e0SAndrey V. Elsukov
27955dc5a0e0SAndrey V. Elsukov hdr = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*hdr));
27965dc5a0e0SAndrey V. Elsukov if (hdr == NULL)
27975dc5a0e0SAndrey V. Elsukov return (EINVAL);
27985dc5a0e0SAndrey V. Elsukov
27995dc5a0e0SAndrey V. Elsukov IPFW_UH_RLOCK(chain);
28005dc5a0e0SAndrey V. Elsukov count = ipfw_objhash_count(CHAIN_TO_SRV(chain));
28015dc5a0e0SAndrey V. Elsukov hdr->size = sizeof(ipfw_obj_lheader) + count * sizeof(ipfw_obj_ntlv);
28025dc5a0e0SAndrey V. Elsukov if (sd->valsize < hdr->size) {
28035dc5a0e0SAndrey V. Elsukov IPFW_UH_RUNLOCK(chain);
28045dc5a0e0SAndrey V. Elsukov return (ENOMEM);
28055dc5a0e0SAndrey V. Elsukov }
28065dc5a0e0SAndrey V. Elsukov hdr->count = count;
28075dc5a0e0SAndrey V. Elsukov hdr->objsize = sizeof(ipfw_obj_ntlv);
28085dc5a0e0SAndrey V. Elsukov if (count > 0)
28095dc5a0e0SAndrey V. Elsukov ipfw_objhash_foreach(CHAIN_TO_SRV(chain),
28105dc5a0e0SAndrey V. Elsukov export_objhash_ntlv_internal, sd);
28115dc5a0e0SAndrey V. Elsukov IPFW_UH_RUNLOCK(chain);
28125dc5a0e0SAndrey V. Elsukov return (0);
28135dc5a0e0SAndrey V. Elsukov }
28145dc5a0e0SAndrey V. Elsukov
28154a77657cSAndrey V. Elsukov void
ipfw_enable_skipto_cache(struct ip_fw_chain * chain)28164a77657cSAndrey V. Elsukov ipfw_enable_skipto_cache(struct ip_fw_chain *chain)
28174a77657cSAndrey V. Elsukov {
28184a77657cSAndrey V. Elsukov
28194a77657cSAndrey V. Elsukov IPFW_UH_WLOCK_ASSERT(chain);
28204a77657cSAndrey V. Elsukov update_skipto_cache(chain, chain->map);
28214a77657cSAndrey V. Elsukov
28224a77657cSAndrey V. Elsukov IPFW_WLOCK(chain);
28234a77657cSAndrey V. Elsukov swap_skipto_cache(chain);
28244a77657cSAndrey V. Elsukov V_skipto_cache = 1;
28254a77657cSAndrey V. Elsukov IPFW_WUNLOCK(chain);
28264a77657cSAndrey V. Elsukov }
28274a77657cSAndrey V. Elsukov
28284a77657cSAndrey V. Elsukov /*
28294a77657cSAndrey V. Elsukov * Enables or disable skipto cache.
28304a77657cSAndrey V. Elsukov * Request: [ ipfw_cmd_header ] size = ipfw_cmd_header.size
28314a77657cSAndrey V. Elsukov * Reply: [ ipfw_cmd_header ]
28324a77657cSAndrey V. Elsukov * Returns 0 on success
28334a77657cSAndrey V. Elsukov */
28344a77657cSAndrey V. Elsukov static int
manage_skiptocache(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)28354a77657cSAndrey V. Elsukov manage_skiptocache(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
28364a77657cSAndrey V. Elsukov struct sockopt_data *sd)
28374a77657cSAndrey V. Elsukov {
28384a77657cSAndrey V. Elsukov ipfw_cmd_header *hdr;
28394a77657cSAndrey V. Elsukov
28404a77657cSAndrey V. Elsukov if (sd->valsize != sizeof(*hdr))
28414a77657cSAndrey V. Elsukov return (EINVAL);
28424a77657cSAndrey V. Elsukov
28434a77657cSAndrey V. Elsukov hdr = (ipfw_cmd_header *)ipfw_get_sopt_space(sd, sd->valsize);
28444a77657cSAndrey V. Elsukov if (hdr->cmd != SKIPTO_CACHE_DISABLE &&
28454a77657cSAndrey V. Elsukov hdr->cmd != SKIPTO_CACHE_ENABLE)
28464a77657cSAndrey V. Elsukov return (EOPNOTSUPP);
28474a77657cSAndrey V. Elsukov
28484a77657cSAndrey V. Elsukov IPFW_UH_WLOCK(chain);
28494a77657cSAndrey V. Elsukov if (hdr->cmd != V_skipto_cache) {
28504a77657cSAndrey V. Elsukov if (hdr->cmd == SKIPTO_CACHE_ENABLE)
28514a77657cSAndrey V. Elsukov ipfw_enable_skipto_cache(chain);
28524a77657cSAndrey V. Elsukov V_skipto_cache = hdr->cmd;
28534a77657cSAndrey V. Elsukov }
28544a77657cSAndrey V. Elsukov IPFW_UH_WUNLOCK(chain);
28554a77657cSAndrey V. Elsukov return (0);
28564a77657cSAndrey V. Elsukov }
28574a77657cSAndrey V. Elsukov
285874b22066SAlexander V. Chernikov /*
28596b988f3aSAlexander V. Chernikov * Compares two sopt handlers (code, version and handler ptr).
28606b988f3aSAlexander V. Chernikov * Used both as qsort() and bsearch().
28616b988f3aSAlexander V. Chernikov * Does not compare handler for latter case.
28626b988f3aSAlexander V. Chernikov *
28636b988f3aSAlexander V. Chernikov * Returns 0 if match is found.
28646b988f3aSAlexander V. Chernikov */
28656b988f3aSAlexander V. Chernikov static int
compare_sh(const void * _a,const void * _b)28666b988f3aSAlexander V. Chernikov compare_sh(const void *_a, const void *_b)
28676b988f3aSAlexander V. Chernikov {
2868e530ca73SAlexander V. Chernikov const struct ipfw_sopt_handler *a, *b;
28696b988f3aSAlexander V. Chernikov
2870e530ca73SAlexander V. Chernikov a = (const struct ipfw_sopt_handler *)_a;
2871e530ca73SAlexander V. Chernikov b = (const struct ipfw_sopt_handler *)_b;
28726b988f3aSAlexander V. Chernikov
28736b988f3aSAlexander V. Chernikov if (a->opcode < b->opcode)
28746b988f3aSAlexander V. Chernikov return (-1);
28756b988f3aSAlexander V. Chernikov else if (a->opcode > b->opcode)
28766b988f3aSAlexander V. Chernikov return (1);
28776b988f3aSAlexander V. Chernikov
28786b988f3aSAlexander V. Chernikov if (a->version < b->version)
28796b988f3aSAlexander V. Chernikov return (-1);
28806b988f3aSAlexander V. Chernikov else if (a->version > b->version)
28816b988f3aSAlexander V. Chernikov return (1);
28826b988f3aSAlexander V. Chernikov
28836b988f3aSAlexander V. Chernikov /* bsearch helper */
28846b988f3aSAlexander V. Chernikov if (a->handler == NULL)
28856b988f3aSAlexander V. Chernikov return (0);
28866b988f3aSAlexander V. Chernikov
28876b988f3aSAlexander V. Chernikov if ((uintptr_t)a->handler < (uintptr_t)b->handler)
28886b988f3aSAlexander V. Chernikov return (-1);
2889cd82d21bSGleb Smirnoff else if ((uintptr_t)a->handler > (uintptr_t)b->handler)
28906b988f3aSAlexander V. Chernikov return (1);
28916b988f3aSAlexander V. Chernikov
28926b988f3aSAlexander V. Chernikov return (0);
28936b988f3aSAlexander V. Chernikov }
28946b988f3aSAlexander V. Chernikov
28956b988f3aSAlexander V. Chernikov /*
28966b988f3aSAlexander V. Chernikov * Finds sopt handler based on @code and @version.
28976b988f3aSAlexander V. Chernikov *
28986b988f3aSAlexander V. Chernikov * Returns pointer to handler or NULL.
28996b988f3aSAlexander V. Chernikov */
29006b988f3aSAlexander V. Chernikov static struct ipfw_sopt_handler *
find_sh(uint16_t code,uint8_t version,sopt_handler_f * handler)290162f42cf8SLuigi Rizzo find_sh(uint16_t code, uint8_t version, sopt_handler_f *handler)
29026b988f3aSAlexander V. Chernikov {
29036b988f3aSAlexander V. Chernikov struct ipfw_sopt_handler *sh, h;
29046b988f3aSAlexander V. Chernikov
29056b988f3aSAlexander V. Chernikov memset(&h, 0, sizeof(h));
29066b988f3aSAlexander V. Chernikov h.opcode = code;
29076b988f3aSAlexander V. Chernikov h.version = version;
29086b988f3aSAlexander V. Chernikov h.handler = handler;
29096b988f3aSAlexander V. Chernikov
29106b988f3aSAlexander V. Chernikov sh = (struct ipfw_sopt_handler *)bsearch(&h, ctl3_handlers,
29116b988f3aSAlexander V. Chernikov ctl3_hsize, sizeof(h), compare_sh);
29126b988f3aSAlexander V. Chernikov
29136b988f3aSAlexander V. Chernikov return (sh);
29146b988f3aSAlexander V. Chernikov }
29156b988f3aSAlexander V. Chernikov
29166b988f3aSAlexander V. Chernikov static int
find_ref_sh(uint16_t opcode,uint8_t version,struct ipfw_sopt_handler * psh)29176b988f3aSAlexander V. Chernikov find_ref_sh(uint16_t opcode, uint8_t version, struct ipfw_sopt_handler *psh)
29186b988f3aSAlexander V. Chernikov {
29196b988f3aSAlexander V. Chernikov struct ipfw_sopt_handler *sh;
29206b988f3aSAlexander V. Chernikov
29216b988f3aSAlexander V. Chernikov CTL3_LOCK();
29226b988f3aSAlexander V. Chernikov if ((sh = find_sh(opcode, version, NULL)) == NULL) {
29236b988f3aSAlexander V. Chernikov CTL3_UNLOCK();
29246b988f3aSAlexander V. Chernikov printf("ipfw: ipfw_ctl3 invalid option %d""v""%d\n",
29256b988f3aSAlexander V. Chernikov opcode, version);
29266b988f3aSAlexander V. Chernikov return (EINVAL);
29276b988f3aSAlexander V. Chernikov }
29286b988f3aSAlexander V. Chernikov sh->refcnt++;
29296b988f3aSAlexander V. Chernikov ctl3_refct++;
29306b988f3aSAlexander V. Chernikov /* Copy handler data to requested buffer */
29316b988f3aSAlexander V. Chernikov *psh = *sh;
29326b988f3aSAlexander V. Chernikov CTL3_UNLOCK();
29336b988f3aSAlexander V. Chernikov
29346b988f3aSAlexander V. Chernikov return (0);
29356b988f3aSAlexander V. Chernikov }
29366b988f3aSAlexander V. Chernikov
29376b988f3aSAlexander V. Chernikov static void
find_unref_sh(struct ipfw_sopt_handler * psh)29386b988f3aSAlexander V. Chernikov find_unref_sh(struct ipfw_sopt_handler *psh)
29396b988f3aSAlexander V. Chernikov {
29406b988f3aSAlexander V. Chernikov struct ipfw_sopt_handler *sh;
29416b988f3aSAlexander V. Chernikov
29426b988f3aSAlexander V. Chernikov CTL3_LOCK();
29436b988f3aSAlexander V. Chernikov sh = find_sh(psh->opcode, psh->version, NULL);
29446b988f3aSAlexander V. Chernikov KASSERT(sh != NULL, ("ctl3 handler disappeared"));
29456b988f3aSAlexander V. Chernikov sh->refcnt--;
29466b988f3aSAlexander V. Chernikov ctl3_refct--;
29476b988f3aSAlexander V. Chernikov CTL3_UNLOCK();
29486b988f3aSAlexander V. Chernikov }
29496b988f3aSAlexander V. Chernikov
29506b988f3aSAlexander V. Chernikov void
ipfw_init_sopt_handler(void)2951d62830c5SDimitry Andric ipfw_init_sopt_handler(void)
29526b988f3aSAlexander V. Chernikov {
29536b988f3aSAlexander V. Chernikov CTL3_LOCK_INIT();
29546b988f3aSAlexander V. Chernikov IPFW_ADD_SOPT_HANDLER(1, scodes);
29556b988f3aSAlexander V. Chernikov }
29566b988f3aSAlexander V. Chernikov
29576b988f3aSAlexander V. Chernikov void
ipfw_destroy_sopt_handler(void)2958d62830c5SDimitry Andric ipfw_destroy_sopt_handler(void)
29596b988f3aSAlexander V. Chernikov {
29606b988f3aSAlexander V. Chernikov IPFW_DEL_SOPT_HANDLER(1, scodes);
29616b988f3aSAlexander V. Chernikov CTL3_LOCK_DESTROY();
29626b988f3aSAlexander V. Chernikov }
29636b988f3aSAlexander V. Chernikov
29644a77657cSAndrey V. Elsukov void
ipfw_register_compat(ipfw_check_opcode_t f)29654a77657cSAndrey V. Elsukov ipfw_register_compat(ipfw_check_opcode_t f)
29664a77657cSAndrey V. Elsukov {
29674a77657cSAndrey V. Elsukov check_opcode_f = f;
29684a77657cSAndrey V. Elsukov }
29694a77657cSAndrey V. Elsukov
29704a77657cSAndrey V. Elsukov void
ipfw_unregister_compat(void)29714a77657cSAndrey V. Elsukov ipfw_unregister_compat(void)
29724a77657cSAndrey V. Elsukov {
29734a77657cSAndrey V. Elsukov check_opcode_f = check_opcode_compat_nop;
29744a77657cSAndrey V. Elsukov }
29754a77657cSAndrey V. Elsukov
29766b988f3aSAlexander V. Chernikov /*
29776b988f3aSAlexander V. Chernikov * Adds one or more sockopt handlers to the global array.
29786b988f3aSAlexander V. Chernikov * Function may sleep.
29796b988f3aSAlexander V. Chernikov */
29806b988f3aSAlexander V. Chernikov void
ipfw_add_sopt_handler(struct ipfw_sopt_handler * sh,size_t count)29816b988f3aSAlexander V. Chernikov ipfw_add_sopt_handler(struct ipfw_sopt_handler *sh, size_t count)
29826b988f3aSAlexander V. Chernikov {
29836b988f3aSAlexander V. Chernikov size_t sz;
29846b988f3aSAlexander V. Chernikov struct ipfw_sopt_handler *tmp;
29856b988f3aSAlexander V. Chernikov
29866b988f3aSAlexander V. Chernikov CTL3_LOCK();
29876b988f3aSAlexander V. Chernikov
29886b988f3aSAlexander V. Chernikov for (;;) {
29896b988f3aSAlexander V. Chernikov sz = ctl3_hsize + count;
29906b988f3aSAlexander V. Chernikov CTL3_UNLOCK();
29916b988f3aSAlexander V. Chernikov tmp = malloc(sizeof(*sh) * sz, M_IPFW, M_WAITOK | M_ZERO);
29926b988f3aSAlexander V. Chernikov CTL3_LOCK();
29936b988f3aSAlexander V. Chernikov if (ctl3_hsize + count <= sz)
29946b988f3aSAlexander V. Chernikov break;
29956b988f3aSAlexander V. Chernikov
29966b988f3aSAlexander V. Chernikov /* Retry */
29976b988f3aSAlexander V. Chernikov free(tmp, M_IPFW);
29986b988f3aSAlexander V. Chernikov }
29996b988f3aSAlexander V. Chernikov
30006b988f3aSAlexander V. Chernikov /* Merge old & new arrays */
30016b988f3aSAlexander V. Chernikov sz = ctl3_hsize + count;
30026b988f3aSAlexander V. Chernikov memcpy(tmp, ctl3_handlers, ctl3_hsize * sizeof(*sh));
30036b988f3aSAlexander V. Chernikov memcpy(&tmp[ctl3_hsize], sh, count * sizeof(*sh));
30046b988f3aSAlexander V. Chernikov qsort(tmp, sz, sizeof(*sh), compare_sh);
30056b988f3aSAlexander V. Chernikov /* Switch new and free old */
30066b988f3aSAlexander V. Chernikov if (ctl3_handlers != NULL)
30076b988f3aSAlexander V. Chernikov free(ctl3_handlers, M_IPFW);
30086b988f3aSAlexander V. Chernikov ctl3_handlers = tmp;
30096b988f3aSAlexander V. Chernikov ctl3_hsize = sz;
30106b988f3aSAlexander V. Chernikov ctl3_gencnt++;
30116b988f3aSAlexander V. Chernikov
30126b988f3aSAlexander V. Chernikov CTL3_UNLOCK();
30136b988f3aSAlexander V. Chernikov }
30146b988f3aSAlexander V. Chernikov
30156b988f3aSAlexander V. Chernikov /*
30166b988f3aSAlexander V. Chernikov * Removes one or more sockopt handlers from the global array.
30176b988f3aSAlexander V. Chernikov */
30186b988f3aSAlexander V. Chernikov int
ipfw_del_sopt_handler(struct ipfw_sopt_handler * sh,size_t count)30196b988f3aSAlexander V. Chernikov ipfw_del_sopt_handler(struct ipfw_sopt_handler *sh, size_t count)
30206b988f3aSAlexander V. Chernikov {
30216b988f3aSAlexander V. Chernikov size_t sz;
30226b988f3aSAlexander V. Chernikov struct ipfw_sopt_handler *tmp, *h;
30236b988f3aSAlexander V. Chernikov int i;
30246b988f3aSAlexander V. Chernikov
30256b988f3aSAlexander V. Chernikov CTL3_LOCK();
30266b988f3aSAlexander V. Chernikov
30276b988f3aSAlexander V. Chernikov for (i = 0; i < count; i++) {
30286b988f3aSAlexander V. Chernikov tmp = &sh[i];
30296b988f3aSAlexander V. Chernikov h = find_sh(tmp->opcode, tmp->version, tmp->handler);
30306b988f3aSAlexander V. Chernikov if (h == NULL)
30316b988f3aSAlexander V. Chernikov continue;
30326b988f3aSAlexander V. Chernikov
30336b988f3aSAlexander V. Chernikov sz = (ctl3_handlers + ctl3_hsize - (h + 1)) * sizeof(*h);
30346b988f3aSAlexander V. Chernikov memmove(h, h + 1, sz);
30356b988f3aSAlexander V. Chernikov ctl3_hsize--;
30366b988f3aSAlexander V. Chernikov }
30376b988f3aSAlexander V. Chernikov
30386b988f3aSAlexander V. Chernikov if (ctl3_hsize == 0) {
30396b988f3aSAlexander V. Chernikov if (ctl3_handlers != NULL)
30406b988f3aSAlexander V. Chernikov free(ctl3_handlers, M_IPFW);
30416b988f3aSAlexander V. Chernikov ctl3_handlers = NULL;
30426b988f3aSAlexander V. Chernikov }
30436b988f3aSAlexander V. Chernikov
30446b988f3aSAlexander V. Chernikov ctl3_gencnt++;
30456b988f3aSAlexander V. Chernikov
30466b988f3aSAlexander V. Chernikov CTL3_UNLOCK();
30476b988f3aSAlexander V. Chernikov
30486b988f3aSAlexander V. Chernikov return (0);
30496b988f3aSAlexander V. Chernikov }
30506b988f3aSAlexander V. Chernikov
30516b988f3aSAlexander V. Chernikov /*
305291e721d7SAlexander V. Chernikov * Writes data accumulated in @sd to sockopt buffer.
305391e721d7SAlexander V. Chernikov * Zeroes internal @sd buffer.
305491e721d7SAlexander V. Chernikov */
305591e721d7SAlexander V. Chernikov static int
ipfw_flush_sopt_data(struct sockopt_data * sd)305691e721d7SAlexander V. Chernikov ipfw_flush_sopt_data(struct sockopt_data *sd)
305791e721d7SAlexander V. Chernikov {
305854b38fcfSAlexander V. Chernikov struct sockopt *sopt;
305991e721d7SAlexander V. Chernikov int error;
306091e721d7SAlexander V. Chernikov size_t sz;
306191e721d7SAlexander V. Chernikov
306254b38fcfSAlexander V. Chernikov sz = sd->koff;
306354b38fcfSAlexander V. Chernikov if (sz == 0)
306491e721d7SAlexander V. Chernikov return (0);
306591e721d7SAlexander V. Chernikov
306654b38fcfSAlexander V. Chernikov sopt = sd->sopt;
306754b38fcfSAlexander V. Chernikov
306854b38fcfSAlexander V. Chernikov if (sopt->sopt_dir == SOPT_GET) {
306954b38fcfSAlexander V. Chernikov error = copyout(sd->kbuf, sopt->sopt_val, sz);
307091e721d7SAlexander V. Chernikov if (error != 0)
307191e721d7SAlexander V. Chernikov return (error);
307291e721d7SAlexander V. Chernikov }
307391e721d7SAlexander V. Chernikov
307491e721d7SAlexander V. Chernikov memset(sd->kbuf, 0, sd->ksize);
307554b38fcfSAlexander V. Chernikov sd->ktotal += sz;
307691e721d7SAlexander V. Chernikov sd->koff = 0;
307791e721d7SAlexander V. Chernikov if (sd->ktotal + sd->ksize < sd->valsize)
307891e721d7SAlexander V. Chernikov sd->kavail = sd->ksize;
307991e721d7SAlexander V. Chernikov else
308091e721d7SAlexander V. Chernikov sd->kavail = sd->valsize - sd->ktotal;
308191e721d7SAlexander V. Chernikov
308254b38fcfSAlexander V. Chernikov /* Update sopt buffer data */
308354b38fcfSAlexander V. Chernikov sopt->sopt_valsize = sd->ktotal;
308454b38fcfSAlexander V. Chernikov sopt->sopt_val = sd->sopt_val + sd->ktotal;
308591e721d7SAlexander V. Chernikov
308691e721d7SAlexander V. Chernikov return (0);
308791e721d7SAlexander V. Chernikov }
308891e721d7SAlexander V. Chernikov
308991e721d7SAlexander V. Chernikov /*
3090a4641f4eSPedro F. Giffuni * Ensures that @sd buffer has contiguous @neeeded number of
309191e721d7SAlexander V. Chernikov * bytes.
309291e721d7SAlexander V. Chernikov *
309391e721d7SAlexander V. Chernikov * Returns pointer to requested space or NULL.
309491e721d7SAlexander V. Chernikov */
309591e721d7SAlexander V. Chernikov caddr_t
ipfw_get_sopt_space(struct sockopt_data * sd,size_t needed)309691e721d7SAlexander V. Chernikov ipfw_get_sopt_space(struct sockopt_data *sd, size_t needed)
309791e721d7SAlexander V. Chernikov {
309891e721d7SAlexander V. Chernikov int error;
309991e721d7SAlexander V. Chernikov caddr_t addr;
310091e721d7SAlexander V. Chernikov
310191e721d7SAlexander V. Chernikov if (sd->kavail < needed) {
310291e721d7SAlexander V. Chernikov /*
310391e721d7SAlexander V. Chernikov * Flush data and try another time.
310491e721d7SAlexander V. Chernikov */
310591e721d7SAlexander V. Chernikov error = ipfw_flush_sopt_data(sd);
310691e721d7SAlexander V. Chernikov
310791e721d7SAlexander V. Chernikov if (sd->kavail < needed || error != 0)
310891e721d7SAlexander V. Chernikov return (NULL);
310991e721d7SAlexander V. Chernikov }
311091e721d7SAlexander V. Chernikov
311191e721d7SAlexander V. Chernikov addr = sd->kbuf + sd->koff;
311291e721d7SAlexander V. Chernikov sd->koff += needed;
311391e721d7SAlexander V. Chernikov sd->kavail -= needed;
311491e721d7SAlexander V. Chernikov return (addr);
311591e721d7SAlexander V. Chernikov }
311691e721d7SAlexander V. Chernikov
311791e721d7SAlexander V. Chernikov /*
3118a4641f4eSPedro F. Giffuni * Requests @needed contiguous bytes from @sd buffer.
311991e721d7SAlexander V. Chernikov * Function is used to notify subsystem that we are
312091e721d7SAlexander V. Chernikov * interesed in first @needed bytes (request header)
312191e721d7SAlexander V. Chernikov * and the rest buffer can be safely zeroed.
312291e721d7SAlexander V. Chernikov *
312391e721d7SAlexander V. Chernikov * Returns pointer to requested space or NULL.
312491e721d7SAlexander V. Chernikov */
312591e721d7SAlexander V. Chernikov caddr_t
ipfw_get_sopt_header(struct sockopt_data * sd,size_t needed)312691e721d7SAlexander V. Chernikov ipfw_get_sopt_header(struct sockopt_data *sd, size_t needed)
312791e721d7SAlexander V. Chernikov {
312891e721d7SAlexander V. Chernikov caddr_t addr;
312991e721d7SAlexander V. Chernikov
313091e721d7SAlexander V. Chernikov if ((addr = ipfw_get_sopt_space(sd, needed)) == NULL)
313191e721d7SAlexander V. Chernikov return (NULL);
313291e721d7SAlexander V. Chernikov
313391e721d7SAlexander V. Chernikov if (sd->kavail > 0)
313491e721d7SAlexander V. Chernikov memset(sd->kbuf + sd->koff, 0, sd->kavail);
313591e721d7SAlexander V. Chernikov
313691e721d7SAlexander V. Chernikov return (addr);
313791e721d7SAlexander V. Chernikov }
313891e721d7SAlexander V. Chernikov
313991e721d7SAlexander V. Chernikov /*
314091e721d7SAlexander V. Chernikov * New sockopt handler.
314191e721d7SAlexander V. Chernikov */
31423b3a8eb9SGleb Smirnoff int
ipfw_ctl3(struct sockopt * sopt)3143b429d43cSAlexander V. Chernikov ipfw_ctl3(struct sockopt *sopt)
31443b3a8eb9SGleb Smirnoff {
3145ce575f53SAlexander V. Chernikov int error, locked;
31466b988f3aSAlexander V. Chernikov size_t size, valsize;
31473b3a8eb9SGleb Smirnoff struct ip_fw_chain *chain;
3148914bffb6SAlexander V. Chernikov char xbuf[256];
31492d99a349SAlexander V. Chernikov struct sockopt_data sdata;
31506b988f3aSAlexander V. Chernikov struct ipfw_sopt_handler h;
31513b3a8eb9SGleb Smirnoff ip_fw3_opheader *op3 = NULL;
31523b3a8eb9SGleb Smirnoff
31533b3a8eb9SGleb Smirnoff error = priv_check(sopt->sopt_td, PRIV_NETINET_IPFW);
3154b429d43cSAlexander V. Chernikov if (error != 0)
31553b3a8eb9SGleb Smirnoff return (error);
3156b429d43cSAlexander V. Chernikov
3157b429d43cSAlexander V. Chernikov if (sopt->sopt_name != IP_FW3)
31584a77657cSAndrey V. Elsukov return (EOPNOTSUPP);
31593b3a8eb9SGleb Smirnoff
31603b3a8eb9SGleb Smirnoff chain = &V_layer3_chain;
31613b3a8eb9SGleb Smirnoff error = 0;
31623b3a8eb9SGleb Smirnoff
31633b3a8eb9SGleb Smirnoff /* Save original valsize before it is altered via sooptcopyin() */
31643b3a8eb9SGleb Smirnoff valsize = sopt->sopt_valsize;
31652d99a349SAlexander V. Chernikov memset(&sdata, 0, sizeof(sdata));
31666c2997ffSAlexander V. Chernikov /* Read op3 header first to determine actual operation */
31676c2997ffSAlexander V. Chernikov op3 = (ip_fw3_opheader *)xbuf;
31686c2997ffSAlexander V. Chernikov error = sooptcopyin(sopt, op3, sizeof(*op3), sizeof(*op3));
31696c2997ffSAlexander V. Chernikov if (error != 0)
31706c2997ffSAlexander V. Chernikov return (error);
31716c2997ffSAlexander V. Chernikov sopt->sopt_valsize = valsize;
31726c2997ffSAlexander V. Chernikov
31736c2997ffSAlexander V. Chernikov /*
31746b988f3aSAlexander V. Chernikov * Find and reference command.
31756c2997ffSAlexander V. Chernikov */
31766b988f3aSAlexander V. Chernikov error = find_ref_sh(op3->opcode, op3->version, &h);
31776b988f3aSAlexander V. Chernikov if (error != 0)
31786b988f3aSAlexander V. Chernikov return (error);
31793a845e10SAlexander V. Chernikov
31803a845e10SAlexander V. Chernikov /*
31813a845e10SAlexander V. Chernikov * Disallow modifications in really-really secure mode, but still allow
31823a845e10SAlexander V. Chernikov * the logging counters to be reset.
31833a845e10SAlexander V. Chernikov */
31846b988f3aSAlexander V. Chernikov if ((h.dir & HDIR_SET) != 0 && h.opcode != IP_FW_XRESETLOG) {
31853a845e10SAlexander V. Chernikov error = securelevel_ge(sopt->sopt_td->td_ucred, 3);
31866b988f3aSAlexander V. Chernikov if (error != 0) {
31876b988f3aSAlexander V. Chernikov find_unref_sh(&h);
31883a845e10SAlexander V. Chernikov return (error);
31893a845e10SAlexander V. Chernikov }
31906b988f3aSAlexander V. Chernikov }
31916c2997ffSAlexander V. Chernikov
31923b3a8eb9SGleb Smirnoff /*
31932d99a349SAlexander V. Chernikov * Fill in sockopt_data structure that may be useful for
31946c2997ffSAlexander V. Chernikov * IP_FW3 get requests.
31953b3a8eb9SGleb Smirnoff */
3196ce575f53SAlexander V. Chernikov locked = 0;
31972d99a349SAlexander V. Chernikov if (valsize <= sizeof(xbuf)) {
31986b988f3aSAlexander V. Chernikov /* use on-stack buffer */
31992d99a349SAlexander V. Chernikov sdata.kbuf = xbuf;
32002d99a349SAlexander V. Chernikov sdata.ksize = sizeof(xbuf);
32012d99a349SAlexander V. Chernikov sdata.kavail = valsize;
32022d99a349SAlexander V. Chernikov } else {
32036b988f3aSAlexander V. Chernikov /*
32046b988f3aSAlexander V. Chernikov * Determine opcode type/buffer size:
32056b988f3aSAlexander V. Chernikov * allocate sliding-window buf for data export or
3206a4641f4eSPedro F. Giffuni * contiguous buffer for special ops.
32076b988f3aSAlexander V. Chernikov */
32086b988f3aSAlexander V. Chernikov if ((h.dir & HDIR_SET) != 0) {
32096b988f3aSAlexander V. Chernikov /* Set request. Allocate contigous buffer. */
32106b988f3aSAlexander V. Chernikov if (valsize > CTL3_LARGEBUF) {
32116b988f3aSAlexander V. Chernikov find_unref_sh(&h);
32126b988f3aSAlexander V. Chernikov return (EFBIG);
32136b988f3aSAlexander V. Chernikov }
32146b988f3aSAlexander V. Chernikov
32152d99a349SAlexander V. Chernikov size = valsize;
32166b988f3aSAlexander V. Chernikov } else {
32176b988f3aSAlexander V. Chernikov /* Get request. Allocate sliding window buffer */
32186b988f3aSAlexander V. Chernikov size = (valsize<CTL3_SMALLBUF) ? valsize:CTL3_SMALLBUF;
3219ce575f53SAlexander V. Chernikov
3220ce575f53SAlexander V. Chernikov if (size < valsize) {
3221ce575f53SAlexander V. Chernikov /* We have to wire user buffer */
3222ce575f53SAlexander V. Chernikov error = vslock(sopt->sopt_val, valsize);
3223ce575f53SAlexander V. Chernikov if (error != 0)
3224ce575f53SAlexander V. Chernikov return (error);
3225ce575f53SAlexander V. Chernikov locked = 1;
3226ce575f53SAlexander V. Chernikov }
32276b988f3aSAlexander V. Chernikov }
32282d99a349SAlexander V. Chernikov
32292d99a349SAlexander V. Chernikov sdata.kbuf = malloc(size, M_TEMP, M_WAITOK | M_ZERO);
32302d99a349SAlexander V. Chernikov sdata.ksize = size;
32312d99a349SAlexander V. Chernikov sdata.kavail = size;
32322d99a349SAlexander V. Chernikov }
32332d99a349SAlexander V. Chernikov
32342d99a349SAlexander V. Chernikov sdata.sopt = sopt;
3235b6ee846eSAlexander V. Chernikov sdata.sopt_val = sopt->sopt_val;
32362d99a349SAlexander V. Chernikov sdata.valsize = valsize;
32372d99a349SAlexander V. Chernikov
32382d99a349SAlexander V. Chernikov /*
32396c2997ffSAlexander V. Chernikov * Copy either all request (if valsize < bsize_max)
32406c2997ffSAlexander V. Chernikov * or first bsize_max bytes to guarantee most consumers
32412d99a349SAlexander V. Chernikov * that all necessary data has been copied).
32422d99a349SAlexander V. Chernikov * Anyway, copy not less than sizeof(ip_fw3_opheader).
32432d99a349SAlexander V. Chernikov */
32442d99a349SAlexander V. Chernikov if ((error = sooptcopyin(sopt, sdata.kbuf, sdata.ksize,
32453b3a8eb9SGleb Smirnoff sizeof(ip_fw3_opheader))) != 0)
32463b3a8eb9SGleb Smirnoff return (error);
32472d99a349SAlexander V. Chernikov op3 = (ip_fw3_opheader *)sdata.kbuf;
3248b429d43cSAlexander V. Chernikov
32496b988f3aSAlexander V. Chernikov /* Finally, run handler */
32506b988f3aSAlexander V. Chernikov error = h.handler(chain, op3, &sdata);
32516b988f3aSAlexander V. Chernikov find_unref_sh(&h);
3252b429d43cSAlexander V. Chernikov
3253b429d43cSAlexander V. Chernikov /* Flush state and free buffers */
3254b429d43cSAlexander V. Chernikov if (error == 0)
3255b429d43cSAlexander V. Chernikov error = ipfw_flush_sopt_data(&sdata);
3256b429d43cSAlexander V. Chernikov else
3257b429d43cSAlexander V. Chernikov ipfw_flush_sopt_data(&sdata);
3258b429d43cSAlexander V. Chernikov
3259ce575f53SAlexander V. Chernikov if (locked != 0)
3260ce575f53SAlexander V. Chernikov vsunlock(sdata.sopt_val, valsize);
3261ce575f53SAlexander V. Chernikov
3262b6ee846eSAlexander V. Chernikov /* Restore original pointer and set number of bytes written */
3263b6ee846eSAlexander V. Chernikov sopt->sopt_val = sdata.sopt_val;
3264b6ee846eSAlexander V. Chernikov sopt->sopt_valsize = sdata.ktotal;
3265b429d43cSAlexander V. Chernikov if (sdata.kbuf != xbuf)
3266b429d43cSAlexander V. Chernikov free(sdata.kbuf, M_TEMP);
3267b429d43cSAlexander V. Chernikov
3268b429d43cSAlexander V. Chernikov return (error);
3269b429d43cSAlexander V. Chernikov }
3270b429d43cSAlexander V. Chernikov
3271b074b7bbSAlexander V. Chernikov /*
3272b074b7bbSAlexander V. Chernikov * Named object api
3273b074b7bbSAlexander V. Chernikov *
3274b074b7bbSAlexander V. Chernikov */
3275b074b7bbSAlexander V. Chernikov
327674b22066SAlexander V. Chernikov void
ipfw_init_srv(struct ip_fw_chain * ch)327774b22066SAlexander V. Chernikov ipfw_init_srv(struct ip_fw_chain *ch)
327874b22066SAlexander V. Chernikov {
32794a77657cSAndrey V. Elsukov ch->srvmap = ipfw_objhash_create(IPFW_OBJECTS_DEFAULT,
32804a77657cSAndrey V. Elsukov DEFAULT_OBJHASH_SIZE);
328174b22066SAlexander V. Chernikov ch->srvstate = malloc(sizeof(void *) * IPFW_OBJECTS_DEFAULT,
328274b22066SAlexander V. Chernikov M_IPFW, M_WAITOK | M_ZERO);
328374b22066SAlexander V. Chernikov }
328474b22066SAlexander V. Chernikov
328574b22066SAlexander V. Chernikov void
ipfw_destroy_srv(struct ip_fw_chain * ch)328674b22066SAlexander V. Chernikov ipfw_destroy_srv(struct ip_fw_chain *ch)
328774b22066SAlexander V. Chernikov {
328874b22066SAlexander V. Chernikov free(ch->srvstate, M_IPFW);
328974b22066SAlexander V. Chernikov ipfw_objhash_destroy(ch->srvmap);
329074b22066SAlexander V. Chernikov }
329174b22066SAlexander V. Chernikov
32929f7d47b0SAlexander V. Chernikov /*
32939f7d47b0SAlexander V. Chernikov * Allocate new bitmask which can be used to enlarge/shrink
32949f7d47b0SAlexander V. Chernikov * named instance index.
32959f7d47b0SAlexander V. Chernikov */
3296b074b7bbSAlexander V. Chernikov void
ipfw_objhash_bitmap_alloc(uint32_t items,void ** idx,int * pblocks)3297b074b7bbSAlexander V. Chernikov ipfw_objhash_bitmap_alloc(uint32_t items, void **idx, int *pblocks)
3298b074b7bbSAlexander V. Chernikov {
3299b074b7bbSAlexander V. Chernikov size_t size;
3300b074b7bbSAlexander V. Chernikov int max_blocks;
330156f43a5eSAlexander V. Chernikov u_long *idx_mask;
3302b074b7bbSAlexander V. Chernikov
33030cba2b28SAlexander V. Chernikov KASSERT((items % BLOCK_ITEMS) == 0,
33045f8ad2bdSAlexander V. Chernikov ("bitmask size needs to power of 2 and greater or equal to %zu",
33050cba2b28SAlexander V. Chernikov BLOCK_ITEMS));
33060cba2b28SAlexander V. Chernikov
3307b074b7bbSAlexander V. Chernikov max_blocks = items / BLOCK_ITEMS;
3308b074b7bbSAlexander V. Chernikov size = items / 8;
3309b074b7bbSAlexander V. Chernikov idx_mask = malloc(size * IPFW_MAX_SETS, M_IPFW, M_WAITOK);
3310b074b7bbSAlexander V. Chernikov /* Mark all as free */
3311b074b7bbSAlexander V. Chernikov memset(idx_mask, 0xFF, size * IPFW_MAX_SETS);
331256f43a5eSAlexander V. Chernikov *idx_mask &= ~(u_long)1; /* Skip index 0 */
3313b074b7bbSAlexander V. Chernikov
3314b074b7bbSAlexander V. Chernikov *idx = idx_mask;
3315b074b7bbSAlexander V. Chernikov *pblocks = max_blocks;
3316b074b7bbSAlexander V. Chernikov }
3317b074b7bbSAlexander V. Chernikov
33189f7d47b0SAlexander V. Chernikov /*
33199f7d47b0SAlexander V. Chernikov * Copy current bitmask index to new one.
33209f7d47b0SAlexander V. Chernikov */
33219f7d47b0SAlexander V. Chernikov void
ipfw_objhash_bitmap_merge(struct namedobj_instance * ni,void ** idx,int * blocks)3322b074b7bbSAlexander V. Chernikov ipfw_objhash_bitmap_merge(struct namedobj_instance *ni, void **idx, int *blocks)
3323b074b7bbSAlexander V. Chernikov {
3324b074b7bbSAlexander V. Chernikov int old_blocks, new_blocks;
3325b074b7bbSAlexander V. Chernikov u_long *old_idx, *new_idx;
3326b074b7bbSAlexander V. Chernikov int i;
3327b074b7bbSAlexander V. Chernikov
3328b074b7bbSAlexander V. Chernikov old_idx = ni->idx_mask;
3329b074b7bbSAlexander V. Chernikov old_blocks = ni->max_blocks;
3330b074b7bbSAlexander V. Chernikov new_idx = *idx;
3331b074b7bbSAlexander V. Chernikov new_blocks = *blocks;
3332b074b7bbSAlexander V. Chernikov
3333b074b7bbSAlexander V. Chernikov for (i = 0; i < IPFW_MAX_SETS; i++) {
3334b074b7bbSAlexander V. Chernikov memcpy(&new_idx[new_blocks * i], &old_idx[old_blocks * i],
3335b074b7bbSAlexander V. Chernikov old_blocks * sizeof(u_long));
3336b074b7bbSAlexander V. Chernikov }
33379f7d47b0SAlexander V. Chernikov }
3338b074b7bbSAlexander V. Chernikov
33399f7d47b0SAlexander V. Chernikov /*
33409f7d47b0SAlexander V. Chernikov * Swaps current @ni index with new one.
33419f7d47b0SAlexander V. Chernikov */
33429f7d47b0SAlexander V. Chernikov void
ipfw_objhash_bitmap_swap(struct namedobj_instance * ni,void ** idx,int * blocks)33439f7d47b0SAlexander V. Chernikov ipfw_objhash_bitmap_swap(struct namedobj_instance *ni, void **idx, int *blocks)
33449f7d47b0SAlexander V. Chernikov {
33459f7d47b0SAlexander V. Chernikov int old_blocks;
33469f7d47b0SAlexander V. Chernikov u_long *old_idx;
33479f7d47b0SAlexander V. Chernikov
33489f7d47b0SAlexander V. Chernikov old_idx = ni->idx_mask;
33499f7d47b0SAlexander V. Chernikov old_blocks = ni->max_blocks;
33509f7d47b0SAlexander V. Chernikov
33519f7d47b0SAlexander V. Chernikov ni->idx_mask = *idx;
33529f7d47b0SAlexander V. Chernikov ni->max_blocks = *blocks;
3353b074b7bbSAlexander V. Chernikov
3354b074b7bbSAlexander V. Chernikov /* Save old values */
3355b074b7bbSAlexander V. Chernikov *idx = old_idx;
3356b074b7bbSAlexander V. Chernikov *blocks = old_blocks;
3357b074b7bbSAlexander V. Chernikov }
3358b074b7bbSAlexander V. Chernikov
3359b074b7bbSAlexander V. Chernikov void
ipfw_objhash_bitmap_free(void * idx,int blocks)3360b074b7bbSAlexander V. Chernikov ipfw_objhash_bitmap_free(void *idx, int blocks)
3361b074b7bbSAlexander V. Chernikov {
3362b074b7bbSAlexander V. Chernikov free(idx, M_IPFW);
3363b074b7bbSAlexander V. Chernikov }
3364b074b7bbSAlexander V. Chernikov
3365b074b7bbSAlexander V. Chernikov /*
3366b074b7bbSAlexander V. Chernikov * Creates named hash instance.
3367b074b7bbSAlexander V. Chernikov * Must be called without holding any locks.
3368b074b7bbSAlexander V. Chernikov * Return pointer to new instance.
3369b074b7bbSAlexander V. Chernikov */
3370b074b7bbSAlexander V. Chernikov struct namedobj_instance *
ipfw_objhash_create(uint32_t items,size_t hash_size)33714a77657cSAndrey V. Elsukov ipfw_objhash_create(uint32_t items, size_t hash_size)
3372b074b7bbSAlexander V. Chernikov {
3373b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni;
3374b074b7bbSAlexander V. Chernikov int i;
3375b074b7bbSAlexander V. Chernikov size_t size;
3376b074b7bbSAlexander V. Chernikov
3377b074b7bbSAlexander V. Chernikov size = sizeof(struct namedobj_instance) +
33784a77657cSAndrey V. Elsukov sizeof(struct namedobjects_head) * hash_size +
33794a77657cSAndrey V. Elsukov sizeof(struct namedobjects_head) * hash_size;
3380b074b7bbSAlexander V. Chernikov
3381b074b7bbSAlexander V. Chernikov ni = malloc(size, M_IPFW, M_WAITOK | M_ZERO);
33824a77657cSAndrey V. Elsukov ni->nn_size = hash_size;
33834a77657cSAndrey V. Elsukov ni->nv_size = hash_size;
3384b074b7bbSAlexander V. Chernikov
3385b074b7bbSAlexander V. Chernikov ni->names = (struct namedobjects_head *)(ni +1);
3386b074b7bbSAlexander V. Chernikov ni->values = &ni->names[ni->nn_size];
3387b074b7bbSAlexander V. Chernikov
3388b074b7bbSAlexander V. Chernikov for (i = 0; i < ni->nn_size; i++)
3389b074b7bbSAlexander V. Chernikov TAILQ_INIT(&ni->names[i]);
3390b074b7bbSAlexander V. Chernikov
3391b074b7bbSAlexander V. Chernikov for (i = 0; i < ni->nv_size; i++)
3392b074b7bbSAlexander V. Chernikov TAILQ_INIT(&ni->values[i]);
3393b074b7bbSAlexander V. Chernikov
339413263632SAlexander V. Chernikov /* Set default hashing/comparison functions */
339513263632SAlexander V. Chernikov ni->hash_f = objhash_hash_name;
339613263632SAlexander V. Chernikov ni->cmp_f = objhash_cmp_name;
339713263632SAlexander V. Chernikov
3398b074b7bbSAlexander V. Chernikov /* Allocate bitmask separately due to possible resize */
3399b074b7bbSAlexander V. Chernikov ipfw_objhash_bitmap_alloc(items, (void*)&ni->idx_mask, &ni->max_blocks);
3400b074b7bbSAlexander V. Chernikov
3401b074b7bbSAlexander V. Chernikov return (ni);
3402b074b7bbSAlexander V. Chernikov }
3403b074b7bbSAlexander V. Chernikov
3404b074b7bbSAlexander V. Chernikov void
ipfw_objhash_destroy(struct namedobj_instance * ni)3405b074b7bbSAlexander V. Chernikov ipfw_objhash_destroy(struct namedobj_instance *ni)
3406b074b7bbSAlexander V. Chernikov {
3407b074b7bbSAlexander V. Chernikov free(ni->idx_mask, M_IPFW);
3408b074b7bbSAlexander V. Chernikov free(ni, M_IPFW);
3409b074b7bbSAlexander V. Chernikov }
3410b074b7bbSAlexander V. Chernikov
341113263632SAlexander V. Chernikov void
ipfw_objhash_set_funcs(struct namedobj_instance * ni,objhash_hash_f * hash_f,objhash_cmp_f * cmp_f)341213263632SAlexander V. Chernikov ipfw_objhash_set_funcs(struct namedobj_instance *ni, objhash_hash_f *hash_f,
341313263632SAlexander V. Chernikov objhash_cmp_f *cmp_f)
341413263632SAlexander V. Chernikov {
341513263632SAlexander V. Chernikov
341613263632SAlexander V. Chernikov ni->hash_f = hash_f;
341713263632SAlexander V. Chernikov ni->cmp_f = cmp_f;
341813263632SAlexander V. Chernikov }
341913263632SAlexander V. Chernikov
3420b074b7bbSAlexander V. Chernikov static uint32_t
objhash_hash_name(struct namedobj_instance * ni,const void * name,uint32_t set)34212acdf79fSAndrey V. Elsukov objhash_hash_name(struct namedobj_instance *ni, const void *name, uint32_t set)
3422b074b7bbSAlexander V. Chernikov {
3423b074b7bbSAlexander V. Chernikov
34242acdf79fSAndrey V. Elsukov return (fnv_32_str((const char *)name, FNV1_32_INIT));
3425b074b7bbSAlexander V. Chernikov }
3426b074b7bbSAlexander V. Chernikov
342713263632SAlexander V. Chernikov static int
objhash_cmp_name(struct named_object * no,const void * name,uint32_t set)34282acdf79fSAndrey V. Elsukov objhash_cmp_name(struct named_object *no, const void *name, uint32_t set)
342913263632SAlexander V. Chernikov {
343013263632SAlexander V. Chernikov
34312acdf79fSAndrey V. Elsukov if ((strcmp(no->name, (const char *)name) == 0) && (no->set == set))
343213263632SAlexander V. Chernikov return (0);
343313263632SAlexander V. Chernikov
343413263632SAlexander V. Chernikov return (1);
343513263632SAlexander V. Chernikov }
343613263632SAlexander V. Chernikov
3437b074b7bbSAlexander V. Chernikov static uint32_t
objhash_hash_idx(struct namedobj_instance * ni,uint32_t val)343813263632SAlexander V. Chernikov objhash_hash_idx(struct namedobj_instance *ni, uint32_t val)
3439b074b7bbSAlexander V. Chernikov {
3440b074b7bbSAlexander V. Chernikov uint32_t v;
3441b074b7bbSAlexander V. Chernikov
3442b074b7bbSAlexander V. Chernikov v = val % (ni->nv_size - 1);
3443b074b7bbSAlexander V. Chernikov
3444b074b7bbSAlexander V. Chernikov return (v);
3445b074b7bbSAlexander V. Chernikov }
3446b074b7bbSAlexander V. Chernikov
3447b074b7bbSAlexander V. Chernikov struct named_object *
ipfw_objhash_lookup_name(struct namedobj_instance * ni,uint32_t set,const char * name)3448e81fcbecSJustin Hibbits ipfw_objhash_lookup_name(struct namedobj_instance *ni, uint32_t set,
3449e81fcbecSJustin Hibbits const char *name)
3450b074b7bbSAlexander V. Chernikov {
3451b074b7bbSAlexander V. Chernikov struct named_object *no;
3452b074b7bbSAlexander V. Chernikov uint32_t hash;
3453b074b7bbSAlexander V. Chernikov
34540cba2b28SAlexander V. Chernikov hash = ni->hash_f(ni, name, set) % ni->nn_size;
3455b074b7bbSAlexander V. Chernikov
3456b074b7bbSAlexander V. Chernikov TAILQ_FOREACH(no, &ni->names[hash], nn_next) {
345713263632SAlexander V. Chernikov if (ni->cmp_f(no, name, set) == 0)
3458b074b7bbSAlexander V. Chernikov return (no);
3459b074b7bbSAlexander V. Chernikov }
3460b074b7bbSAlexander V. Chernikov
3461b074b7bbSAlexander V. Chernikov return (NULL);
3462b074b7bbSAlexander V. Chernikov }
3463b074b7bbSAlexander V. Chernikov
346474b22066SAlexander V. Chernikov /*
34652acdf79fSAndrey V. Elsukov * Find named object by @uid.
34662acdf79fSAndrey V. Elsukov * Check @tlvs for valid data inside.
34672acdf79fSAndrey V. Elsukov *
34682acdf79fSAndrey V. Elsukov * Returns pointer to found TLV or NULL.
34692acdf79fSAndrey V. Elsukov */
34702df1a11fSAndrey V. Elsukov ipfw_obj_ntlv *
ipfw_find_name_tlv_type(void * tlvs,int len,uint32_t uidx,uint32_t etlv)34714a77657cSAndrey V. Elsukov ipfw_find_name_tlv_type(void *tlvs, int len, uint32_t uidx, uint32_t etlv)
34722acdf79fSAndrey V. Elsukov {
34732acdf79fSAndrey V. Elsukov ipfw_obj_ntlv *ntlv;
34742acdf79fSAndrey V. Elsukov uintptr_t pa, pe;
34752acdf79fSAndrey V. Elsukov int l;
34762acdf79fSAndrey V. Elsukov
34772acdf79fSAndrey V. Elsukov pa = (uintptr_t)tlvs;
34782acdf79fSAndrey V. Elsukov pe = pa + len;
34792acdf79fSAndrey V. Elsukov l = 0;
34802acdf79fSAndrey V. Elsukov for (; pa < pe; pa += l) {
34812acdf79fSAndrey V. Elsukov ntlv = (ipfw_obj_ntlv *)pa;
34822acdf79fSAndrey V. Elsukov l = ntlv->head.length;
34832acdf79fSAndrey V. Elsukov
34842acdf79fSAndrey V. Elsukov if (l != sizeof(*ntlv))
34852acdf79fSAndrey V. Elsukov return (NULL);
34862acdf79fSAndrey V. Elsukov
34872acdf79fSAndrey V. Elsukov if (ntlv->idx != uidx)
34882acdf79fSAndrey V. Elsukov continue;
34892acdf79fSAndrey V. Elsukov /*
34902acdf79fSAndrey V. Elsukov * When userland has specified zero TLV type, do
34912acdf79fSAndrey V. Elsukov * not compare it with eltv. In some cases userland
34922acdf79fSAndrey V. Elsukov * doesn't know what type should it have. Use only
34932acdf79fSAndrey V. Elsukov * uidx and name for search named_object.
34942acdf79fSAndrey V. Elsukov */
34952acdf79fSAndrey V. Elsukov if (ntlv->head.type != 0 &&
34962acdf79fSAndrey V. Elsukov ntlv->head.type != (uint16_t)etlv)
34972acdf79fSAndrey V. Elsukov continue;
34982acdf79fSAndrey V. Elsukov
34992acdf79fSAndrey V. Elsukov if (ipfw_check_object_name_generic(ntlv->name) != 0)
35002acdf79fSAndrey V. Elsukov return (NULL);
35012acdf79fSAndrey V. Elsukov
35022acdf79fSAndrey V. Elsukov return (ntlv);
35032acdf79fSAndrey V. Elsukov }
35042acdf79fSAndrey V. Elsukov
35052acdf79fSAndrey V. Elsukov return (NULL);
35062acdf79fSAndrey V. Elsukov }
35072acdf79fSAndrey V. Elsukov
35082acdf79fSAndrey V. Elsukov /*
35092acdf79fSAndrey V. Elsukov * Finds object config based on either legacy index
35102acdf79fSAndrey V. Elsukov * or name in ntlv.
35112acdf79fSAndrey V. Elsukov * Note @ti structure contains unchecked data from userland.
35122acdf79fSAndrey V. Elsukov *
35132acdf79fSAndrey V. Elsukov * Returns 0 in success and fills in @pno with found config
35142acdf79fSAndrey V. Elsukov */
35152acdf79fSAndrey V. Elsukov int
ipfw_objhash_find_type(struct namedobj_instance * ni,struct tid_info * ti,uint32_t etlv,struct named_object ** pno)35162acdf79fSAndrey V. Elsukov ipfw_objhash_find_type(struct namedobj_instance *ni, struct tid_info *ti,
35172acdf79fSAndrey V. Elsukov uint32_t etlv, struct named_object **pno)
35182acdf79fSAndrey V. Elsukov {
35192acdf79fSAndrey V. Elsukov char *name;
35202acdf79fSAndrey V. Elsukov ipfw_obj_ntlv *ntlv;
35212acdf79fSAndrey V. Elsukov uint32_t set;
35222acdf79fSAndrey V. Elsukov
35232acdf79fSAndrey V. Elsukov if (ti->tlvs == NULL)
35242acdf79fSAndrey V. Elsukov return (EINVAL);
35252acdf79fSAndrey V. Elsukov
35262df1a11fSAndrey V. Elsukov ntlv = ipfw_find_name_tlv_type(ti->tlvs, ti->tlen, ti->uidx, etlv);
35272acdf79fSAndrey V. Elsukov if (ntlv == NULL)
35282acdf79fSAndrey V. Elsukov return (EINVAL);
35292acdf79fSAndrey V. Elsukov name = ntlv->name;
35302acdf79fSAndrey V. Elsukov
35312acdf79fSAndrey V. Elsukov /*
35322acdf79fSAndrey V. Elsukov * Use set provided by @ti instead of @ntlv one.
35332acdf79fSAndrey V. Elsukov * This is needed due to different sets behavior
35342acdf79fSAndrey V. Elsukov * controlled by V_fw_tables_sets.
35352acdf79fSAndrey V. Elsukov */
35362acdf79fSAndrey V. Elsukov set = ti->set;
35372acdf79fSAndrey V. Elsukov *pno = ipfw_objhash_lookup_name(ni, set, name);
35382acdf79fSAndrey V. Elsukov if (*pno == NULL)
35392acdf79fSAndrey V. Elsukov return (ESRCH);
35402acdf79fSAndrey V. Elsukov return (0);
35412acdf79fSAndrey V. Elsukov }
35422acdf79fSAndrey V. Elsukov
35432acdf79fSAndrey V. Elsukov /*
354474b22066SAlexander V. Chernikov * Find named object by name, considering also its TLV type.
354574b22066SAlexander V. Chernikov */
354674b22066SAlexander V. Chernikov struct named_object *
ipfw_objhash_lookup_name_type(struct namedobj_instance * ni,uint32_t set,uint32_t type,const char * name)354774b22066SAlexander V. Chernikov ipfw_objhash_lookup_name_type(struct namedobj_instance *ni, uint32_t set,
35482acdf79fSAndrey V. Elsukov uint32_t type, const char *name)
354974b22066SAlexander V. Chernikov {
355074b22066SAlexander V. Chernikov struct named_object *no;
355174b22066SAlexander V. Chernikov uint32_t hash;
355274b22066SAlexander V. Chernikov
355374b22066SAlexander V. Chernikov hash = ni->hash_f(ni, name, set) % ni->nn_size;
355474b22066SAlexander V. Chernikov
355574b22066SAlexander V. Chernikov TAILQ_FOREACH(no, &ni->names[hash], nn_next) {
35564bd91656SAndrey V. Elsukov if (ni->cmp_f(no, name, set) == 0 &&
35574bd91656SAndrey V. Elsukov no->etlv == (uint16_t)type)
355874b22066SAlexander V. Chernikov return (no);
355974b22066SAlexander V. Chernikov }
356074b22066SAlexander V. Chernikov
356174b22066SAlexander V. Chernikov return (NULL);
356274b22066SAlexander V. Chernikov }
356374b22066SAlexander V. Chernikov
3564b074b7bbSAlexander V. Chernikov struct named_object *
ipfw_objhash_lookup_kidx(struct namedobj_instance * ni,uint32_t kidx)35654a77657cSAndrey V. Elsukov ipfw_objhash_lookup_kidx(struct namedobj_instance *ni, uint32_t kidx)
3566b074b7bbSAlexander V. Chernikov {
3567b074b7bbSAlexander V. Chernikov struct named_object *no;
3568b074b7bbSAlexander V. Chernikov uint32_t hash;
3569b074b7bbSAlexander V. Chernikov
357013263632SAlexander V. Chernikov hash = objhash_hash_idx(ni, kidx);
3571b074b7bbSAlexander V. Chernikov
3572b074b7bbSAlexander V. Chernikov TAILQ_FOREACH(no, &ni->values[hash], nv_next) {
3573ac35ff17SAlexander V. Chernikov if (no->kidx == kidx)
3574b074b7bbSAlexander V. Chernikov return (no);
3575b074b7bbSAlexander V. Chernikov }
3576b074b7bbSAlexander V. Chernikov
3577b074b7bbSAlexander V. Chernikov return (NULL);
3578b074b7bbSAlexander V. Chernikov }
3579b074b7bbSAlexander V. Chernikov
35809490a627SAlexander V. Chernikov int
ipfw_objhash_same_name(struct namedobj_instance * ni,struct named_object * a,struct named_object * b)35819490a627SAlexander V. Chernikov ipfw_objhash_same_name(struct namedobj_instance *ni, struct named_object *a,
35829490a627SAlexander V. Chernikov struct named_object *b)
35839490a627SAlexander V. Chernikov {
35849490a627SAlexander V. Chernikov
35859490a627SAlexander V. Chernikov if ((strcmp(a->name, b->name) == 0) && a->set == b->set)
35869490a627SAlexander V. Chernikov return (1);
35879490a627SAlexander V. Chernikov
35889490a627SAlexander V. Chernikov return (0);
35899490a627SAlexander V. Chernikov }
35909490a627SAlexander V. Chernikov
3591b074b7bbSAlexander V. Chernikov void
ipfw_objhash_add(struct namedobj_instance * ni,struct named_object * no)3592b074b7bbSAlexander V. Chernikov ipfw_objhash_add(struct namedobj_instance *ni, struct named_object *no)
3593b074b7bbSAlexander V. Chernikov {
3594b074b7bbSAlexander V. Chernikov uint32_t hash;
3595b074b7bbSAlexander V. Chernikov
35960cba2b28SAlexander V. Chernikov hash = ni->hash_f(ni, no->name, no->set) % ni->nn_size;
3597b074b7bbSAlexander V. Chernikov TAILQ_INSERT_HEAD(&ni->names[hash], no, nn_next);
3598b074b7bbSAlexander V. Chernikov
359913263632SAlexander V. Chernikov hash = objhash_hash_idx(ni, no->kidx);
3600b074b7bbSAlexander V. Chernikov TAILQ_INSERT_HEAD(&ni->values[hash], no, nv_next);
36019f7d47b0SAlexander V. Chernikov
36029f7d47b0SAlexander V. Chernikov ni->count++;
3603b074b7bbSAlexander V. Chernikov }
3604b074b7bbSAlexander V. Chernikov
3605b074b7bbSAlexander V. Chernikov void
ipfw_objhash_del(struct namedobj_instance * ni,struct named_object * no)3606b074b7bbSAlexander V. Chernikov ipfw_objhash_del(struct namedobj_instance *ni, struct named_object *no)
3607b074b7bbSAlexander V. Chernikov {
3608b074b7bbSAlexander V. Chernikov uint32_t hash;
3609b074b7bbSAlexander V. Chernikov
36100cba2b28SAlexander V. Chernikov hash = ni->hash_f(ni, no->name, no->set) % ni->nn_size;
3611b074b7bbSAlexander V. Chernikov TAILQ_REMOVE(&ni->names[hash], no, nn_next);
3612b074b7bbSAlexander V. Chernikov
361313263632SAlexander V. Chernikov hash = objhash_hash_idx(ni, no->kidx);
3614b074b7bbSAlexander V. Chernikov TAILQ_REMOVE(&ni->values[hash], no, nv_next);
36159f7d47b0SAlexander V. Chernikov
36169f7d47b0SAlexander V. Chernikov ni->count--;
36179f7d47b0SAlexander V. Chernikov }
36189f7d47b0SAlexander V. Chernikov
36199f7d47b0SAlexander V. Chernikov uint32_t
ipfw_objhash_count(struct namedobj_instance * ni)36209f7d47b0SAlexander V. Chernikov ipfw_objhash_count(struct namedobj_instance *ni)
36219f7d47b0SAlexander V. Chernikov {
36229f7d47b0SAlexander V. Chernikov
36239f7d47b0SAlexander V. Chernikov return (ni->count);
3624b074b7bbSAlexander V. Chernikov }
3625b074b7bbSAlexander V. Chernikov
36262685841bSAndrey V. Elsukov uint32_t
ipfw_objhash_count_type(struct namedobj_instance * ni,uint16_t type)36272685841bSAndrey V. Elsukov ipfw_objhash_count_type(struct namedobj_instance *ni, uint16_t type)
36282685841bSAndrey V. Elsukov {
36292685841bSAndrey V. Elsukov struct named_object *no;
36302685841bSAndrey V. Elsukov uint32_t count;
36312685841bSAndrey V. Elsukov int i;
36322685841bSAndrey V. Elsukov
36332685841bSAndrey V. Elsukov count = 0;
36342685841bSAndrey V. Elsukov for (i = 0; i < ni->nn_size; i++) {
36352685841bSAndrey V. Elsukov TAILQ_FOREACH(no, &ni->names[i], nn_next) {
36362685841bSAndrey V. Elsukov if (no->etlv == type)
36372685841bSAndrey V. Elsukov count++;
36382685841bSAndrey V. Elsukov }
36392685841bSAndrey V. Elsukov }
36402685841bSAndrey V. Elsukov return (count);
36412685841bSAndrey V. Elsukov }
36422685841bSAndrey V. Elsukov
3643b074b7bbSAlexander V. Chernikov /*
3644b074b7bbSAlexander V. Chernikov * Runs @func for each found named object.
3645b074b7bbSAlexander V. Chernikov * It is safe to delete objects from callback
3646b074b7bbSAlexander V. Chernikov */
3647b309f085SAndrey V. Elsukov int
ipfw_objhash_foreach(struct namedobj_instance * ni,objhash_cb_t * f,void * arg)3648b074b7bbSAlexander V. Chernikov ipfw_objhash_foreach(struct namedobj_instance *ni, objhash_cb_t *f, void *arg)
3649b074b7bbSAlexander V. Chernikov {
3650b074b7bbSAlexander V. Chernikov struct named_object *no, *no_tmp;
3651b309f085SAndrey V. Elsukov int i, ret;
3652b074b7bbSAlexander V. Chernikov
3653b074b7bbSAlexander V. Chernikov for (i = 0; i < ni->nn_size; i++) {
3654b309f085SAndrey V. Elsukov TAILQ_FOREACH_SAFE(no, &ni->names[i], nn_next, no_tmp) {
3655b309f085SAndrey V. Elsukov ret = f(ni, no, arg);
3656b309f085SAndrey V. Elsukov if (ret != 0)
3657b309f085SAndrey V. Elsukov return (ret);
3658b074b7bbSAlexander V. Chernikov }
3659b074b7bbSAlexander V. Chernikov }
3660b309f085SAndrey V. Elsukov return (0);
3661b309f085SAndrey V. Elsukov }
3662b074b7bbSAlexander V. Chernikov
3663b074b7bbSAlexander V. Chernikov /*
36642685841bSAndrey V. Elsukov * Runs @f for each found named object with type @type.
36652685841bSAndrey V. Elsukov * It is safe to delete objects from callback
36662685841bSAndrey V. Elsukov */
36672685841bSAndrey V. Elsukov int
ipfw_objhash_foreach_type(struct namedobj_instance * ni,objhash_cb_t * f,void * arg,uint16_t type)36682685841bSAndrey V. Elsukov ipfw_objhash_foreach_type(struct namedobj_instance *ni, objhash_cb_t *f,
36692685841bSAndrey V. Elsukov void *arg, uint16_t type)
36702685841bSAndrey V. Elsukov {
36712685841bSAndrey V. Elsukov struct named_object *no, *no_tmp;
36722685841bSAndrey V. Elsukov int i, ret;
36732685841bSAndrey V. Elsukov
36742685841bSAndrey V. Elsukov for (i = 0; i < ni->nn_size; i++) {
36752685841bSAndrey V. Elsukov TAILQ_FOREACH_SAFE(no, &ni->names[i], nn_next, no_tmp) {
36762685841bSAndrey V. Elsukov if (no->etlv != type)
36772685841bSAndrey V. Elsukov continue;
36782685841bSAndrey V. Elsukov ret = f(ni, no, arg);
36792685841bSAndrey V. Elsukov if (ret != 0)
36802685841bSAndrey V. Elsukov return (ret);
36812685841bSAndrey V. Elsukov }
36822685841bSAndrey V. Elsukov }
36832685841bSAndrey V. Elsukov return (0);
36842685841bSAndrey V. Elsukov }
36852685841bSAndrey V. Elsukov
36862685841bSAndrey V. Elsukov /*
3687b074b7bbSAlexander V. Chernikov * Removes index from given set.
3688b074b7bbSAlexander V. Chernikov * Returns 0 on success.
3689b074b7bbSAlexander V. Chernikov */
3690b074b7bbSAlexander V. Chernikov int
ipfw_objhash_free_idx(struct namedobj_instance * ni,uint32_t idx)36914a77657cSAndrey V. Elsukov ipfw_objhash_free_idx(struct namedobj_instance *ni, uint32_t idx)
3692b074b7bbSAlexander V. Chernikov {
3693b074b7bbSAlexander V. Chernikov u_long *mask;
3694b074b7bbSAlexander V. Chernikov int i, v;
3695b074b7bbSAlexander V. Chernikov
3696b074b7bbSAlexander V. Chernikov i = idx / BLOCK_ITEMS;
3697b074b7bbSAlexander V. Chernikov v = idx % BLOCK_ITEMS;
3698b074b7bbSAlexander V. Chernikov
3699ac35ff17SAlexander V. Chernikov if (i >= ni->max_blocks)
3700b074b7bbSAlexander V. Chernikov return (1);
3701b074b7bbSAlexander V. Chernikov
3702ac35ff17SAlexander V. Chernikov mask = &ni->idx_mask[i];
3703b074b7bbSAlexander V. Chernikov
3704b074b7bbSAlexander V. Chernikov if ((*mask & ((u_long)1 << v)) != 0)
3705b074b7bbSAlexander V. Chernikov return (1);
3706b074b7bbSAlexander V. Chernikov
3707b074b7bbSAlexander V. Chernikov /* Mark as free */
3708b074b7bbSAlexander V. Chernikov *mask |= (u_long)1 << v;
3709b074b7bbSAlexander V. Chernikov
3710b074b7bbSAlexander V. Chernikov /* Update free offset */
3711ac35ff17SAlexander V. Chernikov if (ni->free_off[0] > i)
3712ac35ff17SAlexander V. Chernikov ni->free_off[0] = i;
3713b074b7bbSAlexander V. Chernikov
3714b074b7bbSAlexander V. Chernikov return (0);
3715b074b7bbSAlexander V. Chernikov }
3716b074b7bbSAlexander V. Chernikov
3717b074b7bbSAlexander V. Chernikov /*
371813263632SAlexander V. Chernikov * Allocate new index in given instance and stores in in @pidx.
3719b074b7bbSAlexander V. Chernikov * Returns 0 on success.
3720b074b7bbSAlexander V. Chernikov */
3721b074b7bbSAlexander V. Chernikov int
ipfw_objhash_alloc_idx(void * n,uint32_t * pidx)37224a77657cSAndrey V. Elsukov ipfw_objhash_alloc_idx(void *n, uint32_t *pidx)
3723b074b7bbSAlexander V. Chernikov {
3724b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni;
3725b074b7bbSAlexander V. Chernikov u_long *mask;
3726b074b7bbSAlexander V. Chernikov int i, off, v;
3727b074b7bbSAlexander V. Chernikov
3728b074b7bbSAlexander V. Chernikov ni = (struct namedobj_instance *)n;
3729b074b7bbSAlexander V. Chernikov
3730ac35ff17SAlexander V. Chernikov off = ni->free_off[0];
3731ac35ff17SAlexander V. Chernikov mask = &ni->idx_mask[off];
3732b074b7bbSAlexander V. Chernikov
3733b074b7bbSAlexander V. Chernikov for (i = off; i < ni->max_blocks; i++, mask++) {
3734b074b7bbSAlexander V. Chernikov if ((v = ffsl(*mask)) == 0)
3735b074b7bbSAlexander V. Chernikov continue;
3736b074b7bbSAlexander V. Chernikov
3737b074b7bbSAlexander V. Chernikov /* Mark as busy */
3738b074b7bbSAlexander V. Chernikov *mask &= ~ ((u_long)1 << (v - 1));
3739b074b7bbSAlexander V. Chernikov
3740ac35ff17SAlexander V. Chernikov ni->free_off[0] = i;
3741b074b7bbSAlexander V. Chernikov
3742b074b7bbSAlexander V. Chernikov v = BLOCK_ITEMS * i + v - 1;
3743b074b7bbSAlexander V. Chernikov
3744b074b7bbSAlexander V. Chernikov *pidx = v;
3745b074b7bbSAlexander V. Chernikov return (0);
3746b074b7bbSAlexander V. Chernikov }
3747b074b7bbSAlexander V. Chernikov
3748b074b7bbSAlexander V. Chernikov return (1);
3749b074b7bbSAlexander V. Chernikov }
3750b074b7bbSAlexander V. Chernikov
37513b3a8eb9SGleb Smirnoff /* end of file */
3752