13b3a8eb9SGleb Smirnoff /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3fe267a55SPedro F. Giffuni * 43b3a8eb9SGleb Smirnoff * Copyright (c) 2002-2009 Luigi Rizzo, Universita` di Pisa 5*4a77657cSAndrey 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 77*4a77657cSAndrey V. Elsukov static enum ipfw_opcheck_result 78*4a77657cSAndrey V. Elsukov check_opcode_compat_nop(ipfw_insn **pcmd, int *plen, 79*4a77657cSAndrey V. Elsukov struct rule_check_info *ci) 80*4a77657cSAndrey V. Elsukov { 81*4a77657cSAndrey V. Elsukov /* Compatibility code is not registered */ 82*4a77657cSAndrey V. Elsukov return (FAILED); 83*4a77657cSAndrey V. Elsukov } 84*4a77657cSAndrey V. Elsukov 85*4a77657cSAndrey V. Elsukov static ipfw_check_opcode_t check_opcode_f = check_opcode_compat_nop; 86*4a77657cSAndrey 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 */ 115*4a77657cSAndrey 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 */ 125*4a77657cSAndrey 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 129*4a77657cSAndrey V. Elsukov static sopt_handler_f dump_config, add_rules, del_rules, clear_rules, 130*4a77657cSAndrey V. Elsukov move_rules, manage_sets, dump_soptcodes, dump_srvobjects, 131*4a77657cSAndrey V. Elsukov manage_skiptocache; 132*4a77657cSAndrey V. Elsukov 1336b988f3aSAlexander V. Chernikov static struct ipfw_sopt_handler scodes[] = { 134*4a77657cSAndrey V. Elsukov { IP_FW_XGET, IP_FW3_OPVER, HDIR_GET, dump_config }, 135*4a77657cSAndrey V. Elsukov { IP_FW_XADD, IP_FW3_OPVER, HDIR_BOTH, add_rules }, 136*4a77657cSAndrey V. Elsukov { IP_FW_XDEL, IP_FW3_OPVER, HDIR_BOTH, del_rules }, 137*4a77657cSAndrey V. Elsukov { IP_FW_XZERO, IP_FW3_OPVER, HDIR_SET, clear_rules }, 138*4a77657cSAndrey V. Elsukov { IP_FW_XRESETLOG, IP_FW3_OPVER, HDIR_SET, clear_rules }, 139*4a77657cSAndrey V. Elsukov { IP_FW_XMOVE, IP_FW3_OPVER, HDIR_SET, move_rules }, 140*4a77657cSAndrey V. Elsukov { IP_FW_SET_SWAP, IP_FW3_OPVER, HDIR_SET, manage_sets }, 141*4a77657cSAndrey V. Elsukov { IP_FW_SET_MOVE, IP_FW3_OPVER, HDIR_SET, manage_sets }, 142*4a77657cSAndrey V. Elsukov { IP_FW_SET_ENABLE, IP_FW3_OPVER, HDIR_SET, manage_sets }, 143*4a77657cSAndrey V. Elsukov { IP_FW_DUMP_SOPTCODES, IP_FW3_OPVER, HDIR_GET, dump_soptcodes }, 144*4a77657cSAndrey V. Elsukov { IP_FW_DUMP_SRVOBJECTS, IP_FW3_OPVER, HDIR_GET, dump_srvobjects }, 145*4a77657cSAndrey 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, 149*4a77657cSAndrey 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); 157*4a77657cSAndrey 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 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 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 * 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 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 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 246d5eb80cbSAlexander V. Chernikov update_skipto_cache(struct ip_fw_chain *chain, struct ip_fw **map) 247d5eb80cbSAlexander V. Chernikov { 248*4a77657cSAndrey 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 260*4a77657cSAndrey 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 */ 263*4a77657cSAndrey 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 276d5eb80cbSAlexander V. Chernikov swap_skipto_cache(struct ip_fw_chain *chain) 277d5eb80cbSAlexander V. Chernikov { 278*4a77657cSAndrey 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 292d5eb80cbSAlexander V. Chernikov ipfw_init_skipto_cache(struct ip_fw_chain *chain) 293d5eb80cbSAlexander V. Chernikov { 294*4a77657cSAndrey V. Elsukov uint32_t *idxmap, *idxmap_back; 295d5eb80cbSAlexander V. Chernikov 296*4a77657cSAndrey V. Elsukov idxmap = malloc((IPFW_DEFAULT_RULE + 1) * sizeof(uint32_t), 297*4a77657cSAndrey V. Elsukov M_IPFW, M_WAITOK | M_ZERO); 298*4a77657cSAndrey V. Elsukov idxmap_back = malloc((IPFW_DEFAULT_RULE + 1) * sizeof(uint32_t), 299*4a77657cSAndrey 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; 317*4a77657cSAndrey 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 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 ** 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 ** 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 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 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 */ 450*4a77657cSAndrey V. Elsukov int 451*4a77657cSAndrey V. Elsukov ipfw_commit_rules(struct ip_fw_chain *chain, struct rule_check_info *rci, 452*4a77657cSAndrey V. Elsukov int count) 4533b3a8eb9SGleb Smirnoff { 454*4a77657cSAndrey V. Elsukov int error, i, insert_before, tcount, rule_idx, last_rule_idx; 455*4a77657cSAndrey 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) { 473*4a77657cSAndrey 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) { 480*4a77657cSAndrey 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); 492*4a77657cSAndrey V. Elsukov 493b074b7bbSAlexander V. Chernikov } 4946c2997ffSAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 495*4a77657cSAndrey 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 527*4a77657cSAndrey V. Elsukov last_rule_idx = 0; 528*4a77657cSAndrey 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 532*4a77657cSAndrey V. Elsukov krule->id = chain->id + 1; 533*4a77657cSAndrey V. Elsukov 5343b3a8eb9SGleb Smirnoff /* find the insertion point, we will insert before */ 5357e767c79SAlexander V. Chernikov insert_before = rulenum ? rulenum + 1 : IPFW_DEFAULT_RULE; 536*4a77657cSAndrey V. Elsukov rule_idx = ipfw_find_rule(chain, insert_before, 0); 537*4a77657cSAndrey V. Elsukov /* duplicate the previous part */ 538*4a77657cSAndrey V. Elsukov if (last_rule_idx < rule_idx) 539*4a77657cSAndrey V. Elsukov bcopy(chain->map + last_rule_idx, map + last_rule_idx + i, 540*4a77657cSAndrey V. Elsukov (rule_idx - last_rule_idx) * sizeof(struct ip_fw *)); 541*4a77657cSAndrey V. Elsukov last_rule_idx = rule_idx; 542*4a77657cSAndrey V. Elsukov map[rule_idx + i] = krule; 5437e767c79SAlexander V. Chernikov if (rulenum == 0) { 5447e767c79SAlexander V. Chernikov /* Compute rule number and write it back */ 545*4a77657cSAndrey 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 */ 550*4a77657cSAndrey V. Elsukov memcpy((char *)ci->urule + ci->urule_numoff, &rulenum, 551*4a77657cSAndrey V. Elsukov sizeof(rulenum)); 552*4a77657cSAndrey V. Elsukov } 5533b3a8eb9SGleb Smirnoff } 5543b3a8eb9SGleb Smirnoff 555*4a77657cSAndrey V. Elsukov /* duplicate the remaining part, we always have the default rule */ 556*4a77657cSAndrey V. Elsukov bcopy(chain->map + last_rule_idx, map + last_rule_idx + count, 557*4a77657cSAndrey V. Elsukov (chain->n_rules - last_rule_idx) * sizeof(struct ip_fw *)); 558*4a77657cSAndrey V. Elsukov 559*4a77657cSAndrey V. Elsukov if (V_skipto_cache != 0) 560d5eb80cbSAlexander V. Chernikov update_skipto_cache(chain, map); 561*4a77657cSAndrey 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 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 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 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 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 { 666*4a77657cSAndrey V. Elsukov uint32_t set; 6672685841bSAndrey V. Elsukov uint8_t new_set; 6682685841bSAndrey V. Elsukov }; 6692685841bSAndrey V. Elsukov 6702685841bSAndrey V. Elsukov static int 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 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 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 7152685841bSAndrey V. Elsukov ipfw_obj_manage_sets(struct namedobj_instance *ni, uint16_t type, 716*4a77657cSAndrey 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 */ 778*4a77657cSAndrey V. Elsukov int 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; 782*4a77657cSAndrey 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 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; 870*4a77657cSAndrey 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 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 * 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 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 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 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 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 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 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 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 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 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 1284*4a77657cSAndrey V. Elsukov /* Check rule format */ 1285*4a77657cSAndrey V. Elsukov int 1286*4a77657cSAndrey 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) { 1299*4a77657cSAndrey 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 1314*4a77657cSAndrey V. Elsukov enum ipfw_opcheck_result 1315*4a77657cSAndrey V. Elsukov ipfw_check_opcode(ipfw_insn **pcmd, int *plen, struct rule_check_info *ci) 13167e767c79SAlexander V. Chernikov { 1317*4a77657cSAndrey V. Elsukov ipfw_insn *cmd; 1318*4a77657cSAndrey V. Elsukov size_t cmdlen; 13197e767c79SAlexander V. Chernikov 1320*4a77657cSAndrey V. Elsukov cmd = *pcmd; 13213b3a8eb9SGleb Smirnoff cmdlen = F_LEN(cmd); 1322*4a77657cSAndrey V. Elsukov 13233b3a8eb9SGleb Smirnoff switch (cmd->opcode) { 13243b3a8eb9SGleb Smirnoff case O_PROBE_STATE: 13253b3a8eb9SGleb Smirnoff case O_KEEP_STATE: 1326*4a77657cSAndrey V. Elsukov if (cmdlen != F_INSN_SIZE(ipfw_insn_kidx)) 1327*4a77657cSAndrey 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)) 1359*4a77657cSAndrey V. Elsukov return (BAD_SIZE); 13603b3a8eb9SGleb Smirnoff break; 13613b3a8eb9SGleb Smirnoff 13622acdf79fSAndrey V. Elsukov case O_EXTERNAL_ACTION: 1363*4a77657cSAndrey V. Elsukov if (cmdlen != F_INSN_SIZE(ipfw_insn_kidx)) 1364*4a77657cSAndrey V. Elsukov return (BAD_SIZE); 1365*4a77657cSAndrey V. Elsukov 1366*4a77657cSAndrey V. Elsukov if (insntod(cmd, kidx)->kidx == 0) 1367*4a77657cSAndrey 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 */ 1373*4a77657cSAndrey V. Elsukov if (*plen != cmdlen) { 1374*4a77657cSAndrey V. Elsukov *plen -= cmdlen; 13752acdf79fSAndrey V. Elsukov cmd += cmdlen; 1376*4a77657cSAndrey V. Elsukov *pcmd = cmd; 13772acdf79fSAndrey V. Elsukov cmdlen = F_LEN(cmd); 137811c56650SAndrey V. Elsukov if (cmd->opcode == O_EXTERNAL_DATA) 1379*4a77657cSAndrey 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); 1384*4a77657cSAndrey V. Elsukov return (FAILED); 13852acdf79fSAndrey V. Elsukov } 1386*4a77657cSAndrey V. Elsukov if (cmdlen != F_INSN_SIZE(ipfw_insn_kidx)) 1387*4a77657cSAndrey V. Elsukov return (BAD_SIZE); 1388*4a77657cSAndrey V. Elsukov if (insntod(cmd, kidx)->kidx == 0) 1389*4a77657cSAndrey V. Elsukov return (FAILED); 13902acdf79fSAndrey V. Elsukov ci->object_opcodes++; 13912acdf79fSAndrey V. Elsukov } 1392*4a77657cSAndrey V. Elsukov return (CHECK_ACTION); 13932acdf79fSAndrey V. Elsukov 13943b3a8eb9SGleb Smirnoff case O_FIB: 13953b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn)) 1396*4a77657cSAndrey 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); 1400*4a77657cSAndrey 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)) 1406*4a77657cSAndrey 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); 1411*4a77657cSAndrey V. Elsukov return (FAILED); 14123b3a8eb9SGleb Smirnoff } 1413*4a77657cSAndrey 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)) 1425*4a77657cSAndrey 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)) 1430*4a77657cSAndrey 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)) 1436*4a77657cSAndrey V. Elsukov return (BAD_SIZE); 1437*4a77657cSAndrey 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) 1444*4a77657cSAndrey 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); 1452*4a77657cSAndrey V. Elsukov return (FAILED); 14533b3a8eb9SGleb Smirnoff } 14543b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 14553b3a8eb9SGleb Smirnoff (cmd->arg1+31)/32 ) 1456*4a77657cSAndrey 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: 1463*4a77657cSAndrey V. Elsukov case O_MAC_DST_LOOKUP: 1464*4a77657cSAndrey V. Elsukov if (cmdlen != F_INSN_SIZE(ipfw_insn_kidx) && 1465*4a77657cSAndrey V. Elsukov cmdlen != F_INSN_SIZE(ipfw_insn_table)) 1466*4a77657cSAndrey V. Elsukov return (BAD_SIZE); 1467*4a77657cSAndrey V. Elsukov if (insntod(cmd, kidx)->kidx >= V_fw_tables_max) { 1468*4a77657cSAndrey V. Elsukov printf("ipfw: invalid table index %u\n", 1469*4a77657cSAndrey V. Elsukov insntod(cmd, kidx)->kidx); 1470*4a77657cSAndrey 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)) 1476*4a77657cSAndrey 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) 1488*4a77657cSAndrey 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) 1494*4a77657cSAndrey 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) 1501*4a77657cSAndrey 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)) 1508*4a77657cSAndrey 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)) 1514*4a77657cSAndrey 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)) 1520*4a77657cSAndrey V. Elsukov return (BAD_SIZE); 1521*4a77657cSAndrey V. Elsukov return (CHECK_ACTION); 15223b3a8eb9SGleb Smirnoff 15233b3a8eb9SGleb Smirnoff case O_FORWARD_IP: 15243b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn_sa)) 1525*4a77657cSAndrey V. Elsukov return (BAD_SIZE); 1526*4a77657cSAndrey 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)) 1530*4a77657cSAndrey V. Elsukov return (BAD_SIZE); 1531*4a77657cSAndrey 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) 1537*4a77657cSAndrey V. Elsukov return (FAILED); 1538*4a77657cSAndrey V. Elsukov if (cmdlen != F_INSN_SIZE(ipfw_insn)) 1539*4a77657cSAndrey V. Elsukov return (BAD_SIZE); 1540*4a77657cSAndrey V. Elsukov return (CHECK_ACTION); 15413b3a8eb9SGleb Smirnoff case O_NETGRAPH: 15423b3a8eb9SGleb Smirnoff case O_NGTEE: 15433b3a8eb9SGleb Smirnoff if (ng_ipfw_input_p == NULL) 1544*4a77657cSAndrey V. Elsukov return (FAILED); 1545*4a77657cSAndrey V. Elsukov if (cmdlen != F_INSN_SIZE(ipfw_insn)) 1546*4a77657cSAndrey V. Elsukov return (BAD_SIZE); 1547*4a77657cSAndrey V. Elsukov return (CHECK_ACTION); 15483b3a8eb9SGleb Smirnoff case O_NAT: 15493b3a8eb9SGleb Smirnoff if (!IPFW_NAT_LOADED) 1550*4a77657cSAndrey V. Elsukov return (FAILED); 15513b3a8eb9SGleb Smirnoff if (cmdlen != F_INSN_SIZE(ipfw_insn_nat)) 1552*4a77657cSAndrey V. Elsukov return (BAD_SIZE); 1553*4a77657cSAndrey V. Elsukov return (CHECK_ACTION); 1554*4a77657cSAndrey V. Elsukov 1555*4a77657cSAndrey V. Elsukov case O_SKIPTO: 1556*4a77657cSAndrey V. Elsukov case O_CALLRETURN: 1557fc727ad6SBoris Lytochkin case O_SETMARK: 1558fc727ad6SBoris Lytochkin if (cmdlen != F_INSN_SIZE(ipfw_insn_u32)) 1559*4a77657cSAndrey V. Elsukov return (BAD_SIZE); 1560*4a77657cSAndrey V. Elsukov return (CHECK_ACTION); 1561*4a77657cSAndrey V. Elsukov 1562*4a77657cSAndrey V. Elsukov case O_CHECK_STATE: 1563*4a77657cSAndrey V. Elsukov if (cmdlen != F_INSN_SIZE(ipfw_insn_kidx)) 1564*4a77657cSAndrey V. Elsukov return (BAD_SIZE); 1565*4a77657cSAndrey V. Elsukov ci->object_opcodes++; 1566*4a77657cSAndrey V. Elsukov return (CHECK_ACTION); 1567*4a77657cSAndrey 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: 1572*4a77657cSAndrey 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)) 1579*4a77657cSAndrey V. Elsukov return (BAD_SIZE); 1580*4a77657cSAndrey 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)) 1586*4a77657cSAndrey 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) 1592*4a77657cSAndrey 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) 1598*4a77657cSAndrey V. Elsukov return (BAD_SIZE); 15993b3a8eb9SGleb Smirnoff break; 16003b3a8eb9SGleb Smirnoff case O_ICMP6TYPE: 16013b3a8eb9SGleb Smirnoff if( cmdlen != F_INSN_SIZE( ipfw_insn_icmp6 ) ) 1602*4a77657cSAndrey 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"); 1621*4a77657cSAndrey V. Elsukov return (FAILED); 16223b3a8eb9SGleb Smirnoff #endif 16233b3a8eb9SGleb Smirnoff default: 1624*4a77657cSAndrey V. Elsukov printf("ipfw: opcode %d: unknown opcode\n", 1625*4a77657cSAndrey V. Elsukov cmd->opcode); 1626*4a77657cSAndrey V. Elsukov return (FAILED); 1627*4a77657cSAndrey V. Elsukov } 1628*4a77657cSAndrey V. Elsukov } 1629*4a77657cSAndrey V. Elsukov return (SUCCESS); 1630*4a77657cSAndrey V. Elsukov } 1631*4a77657cSAndrey V. Elsukov 1632*4a77657cSAndrey V. Elsukov static __noinline int 1633*4a77657cSAndrey V. Elsukov check_ipfw_rule_body(ipfw_insn *cmd, int cmd_len, struct rule_check_info *ci) 1634*4a77657cSAndrey V. Elsukov { 1635*4a77657cSAndrey V. Elsukov int cmdlen, l; 1636*4a77657cSAndrey V. Elsukov int have_action, ret; 1637*4a77657cSAndrey V. Elsukov 1638*4a77657cSAndrey V. Elsukov /* 1639*4a77657cSAndrey V. Elsukov * Now go for the individual checks. Very simple ones, basically only 1640*4a77657cSAndrey V. Elsukov * instruction sizes. 1641*4a77657cSAndrey V. Elsukov */ 1642*4a77657cSAndrey V. Elsukov have_action = 0; 1643*4a77657cSAndrey V. Elsukov for (l = cmd_len; l > 0 ; l -= cmdlen, cmd += cmdlen) { 1644*4a77657cSAndrey V. Elsukov cmdlen = F_LEN(cmd); 1645*4a77657cSAndrey V. Elsukov if (cmdlen > l) { 1646*4a77657cSAndrey V. Elsukov printf("ipfw: opcode %d: size truncated\n", 16473b3a8eb9SGleb Smirnoff cmd->opcode); 16487e767c79SAlexander V. Chernikov return (EINVAL); 16493b3a8eb9SGleb Smirnoff } 1650*4a77657cSAndrey V. Elsukov if (ci->version != IP_FW3_OPVER) 1651*4a77657cSAndrey V. Elsukov ret = (*check_opcode_f)(&cmd, &l, ci); 1652*4a77657cSAndrey V. Elsukov else 1653*4a77657cSAndrey V. Elsukov ret = ipfw_check_opcode(&cmd, &l, ci); 1654*4a77657cSAndrey V. Elsukov 1655*4a77657cSAndrey V. Elsukov if (ret == CHECK_ACTION) { 1656*4a77657cSAndrey V. Elsukov if (have_action != 0) { 1657*4a77657cSAndrey V. Elsukov printf("ipfw: opcode %d: multiple actions" 1658*4a77657cSAndrey V. Elsukov " not allowed\n", cmd->opcode); 1659*4a77657cSAndrey V. Elsukov ret = FAILED; 1660*4a77657cSAndrey V. Elsukov } else 1661*4a77657cSAndrey V. Elsukov have_action = 1; 1662*4a77657cSAndrey V. Elsukov 1663*4a77657cSAndrey V. Elsukov if (l != F_LEN(cmd)) { 1664*4a77657cSAndrey V. Elsukov printf("ipfw: opcode %d: action must be" 1665*4a77657cSAndrey V. Elsukov " last opcode\n", cmd->opcode); 1666*4a77657cSAndrey V. Elsukov ret = FAILED; 1667*4a77657cSAndrey V. Elsukov } 1668*4a77657cSAndrey V. Elsukov } 1669*4a77657cSAndrey V. Elsukov switch (ret) { 1670*4a77657cSAndrey V. Elsukov case SUCCESS: 1671*4a77657cSAndrey V. Elsukov continue; 1672*4a77657cSAndrey V. Elsukov case BAD_SIZE: 1673*4a77657cSAndrey V. Elsukov printf("ipfw: opcode %d: wrong size %d\n", 1674*4a77657cSAndrey V. Elsukov cmd->opcode, cmdlen); 1675*4a77657cSAndrey V. Elsukov /* FALLTHROUGH */ 1676*4a77657cSAndrey V. Elsukov case FAILED: 1677*4a77657cSAndrey 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 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 1714*4a77657cSAndrey 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 1732cefe3d67SAndrey V. Elsukov export_named_objects(struct namedobj_instance *ni, struct dump_args *da, 1733cefe3d67SAndrey V. Elsukov struct sockopt_data *sd) 1734563b5ab1SAlexander V. Chernikov { 1735*4a77657cSAndrey V. Elsukov uint32_t i; 1736*4a77657cSAndrey 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 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 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 1814*4a77657cSAndrey 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 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; 1841*4a77657cSAndrey 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 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 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 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; 2012*4a77657cSAndrey 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 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 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; 2095*4a77657cSAndrey 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 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 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 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 /* 2298*4a77657cSAndrey V. Elsukov * Parses one or more rules from userland. 2299*4a77657cSAndrey 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 */ 2322*4a77657cSAndrey V. Elsukov static __noinline int 2323*4a77657cSAndrey V. Elsukov parse_rules_v1(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 2324*4a77657cSAndrey V. Elsukov struct sockopt_data *sd, ipfw_obj_ctlv **prtlv, 2325*4a77657cSAndrey 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; 2329*4a77657cSAndrey V. Elsukov struct rule_check_info *ci, *cbuf; 23307e767c79SAlexander V. Chernikov struct ip_fw_rule *r; 2331*4a77657cSAndrey V. Elsukov size_t count, clen, read, rsize; 2332*4a77657cSAndrey V. Elsukov uint32_t idx, rulenum; 2333*4a77657cSAndrey 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 2341*4a77657cSAndrey V. Elsukov rtlv = NULL; 2342*4a77657cSAndrey V. Elsukov tstate = NULL; 2343*4a77657cSAndrey V. Elsukov cbuf = NULL; 2344*4a77657cSAndrey V. Elsukov /* Table names or other named objects. */ 23456c2997ffSAlexander V. Chernikov if (ctlv->head.type == IPFW_TLV_TBLNAME_LIST) { 2346*4a77657cSAndrey V. Elsukov /* Check size and alignment. */ 23476c2997ffSAlexander V. Chernikov clen = ctlv->head.length; 2348*4a77657cSAndrey V. Elsukov if (read + clen > sd->valsize || clen < sizeof(*ctlv) || 2349*4a77657cSAndrey V. Elsukov (clen % sizeof(uint64_t)) != 0) 23506c2997ffSAlexander V. Chernikov return (EINVAL); 2351*4a77657cSAndrey 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 */ 2360*4a77657cSAndrey 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); 2384*4a77657cSAndrey V. Elsukov } 23856c2997ffSAlexander V. Chernikov 2386*4a77657cSAndrey V. Elsukov /* List of rules. */ 23876c2997ffSAlexander V. Chernikov if (ctlv->head.type == IPFW_TLV_RULE_LIST) { 23886c2997ffSAlexander V. Chernikov clen = ctlv->head.length; 2389*4a77657cSAndrey V. Elsukov if (read + clen > sd->valsize || clen < sizeof(*ctlv) || 2390*4a77657cSAndrey V. Elsukov (clen % sizeof(uint64_t)) != 0) 23916c2997ffSAlexander V. Chernikov return (EINVAL); 23926c2997ffSAlexander V. Chernikov 23936c2997ffSAlexander V. Chernikov clen -= sizeof(*ctlv); 2394*4a77657cSAndrey V. Elsukov if (ctlv->count == 0 || 2395*4a77657cSAndrey V. Elsukov ctlv->count > clen / sizeof(struct ip_fw_rule)) 23966c2997ffSAlexander V. Chernikov return (EINVAL); 23976c2997ffSAlexander V. Chernikov 2398*4a77657cSAndrey V. Elsukov /* Allocate state for each rule */ 2399*4a77657cSAndrey V. Elsukov cbuf = malloc(ctlv->count * sizeof(struct rule_check_info), 2400*4a77657cSAndrey 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 */ 2407*4a77657cSAndrey V. Elsukov rulenum = 0; 24086c2997ffSAlexander V. Chernikov count = 0; 24096c2997ffSAlexander V. Chernikov error = 0; 2410*4a77657cSAndrey V. Elsukov ci = cbuf; 2411*4a77657cSAndrey V. Elsukov r = (struct ip_fw_rule *)(ctlv + 1); 24126c2997ffSAlexander V. Chernikov while (clen > 0) { 2413*4a77657cSAndrey V. Elsukov rsize = RULEUSIZE1(r); 2414*4a77657cSAndrey 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; 2419*4a77657cSAndrey V. Elsukov ci->version = IP_FW3_OPVER; 2420*4a77657cSAndrey 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 */ 2425*4a77657cSAndrey V. Elsukov if (count != 0 && ((rulenum == 0) != (r->rulenum == 0) || 2426*4a77657cSAndrey V. Elsukov r->rulenum < rulenum)) { 2427*4a77657cSAndrey V. Elsukov printf("ipfw: wrong order: rulenum %u" 2428*4a77657cSAndrey V. Elsukov " vs %u\n", r->rulenum, rulenum); 24296c2997ffSAlexander V. Chernikov error = EINVAL; 24306c2997ffSAlexander V. Chernikov break; 24316c2997ffSAlexander V. Chernikov } 2432*4a77657cSAndrey 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 2450*4a77657cSAndrey 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 2455*4a77657cSAndrey V. Elsukov *prtlv = rtlv; 2456*4a77657cSAndrey V. Elsukov *pci = cbuf; 2457*4a77657cSAndrey V. Elsukov return (0); 24586c2997ffSAlexander V. Chernikov } 24596c2997ffSAlexander V. Chernikov 2460*4a77657cSAndrey V. Elsukov /* 2461*4a77657cSAndrey V. Elsukov * Copy rule @urule from v1 userland format (current) to kernel @krule. 2462*4a77657cSAndrey V. Elsukov */ 2463*4a77657cSAndrey V. Elsukov static void 2464*4a77657cSAndrey V. Elsukov import_rule_v1(struct ip_fw_chain *chain, struct rule_check_info *ci) 2465*4a77657cSAndrey V. Elsukov { 2466*4a77657cSAndrey V. Elsukov struct ip_fw_rule *urule; 2467*4a77657cSAndrey V. Elsukov struct ip_fw *krule; 2468*4a77657cSAndrey V. Elsukov 2469*4a77657cSAndrey V. Elsukov urule = (struct ip_fw_rule *)ci->urule; 2470*4a77657cSAndrey V. Elsukov krule = ci->krule = ipfw_alloc_rule(chain, RULEKSIZE1(urule)); 2471*4a77657cSAndrey V. Elsukov 2472*4a77657cSAndrey V. Elsukov krule->act_ofs = urule->act_ofs; 2473*4a77657cSAndrey V. Elsukov krule->cmd_len = urule->cmd_len; 2474*4a77657cSAndrey V. Elsukov krule->rulenum = urule->rulenum; 2475*4a77657cSAndrey V. Elsukov krule->set = urule->set; 2476*4a77657cSAndrey V. Elsukov krule->flags = urule->flags; 2477*4a77657cSAndrey V. Elsukov 2478*4a77657cSAndrey V. Elsukov /* Save rulenum offset */ 2479*4a77657cSAndrey V. Elsukov ci->urule_numoff = offsetof(struct ip_fw_rule, rulenum); 2480*4a77657cSAndrey V. Elsukov 2481*4a77657cSAndrey V. Elsukov /* Copy opcodes */ 2482*4a77657cSAndrey V. Elsukov memcpy(krule->cmd, urule->cmd, krule->cmd_len * sizeof(uint32_t)); 2483*4a77657cSAndrey V. Elsukov } 2484*4a77657cSAndrey V. Elsukov 2485*4a77657cSAndrey V. Elsukov /* 2486*4a77657cSAndrey V. Elsukov * Adds one or more rules to ipfw @chain. 2487*4a77657cSAndrey V. Elsukov */ 2488*4a77657cSAndrey V. Elsukov static int 2489*4a77657cSAndrey V. Elsukov add_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 2490*4a77657cSAndrey V. Elsukov struct sockopt_data *sd) 2491*4a77657cSAndrey V. Elsukov { 2492*4a77657cSAndrey V. Elsukov ipfw_obj_ctlv *rtlv; 2493*4a77657cSAndrey V. Elsukov struct rule_check_info *ci, *nci; 2494*4a77657cSAndrey V. Elsukov int i, ret; 2495*4a77657cSAndrey V. Elsukov 2496*4a77657cSAndrey V. Elsukov /* 2497*4a77657cSAndrey V. Elsukov * Check rules buffer for validness. 2498*4a77657cSAndrey V. Elsukov */ 2499*4a77657cSAndrey V. Elsukov ret = parse_rules_v1(chain, op3, sd, &rtlv, &nci); 2500*4a77657cSAndrey V. Elsukov if (ret != 0) 2501*4a77657cSAndrey V. Elsukov return (ret); 2502*4a77657cSAndrey V. Elsukov /* 2503*4a77657cSAndrey V. Elsukov * Allocate storage for the kernel representation of rules. 2504*4a77657cSAndrey V. Elsukov */ 2505*4a77657cSAndrey V. Elsukov for (i = 0, ci = nci; i < rtlv->count; i++, ci++) 2506*4a77657cSAndrey V. Elsukov import_rule_v1(chain, ci); 2507*4a77657cSAndrey V. Elsukov /* 2508*4a77657cSAndrey V. Elsukov * Try to add new rules to the chain. 2509*4a77657cSAndrey V. Elsukov */ 2510*4a77657cSAndrey V. Elsukov if ((ret = ipfw_commit_rules(chain, nci, rtlv->count)) != 0) { 2511*4a77657cSAndrey V. Elsukov for (i = 0, ci = nci; i < rtlv->count; i++, ci++) 2512cefe3d67SAndrey V. Elsukov ipfw_free_rule(ci->krule); 25136c2997ffSAlexander V. Chernikov } 2514*4a77657cSAndrey V. Elsukov /* Cleanup after parse_rules() */ 2515*4a77657cSAndrey V. Elsukov free(nci, M_TEMP); 2516*4a77657cSAndrey V. Elsukov return (ret); 25176c2997ffSAlexander V. Chernikov } 25186c2997ffSAlexander V. Chernikov 251991e721d7SAlexander V. Chernikov /* 2520be8bc457SAlexander V. Chernikov * Lists all sopts currently registered. 2521*4a77657cSAndrey 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 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 2536*4a77657cSAndrey V. Elsukov olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd, 2537*4a77657cSAndrey 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 2558be8bc457SAlexander V. Chernikov for (n = 1; 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 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 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 * 2633*4a77657cSAndrey 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; 2636*4a77657cSAndrey 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 2655*4a77657cSAndrey 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 2664*4a77657cSAndrey 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 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 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 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 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 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 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 2815*4a77657cSAndrey V. Elsukov void 2816*4a77657cSAndrey V. Elsukov ipfw_enable_skipto_cache(struct ip_fw_chain *chain) 2817*4a77657cSAndrey V. Elsukov { 2818*4a77657cSAndrey V. Elsukov 2819*4a77657cSAndrey V. Elsukov IPFW_UH_WLOCK_ASSERT(chain); 2820*4a77657cSAndrey V. Elsukov update_skipto_cache(chain, chain->map); 2821*4a77657cSAndrey V. Elsukov 2822*4a77657cSAndrey V. Elsukov IPFW_WLOCK(chain); 2823*4a77657cSAndrey V. Elsukov swap_skipto_cache(chain); 2824*4a77657cSAndrey V. Elsukov V_skipto_cache = 1; 2825*4a77657cSAndrey V. Elsukov IPFW_WUNLOCK(chain); 2826*4a77657cSAndrey V. Elsukov } 2827*4a77657cSAndrey V. Elsukov 2828*4a77657cSAndrey V. Elsukov /* 2829*4a77657cSAndrey V. Elsukov * Enables or disable skipto cache. 2830*4a77657cSAndrey V. Elsukov * Request: [ ipfw_cmd_header ] size = ipfw_cmd_header.size 2831*4a77657cSAndrey V. Elsukov * Reply: [ ipfw_cmd_header ] 2832*4a77657cSAndrey V. Elsukov * Returns 0 on success 2833*4a77657cSAndrey V. Elsukov */ 2834*4a77657cSAndrey V. Elsukov static int 2835*4a77657cSAndrey V. Elsukov manage_skiptocache(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 2836*4a77657cSAndrey V. Elsukov struct sockopt_data *sd) 2837*4a77657cSAndrey V. Elsukov { 2838*4a77657cSAndrey V. Elsukov ipfw_cmd_header *hdr; 2839*4a77657cSAndrey V. Elsukov 2840*4a77657cSAndrey V. Elsukov if (sd->valsize != sizeof(*hdr)) 2841*4a77657cSAndrey V. Elsukov return (EINVAL); 2842*4a77657cSAndrey V. Elsukov 2843*4a77657cSAndrey V. Elsukov hdr = (ipfw_cmd_header *)ipfw_get_sopt_space(sd, sd->valsize); 2844*4a77657cSAndrey V. Elsukov if (hdr->cmd != SKIPTO_CACHE_DISABLE && 2845*4a77657cSAndrey V. Elsukov hdr->cmd != SKIPTO_CACHE_ENABLE) 2846*4a77657cSAndrey V. Elsukov return (EOPNOTSUPP); 2847*4a77657cSAndrey V. Elsukov 2848*4a77657cSAndrey V. Elsukov IPFW_UH_WLOCK(chain); 2849*4a77657cSAndrey V. Elsukov if (hdr->cmd != V_skipto_cache) { 2850*4a77657cSAndrey V. Elsukov if (hdr->cmd == SKIPTO_CACHE_ENABLE) 2851*4a77657cSAndrey V. Elsukov ipfw_enable_skipto_cache(chain); 2852*4a77657cSAndrey V. Elsukov V_skipto_cache = hdr->cmd; 2853*4a77657cSAndrey V. Elsukov } 2854*4a77657cSAndrey V. Elsukov IPFW_UH_WUNLOCK(chain); 2855*4a77657cSAndrey V. Elsukov return (0); 2856*4a77657cSAndrey V. Elsukov } 2857*4a77657cSAndrey 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 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 * 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 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 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 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 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 2964*4a77657cSAndrey V. Elsukov void 2965*4a77657cSAndrey V. Elsukov ipfw_register_compat(ipfw_check_opcode_t f) 2966*4a77657cSAndrey V. Elsukov { 2967*4a77657cSAndrey V. Elsukov check_opcode_f = f; 2968*4a77657cSAndrey V. Elsukov } 2969*4a77657cSAndrey V. Elsukov 2970*4a77657cSAndrey V. Elsukov void 2971*4a77657cSAndrey V. Elsukov ipfw_unregister_compat(void) 2972*4a77657cSAndrey V. Elsukov { 2973*4a77657cSAndrey V. Elsukov check_opcode_f = check_opcode_compat_nop; 2974*4a77657cSAndrey V. Elsukov } 2975*4a77657cSAndrey 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 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 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 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 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 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 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) 3158*4a77657cSAndrey 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 327774b22066SAlexander V. Chernikov ipfw_init_srv(struct ip_fw_chain *ch) 327874b22066SAlexander V. Chernikov { 3279*4a77657cSAndrey V. Elsukov ch->srvmap = ipfw_objhash_create(IPFW_OBJECTS_DEFAULT, 3280*4a77657cSAndrey 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 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 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 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 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 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 * 3371*4a77657cSAndrey 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) + 3378*4a77657cSAndrey V. Elsukov sizeof(struct namedobjects_head) * hash_size + 3379*4a77657cSAndrey V. Elsukov sizeof(struct namedobjects_head) * hash_size; 3380b074b7bbSAlexander V. Chernikov 3381b074b7bbSAlexander V. Chernikov ni = malloc(size, M_IPFW, M_WAITOK | M_ZERO); 3382*4a77657cSAndrey V. Elsukov ni->nn_size = hash_size; 3383*4a77657cSAndrey 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 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 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 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 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 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 * 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 * 3471*4a77657cSAndrey 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 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 * 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 * 3565*4a77657cSAndrey 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 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 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 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 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 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 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 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 3691*4a77657cSAndrey 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 3722*4a77657cSAndrey 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