13b3a8eb9SGleb Smirnoff /*- 23b3a8eb9SGleb Smirnoff * Copyright (c) 2004 Ruslan Ermilov and Vsevolod Lobko. 33b3a8eb9SGleb Smirnoff * 43b3a8eb9SGleb Smirnoff * Redistribution and use in source and binary forms, with or without 53b3a8eb9SGleb Smirnoff * modification, are permitted provided that the following conditions 63b3a8eb9SGleb Smirnoff * are met: 73b3a8eb9SGleb Smirnoff * 1. Redistributions of source code must retain the above copyright 83b3a8eb9SGleb Smirnoff * notice, this list of conditions and the following disclaimer. 93b3a8eb9SGleb Smirnoff * 2. Redistributions in binary form must reproduce the above copyright 103b3a8eb9SGleb Smirnoff * notice, this list of conditions and the following disclaimer in the 113b3a8eb9SGleb Smirnoff * documentation and/or other materials provided with the distribution. 123b3a8eb9SGleb Smirnoff * 133b3a8eb9SGleb Smirnoff * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 143b3a8eb9SGleb Smirnoff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 153b3a8eb9SGleb Smirnoff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 163b3a8eb9SGleb Smirnoff * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 173b3a8eb9SGleb Smirnoff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 183b3a8eb9SGleb Smirnoff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 193b3a8eb9SGleb Smirnoff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 203b3a8eb9SGleb Smirnoff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 213b3a8eb9SGleb Smirnoff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 223b3a8eb9SGleb Smirnoff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 233b3a8eb9SGleb Smirnoff * SUCH DAMAGE. 243b3a8eb9SGleb Smirnoff */ 253b3a8eb9SGleb Smirnoff 263b3a8eb9SGleb Smirnoff #include <sys/cdefs.h> 273b3a8eb9SGleb Smirnoff __FBSDID("$FreeBSD$"); 283b3a8eb9SGleb Smirnoff 293b3a8eb9SGleb Smirnoff /* 30563b5ab1SAlexander V. Chernikov * Lookup table support for ipfw. 313b3a8eb9SGleb Smirnoff * 32ac35ff17SAlexander V. Chernikov * This file contains handlers for all generic tables' operations: 33563b5ab1SAlexander V. Chernikov * add/del/flush entries, list/dump tables etc.. 343b3a8eb9SGleb Smirnoff * 35563b5ab1SAlexander V. Chernikov * Table data modification is protected by both UH and runtimg lock 36563b5ab1SAlexander V. Chernikov * while reading configuration/data is protected by UH lock. 37563b5ab1SAlexander V. Chernikov * 38563b5ab1SAlexander V. Chernikov * Lookup algorithms for all table types are located in ip_fw_table_algo.c 393b3a8eb9SGleb Smirnoff */ 403b3a8eb9SGleb Smirnoff 413b3a8eb9SGleb Smirnoff #include "opt_ipfw.h" 423b3a8eb9SGleb Smirnoff 433b3a8eb9SGleb Smirnoff #include <sys/param.h> 443b3a8eb9SGleb Smirnoff #include <sys/systm.h> 453b3a8eb9SGleb Smirnoff #include <sys/malloc.h> 463b3a8eb9SGleb Smirnoff #include <sys/kernel.h> 473b3a8eb9SGleb Smirnoff #include <sys/lock.h> 483b3a8eb9SGleb Smirnoff #include <sys/rwlock.h> 493b3a8eb9SGleb Smirnoff #include <sys/socket.h> 50f1220db8SAlexander V. Chernikov #include <sys/socketvar.h> 513b3a8eb9SGleb Smirnoff #include <sys/queue.h> 523b3a8eb9SGleb Smirnoff #include <net/if.h> /* ip_fw.h requires IFNAMSIZ */ 533b3a8eb9SGleb Smirnoff 543b3a8eb9SGleb Smirnoff #include <netinet/in.h> 553b3a8eb9SGleb Smirnoff #include <netinet/ip_var.h> /* struct ipfw_rule_ref */ 563b3a8eb9SGleb Smirnoff #include <netinet/ip_fw.h> 573b3a8eb9SGleb Smirnoff 583b3a8eb9SGleb Smirnoff #include <netpfil/ipfw/ip_fw_private.h> 59ea761a5dSAlexander V. Chernikov #include <netpfil/ipfw/ip_fw_table.h> 603b3a8eb9SGleb Smirnoff 613b3a8eb9SGleb Smirnoff 623b3a8eb9SGleb Smirnoff /* 63b074b7bbSAlexander V. Chernikov * Table has the following `type` concepts: 64b074b7bbSAlexander V. Chernikov * 659f7d47b0SAlexander V. Chernikov * `no.type` represents lookup key type (cidr, ifp, uid, etc..) 669f7d47b0SAlexander V. Chernikov * `ta->atype` represents exact lookup algorithm. 67b074b7bbSAlexander V. Chernikov * For example, we can use more efficient search schemes if we plan 68b074b7bbSAlexander V. Chernikov * to use some specific table for storing host-routes only. 699490a627SAlexander V. Chernikov * `ftype` (at the moment )is pure userland field helping to properly 709490a627SAlexander V. Chernikov * format value data e.g. "value is IPv4 nexthop" or "value is DSCP" 719490a627SAlexander V. Chernikov * or "value is port". 72b074b7bbSAlexander V. Chernikov * 73b074b7bbSAlexander V. Chernikov */ 74b074b7bbSAlexander V. Chernikov struct table_config { 75b074b7bbSAlexander V. Chernikov struct named_object no; 76ac35ff17SAlexander V. Chernikov uint8_t vtype; /* format table type */ 77b074b7bbSAlexander V. Chernikov uint8_t linked; /* 1 if already linked */ 78914bffb6SAlexander V. Chernikov uint8_t tflags; /* type flags */ 79a73d728dSAlexander V. Chernikov uint8_t ochanged; /* used by set swapping */ 80b074b7bbSAlexander V. Chernikov uint32_t count; /* Number of records */ 814c0c07a5SAlexander V. Chernikov uint32_t limit; /* Max number of records */ 82a73d728dSAlexander V. Chernikov uint32_t ocount; /* used by set swapping */ 83a73d728dSAlexander V. Chernikov uint64_t gencnt; /* generation count */ 84b074b7bbSAlexander V. Chernikov char tablename[64]; /* table name */ 859f7d47b0SAlexander V. Chernikov struct table_algo *ta; /* Callbacks for given algo */ 869f7d47b0SAlexander V. Chernikov void *astate; /* algorithm state */ 879f7d47b0SAlexander V. Chernikov struct table_info ti; /* data to put to table_info */ 88b074b7bbSAlexander V. Chernikov }; 89b074b7bbSAlexander V. Chernikov 90b074b7bbSAlexander V. Chernikov struct tables_config { 91b074b7bbSAlexander V. Chernikov struct namedobj_instance *namehash; 929f7d47b0SAlexander V. Chernikov int algo_count; 939f7d47b0SAlexander V. Chernikov struct table_algo *algo[256]; 9457a1cf95SAlexander V. Chernikov struct table_algo *def_algo[IPFW_TABLE_MAXTYPE + 1]; 95b074b7bbSAlexander V. Chernikov }; 96b074b7bbSAlexander V. Chernikov 97b074b7bbSAlexander V. Chernikov static struct table_config *find_table(struct namedobj_instance *ni, 98b074b7bbSAlexander V. Chernikov struct tid_info *ti); 9968394ec8SAlexander V. Chernikov static struct table_config *alloc_table_config(struct ip_fw_chain *ch, 100914bffb6SAlexander V. Chernikov struct tid_info *ti, struct table_algo *ta, char *adata, uint8_t tflags, 101914bffb6SAlexander V. Chernikov uint8_t vtype); 102b074b7bbSAlexander V. Chernikov static void free_table_config(struct namedobj_instance *ni, 103b074b7bbSAlexander V. Chernikov struct table_config *tc); 104db785d31SAlexander V. Chernikov static int create_table_internal(struct ip_fw_chain *ch, struct tid_info *ti, 1054c0c07a5SAlexander V. Chernikov char *aname, ipfw_xtable_info *i); 106b074b7bbSAlexander V. Chernikov static void link_table(struct ip_fw_chain *chain, struct table_config *tc); 107b074b7bbSAlexander V. Chernikov static void unlink_table(struct ip_fw_chain *chain, struct table_config *tc); 108b074b7bbSAlexander V. Chernikov static void free_table_state(void **state, void **xstate, uint8_t type); 1092d99a349SAlexander V. Chernikov static int export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh, 1102d99a349SAlexander V. Chernikov struct sockopt_data *sd); 111ac35ff17SAlexander V. Chernikov static void export_table_info(struct ip_fw_chain *ch, struct table_config *tc, 112ac35ff17SAlexander V. Chernikov ipfw_xtable_info *i); 11381d3153dSAlexander V. Chernikov static int dump_table_tentry(void *e, void *arg); 114f1220db8SAlexander V. Chernikov static int dump_table_xentry(void *e, void *arg); 115b074b7bbSAlexander V. Chernikov 1162d99a349SAlexander V. Chernikov static int ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt_data *sd); 1172d99a349SAlexander V. Chernikov static int ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt_data *sd); 118db785d31SAlexander V. Chernikov static int ipfw_manage_table_ent_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 119ac35ff17SAlexander V. Chernikov struct sockopt_data *sd); 120db785d31SAlexander V. Chernikov static int ipfw_manage_table_ent_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 121ac35ff17SAlexander V. Chernikov struct sockopt_data *sd); 122a73d728dSAlexander V. Chernikov static int swap_tables(struct ip_fw_chain *ch, struct tid_info *a, 12346d52008SAlexander V. Chernikov struct tid_info *b); 124ac35ff17SAlexander V. Chernikov 125b6ee846eSAlexander V. Chernikov static int check_table_space(struct ip_fw_chain *ch, struct table_config *tc, 126b6ee846eSAlexander V. Chernikov struct table_info *ti, uint32_t count); 127ac35ff17SAlexander V. Chernikov static int destroy_table(struct ip_fw_chain *ch, struct tid_info *ti); 128d3a4f924SAlexander V. Chernikov 1299f7d47b0SAlexander V. Chernikov static struct table_algo *find_table_algo(struct tables_config *tableconf, 1309490a627SAlexander V. Chernikov struct tid_info *ti, char *name); 131b074b7bbSAlexander V. Chernikov 13246d52008SAlexander V. Chernikov static void objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti); 13346d52008SAlexander V. Chernikov static void ntlv_to_ti(struct _ipfw_obj_ntlv *ntlv, struct tid_info *ti); 134a73d728dSAlexander V. Chernikov static int classify_table_opcode(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype); 13546d52008SAlexander V. Chernikov 136b074b7bbSAlexander V. Chernikov #define CHAIN_TO_TCFG(chain) ((struct tables_config *)(chain)->tblcfg) 137b074b7bbSAlexander V. Chernikov #define CHAIN_TO_NI(chain) (CHAIN_TO_TCFG(chain)->namehash) 1389f7d47b0SAlexander V. Chernikov #define KIDX_TO_TI(ch, k) (&(((struct table_info *)(ch)->tablestate)[k])) 139b074b7bbSAlexander V. Chernikov 140b6ee846eSAlexander V. Chernikov #define TA_BUF_SZ 128 /* On-stack buffer for add/delete state */ 141b6ee846eSAlexander V. Chernikov 142b074b7bbSAlexander V. Chernikov 1433b3a8eb9SGleb Smirnoff int 1441832a7b3SAlexander V. Chernikov add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti, 145b6ee846eSAlexander V. Chernikov struct tentry_info *tei, uint32_t count) 1463b3a8eb9SGleb Smirnoff { 147db785d31SAlexander V. Chernikov struct table_config *tc; 1489f7d47b0SAlexander V. Chernikov struct table_algo *ta; 149b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 150b074b7bbSAlexander V. Chernikov uint16_t kidx; 1519f7d47b0SAlexander V. Chernikov int error; 152adea6201SAlexander V. Chernikov uint32_t num; 153b6ee846eSAlexander V. Chernikov ipfw_xtable_info *xi; 154b6ee846eSAlexander V. Chernikov char ta_buf[TA_BUF_SZ]; 1553b3a8eb9SGleb Smirnoff 1569f7d47b0SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 1579f7d47b0SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 1589f7d47b0SAlexander V. Chernikov 1599f7d47b0SAlexander V. Chernikov /* 1609f7d47b0SAlexander V. Chernikov * Find and reference existing table. 1619f7d47b0SAlexander V. Chernikov */ 1629f7d47b0SAlexander V. Chernikov ta = NULL; 1639f7d47b0SAlexander V. Chernikov if ((tc = find_table(ni, ti)) != NULL) { 1649f7d47b0SAlexander V. Chernikov /* check table type */ 1659f7d47b0SAlexander V. Chernikov if (tc->no.type != ti->type) { 1669f7d47b0SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1673b3a8eb9SGleb Smirnoff return (EINVAL); 1683b3a8eb9SGleb Smirnoff } 1693b3a8eb9SGleb Smirnoff 1704c0c07a5SAlexander V. Chernikov /* Try to exit early on limit hit */ 1714c0c07a5SAlexander V. Chernikov if (tc->limit != 0 && tc->count == tc->limit && 1724c0c07a5SAlexander V. Chernikov (tei->flags & TEI_FLAGS_UPDATE) == 0) { 1734c0c07a5SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1744c0c07a5SAlexander V. Chernikov return (EFBIG); 1754c0c07a5SAlexander V. Chernikov } 1764c0c07a5SAlexander V. Chernikov 1779f7d47b0SAlexander V. Chernikov /* Reference and unlock */ 1789f7d47b0SAlexander V. Chernikov tc->no.refcnt++; 1799f7d47b0SAlexander V. Chernikov ta = tc->ta; 1809f7d47b0SAlexander V. Chernikov } 1819f7d47b0SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1823b3a8eb9SGleb Smirnoff 183ac35ff17SAlexander V. Chernikov if (tc == NULL) { 184db785d31SAlexander V. Chernikov /* Compability mode: create new table for old clients */ 185db785d31SAlexander V. Chernikov if ((tei->flags & TEI_FLAGS_COMPAT) == 0) 186db785d31SAlexander V. Chernikov return (ESRCH); 1873b3a8eb9SGleb Smirnoff 188b6ee846eSAlexander V. Chernikov xi = malloc(sizeof(ipfw_xtable_info), M_TEMP, M_WAITOK|M_ZERO); 189b6ee846eSAlexander V. Chernikov xi->vtype = IPFW_VTYPE_U32; 1904c0c07a5SAlexander V. Chernikov 191b6ee846eSAlexander V. Chernikov error = create_table_internal(ch, ti, NULL, xi); 192b6ee846eSAlexander V. Chernikov free(xi, M_TEMP); 193db785d31SAlexander V. Chernikov 194db785d31SAlexander V. Chernikov if (error != 0) 195db785d31SAlexander V. Chernikov return (error); 196db785d31SAlexander V. Chernikov 197db785d31SAlexander V. Chernikov /* Let's try to find & reference another time */ 198db785d31SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 199db785d31SAlexander V. Chernikov if ((tc = find_table(ni, ti)) == NULL) { 200db785d31SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 201db785d31SAlexander V. Chernikov return (EINVAL); 202db785d31SAlexander V. Chernikov } 203db785d31SAlexander V. Chernikov 204db785d31SAlexander V. Chernikov if (tc->no.type != ti->type) { 205db785d31SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 206db785d31SAlexander V. Chernikov return (EINVAL); 207db785d31SAlexander V. Chernikov } 208db785d31SAlexander V. Chernikov 209db785d31SAlexander V. Chernikov /* Reference and unlock */ 210db785d31SAlexander V. Chernikov tc->no.refcnt++; 211db785d31SAlexander V. Chernikov ta = tc->ta; 212db785d31SAlexander V. Chernikov 213db785d31SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 214db785d31SAlexander V. Chernikov } 215db785d31SAlexander V. Chernikov 2169f7d47b0SAlexander V. Chernikov /* Prepare record (allocate memory) */ 2179f7d47b0SAlexander V. Chernikov memset(&ta_buf, 0, sizeof(ta_buf)); 21868394ec8SAlexander V. Chernikov error = ta->prepare_add(ch, tei, &ta_buf); 219db785d31SAlexander V. Chernikov if (error != 0) 2209f7d47b0SAlexander V. Chernikov return (error); 2213b3a8eb9SGleb Smirnoff 222b074b7bbSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 2233b3a8eb9SGleb Smirnoff 224b6ee846eSAlexander V. Chernikov /* 225b6ee846eSAlexander V. Chernikov * Ensure we are able to add all entries without additional 226b6ee846eSAlexander V. Chernikov * memory allocations. May release/reacquire UH_WLOCK. 227b6ee846eSAlexander V. Chernikov */ 228b6ee846eSAlexander V. Chernikov kidx = tc->no.kidx; 229b6ee846eSAlexander V. Chernikov error = check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), count); 230b6ee846eSAlexander V. Chernikov if (error != 0) { 231b6ee846eSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 232b6ee846eSAlexander V. Chernikov ta->flush_entry(ch, tei, &ta_buf); 233b6ee846eSAlexander V. Chernikov return (error); 234b6ee846eSAlexander V. Chernikov } 235b6ee846eSAlexander V. Chernikov 236b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 2373b3a8eb9SGleb Smirnoff 2389f7d47b0SAlexander V. Chernikov /* Drop reference we've used in first search */ 2399f7d47b0SAlexander V. Chernikov tc->no.refcnt--; 240b074b7bbSAlexander V. Chernikov 2414c0c07a5SAlexander V. Chernikov /* Check limit before adding */ 2424c0c07a5SAlexander V. Chernikov if (tc->limit != 0 && tc->count == tc->limit) { 2434c0c07a5SAlexander V. Chernikov if ((tei->flags & TEI_FLAGS_UPDATE) == 0) { 2444c0c07a5SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 245b6ee846eSAlexander V. Chernikov ta->flush_entry(ch, tei, &ta_buf); 2464c0c07a5SAlexander V. Chernikov return (EFBIG); 2474c0c07a5SAlexander V. Chernikov } 2484c0c07a5SAlexander V. Chernikov 2494c0c07a5SAlexander V. Chernikov /* 2504c0c07a5SAlexander V. Chernikov * We have UPDATE flag set. 2514c0c07a5SAlexander V. Chernikov * Permit updating record (if found), 2524c0c07a5SAlexander V. Chernikov * but restrict adding new one since we've 2534c0c07a5SAlexander V. Chernikov * already hit the limit. 2544c0c07a5SAlexander V. Chernikov */ 2554c0c07a5SAlexander V. Chernikov tei->flags |= TEI_FLAGS_DONTADD; 2564c0c07a5SAlexander V. Chernikov } 2574c0c07a5SAlexander V. Chernikov 258b074b7bbSAlexander V. Chernikov /* We've got valid table in @tc. Let's add data */ 2599f7d47b0SAlexander V. Chernikov kidx = tc->no.kidx; 2609f7d47b0SAlexander V. Chernikov ta = tc->ta; 261adea6201SAlexander V. Chernikov num = 0; 2629f7d47b0SAlexander V. Chernikov 263b074b7bbSAlexander V. Chernikov IPFW_WLOCK(ch); 264b6ee846eSAlexander V. Chernikov error = ta->add(tc->astate, KIDX_TO_TI(ch, kidx), tei, &ta_buf, &num); 2653b3a8eb9SGleb Smirnoff IPFW_WUNLOCK(ch); 2669f7d47b0SAlexander V. Chernikov 267ac35ff17SAlexander V. Chernikov /* Update number of records. */ 268b6ee846eSAlexander V. Chernikov if (error == 0) { 269adea6201SAlexander V. Chernikov tc->count += num; 270b6ee846eSAlexander V. Chernikov /* Permit post-add algorithm grow/rehash. */ 271b6ee846eSAlexander V. Chernikov error = check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), 0); 272b6ee846eSAlexander V. Chernikov } 273db785d31SAlexander V. Chernikov 274b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 275b074b7bbSAlexander V. Chernikov 276ac35ff17SAlexander V. Chernikov /* Run cleaning callback anyway */ 27768394ec8SAlexander V. Chernikov ta->flush_entry(ch, tei, &ta_buf); 278b074b7bbSAlexander V. Chernikov 2799f7d47b0SAlexander V. Chernikov return (error); 2803b3a8eb9SGleb Smirnoff } 2813b3a8eb9SGleb Smirnoff 2823b3a8eb9SGleb Smirnoff int 2831832a7b3SAlexander V. Chernikov del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti, 284b6ee846eSAlexander V. Chernikov struct tentry_info *tei, uint32_t count) 2853b3a8eb9SGleb Smirnoff { 286b074b7bbSAlexander V. Chernikov struct table_config *tc; 2879f7d47b0SAlexander V. Chernikov struct table_algo *ta; 288b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 289b074b7bbSAlexander V. Chernikov uint16_t kidx; 2909f7d47b0SAlexander V. Chernikov int error; 291adea6201SAlexander V. Chernikov uint32_t num; 292b6ee846eSAlexander V. Chernikov char ta_buf[TA_BUF_SZ]; 2933b3a8eb9SGleb Smirnoff 2949f7d47b0SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 295b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 296b074b7bbSAlexander V. Chernikov if ((tc = find_table(ni, ti)) == NULL) { 2979f7d47b0SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 2983b3a8eb9SGleb Smirnoff return (ESRCH); 2993b3a8eb9SGleb Smirnoff } 3003b3a8eb9SGleb Smirnoff 301b074b7bbSAlexander V. Chernikov if (tc->no.type != ti->type) { 3029f7d47b0SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 3033b3a8eb9SGleb Smirnoff return (EINVAL); 3043b3a8eb9SGleb Smirnoff } 3059f7d47b0SAlexander V. Chernikov 3069f7d47b0SAlexander V. Chernikov ta = tc->ta; 3079f7d47b0SAlexander V. Chernikov 308db785d31SAlexander V. Chernikov /* 309b6ee846eSAlexander V. Chernikov * Give a chance for algorithm to shrink. 310b6ee846eSAlexander V. Chernikov * May release/reacquire UH_WLOCK. 311db785d31SAlexander V. Chernikov */ 312b6ee846eSAlexander V. Chernikov kidx = tc->no.kidx; 313b6ee846eSAlexander V. Chernikov error = check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), 0); 314db785d31SAlexander V. Chernikov if (error != 0) { 315db785d31SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 316b6ee846eSAlexander V. Chernikov ta->flush_entry(ch, tei, &ta_buf); 317db785d31SAlexander V. Chernikov return (error); 318db785d31SAlexander V. Chernikov } 319db785d31SAlexander V. Chernikov 320db785d31SAlexander V. Chernikov /* 321db785d31SAlexander V. Chernikov * We assume ta_buf size is enough for storing 322b6ee846eSAlexander V. Chernikov * prepare_del() key, so we're running under UH_WLOCK here. 323db785d31SAlexander V. Chernikov */ 3249f7d47b0SAlexander V. Chernikov memset(&ta_buf, 0, sizeof(ta_buf)); 32568394ec8SAlexander V. Chernikov if ((error = ta->prepare_del(ch, tei, &ta_buf)) != 0) { 3269f7d47b0SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 3279f7d47b0SAlexander V. Chernikov return (error); 3289f7d47b0SAlexander V. Chernikov } 3299f7d47b0SAlexander V. Chernikov 330b074b7bbSAlexander V. Chernikov kidx = tc->no.kidx; 331adea6201SAlexander V. Chernikov num = 0; 332b074b7bbSAlexander V. Chernikov 333b074b7bbSAlexander V. Chernikov IPFW_WLOCK(ch); 334b6ee846eSAlexander V. Chernikov error = ta->del(tc->astate, KIDX_TO_TI(ch, kidx), tei, &ta_buf, &num); 3353b3a8eb9SGleb Smirnoff IPFW_WUNLOCK(ch); 3363b3a8eb9SGleb Smirnoff 337b6ee846eSAlexander V. Chernikov if (error == 0) { 338adea6201SAlexander V. Chernikov tc->count -= num; 339b6ee846eSAlexander V. Chernikov /* Run post-del hook to permit shrinking */ 340b6ee846eSAlexander V. Chernikov error = check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), 0); 341b6ee846eSAlexander V. Chernikov } 342b074b7bbSAlexander V. Chernikov 3439f7d47b0SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 3443b3a8eb9SGleb Smirnoff 34568394ec8SAlexander V. Chernikov ta->flush_entry(ch, tei, &ta_buf); 346ac35ff17SAlexander V. Chernikov 347ac35ff17SAlexander V. Chernikov return (error); 348ac35ff17SAlexander V. Chernikov } 349ac35ff17SAlexander V. Chernikov 350db785d31SAlexander V. Chernikov /* 351b6ee846eSAlexander V. Chernikov * Ensure that table @tc has enough space to add @count entries without 352b6ee846eSAlexander V. Chernikov * need for reallocation. 353db785d31SAlexander V. Chernikov * 354db785d31SAlexander V. Chernikov * Callbacks order: 355b6ee846eSAlexander V. Chernikov * 0) has_space() (UH_WLOCK) - checks if @count items can be added w/o resize. 356b6ee846eSAlexander V. Chernikov * 357db785d31SAlexander V. Chernikov * 1) alloc_modify (no locks, M_WAITOK) - alloc new state based on @pflags. 358db785d31SAlexander V. Chernikov * 2) prepare_modifyt (UH_WLOCK) - copy old data into new storage 359db785d31SAlexander V. Chernikov * 3) modify (UH_WLOCK + WLOCK) - switch pointers 360b6ee846eSAlexander V. Chernikov * 4) flush_modify (UH_WLOCK) - free state, if needed 361b6ee846eSAlexander V. Chernikov * 362b6ee846eSAlexander V. Chernikov * Returns 0 on success. 363db785d31SAlexander V. Chernikov */ 364db785d31SAlexander V. Chernikov static int 365b6ee846eSAlexander V. Chernikov check_table_space(struct ip_fw_chain *ch, struct table_config *tc, 366b6ee846eSAlexander V. Chernikov struct table_info *ti, uint32_t count) 367db785d31SAlexander V. Chernikov { 368b6ee846eSAlexander V. Chernikov struct table_algo *ta; 369b6ee846eSAlexander V. Chernikov uint64_t pflags; 370b6ee846eSAlexander V. Chernikov char ta_buf[TA_BUF_SZ]; 371db785d31SAlexander V. Chernikov int error; 372db785d31SAlexander V. Chernikov 373b6ee846eSAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(ch); 374db785d31SAlexander V. Chernikov 375b6ee846eSAlexander V. Chernikov error = 0; 376b6ee846eSAlexander V. Chernikov ta = tc->ta; 377b6ee846eSAlexander V. Chernikov /* Acquire reference not to loose @tc between locks/unlocks */ 378b6ee846eSAlexander V. Chernikov tc->no.refcnt++; 379db785d31SAlexander V. Chernikov 380db785d31SAlexander V. Chernikov /* 381b6ee846eSAlexander V. Chernikov * TODO: think about avoiding race between large add/large delete 382b6ee846eSAlexander V. Chernikov * operation on algorithm which implements shrinking along with 383b6ee846eSAlexander V. Chernikov * growing. 384db785d31SAlexander V. Chernikov */ 385b6ee846eSAlexander V. Chernikov while (true) { 386b6ee846eSAlexander V. Chernikov pflags = 0; 387b6ee846eSAlexander V. Chernikov if (ta->has_space(tc->astate, ti, count, &pflags) != 0) { 388b6ee846eSAlexander V. Chernikov tc->no.refcnt--; 389b6ee846eSAlexander V. Chernikov return (0); 390b6ee846eSAlexander V. Chernikov } 391db785d31SAlexander V. Chernikov 392b6ee846eSAlexander V. Chernikov /* We have to shrink/grow table */ 393b6ee846eSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 394b6ee846eSAlexander V. Chernikov memset(&ta_buf, 0, sizeof(ta_buf)); 395b6ee846eSAlexander V. Chernikov 396b6ee846eSAlexander V. Chernikov if ((error = ta->prepare_mod(ta_buf, &pflags)) != 0) { 397b6ee846eSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 398b6ee846eSAlexander V. Chernikov break; 399b6ee846eSAlexander V. Chernikov } 400b6ee846eSAlexander V. Chernikov 401b6ee846eSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 402b6ee846eSAlexander V. Chernikov 403b6ee846eSAlexander V. Chernikov /* Check if we still need to alter table */ 404b6ee846eSAlexander V. Chernikov ti = KIDX_TO_TI(ch, tc->no.kidx); 405b6ee846eSAlexander V. Chernikov if (ta->has_space(tc->astate, ti, count, &pflags) != 0) { 406b6ee846eSAlexander V. Chernikov 407b6ee846eSAlexander V. Chernikov /* 408b6ee846eSAlexander V. Chernikov * Other threads has already performed resize. 409b6ee846eSAlexander V. Chernikov * Flush our state and return/ 410b6ee846eSAlexander V. Chernikov */ 411b6ee846eSAlexander V. Chernikov ta->flush_mod(ta_buf); 412b6ee846eSAlexander V. Chernikov break; 413b6ee846eSAlexander V. Chernikov } 414b6ee846eSAlexander V. Chernikov 415b6ee846eSAlexander V. Chernikov error = ta->fill_mod(tc->astate, ti, ta_buf, &pflags); 416b6ee846eSAlexander V. Chernikov if (error == 0) { 417db785d31SAlexander V. Chernikov /* Do actual modification */ 418db785d31SAlexander V. Chernikov IPFW_WLOCK(ch); 41968394ec8SAlexander V. Chernikov ta->modify(tc->astate, ti, ta_buf, pflags); 420db785d31SAlexander V. Chernikov IPFW_WUNLOCK(ch); 421db785d31SAlexander V. Chernikov } 422db785d31SAlexander V. Chernikov 423b6ee846eSAlexander V. Chernikov /* Anyway, flush data and retry */ 424db785d31SAlexander V. Chernikov ta->flush_mod(ta_buf); 425b6ee846eSAlexander V. Chernikov } 426db785d31SAlexander V. Chernikov 427b6ee846eSAlexander V. Chernikov tc->no.refcnt--; 428db785d31SAlexander V. Chernikov return (error); 429db785d31SAlexander V. Chernikov } 430db785d31SAlexander V. Chernikov 431b6ee846eSAlexander V. Chernikov 432b6ee846eSAlexander V. Chernikov 433ac35ff17SAlexander V. Chernikov int 434db785d31SAlexander V. Chernikov ipfw_manage_table_ent(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 435ac35ff17SAlexander V. Chernikov struct sockopt_data *sd) 436ac35ff17SAlexander V. Chernikov { 437ac35ff17SAlexander V. Chernikov int error; 438ac35ff17SAlexander V. Chernikov 439ac35ff17SAlexander V. Chernikov switch (op3->version) { 440ac35ff17SAlexander V. Chernikov case 0: 441db785d31SAlexander V. Chernikov error = ipfw_manage_table_ent_v0(ch, op3, sd); 442ac35ff17SAlexander V. Chernikov break; 443ac35ff17SAlexander V. Chernikov case 1: 444db785d31SAlexander V. Chernikov error = ipfw_manage_table_ent_v1(ch, op3, sd); 445ac35ff17SAlexander V. Chernikov break; 446ac35ff17SAlexander V. Chernikov default: 447ac35ff17SAlexander V. Chernikov error = ENOTSUP; 448ac35ff17SAlexander V. Chernikov } 449ac35ff17SAlexander V. Chernikov 450ac35ff17SAlexander V. Chernikov return (error); 451ac35ff17SAlexander V. Chernikov } 452ac35ff17SAlexander V. Chernikov 453ac35ff17SAlexander V. Chernikov /* 454ac35ff17SAlexander V. Chernikov * Adds or deletes record in table. 455ac35ff17SAlexander V. Chernikov * Data layout (v0): 456ac35ff17SAlexander V. Chernikov * Request: [ ip_fw3_opheader ipfw_table_xentry ] 457ac35ff17SAlexander V. Chernikov * 458ac35ff17SAlexander V. Chernikov * Returns 0 on success 459ac35ff17SAlexander V. Chernikov */ 460ac35ff17SAlexander V. Chernikov static int 461db785d31SAlexander V. Chernikov ipfw_manage_table_ent_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 462ac35ff17SAlexander V. Chernikov struct sockopt_data *sd) 463ac35ff17SAlexander V. Chernikov { 464ac35ff17SAlexander V. Chernikov ipfw_table_xentry *xent; 465ac35ff17SAlexander V. Chernikov struct tentry_info tei; 466ac35ff17SAlexander V. Chernikov struct tid_info ti; 467ac35ff17SAlexander V. Chernikov int error, hdrlen, read; 468ac35ff17SAlexander V. Chernikov 469ac35ff17SAlexander V. Chernikov hdrlen = offsetof(ipfw_table_xentry, k); 470ac35ff17SAlexander V. Chernikov 471ac35ff17SAlexander V. Chernikov /* Check minimum header size */ 472ac35ff17SAlexander V. Chernikov if (sd->valsize < (sizeof(*op3) + hdrlen)) 473ac35ff17SAlexander V. Chernikov return (EINVAL); 474ac35ff17SAlexander V. Chernikov 475ac35ff17SAlexander V. Chernikov read = sizeof(ip_fw3_opheader); 476ac35ff17SAlexander V. Chernikov 477ac35ff17SAlexander V. Chernikov /* Check if xentry len field is valid */ 478ac35ff17SAlexander V. Chernikov xent = (ipfw_table_xentry *)(op3 + 1); 479ac35ff17SAlexander V. Chernikov if (xent->len < hdrlen || xent->len + read > sd->valsize) 480ac35ff17SAlexander V. Chernikov return (EINVAL); 481ac35ff17SAlexander V. Chernikov 482ac35ff17SAlexander V. Chernikov memset(&tei, 0, sizeof(tei)); 483ac35ff17SAlexander V. Chernikov tei.paddr = &xent->k; 484ac35ff17SAlexander V. Chernikov tei.masklen = xent->masklen; 485ac35ff17SAlexander V. Chernikov tei.value = xent->value; 486ac35ff17SAlexander V. Chernikov /* Old requests compability */ 487db785d31SAlexander V. Chernikov tei.flags = TEI_FLAGS_COMPAT; 488ac35ff17SAlexander V. Chernikov if (xent->type == IPFW_TABLE_CIDR) { 489ac35ff17SAlexander V. Chernikov if (xent->len - hdrlen == sizeof(in_addr_t)) 490ac35ff17SAlexander V. Chernikov tei.subtype = AF_INET; 491ac35ff17SAlexander V. Chernikov else 492ac35ff17SAlexander V. Chernikov tei.subtype = AF_INET6; 493ac35ff17SAlexander V. Chernikov } 494ac35ff17SAlexander V. Chernikov 495ac35ff17SAlexander V. Chernikov memset(&ti, 0, sizeof(ti)); 496ac35ff17SAlexander V. Chernikov ti.uidx = xent->tbl; 497ac35ff17SAlexander V. Chernikov ti.type = xent->type; 498ac35ff17SAlexander V. Chernikov 499ac35ff17SAlexander V. Chernikov error = (op3->opcode == IP_FW_TABLE_XADD) ? 500b6ee846eSAlexander V. Chernikov add_table_entry(ch, &ti, &tei, 1) : 501b6ee846eSAlexander V. Chernikov del_table_entry(ch, &ti, &tei, 1); 502ac35ff17SAlexander V. Chernikov 503ac35ff17SAlexander V. Chernikov return (error); 504ac35ff17SAlexander V. Chernikov } 505ac35ff17SAlexander V. Chernikov 506ac35ff17SAlexander V. Chernikov /* 507ac35ff17SAlexander V. Chernikov * Adds or deletes record in table. 508ac35ff17SAlexander V. Chernikov * Data layout (v1)(current): 509db785d31SAlexander V. Chernikov * Request: [ ipfw_obj_header 510db785d31SAlexander V. Chernikov * ipfw_obj_ctlv(IPFW_TLV_TBLENT_LIST) [ ipfw_obj_tentry x N ] 511db785d31SAlexander V. Chernikov * ] 512ac35ff17SAlexander V. Chernikov * 513ac35ff17SAlexander V. Chernikov * Returns 0 on success 514ac35ff17SAlexander V. Chernikov */ 515ac35ff17SAlexander V. Chernikov static int 516db785d31SAlexander V. Chernikov ipfw_manage_table_ent_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 517ac35ff17SAlexander V. Chernikov struct sockopt_data *sd) 518ac35ff17SAlexander V. Chernikov { 519ac35ff17SAlexander V. Chernikov ipfw_obj_tentry *tent; 520db785d31SAlexander V. Chernikov ipfw_obj_ctlv *ctlv; 521ac35ff17SAlexander V. Chernikov ipfw_obj_header *oh; 522ac35ff17SAlexander V. Chernikov struct tentry_info tei; 523ac35ff17SAlexander V. Chernikov struct tid_info ti; 524ac35ff17SAlexander V. Chernikov int error, read; 525ac35ff17SAlexander V. Chernikov 526ac35ff17SAlexander V. Chernikov /* Check minimum header size */ 527db785d31SAlexander V. Chernikov if (sd->valsize < (sizeof(*oh) + sizeof(*ctlv))) 528ac35ff17SAlexander V. Chernikov return (EINVAL); 529ac35ff17SAlexander V. Chernikov 530ac35ff17SAlexander V. Chernikov /* Check if passed data is too long */ 531ac35ff17SAlexander V. Chernikov if (sd->valsize != sd->kavail) 532ac35ff17SAlexander V. Chernikov return (EINVAL); 533ac35ff17SAlexander V. Chernikov 534ac35ff17SAlexander V. Chernikov oh = (ipfw_obj_header *)sd->kbuf; 535ac35ff17SAlexander V. Chernikov 536ac35ff17SAlexander V. Chernikov /* Basic length checks for TLVs */ 537ac35ff17SAlexander V. Chernikov if (oh->ntlv.head.length != sizeof(oh->ntlv)) 538ac35ff17SAlexander V. Chernikov return (EINVAL); 539ac35ff17SAlexander V. Chernikov 540ac35ff17SAlexander V. Chernikov read = sizeof(*oh); 541ac35ff17SAlexander V. Chernikov 542db785d31SAlexander V. Chernikov ctlv = (ipfw_obj_ctlv *)(oh + 1); 543db785d31SAlexander V. Chernikov if (ctlv->head.length + read != sd->valsize) 544db785d31SAlexander V. Chernikov return (EINVAL); 545db785d31SAlexander V. Chernikov 546db785d31SAlexander V. Chernikov /* 547db785d31SAlexander V. Chernikov * TODO: permit adding multiple entries for given table 548db785d31SAlexander V. Chernikov * at once 549db785d31SAlexander V. Chernikov */ 550db785d31SAlexander V. Chernikov if (ctlv->count != 1) 551db785d31SAlexander V. Chernikov return (EOPNOTSUPP); 552db785d31SAlexander V. Chernikov 553db785d31SAlexander V. Chernikov read += sizeof(*ctlv); 554db785d31SAlexander V. Chernikov 555ac35ff17SAlexander V. Chernikov /* Assume tentry may grow to support larger keys */ 556db785d31SAlexander V. Chernikov tent = (ipfw_obj_tentry *)(ctlv + 1); 557ac35ff17SAlexander V. Chernikov if (tent->head.length < sizeof(*tent) || 558ac35ff17SAlexander V. Chernikov tent->head.length + read > sd->valsize) 559ac35ff17SAlexander V. Chernikov return (EINVAL); 560ac35ff17SAlexander V. Chernikov 56181d3153dSAlexander V. Chernikov /* Convert data into kernel request objects */ 562ac35ff17SAlexander V. Chernikov memset(&tei, 0, sizeof(tei)); 563ac35ff17SAlexander V. Chernikov tei.paddr = &tent->k; 564ac35ff17SAlexander V. Chernikov tei.subtype = tent->subtype; 565ac35ff17SAlexander V. Chernikov tei.masklen = tent->masklen; 56681d3153dSAlexander V. Chernikov if (tent->head.flags & IPFW_TF_UPDATE) 567ac35ff17SAlexander V. Chernikov tei.flags |= TEI_FLAGS_UPDATE; 568ac35ff17SAlexander V. Chernikov tei.value = tent->value; 569ac35ff17SAlexander V. Chernikov 57081d3153dSAlexander V. Chernikov objheader_to_ti(oh, &ti); 571ac35ff17SAlexander V. Chernikov ti.type = oh->ntlv.type; 57281d3153dSAlexander V. Chernikov ti.uidx = tent->idx; 573ac35ff17SAlexander V. Chernikov 574ac35ff17SAlexander V. Chernikov error = (oh->opheader.opcode == IP_FW_TABLE_XADD) ? 575b6ee846eSAlexander V. Chernikov add_table_entry(ch, &ti, &tei, 1) : 576b6ee846eSAlexander V. Chernikov del_table_entry(ch, &ti, &tei, 1); 577ac35ff17SAlexander V. Chernikov 578ac35ff17SAlexander V. Chernikov return (error); 579ac35ff17SAlexander V. Chernikov } 580ac35ff17SAlexander V. Chernikov 58181d3153dSAlexander V. Chernikov /* 58281d3153dSAlexander V. Chernikov * Looks up an entry in given table. 58381d3153dSAlexander V. Chernikov * Data layout (v0)(current): 58481d3153dSAlexander V. Chernikov * Request: [ ipfw_obj_header ipfw_obj_tentry ] 58581d3153dSAlexander V. Chernikov * Reply: [ ipfw_obj_header ipfw_obj_tentry ] 58681d3153dSAlexander V. Chernikov * 58781d3153dSAlexander V. Chernikov * Returns 0 on success 58881d3153dSAlexander V. Chernikov */ 58981d3153dSAlexander V. Chernikov int 59081d3153dSAlexander V. Chernikov ipfw_find_table_entry(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 59181d3153dSAlexander V. Chernikov struct sockopt_data *sd) 59281d3153dSAlexander V. Chernikov { 59381d3153dSAlexander V. Chernikov ipfw_obj_tentry *tent; 59481d3153dSAlexander V. Chernikov ipfw_obj_header *oh; 59581d3153dSAlexander V. Chernikov struct tid_info ti; 59681d3153dSAlexander V. Chernikov struct table_config *tc; 59781d3153dSAlexander V. Chernikov struct table_algo *ta; 59881d3153dSAlexander V. Chernikov struct table_info *kti; 59981d3153dSAlexander V. Chernikov struct namedobj_instance *ni; 600914bffb6SAlexander V. Chernikov int error; 60181d3153dSAlexander V. Chernikov size_t sz; 60281d3153dSAlexander V. Chernikov 60381d3153dSAlexander V. Chernikov /* Check minimum header size */ 60481d3153dSAlexander V. Chernikov sz = sizeof(*oh) + sizeof(*tent); 60581d3153dSAlexander V. Chernikov if (sd->valsize != sz) 60681d3153dSAlexander V. Chernikov return (EINVAL); 60781d3153dSAlexander V. Chernikov 60881d3153dSAlexander V. Chernikov oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 60981d3153dSAlexander V. Chernikov tent = (ipfw_obj_tentry *)(oh + 1); 61081d3153dSAlexander V. Chernikov 61181d3153dSAlexander V. Chernikov /* Basic length checks for TLVs */ 61281d3153dSAlexander V. Chernikov if (oh->ntlv.head.length != sizeof(oh->ntlv)) 61381d3153dSAlexander V. Chernikov return (EINVAL); 61481d3153dSAlexander V. Chernikov 61581d3153dSAlexander V. Chernikov objheader_to_ti(oh, &ti); 61681d3153dSAlexander V. Chernikov ti.type = oh->ntlv.type; 61781d3153dSAlexander V. Chernikov ti.uidx = tent->idx; 61881d3153dSAlexander V. Chernikov 61981d3153dSAlexander V. Chernikov IPFW_UH_RLOCK(ch); 62081d3153dSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 62181d3153dSAlexander V. Chernikov 62281d3153dSAlexander V. Chernikov /* 62381d3153dSAlexander V. Chernikov * Find existing table and check its type . 62481d3153dSAlexander V. Chernikov */ 62581d3153dSAlexander V. Chernikov ta = NULL; 62681d3153dSAlexander V. Chernikov if ((tc = find_table(ni, &ti)) == NULL) { 62781d3153dSAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 62881d3153dSAlexander V. Chernikov return (ESRCH); 62981d3153dSAlexander V. Chernikov } 63081d3153dSAlexander V. Chernikov 63181d3153dSAlexander V. Chernikov /* check table type */ 63281d3153dSAlexander V. Chernikov if (tc->no.type != ti.type) { 63381d3153dSAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 63481d3153dSAlexander V. Chernikov return (EINVAL); 63581d3153dSAlexander V. Chernikov } 63681d3153dSAlexander V. Chernikov 63781d3153dSAlexander V. Chernikov kti = KIDX_TO_TI(ch, tc->no.kidx); 63881d3153dSAlexander V. Chernikov ta = tc->ta; 63981d3153dSAlexander V. Chernikov 640914bffb6SAlexander V. Chernikov if (ta->find_tentry == NULL) 641914bffb6SAlexander V. Chernikov return (ENOTSUP); 642914bffb6SAlexander V. Chernikov 643914bffb6SAlexander V. Chernikov error = ta->find_tentry(tc->astate, kti, tent); 64481d3153dSAlexander V. Chernikov 64581d3153dSAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 64681d3153dSAlexander V. Chernikov 64781d3153dSAlexander V. Chernikov return (error); 64881d3153dSAlexander V. Chernikov } 64981d3153dSAlexander V. Chernikov 65046d52008SAlexander V. Chernikov /* 65146d52008SAlexander V. Chernikov * Flushes all entries or destroys given table. 65246d52008SAlexander V. Chernikov * Data layout (v0)(current): 65346d52008SAlexander V. Chernikov * Request: [ ipfw_obj_header ] 65446d52008SAlexander V. Chernikov * 65546d52008SAlexander V. Chernikov * Returns 0 on success 65646d52008SAlexander V. Chernikov */ 657ac35ff17SAlexander V. Chernikov int 658ac35ff17SAlexander V. Chernikov ipfw_flush_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 659ac35ff17SAlexander V. Chernikov struct sockopt_data *sd) 660ac35ff17SAlexander V. Chernikov { 661ac35ff17SAlexander V. Chernikov int error; 662ac35ff17SAlexander V. Chernikov struct _ipfw_obj_header *oh; 663ac35ff17SAlexander V. Chernikov struct tid_info ti; 664ac35ff17SAlexander V. Chernikov 665ac35ff17SAlexander V. Chernikov if (sd->valsize != sizeof(*oh)) 666ac35ff17SAlexander V. Chernikov return (EINVAL); 667ac35ff17SAlexander V. Chernikov 668ac35ff17SAlexander V. Chernikov oh = (struct _ipfw_obj_header *)op3; 669ac35ff17SAlexander V. Chernikov objheader_to_ti(oh, &ti); 670ac35ff17SAlexander V. Chernikov 6711832a7b3SAlexander V. Chernikov if (op3->opcode == IP_FW_TABLE_XDESTROY) 672ac35ff17SAlexander V. Chernikov error = destroy_table(ch, &ti); 6731832a7b3SAlexander V. Chernikov else if (op3->opcode == IP_FW_TABLE_XFLUSH) 674ac35ff17SAlexander V. Chernikov error = flush_table(ch, &ti); 675ac35ff17SAlexander V. Chernikov else 676ac35ff17SAlexander V. Chernikov return (ENOTSUP); 677ac35ff17SAlexander V. Chernikov 678ac35ff17SAlexander V. Chernikov return (error); 6793b3a8eb9SGleb Smirnoff } 6803b3a8eb9SGleb Smirnoff 6811832a7b3SAlexander V. Chernikov int 682ac35ff17SAlexander V. Chernikov flush_table(struct ip_fw_chain *ch, struct tid_info *ti) 6833b3a8eb9SGleb Smirnoff { 684b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 685b074b7bbSAlexander V. Chernikov struct table_config *tc; 6869f7d47b0SAlexander V. Chernikov struct table_algo *ta; 6879f7d47b0SAlexander V. Chernikov struct table_info ti_old, ti_new, *tablestate; 6889f7d47b0SAlexander V. Chernikov void *astate_old, *astate_new; 689914bffb6SAlexander V. Chernikov char algostate[64], *pstate; 690b074b7bbSAlexander V. Chernikov int error; 691b074b7bbSAlexander V. Chernikov uint16_t kidx; 692914bffb6SAlexander V. Chernikov uint8_t tflags; 6933b3a8eb9SGleb Smirnoff 6943b3a8eb9SGleb Smirnoff /* 6959f7d47b0SAlexander V. Chernikov * Stage 1: save table algoritm. 696b074b7bbSAlexander V. Chernikov * Reference found table to ensure it won't disappear. 6973b3a8eb9SGleb Smirnoff */ 698b074b7bbSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 699b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 700b074b7bbSAlexander V. Chernikov if ((tc = find_table(ni, ti)) == NULL) { 701b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 702b074b7bbSAlexander V. Chernikov return (ESRCH); 703b074b7bbSAlexander V. Chernikov } 7049f7d47b0SAlexander V. Chernikov ta = tc->ta; 705b074b7bbSAlexander V. Chernikov tc->no.refcnt++; 706daabb523SAlexander V. Chernikov /* Save statup algo parameters */ 707daabb523SAlexander V. Chernikov if (ta->print_config != NULL) { 708daabb523SAlexander V. Chernikov ta->print_config(tc->astate, KIDX_TO_TI(ch, tc->no.kidx), 709daabb523SAlexander V. Chernikov algostate, sizeof(algostate)); 710daabb523SAlexander V. Chernikov pstate = algostate; 711daabb523SAlexander V. Chernikov } else 712daabb523SAlexander V. Chernikov pstate = NULL; 713914bffb6SAlexander V. Chernikov tflags = tc->tflags; 714b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 7153b3a8eb9SGleb Smirnoff 716b074b7bbSAlexander V. Chernikov /* 7179f7d47b0SAlexander V. Chernikov * Stage 2: allocate new table instance using same algo. 718b074b7bbSAlexander V. Chernikov */ 7199f7d47b0SAlexander V. Chernikov memset(&ti_new, 0, sizeof(struct table_info)); 720914bffb6SAlexander V. Chernikov if ((error = ta->init(ch, &astate_new, &ti_new, pstate, tflags)) != 0) { 721b074b7bbSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 722b074b7bbSAlexander V. Chernikov tc->no.refcnt--; 723b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 724b074b7bbSAlexander V. Chernikov return (error); 725b074b7bbSAlexander V. Chernikov } 726b074b7bbSAlexander V. Chernikov 727b074b7bbSAlexander V. Chernikov /* 728b074b7bbSAlexander V. Chernikov * Stage 3: swap old state pointers with newly-allocated ones. 729b074b7bbSAlexander V. Chernikov * Decrease refcount. 730b074b7bbSAlexander V. Chernikov */ 731b074b7bbSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 732b074b7bbSAlexander V. Chernikov 733b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 734b074b7bbSAlexander V. Chernikov kidx = tc->no.kidx; 7359f7d47b0SAlexander V. Chernikov tablestate = (struct table_info *)ch->tablestate; 736b074b7bbSAlexander V. Chernikov 7379f7d47b0SAlexander V. Chernikov IPFW_WLOCK(ch); 7389f7d47b0SAlexander V. Chernikov ti_old = tablestate[kidx]; 7399f7d47b0SAlexander V. Chernikov tablestate[kidx] = ti_new; 7409f7d47b0SAlexander V. Chernikov IPFW_WUNLOCK(ch); 741b074b7bbSAlexander V. Chernikov 7429f7d47b0SAlexander V. Chernikov astate_old = tc->astate; 7439f7d47b0SAlexander V. Chernikov tc->astate = astate_new; 7449f7d47b0SAlexander V. Chernikov tc->ti = ti_new; 7459f7d47b0SAlexander V. Chernikov tc->count = 0; 746b074b7bbSAlexander V. Chernikov tc->no.refcnt--; 747b074b7bbSAlexander V. Chernikov 748b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 7493b3a8eb9SGleb Smirnoff 750b074b7bbSAlexander V. Chernikov /* 751b074b7bbSAlexander V. Chernikov * Stage 4: perform real flush. 752b074b7bbSAlexander V. Chernikov */ 7539f7d47b0SAlexander V. Chernikov ta->destroy(astate_old, &ti_old); 7543b3a8eb9SGleb Smirnoff 7553b3a8eb9SGleb Smirnoff return (0); 7563b3a8eb9SGleb Smirnoff } 7573b3a8eb9SGleb Smirnoff 758b074b7bbSAlexander V. Chernikov /* 75946d52008SAlexander V. Chernikov * Swaps two tables. 76046d52008SAlexander V. Chernikov * Data layout (v0)(current): 76146d52008SAlexander V. Chernikov * Request: [ ipfw_obj_header ipfw_obj_ntlv ] 76246d52008SAlexander V. Chernikov * 76346d52008SAlexander V. Chernikov * Returns 0 on success 76446d52008SAlexander V. Chernikov */ 76546d52008SAlexander V. Chernikov int 76646d52008SAlexander V. Chernikov ipfw_swap_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 76746d52008SAlexander V. Chernikov struct sockopt_data *sd) 76846d52008SAlexander V. Chernikov { 76946d52008SAlexander V. Chernikov int error; 77046d52008SAlexander V. Chernikov struct _ipfw_obj_header *oh; 77146d52008SAlexander V. Chernikov struct tid_info ti_a, ti_b; 77246d52008SAlexander V. Chernikov 77346d52008SAlexander V. Chernikov if (sd->valsize != sizeof(*oh) + sizeof(ipfw_obj_ntlv)) 77446d52008SAlexander V. Chernikov return (EINVAL); 77546d52008SAlexander V. Chernikov 77646d52008SAlexander V. Chernikov oh = (struct _ipfw_obj_header *)op3; 77746d52008SAlexander V. Chernikov ntlv_to_ti(&oh->ntlv, &ti_a); 77846d52008SAlexander V. Chernikov ntlv_to_ti((ipfw_obj_ntlv *)(oh + 1), &ti_b); 77946d52008SAlexander V. Chernikov 780a73d728dSAlexander V. Chernikov error = swap_tables(ch, &ti_a, &ti_b); 78146d52008SAlexander V. Chernikov 78246d52008SAlexander V. Chernikov return (error); 78346d52008SAlexander V. Chernikov } 78446d52008SAlexander V. Chernikov 78546d52008SAlexander V. Chernikov static int 786a73d728dSAlexander V. Chernikov swap_tables(struct ip_fw_chain *ch, struct tid_info *a, 78746d52008SAlexander V. Chernikov struct tid_info *b) 78846d52008SAlexander V. Chernikov { 78946d52008SAlexander V. Chernikov struct namedobj_instance *ni; 79046d52008SAlexander V. Chernikov struct table_config *tc_a, *tc_b; 79146d52008SAlexander V. Chernikov struct table_algo *ta; 79246d52008SAlexander V. Chernikov struct table_info ti, *tablestate; 79346d52008SAlexander V. Chernikov void *astate; 79446d52008SAlexander V. Chernikov uint32_t count; 79546d52008SAlexander V. Chernikov 79646d52008SAlexander V. Chernikov /* 79746d52008SAlexander V. Chernikov * Stage 1: find both tables and ensure they are of 79846d52008SAlexander V. Chernikov * the same type and algo. 79946d52008SAlexander V. Chernikov */ 80046d52008SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 80146d52008SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 80246d52008SAlexander V. Chernikov if ((tc_a = find_table(ni, a)) == NULL) { 80346d52008SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 80446d52008SAlexander V. Chernikov return (ESRCH); 80546d52008SAlexander V. Chernikov } 80646d52008SAlexander V. Chernikov if ((tc_b = find_table(ni, b)) == NULL) { 80746d52008SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 80846d52008SAlexander V. Chernikov return (ESRCH); 80946d52008SAlexander V. Chernikov } 81046d52008SAlexander V. Chernikov 81146d52008SAlexander V. Chernikov /* It is very easy to swap between the same table */ 81246d52008SAlexander V. Chernikov if (tc_a == tc_b) { 81346d52008SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 81446d52008SAlexander V. Chernikov return (0); 81546d52008SAlexander V. Chernikov } 81646d52008SAlexander V. Chernikov 81746d52008SAlexander V. Chernikov /* Check type and value are the same */ 81846d52008SAlexander V. Chernikov if (tc_a->no.type != tc_b->no.type || tc_a->tflags != tc_b->tflags || 81946d52008SAlexander V. Chernikov tc_a->vtype != tc_b->vtype) { 82046d52008SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 82146d52008SAlexander V. Chernikov return (EINVAL); 82246d52008SAlexander V. Chernikov } 82346d52008SAlexander V. Chernikov 82446d52008SAlexander V. Chernikov /* Check limits before swap */ 82546d52008SAlexander V. Chernikov if ((tc_a->limit != 0 && tc_b->count > tc_a->limit) || 82646d52008SAlexander V. Chernikov (tc_b->limit != 0 && tc_a->count > tc_b->limit)) { 82746d52008SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 82846d52008SAlexander V. Chernikov return (EFBIG); 82946d52008SAlexander V. Chernikov } 83046d52008SAlexander V. Chernikov 83146d52008SAlexander V. Chernikov /* Everything is fine, prepare to swap */ 83246d52008SAlexander V. Chernikov tablestate = (struct table_info *)ch->tablestate; 83346d52008SAlexander V. Chernikov ti = tablestate[tc_a->no.kidx]; 83446d52008SAlexander V. Chernikov ta = tc_a->ta; 83546d52008SAlexander V. Chernikov astate = tc_a->astate; 83646d52008SAlexander V. Chernikov count = tc_a->count; 83746d52008SAlexander V. Chernikov 83846d52008SAlexander V. Chernikov IPFW_WLOCK(ch); 83946d52008SAlexander V. Chernikov /* a <- b */ 84046d52008SAlexander V. Chernikov tablestate[tc_a->no.kidx] = tablestate[tc_b->no.kidx]; 84146d52008SAlexander V. Chernikov tc_a->ta = tc_b->ta; 84246d52008SAlexander V. Chernikov tc_a->astate = tc_b->astate; 84346d52008SAlexander V. Chernikov tc_a->count = tc_b->count; 84446d52008SAlexander V. Chernikov /* b <- a */ 84546d52008SAlexander V. Chernikov tablestate[tc_b->no.kidx] = ti; 84646d52008SAlexander V. Chernikov tc_b->ta = ta; 84746d52008SAlexander V. Chernikov tc_b->astate = astate; 84846d52008SAlexander V. Chernikov tc_b->count = count; 84946d52008SAlexander V. Chernikov IPFW_WUNLOCK(ch); 85046d52008SAlexander V. Chernikov 85146d52008SAlexander V. Chernikov /* Ensure tc.ti copies are in sync */ 85246d52008SAlexander V. Chernikov tc_a->ti = tablestate[tc_a->no.kidx]; 85346d52008SAlexander V. Chernikov tc_b->ti = tablestate[tc_b->no.kidx]; 85446d52008SAlexander V. Chernikov 85546d52008SAlexander V. Chernikov /* Notify both tables on @ti change */ 85646d52008SAlexander V. Chernikov if (tc_a->ta->change_ti != NULL) 85746d52008SAlexander V. Chernikov tc_a->ta->change_ti(tc_a->astate, &tablestate[tc_a->no.kidx]); 85846d52008SAlexander V. Chernikov if (tc_b->ta->change_ti != NULL) 85946d52008SAlexander V. Chernikov tc_b->ta->change_ti(tc_b->astate, &tablestate[tc_b->no.kidx]); 86046d52008SAlexander V. Chernikov 86146d52008SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 86246d52008SAlexander V. Chernikov 86346d52008SAlexander V. Chernikov return (0); 86446d52008SAlexander V. Chernikov } 86546d52008SAlexander V. Chernikov 86646d52008SAlexander V. Chernikov /* 8679f7d47b0SAlexander V. Chernikov * Destroys table specified by @ti. 868ac35ff17SAlexander V. Chernikov * Data layout (v0)(current): 869ac35ff17SAlexander V. Chernikov * Request: [ ip_fw3_opheader ] 870ac35ff17SAlexander V. Chernikov * 871ac35ff17SAlexander V. Chernikov * Returns 0 on success 872b074b7bbSAlexander V. Chernikov */ 873ac35ff17SAlexander V. Chernikov static int 874ac35ff17SAlexander V. Chernikov destroy_table(struct ip_fw_chain *ch, struct tid_info *ti) 875b074b7bbSAlexander V. Chernikov { 876b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 877b074b7bbSAlexander V. Chernikov struct table_config *tc; 878b074b7bbSAlexander V. Chernikov 879b074b7bbSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 880b074b7bbSAlexander V. Chernikov 881b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 882b074b7bbSAlexander V. Chernikov if ((tc = find_table(ni, ti)) == NULL) { 883b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 884b074b7bbSAlexander V. Chernikov return (ESRCH); 885b074b7bbSAlexander V. Chernikov } 886b074b7bbSAlexander V. Chernikov 8879f7d47b0SAlexander V. Chernikov /* Do not permit destroying referenced tables */ 8889f7d47b0SAlexander V. Chernikov if (tc->no.refcnt > 0) { 889b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 890b074b7bbSAlexander V. Chernikov return (EBUSY); 891b074b7bbSAlexander V. Chernikov } 892b074b7bbSAlexander V. Chernikov 893b074b7bbSAlexander V. Chernikov IPFW_WLOCK(ch); 894b074b7bbSAlexander V. Chernikov unlink_table(ch, tc); 895b074b7bbSAlexander V. Chernikov IPFW_WUNLOCK(ch); 896b074b7bbSAlexander V. Chernikov 897b074b7bbSAlexander V. Chernikov /* Free obj index */ 898ac35ff17SAlexander V. Chernikov if (ipfw_objhash_free_idx(ni, tc->no.kidx) != 0) 899b074b7bbSAlexander V. Chernikov printf("Error unlinking kidx %d from table %s\n", 900b074b7bbSAlexander V. Chernikov tc->no.kidx, tc->tablename); 901b074b7bbSAlexander V. Chernikov 902b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 903b074b7bbSAlexander V. Chernikov 904b074b7bbSAlexander V. Chernikov free_table_config(ni, tc); 905b074b7bbSAlexander V. Chernikov 906b074b7bbSAlexander V. Chernikov return (0); 907b074b7bbSAlexander V. Chernikov } 908b074b7bbSAlexander V. Chernikov 909b074b7bbSAlexander V. Chernikov static void 910b074b7bbSAlexander V. Chernikov destroy_table_locked(struct namedobj_instance *ni, struct named_object *no, 911b074b7bbSAlexander V. Chernikov void *arg) 912b074b7bbSAlexander V. Chernikov { 913b074b7bbSAlexander V. Chernikov 914b074b7bbSAlexander V. Chernikov unlink_table((struct ip_fw_chain *)arg, (struct table_config *)no); 915ac35ff17SAlexander V. Chernikov if (ipfw_objhash_free_idx(ni, no->kidx) != 0) 916b074b7bbSAlexander V. Chernikov printf("Error unlinking kidx %d from table %s\n", 917b074b7bbSAlexander V. Chernikov no->kidx, no->name); 918b074b7bbSAlexander V. Chernikov free_table_config(ni, (struct table_config *)no); 919b074b7bbSAlexander V. Chernikov } 920b074b7bbSAlexander V. Chernikov 9213b3a8eb9SGleb Smirnoff void 9223b3a8eb9SGleb Smirnoff ipfw_destroy_tables(struct ip_fw_chain *ch) 9233b3a8eb9SGleb Smirnoff { 9243b3a8eb9SGleb Smirnoff 925b074b7bbSAlexander V. Chernikov /* Remove all tables from working set */ 926b074b7bbSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 927b074b7bbSAlexander V. Chernikov IPFW_WLOCK(ch); 928b074b7bbSAlexander V. Chernikov ipfw_objhash_foreach(CHAIN_TO_NI(ch), destroy_table_locked, ch); 929b074b7bbSAlexander V. Chernikov IPFW_WUNLOCK(ch); 930b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 9313b3a8eb9SGleb Smirnoff 9323b3a8eb9SGleb Smirnoff /* Free pointers itself */ 9339f7d47b0SAlexander V. Chernikov free(ch->tablestate, M_IPFW); 9349f7d47b0SAlexander V. Chernikov 9359f7d47b0SAlexander V. Chernikov ipfw_table_algo_destroy(ch); 936b074b7bbSAlexander V. Chernikov 937b074b7bbSAlexander V. Chernikov ipfw_objhash_destroy(CHAIN_TO_NI(ch)); 938b074b7bbSAlexander V. Chernikov free(CHAIN_TO_TCFG(ch), M_IPFW); 9393b3a8eb9SGleb Smirnoff } 9403b3a8eb9SGleb Smirnoff 9413b3a8eb9SGleb Smirnoff int 9423b3a8eb9SGleb Smirnoff ipfw_init_tables(struct ip_fw_chain *ch) 9433b3a8eb9SGleb Smirnoff { 944b074b7bbSAlexander V. Chernikov struct tables_config *tcfg; 945b074b7bbSAlexander V. Chernikov 9463b3a8eb9SGleb Smirnoff /* Allocate pointers */ 9479f7d47b0SAlexander V. Chernikov ch->tablestate = malloc(V_fw_tables_max * sizeof(struct table_info), 9489f7d47b0SAlexander V. Chernikov M_IPFW, M_WAITOK | M_ZERO); 949b074b7bbSAlexander V. Chernikov 950b074b7bbSAlexander V. Chernikov tcfg = malloc(sizeof(struct tables_config), M_IPFW, M_WAITOK | M_ZERO); 951b074b7bbSAlexander V. Chernikov tcfg->namehash = ipfw_objhash_create(V_fw_tables_max); 952b074b7bbSAlexander V. Chernikov ch->tblcfg = tcfg; 953b074b7bbSAlexander V. Chernikov 9549f7d47b0SAlexander V. Chernikov ipfw_table_algo_init(ch); 9559f7d47b0SAlexander V. Chernikov 9563b3a8eb9SGleb Smirnoff return (0); 9573b3a8eb9SGleb Smirnoff } 9583b3a8eb9SGleb Smirnoff 9593b3a8eb9SGleb Smirnoff int 9603b3a8eb9SGleb Smirnoff ipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables) 9613b3a8eb9SGleb Smirnoff { 9623b3a8eb9SGleb Smirnoff unsigned int ntables_old, tbl; 963b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 9649f7d47b0SAlexander V. Chernikov void *new_idx, *old_tablestate, *tablestate; 96568394ec8SAlexander V. Chernikov struct table_info *ti; 96668394ec8SAlexander V. Chernikov struct table_config *tc; 96768394ec8SAlexander V. Chernikov int i, new_blocks; 9683b3a8eb9SGleb Smirnoff 9693b3a8eb9SGleb Smirnoff /* Check new value for validity */ 9703b3a8eb9SGleb Smirnoff if (ntables > IPFW_TABLES_MAX) 9713b3a8eb9SGleb Smirnoff ntables = IPFW_TABLES_MAX; 9723b3a8eb9SGleb Smirnoff 9733b3a8eb9SGleb Smirnoff /* Allocate new pointers */ 9749f7d47b0SAlexander V. Chernikov tablestate = malloc(ntables * sizeof(struct table_info), 9759f7d47b0SAlexander V. Chernikov M_IPFW, M_WAITOK | M_ZERO); 9769f7d47b0SAlexander V. Chernikov 977b074b7bbSAlexander V. Chernikov ipfw_objhash_bitmap_alloc(ntables, (void *)&new_idx, &new_blocks); 9783b3a8eb9SGleb Smirnoff 9799f7d47b0SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 9803b3a8eb9SGleb Smirnoff 9813b3a8eb9SGleb Smirnoff tbl = (ntables >= V_fw_tables_max) ? V_fw_tables_max : ntables; 982b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 983b074b7bbSAlexander V. Chernikov 9849f7d47b0SAlexander V. Chernikov /* Temporary restrict decreasing max_tables */ 9859f7d47b0SAlexander V. Chernikov if (ntables < V_fw_tables_max) { 9869f7d47b0SAlexander V. Chernikov 9879f7d47b0SAlexander V. Chernikov /* 9889f7d47b0SAlexander V. Chernikov * FIXME: Check if we really can shrink 9899f7d47b0SAlexander V. Chernikov */ 9909f7d47b0SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 991b074b7bbSAlexander V. Chernikov return (EINVAL); 992b074b7bbSAlexander V. Chernikov } 9933b3a8eb9SGleb Smirnoff 9949f7d47b0SAlexander V. Chernikov /* Copy table info/indices */ 9959f7d47b0SAlexander V. Chernikov memcpy(tablestate, ch->tablestate, sizeof(struct table_info) * tbl); 9969f7d47b0SAlexander V. Chernikov ipfw_objhash_bitmap_merge(ni, &new_idx, &new_blocks); 9973b3a8eb9SGleb Smirnoff 9989f7d47b0SAlexander V. Chernikov IPFW_WLOCK(ch); 9999f7d47b0SAlexander V. Chernikov 10009f7d47b0SAlexander V. Chernikov /* Change pointers */ 10019f7d47b0SAlexander V. Chernikov old_tablestate = ch->tablestate; 10029f7d47b0SAlexander V. Chernikov ch->tablestate = tablestate; 10039f7d47b0SAlexander V. Chernikov ipfw_objhash_bitmap_swap(ni, &new_idx, &new_blocks); 10043b3a8eb9SGleb Smirnoff 10053b3a8eb9SGleb Smirnoff ntables_old = V_fw_tables_max; 10063b3a8eb9SGleb Smirnoff V_fw_tables_max = ntables; 10073b3a8eb9SGleb Smirnoff 10083b3a8eb9SGleb Smirnoff IPFW_WUNLOCK(ch); 100968394ec8SAlexander V. Chernikov 101068394ec8SAlexander V. Chernikov /* Notify all consumers that their @ti pointer has changed */ 101168394ec8SAlexander V. Chernikov ti = (struct table_info *)ch->tablestate; 101268394ec8SAlexander V. Chernikov for (i = 0; i < tbl; i++, ti++) { 101368394ec8SAlexander V. Chernikov if (ti->lookup == NULL) 101468394ec8SAlexander V. Chernikov continue; 101568394ec8SAlexander V. Chernikov tc = (struct table_config *)ipfw_objhash_lookup_kidx(ni, i); 101668394ec8SAlexander V. Chernikov if (tc == NULL || tc->ta->change_ti == NULL) 101768394ec8SAlexander V. Chernikov continue; 101868394ec8SAlexander V. Chernikov 101968394ec8SAlexander V. Chernikov tc->ta->change_ti(tc->astate, ti); 102068394ec8SAlexander V. Chernikov } 102168394ec8SAlexander V. Chernikov 10229f7d47b0SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 10233b3a8eb9SGleb Smirnoff 10243b3a8eb9SGleb Smirnoff /* Free old pointers */ 10259f7d47b0SAlexander V. Chernikov free(old_tablestate, M_IPFW); 1026b074b7bbSAlexander V. Chernikov ipfw_objhash_bitmap_free(new_idx, new_blocks); 10273b3a8eb9SGleb Smirnoff 10283b3a8eb9SGleb Smirnoff return (0); 10293b3a8eb9SGleb Smirnoff } 10303b3a8eb9SGleb Smirnoff 1031a73d728dSAlexander V. Chernikov /* 1032a73d728dSAlexander V. Chernikov * Switch between "set 0" and "rule set" table binding, 1033a73d728dSAlexander V. Chernikov * Check all ruleset bindings and permits changing 1034a73d728dSAlexander V. Chernikov * IFF each binding has both rule AND table in default set (set 0). 1035a73d728dSAlexander V. Chernikov * 1036a73d728dSAlexander V. Chernikov * Returns 0 on success. 1037a73d728dSAlexander V. Chernikov */ 1038a73d728dSAlexander V. Chernikov int 1039a73d728dSAlexander V. Chernikov ipfw_switch_tables_namespace(struct ip_fw_chain *ch, unsigned int sets) 1040a73d728dSAlexander V. Chernikov { 1041a73d728dSAlexander V. Chernikov struct namedobj_instance *ni; 1042a73d728dSAlexander V. Chernikov struct named_object *no; 1043a73d728dSAlexander V. Chernikov struct ip_fw *rule; 1044a73d728dSAlexander V. Chernikov ipfw_insn *cmd; 1045a73d728dSAlexander V. Chernikov int cmdlen, i, l; 1046a73d728dSAlexander V. Chernikov uint16_t kidx; 1047a73d728dSAlexander V. Chernikov uint8_t type; 1048a73d728dSAlexander V. Chernikov 1049a73d728dSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 1050a73d728dSAlexander V. Chernikov 1051a73d728dSAlexander V. Chernikov if (V_fw_tables_sets == sets) { 1052a73d728dSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1053a73d728dSAlexander V. Chernikov return (0); 1054a73d728dSAlexander V. Chernikov } 1055a73d728dSAlexander V. Chernikov 1056a73d728dSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 1057a73d728dSAlexander V. Chernikov 1058a73d728dSAlexander V. Chernikov for (i = 0; i < ch->n_rules; i++) { 1059a73d728dSAlexander V. Chernikov rule = ch->map[i]; 1060a73d728dSAlexander V. Chernikov 1061a73d728dSAlexander V. Chernikov l = rule->cmd_len; 1062a73d728dSAlexander V. Chernikov cmd = rule->cmd; 1063a73d728dSAlexander V. Chernikov cmdlen = 0; 1064a73d728dSAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 1065a73d728dSAlexander V. Chernikov cmdlen = F_LEN(cmd); 1066a73d728dSAlexander V. Chernikov 1067a73d728dSAlexander V. Chernikov if (classify_table_opcode(cmd, &kidx, &type) != 0) 1068a73d728dSAlexander V. Chernikov continue; 1069a73d728dSAlexander V. Chernikov 1070a73d728dSAlexander V. Chernikov no = ipfw_objhash_lookup_kidx(ni, kidx); 1071a73d728dSAlexander V. Chernikov 1072a73d728dSAlexander V. Chernikov if (no->set != 0 || rule->set != 0) { 1073a73d728dSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1074a73d728dSAlexander V. Chernikov return (EBUSY); 1075a73d728dSAlexander V. Chernikov } 1076a73d728dSAlexander V. Chernikov 1077a73d728dSAlexander V. Chernikov } 1078a73d728dSAlexander V. Chernikov } 1079a73d728dSAlexander V. Chernikov V_fw_tables_sets = sets; 1080a73d728dSAlexander V. Chernikov 1081a73d728dSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1082a73d728dSAlexander V. Chernikov 1083a73d728dSAlexander V. Chernikov return (0); 1084a73d728dSAlexander V. Chernikov } 1085a73d728dSAlexander V. Chernikov 10863b3a8eb9SGleb Smirnoff int 10873b3a8eb9SGleb Smirnoff ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, 10883b3a8eb9SGleb Smirnoff uint32_t *val) 10893b3a8eb9SGleb Smirnoff { 10909f7d47b0SAlexander V. Chernikov struct table_info *ti; 10913b3a8eb9SGleb Smirnoff 10929f7d47b0SAlexander V. Chernikov ti = &(((struct table_info *)ch->tablestate)[tbl]); 10939f7d47b0SAlexander V. Chernikov 10949f7d47b0SAlexander V. Chernikov return (ti->lookup(ti, &addr, sizeof(in_addr_t), val)); 10953b3a8eb9SGleb Smirnoff } 10969f7d47b0SAlexander V. Chernikov 10979f7d47b0SAlexander V. Chernikov int 10989f7d47b0SAlexander V. Chernikov ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, uint16_t plen, 10999f7d47b0SAlexander V. Chernikov void *paddr, uint32_t *val) 11009f7d47b0SAlexander V. Chernikov { 11019f7d47b0SAlexander V. Chernikov struct table_info *ti; 11029f7d47b0SAlexander V. Chernikov 11039f7d47b0SAlexander V. Chernikov ti = &(((struct table_info *)ch->tablestate)[tbl]); 11049f7d47b0SAlexander V. Chernikov 11059f7d47b0SAlexander V. Chernikov return (ti->lookup(ti, paddr, plen, val)); 11069f7d47b0SAlexander V. Chernikov } 11079f7d47b0SAlexander V. Chernikov 11089f7d47b0SAlexander V. Chernikov /* 11099f7d47b0SAlexander V. Chernikov * Info/List/dump support for tables. 11109f7d47b0SAlexander V. Chernikov * 11119f7d47b0SAlexander V. Chernikov */ 11129f7d47b0SAlexander V. Chernikov 1113f1220db8SAlexander V. Chernikov /* 1114d3a4f924SAlexander V. Chernikov * High-level 'get' cmds sysctl handlers 1115d3a4f924SAlexander V. Chernikov */ 1116d3a4f924SAlexander V. Chernikov 1117d3a4f924SAlexander V. Chernikov /* 1118d3a4f924SAlexander V. Chernikov * Get buffer size needed to list info for all tables. 1119ac35ff17SAlexander V. Chernikov * Data layout (v0)(current): 1120d3a4f924SAlexander V. Chernikov * Request: [ empty ], size = sizeof(ipfw_obj_lheader) 1121d3a4f924SAlexander V. Chernikov * Reply: [ ipfw_obj_lheader ] 1122d3a4f924SAlexander V. Chernikov * 1123d3a4f924SAlexander V. Chernikov * Returns 0 on success 1124f1220db8SAlexander V. Chernikov */ 1125f1220db8SAlexander V. Chernikov int 11262d99a349SAlexander V. Chernikov ipfw_listsize_tables(struct ip_fw_chain *ch, struct sockopt_data *sd) 1127f1220db8SAlexander V. Chernikov { 1128f1220db8SAlexander V. Chernikov struct _ipfw_obj_lheader *olh; 1129f1220db8SAlexander V. Chernikov 11302d99a349SAlexander V. Chernikov olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh)); 11312d99a349SAlexander V. Chernikov if (olh == NULL) 1132d3a4f924SAlexander V. Chernikov return (EINVAL); 1133d3a4f924SAlexander V. Chernikov 1134f1220db8SAlexander V. Chernikov olh->size = sizeof(*olh); /* Make export_table store needed size */ 1135f1220db8SAlexander V. Chernikov 1136f1220db8SAlexander V. Chernikov IPFW_UH_RLOCK(ch); 11372d99a349SAlexander V. Chernikov export_tables(ch, olh, sd); 1138f1220db8SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 1139f1220db8SAlexander V. Chernikov 11402d99a349SAlexander V. Chernikov return (0); 1141f1220db8SAlexander V. Chernikov } 1142f1220db8SAlexander V. Chernikov 1143d3a4f924SAlexander V. Chernikov /* 1144d3a4f924SAlexander V. Chernikov * Lists all tables currently available in kernel. 1145ac35ff17SAlexander V. Chernikov * Data layout (v0)(current): 1146d3a4f924SAlexander V. Chernikov * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size 1147d3a4f924SAlexander V. Chernikov * Reply: [ ipfw_obj_lheader ipfw_xtable_info x N ] 1148d3a4f924SAlexander V. Chernikov * 1149d3a4f924SAlexander V. Chernikov * Returns 0 on success 1150d3a4f924SAlexander V. Chernikov */ 1151f1220db8SAlexander V. Chernikov int 11522d99a349SAlexander V. Chernikov ipfw_list_tables(struct ip_fw_chain *ch, struct sockopt_data *sd) 1153f1220db8SAlexander V. Chernikov { 1154f1220db8SAlexander V. Chernikov struct _ipfw_obj_lheader *olh; 1155f1220db8SAlexander V. Chernikov int error; 1156f1220db8SAlexander V. Chernikov 11572d99a349SAlexander V. Chernikov olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh)); 11582d99a349SAlexander V. Chernikov if (olh == NULL) 1159d3a4f924SAlexander V. Chernikov return (EINVAL); 116068394ec8SAlexander V. Chernikov if (sd->valsize < olh->size) 116168394ec8SAlexander V. Chernikov return (EINVAL); 1162d3a4f924SAlexander V. Chernikov 1163f1220db8SAlexander V. Chernikov IPFW_UH_RLOCK(ch); 11642d99a349SAlexander V. Chernikov error = export_tables(ch, olh, sd); 1165f1220db8SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 1166f1220db8SAlexander V. Chernikov 1167f1220db8SAlexander V. Chernikov return (error); 1168f1220db8SAlexander V. Chernikov } 1169f1220db8SAlexander V. Chernikov 1170f1220db8SAlexander V. Chernikov /* 11712d99a349SAlexander V. Chernikov * Store table info to buffer provided by @sd. 1172ac35ff17SAlexander V. Chernikov * Data layout (v0)(current): 1173d3a4f924SAlexander V. Chernikov * Request: [ ipfw_obj_header ipfw_xtable_info(empty)] 1174d3a4f924SAlexander V. Chernikov * Reply: [ ipfw_obj_header ipfw_xtable_info ] 1175d3a4f924SAlexander V. Chernikov * 1176d3a4f924SAlexander V. Chernikov * Returns 0 on success. 1177d3a4f924SAlexander V. Chernikov */ 1178d3a4f924SAlexander V. Chernikov int 11792d99a349SAlexander V. Chernikov ipfw_describe_table(struct ip_fw_chain *ch, struct sockopt_data *sd) 1180d3a4f924SAlexander V. Chernikov { 1181d3a4f924SAlexander V. Chernikov struct _ipfw_obj_header *oh; 1182d3a4f924SAlexander V. Chernikov struct table_config *tc; 1183d3a4f924SAlexander V. Chernikov struct tid_info ti; 1184d3a4f924SAlexander V. Chernikov size_t sz; 1185d3a4f924SAlexander V. Chernikov 1186d3a4f924SAlexander V. Chernikov sz = sizeof(*oh) + sizeof(ipfw_xtable_info); 11872d99a349SAlexander V. Chernikov oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 11882d99a349SAlexander V. Chernikov if (oh == NULL) 1189d3a4f924SAlexander V. Chernikov return (EINVAL); 1190d3a4f924SAlexander V. Chernikov 1191d3a4f924SAlexander V. Chernikov objheader_to_ti(oh, &ti); 1192d3a4f924SAlexander V. Chernikov 1193d3a4f924SAlexander V. Chernikov IPFW_UH_RLOCK(ch); 1194d3a4f924SAlexander V. Chernikov if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) { 1195d3a4f924SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 1196d3a4f924SAlexander V. Chernikov return (ESRCH); 1197d3a4f924SAlexander V. Chernikov } 1198d3a4f924SAlexander V. Chernikov 1199ac35ff17SAlexander V. Chernikov export_table_info(ch, tc, (ipfw_xtable_info *)(oh + 1)); 1200d3a4f924SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 1201d3a4f924SAlexander V. Chernikov 12022d99a349SAlexander V. Chernikov return (0); 1203d3a4f924SAlexander V. Chernikov } 1204d3a4f924SAlexander V. Chernikov 1205f1220db8SAlexander V. Chernikov struct dump_args { 1206f1220db8SAlexander V. Chernikov struct table_info *ti; 1207f1220db8SAlexander V. Chernikov struct table_config *tc; 12082d99a349SAlexander V. Chernikov struct sockopt_data *sd; 1209f1220db8SAlexander V. Chernikov uint32_t cnt; 1210f1220db8SAlexander V. Chernikov uint16_t uidx; 121181d3153dSAlexander V. Chernikov int error; 12122d99a349SAlexander V. Chernikov ipfw_table_entry *ent; 12132d99a349SAlexander V. Chernikov uint32_t size; 121481d3153dSAlexander V. Chernikov ipfw_obj_tentry tent; 1215f1220db8SAlexander V. Chernikov }; 1216f1220db8SAlexander V. Chernikov 1217f1220db8SAlexander V. Chernikov int 12182d99a349SAlexander V. Chernikov ipfw_dump_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 12192d99a349SAlexander V. Chernikov struct sockopt_data *sd) 1220f1220db8SAlexander V. Chernikov { 1221d3a4f924SAlexander V. Chernikov int error; 1222d3a4f924SAlexander V. Chernikov 1223d3a4f924SAlexander V. Chernikov switch (op3->version) { 1224d3a4f924SAlexander V. Chernikov case 0: 12252d99a349SAlexander V. Chernikov error = ipfw_dump_table_v0(ch, sd); 1226d3a4f924SAlexander V. Chernikov break; 1227d3a4f924SAlexander V. Chernikov case 1: 12282d99a349SAlexander V. Chernikov error = ipfw_dump_table_v1(ch, sd); 1229d3a4f924SAlexander V. Chernikov break; 1230d3a4f924SAlexander V. Chernikov default: 1231d3a4f924SAlexander V. Chernikov error = ENOTSUP; 1232d3a4f924SAlexander V. Chernikov } 1233d3a4f924SAlexander V. Chernikov 1234d3a4f924SAlexander V. Chernikov return (error); 1235d3a4f924SAlexander V. Chernikov } 1236d3a4f924SAlexander V. Chernikov 1237d3a4f924SAlexander V. Chernikov /* 1238d3a4f924SAlexander V. Chernikov * Dumps all table data 1239ac35ff17SAlexander V. Chernikov * Data layout (v1)(current): 12402d99a349SAlexander V. Chernikov * Request: [ ipfw_obj_header ], size = ipfw_xtable_info.size 124181d3153dSAlexander V. Chernikov * Reply: [ ipfw_obj_header ipfw_xtable_info ipfw_obj_tentry x N ] 1242d3a4f924SAlexander V. Chernikov * 1243d3a4f924SAlexander V. Chernikov * Returns 0 on success 1244d3a4f924SAlexander V. Chernikov */ 1245d3a4f924SAlexander V. Chernikov static int 12462d99a349SAlexander V. Chernikov ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt_data *sd) 1247d3a4f924SAlexander V. Chernikov { 1248f1220db8SAlexander V. Chernikov struct _ipfw_obj_header *oh; 1249f1220db8SAlexander V. Chernikov ipfw_xtable_info *i; 1250f1220db8SAlexander V. Chernikov struct tid_info ti; 1251f1220db8SAlexander V. Chernikov struct table_config *tc; 1252f1220db8SAlexander V. Chernikov struct table_algo *ta; 1253f1220db8SAlexander V. Chernikov struct dump_args da; 1254d3a4f924SAlexander V. Chernikov uint32_t sz; 1255f1220db8SAlexander V. Chernikov 12562d99a349SAlexander V. Chernikov sz = sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info); 12572d99a349SAlexander V. Chernikov oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 12582d99a349SAlexander V. Chernikov if (oh == NULL) 1259d3a4f924SAlexander V. Chernikov return (EINVAL); 1260d3a4f924SAlexander V. Chernikov 12612d99a349SAlexander V. Chernikov i = (ipfw_xtable_info *)(oh + 1); 1262d3a4f924SAlexander V. Chernikov objheader_to_ti(oh, &ti); 1263f1220db8SAlexander V. Chernikov 1264f1220db8SAlexander V. Chernikov IPFW_UH_RLOCK(ch); 1265f1220db8SAlexander V. Chernikov if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) { 1266f1220db8SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 1267f1220db8SAlexander V. Chernikov return (ESRCH); 1268f1220db8SAlexander V. Chernikov } 1269ac35ff17SAlexander V. Chernikov export_table_info(ch, tc, i); 12702d99a349SAlexander V. Chernikov sz = tc->count; 12712d99a349SAlexander V. Chernikov 127281d3153dSAlexander V. Chernikov if (sd->valsize < sz + tc->count * sizeof(ipfw_obj_tentry)) { 12732d99a349SAlexander V. Chernikov 12742d99a349SAlexander V. Chernikov /* 12752d99a349SAlexander V. Chernikov * Submitted buffer size is not enough. 12762d99a349SAlexander V. Chernikov * WE've already filled in @i structure with 12772d99a349SAlexander V. Chernikov * relevant table info including size, so we 12782d99a349SAlexander V. Chernikov * can return. Buffer will be flushed automatically. 12792d99a349SAlexander V. Chernikov */ 1280f1220db8SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 12812d99a349SAlexander V. Chernikov return (ENOMEM); 1282f1220db8SAlexander V. Chernikov } 1283f1220db8SAlexander V. Chernikov 1284f1220db8SAlexander V. Chernikov /* 1285f1220db8SAlexander V. Chernikov * Do the actual dump in eXtended format 1286f1220db8SAlexander V. Chernikov */ 1287d3a4f924SAlexander V. Chernikov memset(&da, 0, sizeof(da)); 1288f1220db8SAlexander V. Chernikov da.ti = KIDX_TO_TI(ch, tc->no.kidx); 1289f1220db8SAlexander V. Chernikov da.tc = tc; 12902d99a349SAlexander V. Chernikov da.sd = sd; 1291f1220db8SAlexander V. Chernikov 1292f1220db8SAlexander V. Chernikov ta = tc->ta; 1293f1220db8SAlexander V. Chernikov 129481d3153dSAlexander V. Chernikov ta->foreach(tc->astate, da.ti, dump_table_tentry, &da); 1295f1220db8SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 1296f1220db8SAlexander V. Chernikov 129781d3153dSAlexander V. Chernikov return (da.error); 1298f1220db8SAlexander V. Chernikov } 1299f1220db8SAlexander V. Chernikov 1300d3a4f924SAlexander V. Chernikov /* 1301d3a4f924SAlexander V. Chernikov * Dumps all table data 13022d99a349SAlexander V. Chernikov * Data layout (version 0)(legacy): 1303d3a4f924SAlexander V. Chernikov * Request: [ ipfw_xtable ], size = IP_FW_TABLE_XGETSIZE() 1304d3a4f924SAlexander V. Chernikov * Reply: [ ipfw_xtable ipfw_table_xentry x N ] 1305d3a4f924SAlexander V. Chernikov * 1306d3a4f924SAlexander V. Chernikov * Returns 0 on success 1307d3a4f924SAlexander V. Chernikov */ 1308d3a4f924SAlexander V. Chernikov static int 13092d99a349SAlexander V. Chernikov ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt_data *sd) 1310d3a4f924SAlexander V. Chernikov { 1311d3a4f924SAlexander V. Chernikov ipfw_xtable *xtbl; 1312d3a4f924SAlexander V. Chernikov struct tid_info ti; 1313d3a4f924SAlexander V. Chernikov struct table_config *tc; 1314d3a4f924SAlexander V. Chernikov struct table_algo *ta; 1315d3a4f924SAlexander V. Chernikov struct dump_args da; 1316d3a4f924SAlexander V. Chernikov size_t sz; 1317d3a4f924SAlexander V. Chernikov 13182d99a349SAlexander V. Chernikov xtbl = (ipfw_xtable *)ipfw_get_sopt_header(sd, sizeof(ipfw_xtable)); 13192d99a349SAlexander V. Chernikov if (xtbl == NULL) 1320d3a4f924SAlexander V. Chernikov return (EINVAL); 1321d3a4f924SAlexander V. Chernikov 1322d3a4f924SAlexander V. Chernikov memset(&ti, 0, sizeof(ti)); 1323d3a4f924SAlexander V. Chernikov ti.uidx = xtbl->tbl; 1324d3a4f924SAlexander V. Chernikov 1325d3a4f924SAlexander V. Chernikov IPFW_UH_RLOCK(ch); 1326d3a4f924SAlexander V. Chernikov if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) { 1327d3a4f924SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 1328d3a4f924SAlexander V. Chernikov return (0); 1329d3a4f924SAlexander V. Chernikov } 1330d3a4f924SAlexander V. Chernikov sz = tc->count * sizeof(ipfw_table_xentry) + sizeof(ipfw_xtable); 13312d99a349SAlexander V. Chernikov 13322d99a349SAlexander V. Chernikov xtbl->cnt = tc->count; 13332d99a349SAlexander V. Chernikov xtbl->size = sz; 13342d99a349SAlexander V. Chernikov xtbl->type = tc->no.type; 13352d99a349SAlexander V. Chernikov xtbl->tbl = ti.uidx; 13362d99a349SAlexander V. Chernikov 13372d99a349SAlexander V. Chernikov if (sd->valsize < sz) { 13382d99a349SAlexander V. Chernikov 13392d99a349SAlexander V. Chernikov /* 13402d99a349SAlexander V. Chernikov * Submitted buffer size is not enough. 13412d99a349SAlexander V. Chernikov * WE've already filled in @i structure with 13422d99a349SAlexander V. Chernikov * relevant table info including size, so we 13432d99a349SAlexander V. Chernikov * can return. Buffer will be flushed automatically. 13442d99a349SAlexander V. Chernikov */ 1345d3a4f924SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 13462d99a349SAlexander V. Chernikov return (ENOMEM); 1347d3a4f924SAlexander V. Chernikov } 1348d3a4f924SAlexander V. Chernikov 1349d3a4f924SAlexander V. Chernikov /* Do the actual dump in eXtended format */ 1350d3a4f924SAlexander V. Chernikov memset(&da, 0, sizeof(da)); 1351d3a4f924SAlexander V. Chernikov da.ti = KIDX_TO_TI(ch, tc->no.kidx); 1352d3a4f924SAlexander V. Chernikov da.tc = tc; 13532d99a349SAlexander V. Chernikov da.sd = sd; 13542d99a349SAlexander V. Chernikov 1355d3a4f924SAlexander V. Chernikov ta = tc->ta; 1356d3a4f924SAlexander V. Chernikov 1357d3a4f924SAlexander V. Chernikov ta->foreach(tc->astate, da.ti, dump_table_xentry, &da); 1358d3a4f924SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 1359d3a4f924SAlexander V. Chernikov 13602d99a349SAlexander V. Chernikov return (0); 1361d3a4f924SAlexander V. Chernikov } 1362d3a4f924SAlexander V. Chernikov 1363d3a4f924SAlexander V. Chernikov /* 13649490a627SAlexander V. Chernikov * Creates new table. 1365ac35ff17SAlexander V. Chernikov * Data layout (v0)(current): 13669490a627SAlexander V. Chernikov * Request: [ ipfw_obj_header ipfw_xtable_info ] 13679490a627SAlexander V. Chernikov * 13689490a627SAlexander V. Chernikov * Returns 0 on success 13699490a627SAlexander V. Chernikov */ 13709490a627SAlexander V. Chernikov int 1371ac35ff17SAlexander V. Chernikov ipfw_create_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 1372ac35ff17SAlexander V. Chernikov struct sockopt_data *sd) 13739490a627SAlexander V. Chernikov { 13749490a627SAlexander V. Chernikov struct _ipfw_obj_header *oh; 13759490a627SAlexander V. Chernikov ipfw_xtable_info *i; 13769490a627SAlexander V. Chernikov char *tname, *aname; 13779490a627SAlexander V. Chernikov struct tid_info ti; 13789490a627SAlexander V. Chernikov struct namedobj_instance *ni; 13799490a627SAlexander V. Chernikov struct table_config *tc; 13809490a627SAlexander V. Chernikov 1381ac35ff17SAlexander V. Chernikov if (sd->valsize != sizeof(*oh) + sizeof(ipfw_xtable_info)) 13829490a627SAlexander V. Chernikov return (EINVAL); 13839490a627SAlexander V. Chernikov 1384ac35ff17SAlexander V. Chernikov oh = (struct _ipfw_obj_header *)sd->kbuf; 13859490a627SAlexander V. Chernikov i = (ipfw_xtable_info *)(oh + 1); 13869490a627SAlexander V. Chernikov 13879490a627SAlexander V. Chernikov /* 13889490a627SAlexander V. Chernikov * Verify user-supplied strings. 13892d99a349SAlexander V. Chernikov * Check for null-terminated/zero-length strings/ 13909490a627SAlexander V. Chernikov */ 1391ac35ff17SAlexander V. Chernikov tname = oh->ntlv.name; 13929490a627SAlexander V. Chernikov aname = i->algoname; 1393ac35ff17SAlexander V. Chernikov if (ipfw_check_table_name(tname) != 0 || 13949490a627SAlexander V. Chernikov strnlen(aname, sizeof(i->algoname)) == sizeof(i->algoname)) 13959490a627SAlexander V. Chernikov return (EINVAL); 13969490a627SAlexander V. Chernikov 13979490a627SAlexander V. Chernikov if (aname[0] == '\0') { 13989490a627SAlexander V. Chernikov /* Use default algorithm */ 13999490a627SAlexander V. Chernikov aname = NULL; 14009490a627SAlexander V. Chernikov } 14019490a627SAlexander V. Chernikov 14029490a627SAlexander V. Chernikov objheader_to_ti(oh, &ti); 1403ac35ff17SAlexander V. Chernikov ti.type = i->type; 14049490a627SAlexander V. Chernikov 14059490a627SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 14069490a627SAlexander V. Chernikov 14079490a627SAlexander V. Chernikov IPFW_UH_RLOCK(ch); 14089490a627SAlexander V. Chernikov if ((tc = find_table(ni, &ti)) != NULL) { 14099490a627SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 14109490a627SAlexander V. Chernikov return (EEXIST); 14119490a627SAlexander V. Chernikov } 14129490a627SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 14139490a627SAlexander V. Chernikov 14144c0c07a5SAlexander V. Chernikov return (create_table_internal(ch, &ti, aname, i)); 1415db785d31SAlexander V. Chernikov } 1416db785d31SAlexander V. Chernikov 1417db785d31SAlexander V. Chernikov /* 1418db785d31SAlexander V. Chernikov * Creates new table based on @ti and @aname. 1419db785d31SAlexander V. Chernikov * 1420db785d31SAlexander V. Chernikov * Relies on table name checking inside find_name_tlv() 1421db785d31SAlexander V. Chernikov * Assume @aname to be checked and valid. 1422db785d31SAlexander V. Chernikov * 1423db785d31SAlexander V. Chernikov * Returns 0 on success. 1424db785d31SAlexander V. Chernikov */ 1425db785d31SAlexander V. Chernikov static int 1426db785d31SAlexander V. Chernikov create_table_internal(struct ip_fw_chain *ch, struct tid_info *ti, 14274c0c07a5SAlexander V. Chernikov char *aname, ipfw_xtable_info *i) 1428db785d31SAlexander V. Chernikov { 1429db785d31SAlexander V. Chernikov struct namedobj_instance *ni; 1430db785d31SAlexander V. Chernikov struct table_config *tc; 1431db785d31SAlexander V. Chernikov struct table_algo *ta; 1432db785d31SAlexander V. Chernikov uint16_t kidx; 1433db785d31SAlexander V. Chernikov 1434db785d31SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 1435db785d31SAlexander V. Chernikov 1436db785d31SAlexander V. Chernikov ta = find_table_algo(CHAIN_TO_TCFG(ch), ti, aname); 14379490a627SAlexander V. Chernikov if (ta == NULL) 14389490a627SAlexander V. Chernikov return (ENOTSUP); 14399490a627SAlexander V. Chernikov 14404c0c07a5SAlexander V. Chernikov tc = alloc_table_config(ch, ti, ta, aname, i->tflags, i->vtype); 14414c0c07a5SAlexander V. Chernikov if (tc == NULL) 14429490a627SAlexander V. Chernikov return (ENOMEM); 14439490a627SAlexander V. Chernikov 14444c0c07a5SAlexander V. Chernikov tc->limit = i->limit; 14454c0c07a5SAlexander V. Chernikov 14469490a627SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 1447db785d31SAlexander V. Chernikov 1448db785d31SAlexander V. Chernikov /* Check if table has been already created */ 1449db785d31SAlexander V. Chernikov if (find_table(ni, ti) != NULL) { 1450db785d31SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1451db785d31SAlexander V. Chernikov free_table_config(ni, tc); 1452db785d31SAlexander V. Chernikov return (EEXIST); 1453db785d31SAlexander V. Chernikov } 1454db785d31SAlexander V. Chernikov 1455ac35ff17SAlexander V. Chernikov if (ipfw_objhash_alloc_idx(ni, &kidx) != 0) { 14569490a627SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1457db785d31SAlexander V. Chernikov printf("Unable to allocate table index." 1458db785d31SAlexander V. Chernikov " Consider increasing net.inet.ip.fw.tables_max"); 14599490a627SAlexander V. Chernikov free_table_config(ni, tc); 14609490a627SAlexander V. Chernikov return (EBUSY); 14619490a627SAlexander V. Chernikov } 14629490a627SAlexander V. Chernikov 14639490a627SAlexander V. Chernikov tc->no.kidx = kidx; 14649490a627SAlexander V. Chernikov 14659490a627SAlexander V. Chernikov IPFW_WLOCK(ch); 14669490a627SAlexander V. Chernikov link_table(ch, tc); 14679490a627SAlexander V. Chernikov IPFW_WUNLOCK(ch); 14689490a627SAlexander V. Chernikov 14699490a627SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 14709490a627SAlexander V. Chernikov 14719490a627SAlexander V. Chernikov return (0); 14729490a627SAlexander V. Chernikov } 14739490a627SAlexander V. Chernikov 147446d52008SAlexander V. Chernikov static void 147546d52008SAlexander V. Chernikov ntlv_to_ti(ipfw_obj_ntlv *ntlv, struct tid_info *ti) 1476d3a4f924SAlexander V. Chernikov { 1477d3a4f924SAlexander V. Chernikov 1478d3a4f924SAlexander V. Chernikov memset(ti, 0, sizeof(struct tid_info)); 147946d52008SAlexander V. Chernikov ti->set = ntlv->set; 148046d52008SAlexander V. Chernikov ti->uidx = ntlv->idx; 148146d52008SAlexander V. Chernikov ti->tlvs = ntlv; 148246d52008SAlexander V. Chernikov ti->tlen = ntlv->head.length; 148346d52008SAlexander V. Chernikov } 148446d52008SAlexander V. Chernikov 148546d52008SAlexander V. Chernikov static void 148646d52008SAlexander V. Chernikov objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti) 148746d52008SAlexander V. Chernikov { 148846d52008SAlexander V. Chernikov 148946d52008SAlexander V. Chernikov ntlv_to_ti(&oh->ntlv, ti); 1490d3a4f924SAlexander V. Chernikov } 1491d3a4f924SAlexander V. Chernikov 1492563b5ab1SAlexander V. Chernikov int 1493563b5ab1SAlexander V. Chernikov ipfw_export_table_ntlv(struct ip_fw_chain *ch, uint16_t kidx, 1494563b5ab1SAlexander V. Chernikov struct sockopt_data *sd) 1495563b5ab1SAlexander V. Chernikov { 1496563b5ab1SAlexander V. Chernikov struct namedobj_instance *ni; 1497563b5ab1SAlexander V. Chernikov struct named_object *no; 1498563b5ab1SAlexander V. Chernikov ipfw_obj_ntlv *ntlv; 1499563b5ab1SAlexander V. Chernikov 1500563b5ab1SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 1501563b5ab1SAlexander V. Chernikov 1502ac35ff17SAlexander V. Chernikov no = ipfw_objhash_lookup_kidx(ni, kidx); 1503563b5ab1SAlexander V. Chernikov KASSERT(no != NULL, ("invalid table kidx passed")); 1504563b5ab1SAlexander V. Chernikov 1505563b5ab1SAlexander V. Chernikov ntlv = (ipfw_obj_ntlv *)ipfw_get_sopt_space(sd, sizeof(*ntlv)); 1506563b5ab1SAlexander V. Chernikov if (ntlv == NULL) 1507563b5ab1SAlexander V. Chernikov return (ENOMEM); 1508563b5ab1SAlexander V. Chernikov 1509563b5ab1SAlexander V. Chernikov ntlv->head.type = IPFW_TLV_TBL_NAME; 1510563b5ab1SAlexander V. Chernikov ntlv->head.length = sizeof(*ntlv); 1511563b5ab1SAlexander V. Chernikov ntlv->idx = no->kidx; 1512563b5ab1SAlexander V. Chernikov strlcpy(ntlv->name, no->name, sizeof(ntlv->name)); 1513563b5ab1SAlexander V. Chernikov 1514563b5ab1SAlexander V. Chernikov return (0); 1515563b5ab1SAlexander V. Chernikov } 1516563b5ab1SAlexander V. Chernikov 15179f7d47b0SAlexander V. Chernikov static void 1518ac35ff17SAlexander V. Chernikov export_table_info(struct ip_fw_chain *ch, struct table_config *tc, 1519ac35ff17SAlexander V. Chernikov ipfw_xtable_info *i) 15209f7d47b0SAlexander V. Chernikov { 1521ac35ff17SAlexander V. Chernikov struct table_info *ti; 15225f379342SAlexander V. Chernikov struct table_algo *ta; 15239f7d47b0SAlexander V. Chernikov 15249f7d47b0SAlexander V. Chernikov i->type = tc->no.type; 1525914bffb6SAlexander V. Chernikov i->tflags = tc->tflags; 1526ac35ff17SAlexander V. Chernikov i->vtype = tc->vtype; 15279f7d47b0SAlexander V. Chernikov i->set = tc->no.set; 15289f7d47b0SAlexander V. Chernikov i->kidx = tc->no.kidx; 15299f7d47b0SAlexander V. Chernikov i->refcnt = tc->no.refcnt; 15309f7d47b0SAlexander V. Chernikov i->count = tc->count; 15314c0c07a5SAlexander V. Chernikov i->limit = tc->limit; 153281d3153dSAlexander V. Chernikov i->size = tc->count * sizeof(ipfw_obj_tentry); 1533f1220db8SAlexander V. Chernikov i->size += sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info); 15349f7d47b0SAlexander V. Chernikov strlcpy(i->tablename, tc->tablename, sizeof(i->tablename)); 1535ac35ff17SAlexander V. Chernikov ti = KIDX_TO_TI(ch, tc->no.kidx); 15365f379342SAlexander V. Chernikov ta = tc->ta; 15375f379342SAlexander V. Chernikov if (ta->print_config != NULL) { 15385f379342SAlexander V. Chernikov /* Use algo function to print table config to string */ 15395f379342SAlexander V. Chernikov ta->print_config(tc->astate, ti, i->algoname, 1540ac35ff17SAlexander V. Chernikov sizeof(i->algoname)); 1541ac35ff17SAlexander V. Chernikov } else 15425f379342SAlexander V. Chernikov strlcpy(i->algoname, ta->name, sizeof(i->algoname)); 15435f379342SAlexander V. Chernikov /* Dump algo-specific data, if possible */ 15445f379342SAlexander V. Chernikov if (ta->dump_tinfo != NULL) { 15455f379342SAlexander V. Chernikov ta->dump_tinfo(tc->astate, ti, &i->ta_info); 15465f379342SAlexander V. Chernikov i->ta_info.flags |= IPFW_TATFLAGS_DATA; 15475f379342SAlexander V. Chernikov } 15489f7d47b0SAlexander V. Chernikov } 15499f7d47b0SAlexander V. Chernikov 1550ac35ff17SAlexander V. Chernikov struct dump_table_args { 1551ac35ff17SAlexander V. Chernikov struct ip_fw_chain *ch; 1552ac35ff17SAlexander V. Chernikov struct sockopt_data *sd; 1553ac35ff17SAlexander V. Chernikov }; 1554ac35ff17SAlexander V. Chernikov 15559f7d47b0SAlexander V. Chernikov static void 15569f7d47b0SAlexander V. Chernikov export_table_internal(struct namedobj_instance *ni, struct named_object *no, 15579f7d47b0SAlexander V. Chernikov void *arg) 15583b3a8eb9SGleb Smirnoff { 15599f7d47b0SAlexander V. Chernikov ipfw_xtable_info *i; 1560ac35ff17SAlexander V. Chernikov struct dump_table_args *dta; 15613b3a8eb9SGleb Smirnoff 1562ac35ff17SAlexander V. Chernikov dta = (struct dump_table_args *)arg; 1563ac35ff17SAlexander V. Chernikov 1564ac35ff17SAlexander V. Chernikov i = (ipfw_xtable_info *)ipfw_get_sopt_space(dta->sd, sizeof(*i)); 156568394ec8SAlexander V. Chernikov KASSERT(i != 0, ("previously checked buffer is not enough")); 15669f7d47b0SAlexander V. Chernikov 1567ac35ff17SAlexander V. Chernikov export_table_info(dta->ch, (struct table_config *)no, i); 15689f7d47b0SAlexander V. Chernikov } 15699f7d47b0SAlexander V. Chernikov 1570f1220db8SAlexander V. Chernikov /* 1571f1220db8SAlexander V. Chernikov * Export all tables as ipfw_xtable_info structures to 15722d99a349SAlexander V. Chernikov * storage provided by @sd. 1573f1220db8SAlexander V. Chernikov * Returns 0 on success. 1574f1220db8SAlexander V. Chernikov */ 1575f1220db8SAlexander V. Chernikov static int 15762d99a349SAlexander V. Chernikov export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh, 15772d99a349SAlexander V. Chernikov struct sockopt_data *sd) 15789f7d47b0SAlexander V. Chernikov { 15799f7d47b0SAlexander V. Chernikov uint32_t size; 15809f7d47b0SAlexander V. Chernikov uint32_t count; 1581ac35ff17SAlexander V. Chernikov struct dump_table_args dta; 15829f7d47b0SAlexander V. Chernikov 15839f7d47b0SAlexander V. Chernikov count = ipfw_objhash_count(CHAIN_TO_NI(ch)); 15849f7d47b0SAlexander V. Chernikov size = count * sizeof(ipfw_xtable_info) + sizeof(ipfw_obj_lheader); 15852d99a349SAlexander V. Chernikov 15862d99a349SAlexander V. Chernikov /* Fill in header regadless of buffer size */ 1587f1220db8SAlexander V. Chernikov olh->count = count; 1588f1220db8SAlexander V. Chernikov olh->objsize = sizeof(ipfw_xtable_info); 15892d99a349SAlexander V. Chernikov 15902d99a349SAlexander V. Chernikov if (size > olh->size) { 15912d99a349SAlexander V. Chernikov olh->size = size; 15929f7d47b0SAlexander V. Chernikov return (ENOMEM); 1593f1220db8SAlexander V. Chernikov } 159468394ec8SAlexander V. Chernikov 15959f7d47b0SAlexander V. Chernikov olh->size = size; 15962d99a349SAlexander V. Chernikov 1597ac35ff17SAlexander V. Chernikov dta.ch = ch; 1598ac35ff17SAlexander V. Chernikov dta.sd = sd; 1599ac35ff17SAlexander V. Chernikov 1600ac35ff17SAlexander V. Chernikov ipfw_objhash_foreach(CHAIN_TO_NI(ch), export_table_internal, &dta); 16019f7d47b0SAlexander V. Chernikov 16023b3a8eb9SGleb Smirnoff return (0); 16033b3a8eb9SGleb Smirnoff } 16043b3a8eb9SGleb Smirnoff 160581d3153dSAlexander V. Chernikov /* 160681d3153dSAlexander V. Chernikov * Legacy IP_FW_TABLE_GETSIZE handler 160781d3153dSAlexander V. Chernikov */ 16083b3a8eb9SGleb Smirnoff int 1609b074b7bbSAlexander V. Chernikov ipfw_count_table(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt) 16103b3a8eb9SGleb Smirnoff { 1611b074b7bbSAlexander V. Chernikov struct table_config *tc; 16123b3a8eb9SGleb Smirnoff 1613b074b7bbSAlexander V. Chernikov if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL) 1614b074b7bbSAlexander V. Chernikov return (ESRCH); 16159f7d47b0SAlexander V. Chernikov *cnt = tc->count; 16163b3a8eb9SGleb Smirnoff return (0); 16173b3a8eb9SGleb Smirnoff } 16183b3a8eb9SGleb Smirnoff 16199f7d47b0SAlexander V. Chernikov 162081d3153dSAlexander V. Chernikov /* 162181d3153dSAlexander V. Chernikov * Legacy IP_FW_TABLE_XGETSIZE handler 162281d3153dSAlexander V. Chernikov */ 16239f7d47b0SAlexander V. Chernikov int 16249f7d47b0SAlexander V. Chernikov ipfw_count_xtable(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt) 16253b3a8eb9SGleb Smirnoff { 16269f7d47b0SAlexander V. Chernikov struct table_config *tc; 16279f7d47b0SAlexander V. Chernikov 1628d3a4f924SAlexander V. Chernikov if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL) { 1629d3a4f924SAlexander V. Chernikov *cnt = 0; 16309f7d47b0SAlexander V. Chernikov return (0); /* 'table all list' requires success */ 1631d3a4f924SAlexander V. Chernikov } 16329f7d47b0SAlexander V. Chernikov *cnt = tc->count * sizeof(ipfw_table_xentry); 16339f7d47b0SAlexander V. Chernikov if (tc->count > 0) 16349f7d47b0SAlexander V. Chernikov *cnt += sizeof(ipfw_xtable); 16359f7d47b0SAlexander V. Chernikov return (0); 16369f7d47b0SAlexander V. Chernikov } 16379f7d47b0SAlexander V. Chernikov 16389f7d47b0SAlexander V. Chernikov static int 16399f7d47b0SAlexander V. Chernikov dump_table_entry(void *e, void *arg) 16409f7d47b0SAlexander V. Chernikov { 16419f7d47b0SAlexander V. Chernikov struct dump_args *da; 16429f7d47b0SAlexander V. Chernikov struct table_config *tc; 16439f7d47b0SAlexander V. Chernikov struct table_algo *ta; 16443b3a8eb9SGleb Smirnoff ipfw_table_entry *ent; 164581d3153dSAlexander V. Chernikov int error; 16463b3a8eb9SGleb Smirnoff 16479f7d47b0SAlexander V. Chernikov da = (struct dump_args *)arg; 16489f7d47b0SAlexander V. Chernikov 16499f7d47b0SAlexander V. Chernikov tc = da->tc; 16509f7d47b0SAlexander V. Chernikov ta = tc->ta; 16519f7d47b0SAlexander V. Chernikov 16529f7d47b0SAlexander V. Chernikov /* Out of memory, returning */ 1653f1220db8SAlexander V. Chernikov if (da->cnt == da->size) 16543b3a8eb9SGleb Smirnoff return (1); 1655f1220db8SAlexander V. Chernikov ent = da->ent++; 1656f1220db8SAlexander V. Chernikov ent->tbl = da->uidx; 1657f1220db8SAlexander V. Chernikov da->cnt++; 16589f7d47b0SAlexander V. Chernikov 165981d3153dSAlexander V. Chernikov error = ta->dump_tentry(tc->astate, da->ti, e, &da->tent); 166081d3153dSAlexander V. Chernikov if (error != 0) 166181d3153dSAlexander V. Chernikov return (error); 166281d3153dSAlexander V. Chernikov 166381d3153dSAlexander V. Chernikov ent->addr = da->tent.k.addr.s_addr; 166481d3153dSAlexander V. Chernikov ent->masklen = da->tent.masklen; 166581d3153dSAlexander V. Chernikov ent->value = da->tent.value; 166681d3153dSAlexander V. Chernikov 166781d3153dSAlexander V. Chernikov return (0); 16683b3a8eb9SGleb Smirnoff } 16693b3a8eb9SGleb Smirnoff 1670f1220db8SAlexander V. Chernikov /* 1671f1220db8SAlexander V. Chernikov * Dumps table in pre-8.1 legacy format. 1672f1220db8SAlexander V. Chernikov */ 16733b3a8eb9SGleb Smirnoff int 1674f1220db8SAlexander V. Chernikov ipfw_dump_table_legacy(struct ip_fw_chain *ch, struct tid_info *ti, 1675f1220db8SAlexander V. Chernikov ipfw_table *tbl) 16763b3a8eb9SGleb Smirnoff { 1677b074b7bbSAlexander V. Chernikov struct table_config *tc; 16789f7d47b0SAlexander V. Chernikov struct table_algo *ta; 16799f7d47b0SAlexander V. Chernikov struct dump_args da; 16803b3a8eb9SGleb Smirnoff 16813b3a8eb9SGleb Smirnoff tbl->cnt = 0; 16823b3a8eb9SGleb Smirnoff 1683b074b7bbSAlexander V. Chernikov if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL) 1684b074b7bbSAlexander V. Chernikov return (0); /* XXX: We should return ESRCH */ 16859f7d47b0SAlexander V. Chernikov 16869f7d47b0SAlexander V. Chernikov ta = tc->ta; 16879f7d47b0SAlexander V. Chernikov 168881d3153dSAlexander V. Chernikov /* This dump format supports IPv4 only */ 168981d3153dSAlexander V. Chernikov if (tc->no.type != IPFW_TABLE_CIDR) 169081d3153dSAlexander V. Chernikov return (0); 16919f7d47b0SAlexander V. Chernikov 1692d3a4f924SAlexander V. Chernikov memset(&da, 0, sizeof(da)); 16939f7d47b0SAlexander V. Chernikov da.ti = KIDX_TO_TI(ch, tc->no.kidx); 16949f7d47b0SAlexander V. Chernikov da.tc = tc; 1695f1220db8SAlexander V. Chernikov da.ent = &tbl->ent[0]; 1696f1220db8SAlexander V. Chernikov da.size = tbl->size; 16979f7d47b0SAlexander V. Chernikov 16989f7d47b0SAlexander V. Chernikov tbl->cnt = 0; 16999f7d47b0SAlexander V. Chernikov ta->foreach(tc->astate, da.ti, dump_table_entry, &da); 1700f1220db8SAlexander V. Chernikov tbl->cnt = da.cnt; 17019f7d47b0SAlexander V. Chernikov 17023b3a8eb9SGleb Smirnoff return (0); 17033b3a8eb9SGleb Smirnoff } 17043b3a8eb9SGleb Smirnoff 17059490a627SAlexander V. Chernikov /* 170681d3153dSAlexander V. Chernikov * Dumps table entry in eXtended format (v1)(current). 170781d3153dSAlexander V. Chernikov */ 170881d3153dSAlexander V. Chernikov static int 170981d3153dSAlexander V. Chernikov dump_table_tentry(void *e, void *arg) 171081d3153dSAlexander V. Chernikov { 171181d3153dSAlexander V. Chernikov struct dump_args *da; 171281d3153dSAlexander V. Chernikov struct table_config *tc; 171381d3153dSAlexander V. Chernikov struct table_algo *ta; 171481d3153dSAlexander V. Chernikov ipfw_obj_tentry *tent; 171581d3153dSAlexander V. Chernikov 171681d3153dSAlexander V. Chernikov da = (struct dump_args *)arg; 171781d3153dSAlexander V. Chernikov 171881d3153dSAlexander V. Chernikov tc = da->tc; 171981d3153dSAlexander V. Chernikov ta = tc->ta; 172081d3153dSAlexander V. Chernikov 172181d3153dSAlexander V. Chernikov tent = (ipfw_obj_tentry *)ipfw_get_sopt_space(da->sd, sizeof(*tent)); 172281d3153dSAlexander V. Chernikov /* Out of memory, returning */ 172381d3153dSAlexander V. Chernikov if (tent == NULL) { 172481d3153dSAlexander V. Chernikov da->error = ENOMEM; 172581d3153dSAlexander V. Chernikov return (1); 172681d3153dSAlexander V. Chernikov } 172781d3153dSAlexander V. Chernikov tent->head.length = sizeof(ipfw_obj_tentry); 172881d3153dSAlexander V. Chernikov tent->idx = da->uidx; 172981d3153dSAlexander V. Chernikov 173081d3153dSAlexander V. Chernikov return (ta->dump_tentry(tc->astate, da->ti, e, tent)); 173181d3153dSAlexander V. Chernikov } 173281d3153dSAlexander V. Chernikov 173381d3153dSAlexander V. Chernikov /* 173481d3153dSAlexander V. Chernikov * Dumps table entry in eXtended format (v0). 17359490a627SAlexander V. Chernikov */ 17363b3a8eb9SGleb Smirnoff static int 17379f7d47b0SAlexander V. Chernikov dump_table_xentry(void *e, void *arg) 17383b3a8eb9SGleb Smirnoff { 17399f7d47b0SAlexander V. Chernikov struct dump_args *da; 17409f7d47b0SAlexander V. Chernikov struct table_config *tc; 17419f7d47b0SAlexander V. Chernikov struct table_algo *ta; 17423b3a8eb9SGleb Smirnoff ipfw_table_xentry *xent; 174381d3153dSAlexander V. Chernikov ipfw_obj_tentry *tent; 174481d3153dSAlexander V. Chernikov int error; 17453b3a8eb9SGleb Smirnoff 17469f7d47b0SAlexander V. Chernikov da = (struct dump_args *)arg; 17479f7d47b0SAlexander V. Chernikov 17489f7d47b0SAlexander V. Chernikov tc = da->tc; 17499f7d47b0SAlexander V. Chernikov ta = tc->ta; 17509f7d47b0SAlexander V. Chernikov 17512d99a349SAlexander V. Chernikov xent = (ipfw_table_xentry *)ipfw_get_sopt_space(da->sd, sizeof(*xent)); 17523b3a8eb9SGleb Smirnoff /* Out of memory, returning */ 17532d99a349SAlexander V. Chernikov if (xent == NULL) 17543b3a8eb9SGleb Smirnoff return (1); 17553b3a8eb9SGleb Smirnoff xent->len = sizeof(ipfw_table_xentry); 1756f1220db8SAlexander V. Chernikov xent->tbl = da->uidx; 17579f7d47b0SAlexander V. Chernikov 175881d3153dSAlexander V. Chernikov memset(&da->tent, 0, sizeof(da->tent)); 175981d3153dSAlexander V. Chernikov tent = &da->tent; 176081d3153dSAlexander V. Chernikov error = ta->dump_tentry(tc->astate, da->ti, e, tent); 176181d3153dSAlexander V. Chernikov if (error != 0) 176281d3153dSAlexander V. Chernikov return (error); 176381d3153dSAlexander V. Chernikov 176481d3153dSAlexander V. Chernikov /* Convert current format to previous one */ 176581d3153dSAlexander V. Chernikov xent->masklen = tent->masklen; 176681d3153dSAlexander V. Chernikov xent->value = tent->value; 176781d3153dSAlexander V. Chernikov /* Apply some hacks */ 176881d3153dSAlexander V. Chernikov if (tc->no.type == IPFW_TABLE_CIDR && tent->subtype == AF_INET) { 176981d3153dSAlexander V. Chernikov xent->k.addr6.s6_addr32[3] = tent->k.addr.s_addr; 177081d3153dSAlexander V. Chernikov xent->flags = IPFW_TCF_INET; 177181d3153dSAlexander V. Chernikov } else 177281d3153dSAlexander V. Chernikov memcpy(&xent->k, &tent->k, sizeof(xent->k)); 177381d3153dSAlexander V. Chernikov 177481d3153dSAlexander V. Chernikov return (0); 17759f7d47b0SAlexander V. Chernikov } 17769f7d47b0SAlexander V. Chernikov 17779f7d47b0SAlexander V. Chernikov /* 17789f7d47b0SAlexander V. Chernikov * Table algorithms 17799f7d47b0SAlexander V. Chernikov */ 17803b3a8eb9SGleb Smirnoff 17819f7d47b0SAlexander V. Chernikov /* 17829490a627SAlexander V. Chernikov * Finds algoritm by index, table type or supplied name 17839f7d47b0SAlexander V. Chernikov */ 17849f7d47b0SAlexander V. Chernikov static struct table_algo * 17859490a627SAlexander V. Chernikov find_table_algo(struct tables_config *tcfg, struct tid_info *ti, char *name) 17869f7d47b0SAlexander V. Chernikov { 17879490a627SAlexander V. Chernikov int i, l; 17889490a627SAlexander V. Chernikov struct table_algo *ta; 17899f7d47b0SAlexander V. Chernikov 179057a1cf95SAlexander V. Chernikov if (ti->type > IPFW_TABLE_MAXTYPE) 179157a1cf95SAlexander V. Chernikov return (NULL); 179257a1cf95SAlexander V. Chernikov 17939f7d47b0SAlexander V. Chernikov /* Search by index */ 17949f7d47b0SAlexander V. Chernikov if (ti->atype != 0) { 17959f7d47b0SAlexander V. Chernikov if (ti->atype > tcfg->algo_count) 17969f7d47b0SAlexander V. Chernikov return (NULL); 17979f7d47b0SAlexander V. Chernikov return (tcfg->algo[ti->atype]); 17989f7d47b0SAlexander V. Chernikov } 17999f7d47b0SAlexander V. Chernikov 18009490a627SAlexander V. Chernikov /* Search by name if supplied */ 18019490a627SAlexander V. Chernikov if (name != NULL) { 18029490a627SAlexander V. Chernikov /* TODO: better search */ 18039490a627SAlexander V. Chernikov for (i = 1; i <= tcfg->algo_count; i++) { 18049490a627SAlexander V. Chernikov ta = tcfg->algo[i]; 18059490a627SAlexander V. Chernikov 18069490a627SAlexander V. Chernikov /* 18079490a627SAlexander V. Chernikov * One can supply additional algorithm 18089490a627SAlexander V. Chernikov * parameters so we compare only the first word 18099490a627SAlexander V. Chernikov * of supplied name: 18109490a627SAlexander V. Chernikov * 'hash_cidr hsize=32' 18119490a627SAlexander V. Chernikov * '^^^^^^^^^' 18129490a627SAlexander V. Chernikov * 18139490a627SAlexander V. Chernikov */ 18149490a627SAlexander V. Chernikov l = strlen(ta->name); 18159490a627SAlexander V. Chernikov if (strncmp(name, ta->name, l) == 0) { 18169490a627SAlexander V. Chernikov if (name[l] == '\0' || name[l] == ' ') 18179490a627SAlexander V. Chernikov return (ta); 18189490a627SAlexander V. Chernikov } 18199490a627SAlexander V. Chernikov } 18209490a627SAlexander V. Chernikov 18219490a627SAlexander V. Chernikov return (NULL); 18229490a627SAlexander V. Chernikov } 18239490a627SAlexander V. Chernikov 182457a1cf95SAlexander V. Chernikov /* Return default algorithm for given type if set */ 182557a1cf95SAlexander V. Chernikov return (tcfg->def_algo[ti->type]); 18263b3a8eb9SGleb Smirnoff } 18273b3a8eb9SGleb Smirnoff 1828b6ee846eSAlexander V. Chernikov /* 1829b6ee846eSAlexander V. Chernikov * Register new table algo @ta. 1830b6ee846eSAlexander V. Chernikov * Stores algo id iside @idx.<F2> 1831b6ee846eSAlexander V. Chernikov * 1832b6ee846eSAlexander V. Chernikov * Returns 0 on success. 1833b6ee846eSAlexander V. Chernikov */ 18340b565ac0SAlexander V. Chernikov int 18350b565ac0SAlexander V. Chernikov ipfw_add_table_algo(struct ip_fw_chain *ch, struct table_algo *ta, size_t size, 18360b565ac0SAlexander V. Chernikov int *idx) 18373b3a8eb9SGleb Smirnoff { 18389f7d47b0SAlexander V. Chernikov struct tables_config *tcfg; 18390b565ac0SAlexander V. Chernikov struct table_algo *ta_new; 1840b6ee846eSAlexander V. Chernikov size_t sz; 18410b565ac0SAlexander V. Chernikov 18420b565ac0SAlexander V. Chernikov if (size > sizeof(struct table_algo)) 18430b565ac0SAlexander V. Chernikov return (EINVAL); 18440b565ac0SAlexander V. Chernikov 1845b6ee846eSAlexander V. Chernikov /* Check for the required on-stack size for add/del */ 1846b6ee846eSAlexander V. Chernikov sz = roundup2(ta->ta_buf_size, sizeof(void *)); 1847b6ee846eSAlexander V. Chernikov if (sz > TA_BUF_SZ) 1848b6ee846eSAlexander V. Chernikov return (EINVAL); 1849b6ee846eSAlexander V. Chernikov 185057a1cf95SAlexander V. Chernikov KASSERT(ta->type >= IPFW_TABLE_MAXTYPE,("Increase IPFW_TABLE_MAXTYPE")); 185157a1cf95SAlexander V. Chernikov 18520b565ac0SAlexander V. Chernikov ta_new = malloc(sizeof(struct table_algo), M_IPFW, M_WAITOK | M_ZERO); 18530b565ac0SAlexander V. Chernikov memcpy(ta_new, ta, size); 18543b3a8eb9SGleb Smirnoff 18559f7d47b0SAlexander V. Chernikov tcfg = CHAIN_TO_TCFG(ch); 1856b074b7bbSAlexander V. Chernikov 18579f7d47b0SAlexander V. Chernikov KASSERT(tcfg->algo_count < 255, ("Increase algo array size")); 18589f7d47b0SAlexander V. Chernikov 18590b565ac0SAlexander V. Chernikov tcfg->algo[++tcfg->algo_count] = ta_new; 18600b565ac0SAlexander V. Chernikov ta_new->idx = tcfg->algo_count; 18610b565ac0SAlexander V. Chernikov 186257a1cf95SAlexander V. Chernikov /* Set algorithm as default one for given type */ 186357a1cf95SAlexander V. Chernikov if ((ta_new->flags & TA_FLAG_DEFAULT) != 0 && 186457a1cf95SAlexander V. Chernikov tcfg->def_algo[ta_new->type] == NULL) 186557a1cf95SAlexander V. Chernikov tcfg->def_algo[ta_new->type] = ta_new; 186657a1cf95SAlexander V. Chernikov 18670b565ac0SAlexander V. Chernikov *idx = ta_new->idx; 18680b565ac0SAlexander V. Chernikov 18690b565ac0SAlexander V. Chernikov return (0); 18700b565ac0SAlexander V. Chernikov } 18710b565ac0SAlexander V. Chernikov 1872b6ee846eSAlexander V. Chernikov /* 1873b6ee846eSAlexander V. Chernikov * Unregisters table algo using @idx as id. 1874b6ee846eSAlexander V. Chernikov */ 18750b565ac0SAlexander V. Chernikov void 18760b565ac0SAlexander V. Chernikov ipfw_del_table_algo(struct ip_fw_chain *ch, int idx) 18770b565ac0SAlexander V. Chernikov { 18780b565ac0SAlexander V. Chernikov struct tables_config *tcfg; 18790b565ac0SAlexander V. Chernikov struct table_algo *ta; 18800b565ac0SAlexander V. Chernikov 18810b565ac0SAlexander V. Chernikov tcfg = CHAIN_TO_TCFG(ch); 18820b565ac0SAlexander V. Chernikov 1883b6ee846eSAlexander V. Chernikov KASSERT(idx <= tcfg->algo_count, ("algo idx %d out of range 1..%d", 1884b6ee846eSAlexander V. Chernikov idx, tcfg->algo_count)); 18850b565ac0SAlexander V. Chernikov 18860b565ac0SAlexander V. Chernikov ta = tcfg->algo[idx]; 18870b565ac0SAlexander V. Chernikov KASSERT(ta != NULL, ("algo idx %d is NULL", idx)); 188857a1cf95SAlexander V. Chernikov 188957a1cf95SAlexander V. Chernikov if (tcfg->def_algo[ta->type] == ta) 189057a1cf95SAlexander V. Chernikov tcfg->def_algo[ta->type] = NULL; 189157a1cf95SAlexander V. Chernikov 18920b565ac0SAlexander V. Chernikov free(ta, M_IPFW); 18933b3a8eb9SGleb Smirnoff } 18943b3a8eb9SGleb Smirnoff 18959d099b4fSAlexander V. Chernikov /* 18969d099b4fSAlexander V. Chernikov * Lists all table algorithms currently available. 18979d099b4fSAlexander V. Chernikov * Data layout (v0)(current): 18989d099b4fSAlexander V. Chernikov * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size 18999d099b4fSAlexander V. Chernikov * Reply: [ ipfw_obj_lheader ipfw_ta_info x N ] 19009d099b4fSAlexander V. Chernikov * 19019d099b4fSAlexander V. Chernikov * Returns 0 on success 19029d099b4fSAlexander V. Chernikov */ 19039d099b4fSAlexander V. Chernikov int 19049d099b4fSAlexander V. Chernikov ipfw_list_table_algo(struct ip_fw_chain *ch, struct sockopt_data *sd) 19059d099b4fSAlexander V. Chernikov { 19069d099b4fSAlexander V. Chernikov struct _ipfw_obj_lheader *olh; 19079d099b4fSAlexander V. Chernikov struct tables_config *tcfg; 19089d099b4fSAlexander V. Chernikov ipfw_ta_info *i; 19099d099b4fSAlexander V. Chernikov struct table_algo *ta; 19109d099b4fSAlexander V. Chernikov uint32_t count, n, size; 19119d099b4fSAlexander V. Chernikov 19129d099b4fSAlexander V. Chernikov olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh)); 19139d099b4fSAlexander V. Chernikov if (olh == NULL) 19149d099b4fSAlexander V. Chernikov return (EINVAL); 19159d099b4fSAlexander V. Chernikov if (sd->valsize < olh->size) 19169d099b4fSAlexander V. Chernikov return (EINVAL); 19179d099b4fSAlexander V. Chernikov 19189d099b4fSAlexander V. Chernikov IPFW_UH_RLOCK(ch); 19199d099b4fSAlexander V. Chernikov tcfg = CHAIN_TO_TCFG(ch); 19209d099b4fSAlexander V. Chernikov count = tcfg->algo_count; 19219d099b4fSAlexander V. Chernikov size = count * sizeof(ipfw_ta_info) + sizeof(ipfw_obj_lheader); 19229d099b4fSAlexander V. Chernikov 19239d099b4fSAlexander V. Chernikov /* Fill in header regadless of buffer size */ 19249d099b4fSAlexander V. Chernikov olh->count = count; 19259d099b4fSAlexander V. Chernikov olh->objsize = sizeof(ipfw_ta_info); 19269d099b4fSAlexander V. Chernikov 19279d099b4fSAlexander V. Chernikov if (size > olh->size) { 19289d099b4fSAlexander V. Chernikov olh->size = size; 19299d099b4fSAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 19309d099b4fSAlexander V. Chernikov return (ENOMEM); 19319d099b4fSAlexander V. Chernikov } 19329d099b4fSAlexander V. Chernikov olh->size = size; 19339d099b4fSAlexander V. Chernikov 19349d099b4fSAlexander V. Chernikov for (n = 1; n <= count; n++) { 19359d099b4fSAlexander V. Chernikov i = (ipfw_ta_info *)ipfw_get_sopt_space(sd, sizeof(*i)); 19369d099b4fSAlexander V. Chernikov KASSERT(i != 0, ("previously checked buffer is not enough")); 19379d099b4fSAlexander V. Chernikov ta = tcfg->algo[n]; 19389d099b4fSAlexander V. Chernikov strlcpy(i->algoname, ta->name, sizeof(i->algoname)); 19399d099b4fSAlexander V. Chernikov i->type = ta->type; 19409d099b4fSAlexander V. Chernikov i->refcnt = ta->refcnt; 19419d099b4fSAlexander V. Chernikov } 19429d099b4fSAlexander V. Chernikov 19439d099b4fSAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 19449d099b4fSAlexander V. Chernikov 19459d099b4fSAlexander V. Chernikov return (0); 19469d099b4fSAlexander V. Chernikov } 19479d099b4fSAlexander V. Chernikov 19489f7d47b0SAlexander V. Chernikov 1949b074b7bbSAlexander V. Chernikov /* 1950b074b7bbSAlexander V. Chernikov * Tables rewriting code 1951b074b7bbSAlexander V. Chernikov * 1952b074b7bbSAlexander V. Chernikov */ 1953b074b7bbSAlexander V. Chernikov 1954b074b7bbSAlexander V. Chernikov /* 1955b074b7bbSAlexander V. Chernikov * Determine table number and lookup type for @cmd. 1956b074b7bbSAlexander V. Chernikov * Fill @tbl and @type with appropriate values. 1957b074b7bbSAlexander V. Chernikov * Returns 0 for relevant opcodes, 1 otherwise. 1958b074b7bbSAlexander V. Chernikov */ 1959b074b7bbSAlexander V. Chernikov static int 1960b074b7bbSAlexander V. Chernikov classify_table_opcode(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) 1961b074b7bbSAlexander V. Chernikov { 1962b074b7bbSAlexander V. Chernikov ipfw_insn_if *cmdif; 1963b074b7bbSAlexander V. Chernikov int skip; 1964b074b7bbSAlexander V. Chernikov uint16_t v; 1965b074b7bbSAlexander V. Chernikov 1966b074b7bbSAlexander V. Chernikov skip = 1; 1967b074b7bbSAlexander V. Chernikov 1968b074b7bbSAlexander V. Chernikov switch (cmd->opcode) { 1969b074b7bbSAlexander V. Chernikov case O_IP_SRC_LOOKUP: 1970b074b7bbSAlexander V. Chernikov case O_IP_DST_LOOKUP: 1971b074b7bbSAlexander V. Chernikov /* Basic IPv4/IPv6 or u32 lookups */ 1972b074b7bbSAlexander V. Chernikov *puidx = cmd->arg1; 1973b074b7bbSAlexander V. Chernikov /* Assume CIDR by default */ 1974b074b7bbSAlexander V. Chernikov *ptype = IPFW_TABLE_CIDR; 1975b074b7bbSAlexander V. Chernikov skip = 0; 1976b074b7bbSAlexander V. Chernikov 1977b074b7bbSAlexander V. Chernikov if (F_LEN(cmd) > F_INSN_SIZE(ipfw_insn_u32)) { 1978b074b7bbSAlexander V. Chernikov /* 1979b074b7bbSAlexander V. Chernikov * generic lookup. The key must be 1980b074b7bbSAlexander V. Chernikov * in 32bit big-endian format. 1981b074b7bbSAlexander V. Chernikov */ 1982b074b7bbSAlexander V. Chernikov v = ((ipfw_insn_u32 *)cmd)->d[1]; 1983b074b7bbSAlexander V. Chernikov switch (v) { 1984b074b7bbSAlexander V. Chernikov case 0: 1985b074b7bbSAlexander V. Chernikov case 1: 1986b074b7bbSAlexander V. Chernikov /* IPv4 src/dst */ 1987b074b7bbSAlexander V. Chernikov break; 1988b074b7bbSAlexander V. Chernikov case 2: 1989b074b7bbSAlexander V. Chernikov case 3: 1990b074b7bbSAlexander V. Chernikov /* src/dst port */ 1991b23d5de9SAlexander V. Chernikov *ptype = IPFW_TABLE_NUMBER; 1992b074b7bbSAlexander V. Chernikov break; 1993b074b7bbSAlexander V. Chernikov case 4: 1994b074b7bbSAlexander V. Chernikov /* uid/gid */ 1995b23d5de9SAlexander V. Chernikov *ptype = IPFW_TABLE_NUMBER; 1996b23d5de9SAlexander V. Chernikov break; 1997b074b7bbSAlexander V. Chernikov case 5: 1998b074b7bbSAlexander V. Chernikov /* jid */ 1999b23d5de9SAlexander V. Chernikov *ptype = IPFW_TABLE_NUMBER; 2000b23d5de9SAlexander V. Chernikov break; 2001b074b7bbSAlexander V. Chernikov case 6: 2002b074b7bbSAlexander V. Chernikov /* dscp */ 2003b23d5de9SAlexander V. Chernikov *ptype = IPFW_TABLE_NUMBER; 2004b074b7bbSAlexander V. Chernikov break; 2005b074b7bbSAlexander V. Chernikov } 2006b074b7bbSAlexander V. Chernikov } 2007b074b7bbSAlexander V. Chernikov break; 2008b074b7bbSAlexander V. Chernikov case O_XMIT: 2009b074b7bbSAlexander V. Chernikov case O_RECV: 2010b074b7bbSAlexander V. Chernikov case O_VIA: 2011b074b7bbSAlexander V. Chernikov /* Interface table, possibly */ 2012b074b7bbSAlexander V. Chernikov cmdif = (ipfw_insn_if *)cmd; 2013b074b7bbSAlexander V. Chernikov if (cmdif->name[0] != '\1') 2014b074b7bbSAlexander V. Chernikov break; 2015b074b7bbSAlexander V. Chernikov 2016b074b7bbSAlexander V. Chernikov *ptype = IPFW_TABLE_INTERFACE; 2017b074b7bbSAlexander V. Chernikov *puidx = cmdif->p.glob; 2018b074b7bbSAlexander V. Chernikov skip = 0; 2019b074b7bbSAlexander V. Chernikov break; 2020914bffb6SAlexander V. Chernikov case O_IP_FLOW_LOOKUP: 2021914bffb6SAlexander V. Chernikov *puidx = cmd->arg1; 2022914bffb6SAlexander V. Chernikov *ptype = IPFW_TABLE_FLOW; 2023914bffb6SAlexander V. Chernikov skip = 0; 2024914bffb6SAlexander V. Chernikov break; 2025b074b7bbSAlexander V. Chernikov } 2026b074b7bbSAlexander V. Chernikov 2027b074b7bbSAlexander V. Chernikov return (skip); 2028b074b7bbSAlexander V. Chernikov } 2029b074b7bbSAlexander V. Chernikov 2030b074b7bbSAlexander V. Chernikov /* 2031b074b7bbSAlexander V. Chernikov * Sets new table value for given opcode. 2032b074b7bbSAlexander V. Chernikov * Assume the same opcodes as classify_table_opcode() 2033b074b7bbSAlexander V. Chernikov */ 2034b074b7bbSAlexander V. Chernikov static void 2035b074b7bbSAlexander V. Chernikov update_table_opcode(ipfw_insn *cmd, uint16_t idx) 2036b074b7bbSAlexander V. Chernikov { 2037b074b7bbSAlexander V. Chernikov ipfw_insn_if *cmdif; 2038b074b7bbSAlexander V. Chernikov 2039b074b7bbSAlexander V. Chernikov switch (cmd->opcode) { 2040b074b7bbSAlexander V. Chernikov case O_IP_SRC_LOOKUP: 2041b074b7bbSAlexander V. Chernikov case O_IP_DST_LOOKUP: 2042b074b7bbSAlexander V. Chernikov /* Basic IPv4/IPv6 or u32 lookups */ 2043b074b7bbSAlexander V. Chernikov cmd->arg1 = idx; 2044b074b7bbSAlexander V. Chernikov break; 2045b074b7bbSAlexander V. Chernikov case O_XMIT: 2046b074b7bbSAlexander V. Chernikov case O_RECV: 2047b074b7bbSAlexander V. Chernikov case O_VIA: 2048b074b7bbSAlexander V. Chernikov /* Interface table, possibly */ 2049b074b7bbSAlexander V. Chernikov cmdif = (ipfw_insn_if *)cmd; 2050b074b7bbSAlexander V. Chernikov cmdif->p.glob = idx; 2051b074b7bbSAlexander V. Chernikov break; 2052914bffb6SAlexander V. Chernikov case O_IP_FLOW_LOOKUP: 2053914bffb6SAlexander V. Chernikov cmd->arg1 = idx; 2054914bffb6SAlexander V. Chernikov break; 2055b074b7bbSAlexander V. Chernikov } 2056b074b7bbSAlexander V. Chernikov } 2057b074b7bbSAlexander V. Chernikov 2058ac35ff17SAlexander V. Chernikov /* 2059ac35ff17SAlexander V. Chernikov * Checks table name for validity. 2060ac35ff17SAlexander V. Chernikov * Enforce basic length checks, the rest 2061ac35ff17SAlexander V. Chernikov * should be done in userland. 2062ac35ff17SAlexander V. Chernikov * 2063ac35ff17SAlexander V. Chernikov * Returns 0 if name is considered valid. 2064ac35ff17SAlexander V. Chernikov */ 2065ac35ff17SAlexander V. Chernikov int 2066ac35ff17SAlexander V. Chernikov ipfw_check_table_name(char *name) 2067ac35ff17SAlexander V. Chernikov { 2068ac35ff17SAlexander V. Chernikov int nsize; 2069ac35ff17SAlexander V. Chernikov ipfw_obj_ntlv *ntlv = NULL; 2070ac35ff17SAlexander V. Chernikov 2071ac35ff17SAlexander V. Chernikov nsize = sizeof(ntlv->name); 2072ac35ff17SAlexander V. Chernikov 2073ac35ff17SAlexander V. Chernikov if (strnlen(name, nsize) == nsize) 2074ac35ff17SAlexander V. Chernikov return (EINVAL); 2075ac35ff17SAlexander V. Chernikov 2076ac35ff17SAlexander V. Chernikov if (name[0] == '\0') 2077ac35ff17SAlexander V. Chernikov return (EINVAL); 2078ac35ff17SAlexander V. Chernikov 2079ac35ff17SAlexander V. Chernikov /* 2080ac35ff17SAlexander V. Chernikov * TODO: do some more complicated checks 2081ac35ff17SAlexander V. Chernikov */ 2082ac35ff17SAlexander V. Chernikov 2083ac35ff17SAlexander V. Chernikov return (0); 2084ac35ff17SAlexander V. Chernikov } 2085ac35ff17SAlexander V. Chernikov 2086ac35ff17SAlexander V. Chernikov /* 2087ac35ff17SAlexander V. Chernikov * Find tablename TLV by @uid. 2088ac35ff17SAlexander V. Chernikov * Check @tlvs for valid data inside. 2089ac35ff17SAlexander V. Chernikov * 2090ac35ff17SAlexander V. Chernikov * Returns pointer to found TLV or NULL. 2091ac35ff17SAlexander V. Chernikov */ 2092ac35ff17SAlexander V. Chernikov static ipfw_obj_ntlv * 2093b074b7bbSAlexander V. Chernikov find_name_tlv(void *tlvs, int len, uint16_t uidx) 2094b074b7bbSAlexander V. Chernikov { 20959f7d47b0SAlexander V. Chernikov ipfw_obj_ntlv *ntlv; 2096b074b7bbSAlexander V. Chernikov uintptr_t pa, pe; 2097b074b7bbSAlexander V. Chernikov int l; 2098b074b7bbSAlexander V. Chernikov 2099b074b7bbSAlexander V. Chernikov pa = (uintptr_t)tlvs; 2100b074b7bbSAlexander V. Chernikov pe = pa + len; 2101b074b7bbSAlexander V. Chernikov l = 0; 2102b074b7bbSAlexander V. Chernikov for (; pa < pe; pa += l) { 21039f7d47b0SAlexander V. Chernikov ntlv = (ipfw_obj_ntlv *)pa; 2104b074b7bbSAlexander V. Chernikov l = ntlv->head.length; 2105ac35ff17SAlexander V. Chernikov 2106ac35ff17SAlexander V. Chernikov if (l != sizeof(*ntlv)) 2107ac35ff17SAlexander V. Chernikov return (NULL); 2108ac35ff17SAlexander V. Chernikov 2109563b5ab1SAlexander V. Chernikov if (ntlv->head.type != IPFW_TLV_TBL_NAME) 2110b074b7bbSAlexander V. Chernikov continue; 2111ac35ff17SAlexander V. Chernikov 2112b074b7bbSAlexander V. Chernikov if (ntlv->idx != uidx) 2113b074b7bbSAlexander V. Chernikov continue; 2114b074b7bbSAlexander V. Chernikov 2115ac35ff17SAlexander V. Chernikov if (ipfw_check_table_name(ntlv->name) != 0) 2116ac35ff17SAlexander V. Chernikov return (NULL); 2117ac35ff17SAlexander V. Chernikov 2118ac35ff17SAlexander V. Chernikov return (ntlv); 2119b074b7bbSAlexander V. Chernikov } 2120b074b7bbSAlexander V. Chernikov 2121b074b7bbSAlexander V. Chernikov return (NULL); 2122b074b7bbSAlexander V. Chernikov } 2123b074b7bbSAlexander V. Chernikov 2124ac35ff17SAlexander V. Chernikov /* 2125ac35ff17SAlexander V. Chernikov * Finds table config based on either legacy index 2126ac35ff17SAlexander V. Chernikov * or name in ntlv. 2127ac35ff17SAlexander V. Chernikov * Note @ti structure contains unchecked data from userland. 2128ac35ff17SAlexander V. Chernikov * 2129ac35ff17SAlexander V. Chernikov * Returns pointer to table_config or NULL. 2130ac35ff17SAlexander V. Chernikov */ 2131b074b7bbSAlexander V. Chernikov static struct table_config * 2132b074b7bbSAlexander V. Chernikov find_table(struct namedobj_instance *ni, struct tid_info *ti) 2133b074b7bbSAlexander V. Chernikov { 2134b074b7bbSAlexander V. Chernikov char *name, bname[16]; 2135b074b7bbSAlexander V. Chernikov struct named_object *no; 2136ac35ff17SAlexander V. Chernikov ipfw_obj_ntlv *ntlv; 2137ac35ff17SAlexander V. Chernikov uint32_t set; 2138b074b7bbSAlexander V. Chernikov 2139b074b7bbSAlexander V. Chernikov if (ti->tlvs != NULL) { 2140ac35ff17SAlexander V. Chernikov ntlv = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx); 2141ac35ff17SAlexander V. Chernikov if (ntlv == NULL) 2142b074b7bbSAlexander V. Chernikov return (NULL); 2143ac35ff17SAlexander V. Chernikov name = ntlv->name; 2144ac35ff17SAlexander V. Chernikov set = ntlv->set; 2145b074b7bbSAlexander V. Chernikov } else { 2146b074b7bbSAlexander V. Chernikov snprintf(bname, sizeof(bname), "%d", ti->uidx); 2147b074b7bbSAlexander V. Chernikov name = bname; 2148ac35ff17SAlexander V. Chernikov set = 0; 2149b074b7bbSAlexander V. Chernikov } 2150b074b7bbSAlexander V. Chernikov 2151ac35ff17SAlexander V. Chernikov no = ipfw_objhash_lookup_name(ni, set, name); 2152b074b7bbSAlexander V. Chernikov 2153b074b7bbSAlexander V. Chernikov return ((struct table_config *)no); 2154b074b7bbSAlexander V. Chernikov } 2155b074b7bbSAlexander V. Chernikov 2156b074b7bbSAlexander V. Chernikov static struct table_config * 215768394ec8SAlexander V. Chernikov alloc_table_config(struct ip_fw_chain *ch, struct tid_info *ti, 2158914bffb6SAlexander V. Chernikov struct table_algo *ta, char *aname, uint8_t tflags, uint8_t vtype) 2159b074b7bbSAlexander V. Chernikov { 2160b074b7bbSAlexander V. Chernikov char *name, bname[16]; 2161b074b7bbSAlexander V. Chernikov struct table_config *tc; 2162b074b7bbSAlexander V. Chernikov int error; 2163ac35ff17SAlexander V. Chernikov ipfw_obj_ntlv *ntlv; 2164ac35ff17SAlexander V. Chernikov uint32_t set; 2165b074b7bbSAlexander V. Chernikov 2166b074b7bbSAlexander V. Chernikov if (ti->tlvs != NULL) { 2167ac35ff17SAlexander V. Chernikov ntlv = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx); 2168ac35ff17SAlexander V. Chernikov if (ntlv == NULL) 2169b074b7bbSAlexander V. Chernikov return (NULL); 2170ac35ff17SAlexander V. Chernikov name = ntlv->name; 2171ac35ff17SAlexander V. Chernikov set = ntlv->set; 2172b074b7bbSAlexander V. Chernikov } else { 2173b074b7bbSAlexander V. Chernikov snprintf(bname, sizeof(bname), "%d", ti->uidx); 2174b074b7bbSAlexander V. Chernikov name = bname; 2175ac35ff17SAlexander V. Chernikov set = 0; 2176b074b7bbSAlexander V. Chernikov } 2177b074b7bbSAlexander V. Chernikov 2178b074b7bbSAlexander V. Chernikov tc = malloc(sizeof(struct table_config), M_IPFW, M_WAITOK | M_ZERO); 2179b074b7bbSAlexander V. Chernikov tc->no.name = tc->tablename; 2180b074b7bbSAlexander V. Chernikov tc->no.type = ti->type; 2181ac35ff17SAlexander V. Chernikov tc->no.set = set; 2182914bffb6SAlexander V. Chernikov tc->tflags = tflags; 21839f7d47b0SAlexander V. Chernikov tc->ta = ta; 2184b074b7bbSAlexander V. Chernikov strlcpy(tc->tablename, name, sizeof(tc->tablename)); 2185ac35ff17SAlexander V. Chernikov /* Set default value type to u32 for compability reasons */ 2186db785d31SAlexander V. Chernikov if (vtype == 0) 2187ac35ff17SAlexander V. Chernikov tc->vtype = IPFW_VTYPE_U32; 2188db785d31SAlexander V. Chernikov else 2189db785d31SAlexander V. Chernikov tc->vtype = vtype; 2190b074b7bbSAlexander V. Chernikov 2191b074b7bbSAlexander V. Chernikov if (ti->tlvs == NULL) { 2192b074b7bbSAlexander V. Chernikov tc->no.compat = 1; 2193b074b7bbSAlexander V. Chernikov tc->no.uidx = ti->uidx; 2194b074b7bbSAlexander V. Chernikov } 2195b074b7bbSAlexander V. Chernikov 2196b074b7bbSAlexander V. Chernikov /* Preallocate data structures for new tables */ 2197914bffb6SAlexander V. Chernikov error = ta->init(ch, &tc->astate, &tc->ti, aname, tflags); 2198b074b7bbSAlexander V. Chernikov if (error != 0) { 2199b074b7bbSAlexander V. Chernikov free(tc, M_IPFW); 2200b074b7bbSAlexander V. Chernikov return (NULL); 2201b074b7bbSAlexander V. Chernikov } 2202b074b7bbSAlexander V. Chernikov 2203b074b7bbSAlexander V. Chernikov return (tc); 2204b074b7bbSAlexander V. Chernikov } 2205b074b7bbSAlexander V. Chernikov 2206b074b7bbSAlexander V. Chernikov static void 2207b074b7bbSAlexander V. Chernikov free_table_config(struct namedobj_instance *ni, struct table_config *tc) 2208b074b7bbSAlexander V. Chernikov { 2209b074b7bbSAlexander V. Chernikov 2210b074b7bbSAlexander V. Chernikov if (tc->linked == 0) 221168394ec8SAlexander V. Chernikov tc->ta->destroy(tc->astate, &tc->ti); 2212b074b7bbSAlexander V. Chernikov 2213b074b7bbSAlexander V. Chernikov free(tc, M_IPFW); 2214b074b7bbSAlexander V. Chernikov } 2215b074b7bbSAlexander V. Chernikov 2216b074b7bbSAlexander V. Chernikov /* 2217b074b7bbSAlexander V. Chernikov * Links @tc to @chain table named instance. 2218b074b7bbSAlexander V. Chernikov * Sets appropriate type/states in @chain table info. 2219b074b7bbSAlexander V. Chernikov */ 2220b074b7bbSAlexander V. Chernikov static void 22219f7d47b0SAlexander V. Chernikov link_table(struct ip_fw_chain *ch, struct table_config *tc) 2222b074b7bbSAlexander V. Chernikov { 2223b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 22249f7d47b0SAlexander V. Chernikov struct table_info *ti; 2225b074b7bbSAlexander V. Chernikov uint16_t kidx; 2226b074b7bbSAlexander V. Chernikov 22279f7d47b0SAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(ch); 22289f7d47b0SAlexander V. Chernikov IPFW_WLOCK_ASSERT(ch); 2229b074b7bbSAlexander V. Chernikov 22309f7d47b0SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 2231b074b7bbSAlexander V. Chernikov kidx = tc->no.kidx; 2232b074b7bbSAlexander V. Chernikov 2233b074b7bbSAlexander V. Chernikov ipfw_objhash_add(ni, &tc->no); 22349f7d47b0SAlexander V. Chernikov 22359f7d47b0SAlexander V. Chernikov ti = KIDX_TO_TI(ch, kidx); 22369f7d47b0SAlexander V. Chernikov *ti = tc->ti; 2237b074b7bbSAlexander V. Chernikov 223868394ec8SAlexander V. Chernikov /* Notify algo on real @ti address */ 223968394ec8SAlexander V. Chernikov if (tc->ta->change_ti != NULL) 224068394ec8SAlexander V. Chernikov tc->ta->change_ti(tc->astate, ti); 224168394ec8SAlexander V. Chernikov 2242b074b7bbSAlexander V. Chernikov tc->linked = 1; 22439d099b4fSAlexander V. Chernikov tc->ta->refcnt++; 2244b074b7bbSAlexander V. Chernikov } 2245b074b7bbSAlexander V. Chernikov 2246b074b7bbSAlexander V. Chernikov /* 2247b074b7bbSAlexander V. Chernikov * Unlinks @tc from @chain table named instance. 2248b074b7bbSAlexander V. Chernikov * Zeroes states in @chain and stores them in @tc. 2249b074b7bbSAlexander V. Chernikov */ 2250b074b7bbSAlexander V. Chernikov static void 22519f7d47b0SAlexander V. Chernikov unlink_table(struct ip_fw_chain *ch, struct table_config *tc) 2252b074b7bbSAlexander V. Chernikov { 2253b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 22549f7d47b0SAlexander V. Chernikov struct table_info *ti; 2255b074b7bbSAlexander V. Chernikov uint16_t kidx; 2256b074b7bbSAlexander V. Chernikov 22579f7d47b0SAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(ch); 22589f7d47b0SAlexander V. Chernikov IPFW_WLOCK_ASSERT(ch); 2259b074b7bbSAlexander V. Chernikov 22609f7d47b0SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 2261b074b7bbSAlexander V. Chernikov kidx = tc->no.kidx; 2262b074b7bbSAlexander V. Chernikov 22639f7d47b0SAlexander V. Chernikov /* Clear state. @ti copy is already saved inside @tc */ 2264b074b7bbSAlexander V. Chernikov ipfw_objhash_del(ni, &tc->no); 22659f7d47b0SAlexander V. Chernikov ti = KIDX_TO_TI(ch, kidx); 22669f7d47b0SAlexander V. Chernikov memset(ti, 0, sizeof(struct table_info)); 2267b074b7bbSAlexander V. Chernikov tc->linked = 0; 22689d099b4fSAlexander V. Chernikov tc->ta->refcnt--; 226968394ec8SAlexander V. Chernikov 227068394ec8SAlexander V. Chernikov /* Notify algo on real @ti address */ 227168394ec8SAlexander V. Chernikov if (tc->ta->change_ti != NULL) 227268394ec8SAlexander V. Chernikov tc->ta->change_ti(tc->astate, NULL); 2273b074b7bbSAlexander V. Chernikov } 2274b074b7bbSAlexander V. Chernikov 2275b074b7bbSAlexander V. Chernikov /* 2276b074b7bbSAlexander V. Chernikov * Finds named object by @uidx number. 2277b074b7bbSAlexander V. Chernikov * Refs found object, allocate new index for non-existing object. 22789490a627SAlexander V. Chernikov * Fills in @oib with userland/kernel indexes. 22799490a627SAlexander V. Chernikov * First free oidx pointer is saved back in @oib. 2280b074b7bbSAlexander V. Chernikov * 2281b074b7bbSAlexander V. Chernikov * Returns 0 on success. 2282b074b7bbSAlexander V. Chernikov */ 2283b074b7bbSAlexander V. Chernikov static int 22849490a627SAlexander V. Chernikov bind_table_rule(struct ip_fw_chain *ch, struct ip_fw *rule, 22859490a627SAlexander V. Chernikov struct rule_check_info *ci, struct obj_idx **oib, struct tid_info *ti) 2286b074b7bbSAlexander V. Chernikov { 2287b074b7bbSAlexander V. Chernikov struct table_config *tc; 22889490a627SAlexander V. Chernikov struct namedobj_instance *ni; 22899490a627SAlexander V. Chernikov struct named_object *no; 22909490a627SAlexander V. Chernikov int error, l, cmdlen; 22919490a627SAlexander V. Chernikov ipfw_insn *cmd; 22929490a627SAlexander V. Chernikov struct obj_idx *pidx, *p; 22939490a627SAlexander V. Chernikov 22949490a627SAlexander V. Chernikov pidx = *oib; 22959490a627SAlexander V. Chernikov l = rule->cmd_len; 22969490a627SAlexander V. Chernikov cmd = rule->cmd; 22979490a627SAlexander V. Chernikov cmdlen = 0; 22989490a627SAlexander V. Chernikov error = 0; 22999490a627SAlexander V. Chernikov 23009490a627SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 23019490a627SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 23029490a627SAlexander V. Chernikov 23039490a627SAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 23049490a627SAlexander V. Chernikov cmdlen = F_LEN(cmd); 23059490a627SAlexander V. Chernikov 23069490a627SAlexander V. Chernikov if (classify_table_opcode(cmd, &ti->uidx, &ti->type) != 0) 23079490a627SAlexander V. Chernikov continue; 2308b074b7bbSAlexander V. Chernikov 2309b074b7bbSAlexander V. Chernikov pidx->uidx = ti->uidx; 2310b074b7bbSAlexander V. Chernikov pidx->type = ti->type; 2311b074b7bbSAlexander V. Chernikov 23129490a627SAlexander V. Chernikov if ((tc = find_table(ni, ti)) != NULL) { 23139490a627SAlexander V. Chernikov if (tc->no.type != ti->type) { 23149490a627SAlexander V. Chernikov /* Incompatible types */ 23159490a627SAlexander V. Chernikov error = EINVAL; 23169490a627SAlexander V. Chernikov break; 23179490a627SAlexander V. Chernikov } 23189f7d47b0SAlexander V. Chernikov 23199490a627SAlexander V. Chernikov /* Reference found table and save kidx */ 23209490a627SAlexander V. Chernikov tc->no.refcnt++; 23219490a627SAlexander V. Chernikov pidx->kidx = tc->no.kidx; 23229490a627SAlexander V. Chernikov pidx++; 23239490a627SAlexander V. Chernikov continue; 23249490a627SAlexander V. Chernikov } 23259490a627SAlexander V. Chernikov 23269490a627SAlexander V. Chernikov /* Table not found. Allocate new index and save for later */ 2327ac35ff17SAlexander V. Chernikov if (ipfw_objhash_alloc_idx(ni, &pidx->kidx) != 0) { 2328ac35ff17SAlexander V. Chernikov printf("Unable to allocate table %s index in set %u." 2329b074b7bbSAlexander V. Chernikov " Consider increasing net.inet.ip.fw.tables_max", 2330ac35ff17SAlexander V. Chernikov "", ti->set); 23319490a627SAlexander V. Chernikov error = EBUSY; 23329490a627SAlexander V. Chernikov break; 2333b074b7bbSAlexander V. Chernikov } 2334b074b7bbSAlexander V. Chernikov 2335b074b7bbSAlexander V. Chernikov ci->new_tables++; 23369490a627SAlexander V. Chernikov pidx->new = 1; 23379490a627SAlexander V. Chernikov pidx++; 2338b074b7bbSAlexander V. Chernikov } 2339b074b7bbSAlexander V. Chernikov 23409490a627SAlexander V. Chernikov if (error != 0) { 23419490a627SAlexander V. Chernikov /* Unref everything we have already done */ 23429490a627SAlexander V. Chernikov for (p = *oib; p < pidx; p++) { 23439490a627SAlexander V. Chernikov if (p->new != 0) { 2344ac35ff17SAlexander V. Chernikov ipfw_objhash_free_idx(ni, p->kidx); 23459490a627SAlexander V. Chernikov continue; 23469490a627SAlexander V. Chernikov } 2347b074b7bbSAlexander V. Chernikov 23489490a627SAlexander V. Chernikov /* Find & unref by existing idx */ 2349ac35ff17SAlexander V. Chernikov no = ipfw_objhash_lookup_kidx(ni, p->kidx); 23509490a627SAlexander V. Chernikov KASSERT(no != NULL, ("Ref'd table %d disappeared", 23519490a627SAlexander V. Chernikov p->kidx)); 2352b074b7bbSAlexander V. Chernikov 23539490a627SAlexander V. Chernikov no->refcnt--; 23549490a627SAlexander V. Chernikov } 23559490a627SAlexander V. Chernikov } 23569490a627SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 2357b074b7bbSAlexander V. Chernikov 23589490a627SAlexander V. Chernikov *oib = pidx; 23599490a627SAlexander V. Chernikov 23609490a627SAlexander V. Chernikov return (error); 2361b074b7bbSAlexander V. Chernikov } 2362b074b7bbSAlexander V. Chernikov 2363a73d728dSAlexander V. Chernikov struct swap_table_args { 2364a73d728dSAlexander V. Chernikov int set; 2365a73d728dSAlexander V. Chernikov int new_set; 2366a73d728dSAlexander V. Chernikov int mv; 2367a73d728dSAlexander V. Chernikov }; 2368a73d728dSAlexander V. Chernikov 2369a73d728dSAlexander V. Chernikov /* 2370a73d728dSAlexander V. Chernikov * Change set for each matching table. 2371a73d728dSAlexander V. Chernikov * 2372a73d728dSAlexander V. Chernikov * Ensure we dispatch each table once by setting/checking ochange 2373a73d728dSAlexander V. Chernikov * fields. 2374a73d728dSAlexander V. Chernikov */ 2375a73d728dSAlexander V. Chernikov static void 2376a73d728dSAlexander V. Chernikov swap_table_set(struct namedobj_instance *ni, struct named_object *no, 2377a73d728dSAlexander V. Chernikov void *arg) 2378a73d728dSAlexander V. Chernikov { 2379a73d728dSAlexander V. Chernikov struct table_config *tc; 2380a73d728dSAlexander V. Chernikov struct swap_table_args *sta; 2381a73d728dSAlexander V. Chernikov 2382a73d728dSAlexander V. Chernikov tc = (struct table_config *)no; 2383a73d728dSAlexander V. Chernikov sta = (struct swap_table_args *)arg; 2384a73d728dSAlexander V. Chernikov 2385a73d728dSAlexander V. Chernikov if (no->set != sta->set && (no->set != sta->new_set || sta->mv != 0)) 2386a73d728dSAlexander V. Chernikov return; 2387a73d728dSAlexander V. Chernikov 2388a73d728dSAlexander V. Chernikov if (tc->ochanged != 0) 2389a73d728dSAlexander V. Chernikov return; 2390a73d728dSAlexander V. Chernikov 2391a73d728dSAlexander V. Chernikov tc->ochanged = 1; 2392a73d728dSAlexander V. Chernikov ipfw_objhash_del(ni, no); 2393a73d728dSAlexander V. Chernikov if (no->set == sta->set) 2394a73d728dSAlexander V. Chernikov no->set = sta->new_set; 2395a73d728dSAlexander V. Chernikov else 2396a73d728dSAlexander V. Chernikov no->set = sta->set; 2397a73d728dSAlexander V. Chernikov ipfw_objhash_add(ni, no); 2398a73d728dSAlexander V. Chernikov } 2399a73d728dSAlexander V. Chernikov 2400a73d728dSAlexander V. Chernikov /* 2401a73d728dSAlexander V. Chernikov * Cleans up ochange field for all tables. 2402a73d728dSAlexander V. Chernikov */ 2403a73d728dSAlexander V. Chernikov static void 2404a73d728dSAlexander V. Chernikov clean_table_set_data(struct namedobj_instance *ni, struct named_object *no, 2405a73d728dSAlexander V. Chernikov void *arg) 2406a73d728dSAlexander V. Chernikov { 2407a73d728dSAlexander V. Chernikov struct table_config *tc; 2408a73d728dSAlexander V. Chernikov struct swap_table_args *sta; 2409a73d728dSAlexander V. Chernikov 2410a73d728dSAlexander V. Chernikov tc = (struct table_config *)no; 2411a73d728dSAlexander V. Chernikov sta = (struct swap_table_args *)arg; 2412a73d728dSAlexander V. Chernikov 2413a73d728dSAlexander V. Chernikov tc->ochanged = 0; 2414a73d728dSAlexander V. Chernikov } 2415a73d728dSAlexander V. Chernikov 2416a73d728dSAlexander V. Chernikov /* 2417a73d728dSAlexander V. Chernikov * Swaps tables within two sets. 2418a73d728dSAlexander V. Chernikov */ 2419a73d728dSAlexander V. Chernikov void 2420a73d728dSAlexander V. Chernikov ipfw_swap_tables_sets(struct ip_fw_chain *ch, uint32_t set, 2421a73d728dSAlexander V. Chernikov uint32_t new_set, int mv) 2422a73d728dSAlexander V. Chernikov { 2423a73d728dSAlexander V. Chernikov struct swap_table_args sta; 2424a73d728dSAlexander V. Chernikov 2425a73d728dSAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(ch); 2426a73d728dSAlexander V. Chernikov 2427a73d728dSAlexander V. Chernikov sta.set = set; 2428a73d728dSAlexander V. Chernikov sta.new_set = new_set; 2429a73d728dSAlexander V. Chernikov sta.mv = mv; 2430a73d728dSAlexander V. Chernikov 2431a73d728dSAlexander V. Chernikov ipfw_objhash_foreach(CHAIN_TO_NI(ch), swap_table_set, &sta); 2432a73d728dSAlexander V. Chernikov ipfw_objhash_foreach(CHAIN_TO_NI(ch), clean_table_set_data, &sta); 2433a73d728dSAlexander V. Chernikov } 2434a73d728dSAlexander V. Chernikov 2435a73d728dSAlexander V. Chernikov /* 2436a73d728dSAlexander V. Chernikov * Move all tables which are reference by rules in @rr to set @new_set. 2437a73d728dSAlexander V. Chernikov * Makes sure that all relevant tables are referenced ONLLY by given rules. 2438a73d728dSAlexander V. Chernikov * 2439a73d728dSAlexander V. Chernikov * Retuns 0 on success, 2440a73d728dSAlexander V. Chernikov */ 2441a73d728dSAlexander V. Chernikov int 2442a73d728dSAlexander V. Chernikov ipfw_move_tables_sets(struct ip_fw_chain *ch, ipfw_range_tlv *rt, 2443a73d728dSAlexander V. Chernikov uint32_t new_set) 2444a73d728dSAlexander V. Chernikov { 2445a73d728dSAlexander V. Chernikov struct ip_fw *rule; 2446a73d728dSAlexander V. Chernikov struct table_config *tc; 2447a73d728dSAlexander V. Chernikov struct named_object *no; 2448a73d728dSAlexander V. Chernikov struct namedobj_instance *ni; 2449a73d728dSAlexander V. Chernikov int bad, i, l, cmdlen; 2450a73d728dSAlexander V. Chernikov uint16_t kidx; 2451a73d728dSAlexander V. Chernikov uint8_t type; 2452a73d728dSAlexander V. Chernikov ipfw_insn *cmd; 2453a73d728dSAlexander V. Chernikov 2454a73d728dSAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(ch); 2455a73d728dSAlexander V. Chernikov 2456a73d728dSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 2457a73d728dSAlexander V. Chernikov 2458a73d728dSAlexander V. Chernikov /* Stage 1: count number of references by given rules */ 2459a73d728dSAlexander V. Chernikov for (i = 0; i < ch->n_rules - 1; i++) { 2460a73d728dSAlexander V. Chernikov rule = ch->map[i]; 2461a73d728dSAlexander V. Chernikov if (ipfw_match_range(rule, rt) == 0) 2462a73d728dSAlexander V. Chernikov continue; 2463a73d728dSAlexander V. Chernikov 2464a73d728dSAlexander V. Chernikov l = rule->cmd_len; 2465a73d728dSAlexander V. Chernikov cmd = rule->cmd; 2466a73d728dSAlexander V. Chernikov cmdlen = 0; 2467a73d728dSAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 2468a73d728dSAlexander V. Chernikov cmdlen = F_LEN(cmd); 2469a73d728dSAlexander V. Chernikov if (classify_table_opcode(cmd, &kidx, &type) != 0) 2470a73d728dSAlexander V. Chernikov continue; 2471a73d728dSAlexander V. Chernikov no = ipfw_objhash_lookup_kidx(ni, kidx); 2472a73d728dSAlexander V. Chernikov KASSERT(no != NULL, 2473a73d728dSAlexander V. Chernikov ("objhash lookup failed on index %d", kidx)); 2474a73d728dSAlexander V. Chernikov tc = (struct table_config *)no; 2475a73d728dSAlexander V. Chernikov tc->ocount++; 2476a73d728dSAlexander V. Chernikov } 2477a73d728dSAlexander V. Chernikov 2478a73d728dSAlexander V. Chernikov } 2479a73d728dSAlexander V. Chernikov 2480a73d728dSAlexander V. Chernikov /* Stage 2: verify "ownership" */ 2481a73d728dSAlexander V. Chernikov bad = 0; 2482a73d728dSAlexander V. Chernikov for (i = 0; i < ch->n_rules - 1; i++) { 2483a73d728dSAlexander V. Chernikov rule = ch->map[i]; 2484a73d728dSAlexander V. Chernikov if (ipfw_match_range(rule, rt) == 0) 2485a73d728dSAlexander V. Chernikov continue; 2486a73d728dSAlexander V. Chernikov 2487a73d728dSAlexander V. Chernikov l = rule->cmd_len; 2488a73d728dSAlexander V. Chernikov cmd = rule->cmd; 2489a73d728dSAlexander V. Chernikov cmdlen = 0; 2490a73d728dSAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 2491a73d728dSAlexander V. Chernikov cmdlen = F_LEN(cmd); 2492a73d728dSAlexander V. Chernikov if (classify_table_opcode(cmd, &kidx, &type) != 0) 2493a73d728dSAlexander V. Chernikov continue; 2494a73d728dSAlexander V. Chernikov no = ipfw_objhash_lookup_kidx(ni, kidx); 2495a73d728dSAlexander V. Chernikov KASSERT(no != NULL, 2496a73d728dSAlexander V. Chernikov ("objhash lookup failed on index %d", kidx)); 2497a73d728dSAlexander V. Chernikov tc = (struct table_config *)no; 2498a73d728dSAlexander V. Chernikov if (tc->no.refcnt != tc->ocount) { 2499a73d728dSAlexander V. Chernikov 2500a73d728dSAlexander V. Chernikov /* 2501a73d728dSAlexander V. Chernikov * Number of references differ: 2502a73d728dSAlexander V. Chernikov * Other rule(s) are holding reference to given 2503a73d728dSAlexander V. Chernikov * table, so it is not possible to change its set. 2504a73d728dSAlexander V. Chernikov * 2505a73d728dSAlexander V. Chernikov * Note that refcnt may account 2506a73d728dSAlexander V. Chernikov * references to some going-to-be-added rules. 2507a73d728dSAlexander V. Chernikov * Since we don't know their numbers (and event 2508a73d728dSAlexander V. Chernikov * if they will be added) it is perfectly OK 2509a73d728dSAlexander V. Chernikov * to return error here. 2510a73d728dSAlexander V. Chernikov */ 2511a73d728dSAlexander V. Chernikov bad = 1; 2512a73d728dSAlexander V. Chernikov break; 2513a73d728dSAlexander V. Chernikov } 2514a73d728dSAlexander V. Chernikov } 2515a73d728dSAlexander V. Chernikov 2516a73d728dSAlexander V. Chernikov if (bad != 0) 2517a73d728dSAlexander V. Chernikov break; 2518a73d728dSAlexander V. Chernikov } 2519a73d728dSAlexander V. Chernikov 2520a73d728dSAlexander V. Chernikov /* Stage 3: change set or cleanup */ 2521a73d728dSAlexander V. Chernikov for (i = 0; i < ch->n_rules - 1; i++) { 2522a73d728dSAlexander V. Chernikov rule = ch->map[i]; 2523a73d728dSAlexander V. Chernikov if (ipfw_match_range(rule, rt) == 0) 2524a73d728dSAlexander V. Chernikov continue; 2525a73d728dSAlexander V. Chernikov 2526a73d728dSAlexander V. Chernikov l = rule->cmd_len; 2527a73d728dSAlexander V. Chernikov cmd = rule->cmd; 2528a73d728dSAlexander V. Chernikov cmdlen = 0; 2529a73d728dSAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 2530a73d728dSAlexander V. Chernikov cmdlen = F_LEN(cmd); 2531a73d728dSAlexander V. Chernikov if (classify_table_opcode(cmd, &kidx, &type) != 0) 2532a73d728dSAlexander V. Chernikov continue; 2533a73d728dSAlexander V. Chernikov no = ipfw_objhash_lookup_kidx(ni, kidx); 2534a73d728dSAlexander V. Chernikov KASSERT(no != NULL, 2535a73d728dSAlexander V. Chernikov ("objhash lookup failed on index %d", kidx)); 2536a73d728dSAlexander V. Chernikov tc = (struct table_config *)no; 2537a73d728dSAlexander V. Chernikov 2538a73d728dSAlexander V. Chernikov tc->ocount = 0; 2539a73d728dSAlexander V. Chernikov if (bad != 0) 2540a73d728dSAlexander V. Chernikov continue; 2541a73d728dSAlexander V. Chernikov 2542a73d728dSAlexander V. Chernikov /* Actually change set. */ 2543a73d728dSAlexander V. Chernikov ipfw_objhash_del(ni, no); 2544a73d728dSAlexander V. Chernikov no->set = new_set; 2545a73d728dSAlexander V. Chernikov ipfw_objhash_add(ni, no); 2546a73d728dSAlexander V. Chernikov } 2547a73d728dSAlexander V. Chernikov } 2548a73d728dSAlexander V. Chernikov 2549a73d728dSAlexander V. Chernikov return (bad); 2550a73d728dSAlexander V. Chernikov } 2551a73d728dSAlexander V. Chernikov 2552b074b7bbSAlexander V. Chernikov /* 2553b074b7bbSAlexander V. Chernikov * Compatibility function for old ipfw(8) binaries. 2554b074b7bbSAlexander V. Chernikov * Rewrites table kernel indices with userland ones. 2555b074b7bbSAlexander V. Chernikov * Works for \d+ talbes only (e.g. for tables, converted 2556b074b7bbSAlexander V. Chernikov * from old numbered system calls). 2557b074b7bbSAlexander V. Chernikov * 2558b074b7bbSAlexander V. Chernikov * Returns 0 on success. 2559b074b7bbSAlexander V. Chernikov * Raises error on any other tables. 2560b074b7bbSAlexander V. Chernikov */ 2561b074b7bbSAlexander V. Chernikov int 25627e767c79SAlexander V. Chernikov ipfw_rewrite_table_kidx(struct ip_fw_chain *chain, struct ip_fw_rule0 *rule) 2563b074b7bbSAlexander V. Chernikov { 25641832a7b3SAlexander V. Chernikov int cmdlen, error, l; 2565b074b7bbSAlexander V. Chernikov ipfw_insn *cmd; 25661832a7b3SAlexander V. Chernikov uint16_t kidx, uidx; 2567b074b7bbSAlexander V. Chernikov uint8_t type; 2568b074b7bbSAlexander V. Chernikov struct named_object *no; 2569b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 2570b074b7bbSAlexander V. Chernikov 2571b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(chain); 25721832a7b3SAlexander V. Chernikov error = 0; 2573b074b7bbSAlexander V. Chernikov 2574b074b7bbSAlexander V. Chernikov l = rule->cmd_len; 2575b074b7bbSAlexander V. Chernikov cmd = rule->cmd; 2576b074b7bbSAlexander V. Chernikov cmdlen = 0; 2577b074b7bbSAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 2578b074b7bbSAlexander V. Chernikov cmdlen = F_LEN(cmd); 2579b074b7bbSAlexander V. Chernikov 2580b074b7bbSAlexander V. Chernikov if (classify_table_opcode(cmd, &kidx, &type) != 0) 2581b074b7bbSAlexander V. Chernikov continue; 2582b074b7bbSAlexander V. Chernikov 2583ac35ff17SAlexander V. Chernikov if ((no = ipfw_objhash_lookup_kidx(ni, kidx)) == NULL) 2584b074b7bbSAlexander V. Chernikov return (1); 2585b074b7bbSAlexander V. Chernikov 25861832a7b3SAlexander V. Chernikov uidx = no->uidx; 25871832a7b3SAlexander V. Chernikov if (no->compat == 0) { 2588b074b7bbSAlexander V. Chernikov 25891832a7b3SAlexander V. Chernikov /* 25901832a7b3SAlexander V. Chernikov * We are called via legacy opcode. 25911832a7b3SAlexander V. Chernikov * Save error and show table as fake number 25921832a7b3SAlexander V. Chernikov * not to make ipfw(8) hang. 25931832a7b3SAlexander V. Chernikov */ 25941832a7b3SAlexander V. Chernikov uidx = 65535; 25951832a7b3SAlexander V. Chernikov error = 2; 2596b074b7bbSAlexander V. Chernikov } 2597b074b7bbSAlexander V. Chernikov 25981832a7b3SAlexander V. Chernikov update_table_opcode(cmd, uidx); 25991832a7b3SAlexander V. Chernikov } 26001832a7b3SAlexander V. Chernikov 26011832a7b3SAlexander V. Chernikov return (error); 2602b074b7bbSAlexander V. Chernikov } 2603b074b7bbSAlexander V. Chernikov 2604563b5ab1SAlexander V. Chernikov /* 2605563b5ab1SAlexander V. Chernikov * Sets every table kidx in @bmask which is used in rule @rule. 2606563b5ab1SAlexander V. Chernikov * 2607563b5ab1SAlexander V. Chernikov * Returns number of newly-referenced tables. 2608563b5ab1SAlexander V. Chernikov */ 2609563b5ab1SAlexander V. Chernikov int 2610563b5ab1SAlexander V. Chernikov ipfw_mark_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule, 2611563b5ab1SAlexander V. Chernikov uint32_t *bmask) 2612563b5ab1SAlexander V. Chernikov { 2613563b5ab1SAlexander V. Chernikov int cmdlen, l, count; 2614563b5ab1SAlexander V. Chernikov ipfw_insn *cmd; 2615563b5ab1SAlexander V. Chernikov uint16_t kidx; 2616563b5ab1SAlexander V. Chernikov uint8_t type; 2617563b5ab1SAlexander V. Chernikov 2618563b5ab1SAlexander V. Chernikov l = rule->cmd_len; 2619563b5ab1SAlexander V. Chernikov cmd = rule->cmd; 2620563b5ab1SAlexander V. Chernikov cmdlen = 0; 2621563b5ab1SAlexander V. Chernikov count = 0; 2622563b5ab1SAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 2623563b5ab1SAlexander V. Chernikov cmdlen = F_LEN(cmd); 2624563b5ab1SAlexander V. Chernikov 2625563b5ab1SAlexander V. Chernikov if (classify_table_opcode(cmd, &kidx, &type) != 0) 2626563b5ab1SAlexander V. Chernikov continue; 2627563b5ab1SAlexander V. Chernikov 2628563b5ab1SAlexander V. Chernikov if ((bmask[kidx / 32] & (1 << (kidx % 32))) == 0) 2629563b5ab1SAlexander V. Chernikov count++; 2630563b5ab1SAlexander V. Chernikov 2631563b5ab1SAlexander V. Chernikov bmask[kidx / 32] |= 1 << (kidx % 32); 2632563b5ab1SAlexander V. Chernikov } 2633563b5ab1SAlexander V. Chernikov 2634563b5ab1SAlexander V. Chernikov return (count); 2635563b5ab1SAlexander V. Chernikov } 2636563b5ab1SAlexander V. Chernikov 2637563b5ab1SAlexander V. Chernikov 2638b074b7bbSAlexander V. Chernikov 2639b074b7bbSAlexander V. Chernikov /* 2640b074b7bbSAlexander V. Chernikov * Checks is opcode is referencing table of appropriate type. 2641b074b7bbSAlexander V. Chernikov * Adds reference count for found table if true. 2642b074b7bbSAlexander V. Chernikov * Rewrites user-supplied opcode values with kernel ones. 2643b074b7bbSAlexander V. Chernikov * 2644b074b7bbSAlexander V. Chernikov * Returns 0 on success and appropriate error code otherwise. 2645b074b7bbSAlexander V. Chernikov */ 2646b074b7bbSAlexander V. Chernikov int 2647b074b7bbSAlexander V. Chernikov ipfw_rewrite_table_uidx(struct ip_fw_chain *chain, 2648b074b7bbSAlexander V. Chernikov struct rule_check_info *ci) 2649b074b7bbSAlexander V. Chernikov { 2650b074b7bbSAlexander V. Chernikov int cmdlen, error, ftype, l; 2651b074b7bbSAlexander V. Chernikov ipfw_insn *cmd; 2652b074b7bbSAlexander V. Chernikov uint16_t uidx; 2653b074b7bbSAlexander V. Chernikov uint8_t type; 2654b074b7bbSAlexander V. Chernikov struct table_config *tc; 26559f7d47b0SAlexander V. Chernikov struct table_algo *ta; 2656b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 2657b074b7bbSAlexander V. Chernikov struct named_object *no, *no_n, *no_tmp; 26589490a627SAlexander V. Chernikov struct obj_idx *p, *pidx_first, *pidx_last; 2659b074b7bbSAlexander V. Chernikov struct namedobjects_head nh; 2660b074b7bbSAlexander V. Chernikov struct tid_info ti; 2661b074b7bbSAlexander V. Chernikov 2662b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(chain); 2663b074b7bbSAlexander V. Chernikov 26649490a627SAlexander V. Chernikov /* Prepare queue to store configs */ 26659490a627SAlexander V. Chernikov TAILQ_INIT(&nh); 26669490a627SAlexander V. Chernikov 2667b074b7bbSAlexander V. Chernikov /* 2668b074b7bbSAlexander V. Chernikov * Prepare an array for storing opcode indices. 2669b074b7bbSAlexander V. Chernikov * Use stack allocation by default. 2670b074b7bbSAlexander V. Chernikov */ 2671b074b7bbSAlexander V. Chernikov if (ci->table_opcodes <= (sizeof(ci->obuf)/sizeof(ci->obuf[0]))) { 2672b074b7bbSAlexander V. Chernikov /* Stack */ 26739490a627SAlexander V. Chernikov pidx_first = ci->obuf; 2674b074b7bbSAlexander V. Chernikov } else 26759490a627SAlexander V. Chernikov pidx_first = malloc(ci->table_opcodes * sizeof(struct obj_idx), 2676b074b7bbSAlexander V. Chernikov M_IPFW, M_WAITOK | M_ZERO); 2677b074b7bbSAlexander V. Chernikov 26789490a627SAlexander V. Chernikov pidx_last = pidx_first; 2679b074b7bbSAlexander V. Chernikov error = 0; 2680b074b7bbSAlexander V. Chernikov 2681b074b7bbSAlexander V. Chernikov type = 0; 2682b074b7bbSAlexander V. Chernikov ftype = 0; 2683b074b7bbSAlexander V. Chernikov 2684b074b7bbSAlexander V. Chernikov memset(&ti, 0, sizeof(ti)); 26851832a7b3SAlexander V. Chernikov 26861832a7b3SAlexander V. Chernikov /* 26871832a7b3SAlexander V. Chernikov * Use default set for looking up tables (old way) or 26881832a7b3SAlexander V. Chernikov * use set rule is assigned to (new way). 26891832a7b3SAlexander V. Chernikov */ 26901832a7b3SAlexander V. Chernikov ti.set = (V_fw_tables_sets != 0) ? ci->krule->set : 0; 26916c2997ffSAlexander V. Chernikov if (ci->ctlv != NULL) { 26926c2997ffSAlexander V. Chernikov ti.tlvs = (void *)(ci->ctlv + 1); 26936c2997ffSAlexander V. Chernikov ti.tlen = ci->ctlv->head.length - sizeof(ipfw_obj_ctlv); 26946c2997ffSAlexander V. Chernikov } 2695b074b7bbSAlexander V. Chernikov 2696b074b7bbSAlexander V. Chernikov /* 26979490a627SAlexander V. Chernikov * Stage 1: reference existing tables, determine number 26989490a627SAlexander V. Chernikov * of tables we need to allocate and allocate indexes for each. 2699b074b7bbSAlexander V. Chernikov */ 27009490a627SAlexander V. Chernikov error = bind_table_rule(chain, ci->krule, ci, &pidx_last, &ti); 2701b074b7bbSAlexander V. Chernikov 2702b074b7bbSAlexander V. Chernikov if (error != 0) { 27039490a627SAlexander V. Chernikov if (pidx_first != ci->obuf) 27049490a627SAlexander V. Chernikov free(pidx_first, M_IPFW); 2705b074b7bbSAlexander V. Chernikov 2706b074b7bbSAlexander V. Chernikov return (error); 2707b074b7bbSAlexander V. Chernikov } 2708b074b7bbSAlexander V. Chernikov 2709b074b7bbSAlexander V. Chernikov /* 2710b074b7bbSAlexander V. Chernikov * Stage 2: allocate table configs for every non-existent table 2711b074b7bbSAlexander V. Chernikov */ 2712b074b7bbSAlexander V. Chernikov 27139f7d47b0SAlexander V. Chernikov if (ci->new_tables > 0) { 27149490a627SAlexander V. Chernikov for (p = pidx_first; p < pidx_last; p++) { 2715b074b7bbSAlexander V. Chernikov if (p->new == 0) 2716b074b7bbSAlexander V. Chernikov continue; 2717b074b7bbSAlexander V. Chernikov 2718b074b7bbSAlexander V. Chernikov ti.uidx = p->uidx; 2719b074b7bbSAlexander V. Chernikov ti.type = p->type; 27209f7d47b0SAlexander V. Chernikov ti.atype = 0; 2721b074b7bbSAlexander V. Chernikov 27229490a627SAlexander V. Chernikov ta = find_table_algo(CHAIN_TO_TCFG(chain), &ti, NULL); 27239f7d47b0SAlexander V. Chernikov if (ta == NULL) { 27249f7d47b0SAlexander V. Chernikov error = ENOTSUP; 27259f7d47b0SAlexander V. Chernikov goto free; 27269f7d47b0SAlexander V. Chernikov } 2727914bffb6SAlexander V. Chernikov tc = alloc_table_config(chain, &ti, ta, NULL, 0, 272868394ec8SAlexander V. Chernikov IPFW_VTYPE_U32); 2729b074b7bbSAlexander V. Chernikov 2730b074b7bbSAlexander V. Chernikov if (tc == NULL) { 2731b074b7bbSAlexander V. Chernikov error = ENOMEM; 2732b074b7bbSAlexander V. Chernikov goto free; 2733b074b7bbSAlexander V. Chernikov } 2734b074b7bbSAlexander V. Chernikov 2735b074b7bbSAlexander V. Chernikov tc->no.kidx = p->kidx; 2736b074b7bbSAlexander V. Chernikov tc->no.refcnt = 1; 2737b074b7bbSAlexander V. Chernikov 2738b074b7bbSAlexander V. Chernikov /* Add to list */ 2739b074b7bbSAlexander V. Chernikov TAILQ_INSERT_TAIL(&nh, &tc->no, nn_next); 2740b074b7bbSAlexander V. Chernikov } 2741b074b7bbSAlexander V. Chernikov 2742b074b7bbSAlexander V. Chernikov /* 2743b074b7bbSAlexander V. Chernikov * Stage 2.1: Check if we're going to create 2 tables 2744b074b7bbSAlexander V. Chernikov * with the same name, but different table types. 2745b074b7bbSAlexander V. Chernikov */ 2746b074b7bbSAlexander V. Chernikov TAILQ_FOREACH(no, &nh, nn_next) { 2747b074b7bbSAlexander V. Chernikov TAILQ_FOREACH(no_tmp, &nh, nn_next) { 27489490a627SAlexander V. Chernikov if (ipfw_objhash_same_name(ni, no, no_tmp) == 0) 2749b074b7bbSAlexander V. Chernikov continue; 2750b074b7bbSAlexander V. Chernikov if (no->type != no_tmp->type) { 2751b074b7bbSAlexander V. Chernikov error = EINVAL; 2752b074b7bbSAlexander V. Chernikov goto free; 2753b074b7bbSAlexander V. Chernikov } 2754b074b7bbSAlexander V. Chernikov } 2755b074b7bbSAlexander V. Chernikov } 27569f7d47b0SAlexander V. Chernikov } 2757b074b7bbSAlexander V. Chernikov 27589f7d47b0SAlexander V. Chernikov IPFW_UH_WLOCK(chain); 27599f7d47b0SAlexander V. Chernikov 27609f7d47b0SAlexander V. Chernikov if (ci->new_tables > 0) { 2761b074b7bbSAlexander V. Chernikov /* 2762b074b7bbSAlexander V. Chernikov * Stage 3: link & reference new table configs 2763b074b7bbSAlexander V. Chernikov */ 2764b074b7bbSAlexander V. Chernikov 2765b074b7bbSAlexander V. Chernikov 2766b074b7bbSAlexander V. Chernikov /* 2767b074b7bbSAlexander V. Chernikov * Step 3.1: Check if some tables we need to create have been 2768b074b7bbSAlexander V. Chernikov * already created with different table type. 2769b074b7bbSAlexander V. Chernikov */ 2770b074b7bbSAlexander V. Chernikov 2771b074b7bbSAlexander V. Chernikov error = 0; 2772b074b7bbSAlexander V. Chernikov TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) { 2773b074b7bbSAlexander V. Chernikov no_n = ipfw_objhash_lookup_name(ni, no->set, no->name); 2774b074b7bbSAlexander V. Chernikov if (no_n == NULL) 2775b074b7bbSAlexander V. Chernikov continue; 2776b074b7bbSAlexander V. Chernikov 2777b074b7bbSAlexander V. Chernikov if (no_n->type != no->type) { 2778b074b7bbSAlexander V. Chernikov error = EINVAL; 2779b074b7bbSAlexander V. Chernikov break; 2780b074b7bbSAlexander V. Chernikov } 2781b074b7bbSAlexander V. Chernikov 2782b074b7bbSAlexander V. Chernikov } 2783b074b7bbSAlexander V. Chernikov 2784b074b7bbSAlexander V. Chernikov if (error != 0) { 2785b074b7bbSAlexander V. Chernikov /* 2786b074b7bbSAlexander V. Chernikov * Someone has allocated table with different table type. 2787b074b7bbSAlexander V. Chernikov * We have to rollback everything. 2788b074b7bbSAlexander V. Chernikov */ 2789b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 2790b074b7bbSAlexander V. Chernikov goto free; 2791b074b7bbSAlexander V. Chernikov } 2792b074b7bbSAlexander V. Chernikov 2793b074b7bbSAlexander V. Chernikov /* 27949f7d47b0SAlexander V. Chernikov * Attach new tables. 27959f7d47b0SAlexander V. Chernikov * We need to set table pointers for each new table, 2796b074b7bbSAlexander V. Chernikov * so we have to acquire main WLOCK. 2797b074b7bbSAlexander V. Chernikov */ 2798b074b7bbSAlexander V. Chernikov IPFW_WLOCK(chain); 2799b074b7bbSAlexander V. Chernikov TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) { 2800b074b7bbSAlexander V. Chernikov no_n = ipfw_objhash_lookup_name(ni, no->set, no->name); 2801b074b7bbSAlexander V. Chernikov 28029490a627SAlexander V. Chernikov if (no_n == NULL) { 28039490a627SAlexander V. Chernikov /* New table. Attach to runtime hash */ 28049490a627SAlexander V. Chernikov TAILQ_REMOVE(&nh, no, nn_next); 28059490a627SAlexander V. Chernikov link_table(chain, (struct table_config *)no); 2806b074b7bbSAlexander V. Chernikov continue; 2807b074b7bbSAlexander V. Chernikov } 2808b074b7bbSAlexander V. Chernikov 28099490a627SAlexander V. Chernikov /* 28109490a627SAlexander V. Chernikov * Newly-allocated table with the same type. 28119490a627SAlexander V. Chernikov * Reference it and update out @pidx array 28129490a627SAlexander V. Chernikov * rewrite info. 28139490a627SAlexander V. Chernikov */ 28149490a627SAlexander V. Chernikov no_n->refcnt++; 28159490a627SAlexander V. Chernikov /* Keep oib array in sync: update kidx */ 28169490a627SAlexander V. Chernikov for (p = pidx_first; p < pidx_last; p++) { 28179490a627SAlexander V. Chernikov if (p->kidx != no->kidx) 28189490a627SAlexander V. Chernikov continue; 28199490a627SAlexander V. Chernikov /* Update kidx */ 28209490a627SAlexander V. Chernikov p->kidx = no_n->kidx; 28219490a627SAlexander V. Chernikov break; 28229490a627SAlexander V. Chernikov } 2823b074b7bbSAlexander V. Chernikov } 2824b074b7bbSAlexander V. Chernikov IPFW_WUNLOCK(chain); 28259f7d47b0SAlexander V. Chernikov } 2826b074b7bbSAlexander V. Chernikov 2827b074b7bbSAlexander V. Chernikov /* Perform rule rewrite */ 2828b074b7bbSAlexander V. Chernikov l = ci->krule->cmd_len; 2829b074b7bbSAlexander V. Chernikov cmd = ci->krule->cmd; 2830b074b7bbSAlexander V. Chernikov cmdlen = 0; 28319490a627SAlexander V. Chernikov p = pidx_first; 2832b074b7bbSAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 2833b074b7bbSAlexander V. Chernikov cmdlen = F_LEN(cmd); 2834b074b7bbSAlexander V. Chernikov 2835b074b7bbSAlexander V. Chernikov if (classify_table_opcode(cmd, &uidx, &type) != 0) 2836b074b7bbSAlexander V. Chernikov continue; 28379490a627SAlexander V. Chernikov update_table_opcode(cmd, p->kidx); 28389490a627SAlexander V. Chernikov p++; 2839b074b7bbSAlexander V. Chernikov } 2840b074b7bbSAlexander V. Chernikov 2841b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 2842b074b7bbSAlexander V. Chernikov 2843b074b7bbSAlexander V. Chernikov error = 0; 2844b074b7bbSAlexander V. Chernikov 2845b074b7bbSAlexander V. Chernikov /* 2846b074b7bbSAlexander V. Chernikov * Stage 4: free resources 2847b074b7bbSAlexander V. Chernikov */ 2848b074b7bbSAlexander V. Chernikov free: 28499490a627SAlexander V. Chernikov if (!TAILQ_EMPTY(&nh)) { 28509490a627SAlexander V. Chernikov /* Free indexes first */ 28519490a627SAlexander V. Chernikov IPFW_UH_WLOCK(chain); 28529490a627SAlexander V. Chernikov TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) { 2853ac35ff17SAlexander V. Chernikov ipfw_objhash_free_idx(ni, no->kidx); 28549490a627SAlexander V. Chernikov } 28559490a627SAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 28569490a627SAlexander V. Chernikov /* Free configs */ 2857b074b7bbSAlexander V. Chernikov TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) 2858b074b7bbSAlexander V. Chernikov free_table_config(ni, tc); 28599490a627SAlexander V. Chernikov } 2860b074b7bbSAlexander V. Chernikov 28619490a627SAlexander V. Chernikov if (pidx_first != ci->obuf) 28629490a627SAlexander V. Chernikov free(pidx_first, M_IPFW); 2863b074b7bbSAlexander V. Chernikov 2864b074b7bbSAlexander V. Chernikov return (error); 2865b074b7bbSAlexander V. Chernikov } 2866b074b7bbSAlexander V. Chernikov 2867b074b7bbSAlexander V. Chernikov /* 2868b074b7bbSAlexander V. Chernikov * Remove references from every table used in @rule. 2869b074b7bbSAlexander V. Chernikov */ 2870b074b7bbSAlexander V. Chernikov void 2871b074b7bbSAlexander V. Chernikov ipfw_unbind_table_rule(struct ip_fw_chain *chain, struct ip_fw *rule) 2872b074b7bbSAlexander V. Chernikov { 2873b074b7bbSAlexander V. Chernikov int cmdlen, l; 2874b074b7bbSAlexander V. Chernikov ipfw_insn *cmd; 2875b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 2876b074b7bbSAlexander V. Chernikov struct named_object *no; 2877b074b7bbSAlexander V. Chernikov uint16_t kidx; 2878b074b7bbSAlexander V. Chernikov uint8_t type; 2879b074b7bbSAlexander V. Chernikov 2880b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(chain); 2881b074b7bbSAlexander V. Chernikov 2882b074b7bbSAlexander V. Chernikov l = rule->cmd_len; 2883b074b7bbSAlexander V. Chernikov cmd = rule->cmd; 2884b074b7bbSAlexander V. Chernikov cmdlen = 0; 2885b074b7bbSAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 2886b074b7bbSAlexander V. Chernikov cmdlen = F_LEN(cmd); 2887b074b7bbSAlexander V. Chernikov 2888b074b7bbSAlexander V. Chernikov if (classify_table_opcode(cmd, &kidx, &type) != 0) 2889b074b7bbSAlexander V. Chernikov continue; 2890b074b7bbSAlexander V. Chernikov 2891ac35ff17SAlexander V. Chernikov no = ipfw_objhash_lookup_kidx(ni, kidx); 2892b074b7bbSAlexander V. Chernikov 2893b074b7bbSAlexander V. Chernikov KASSERT(no != NULL, ("table id %d not found", kidx)); 2894b074b7bbSAlexander V. Chernikov KASSERT(no->type == type, ("wrong type %d (%d) for table id %d", 2895b074b7bbSAlexander V. Chernikov no->type, type, kidx)); 2896b074b7bbSAlexander V. Chernikov KASSERT(no->refcnt > 0, ("refcount for table %d is %d", 2897b074b7bbSAlexander V. Chernikov kidx, no->refcnt)); 2898b074b7bbSAlexander V. Chernikov 2899b074b7bbSAlexander V. Chernikov no->refcnt--; 2900b074b7bbSAlexander V. Chernikov } 2901b074b7bbSAlexander V. Chernikov } 2902b074b7bbSAlexander V. Chernikov 2903b074b7bbSAlexander V. Chernikov 2904b074b7bbSAlexander V. Chernikov /* 2905b074b7bbSAlexander V. Chernikov * Removes table bindings for every rule in rule chain @head. 2906b074b7bbSAlexander V. Chernikov */ 2907b074b7bbSAlexander V. Chernikov void 2908b074b7bbSAlexander V. Chernikov ipfw_unbind_table_list(struct ip_fw_chain *chain, struct ip_fw *head) 2909b074b7bbSAlexander V. Chernikov { 2910b074b7bbSAlexander V. Chernikov struct ip_fw *rule; 2911b074b7bbSAlexander V. Chernikov 2912b074b7bbSAlexander V. Chernikov while ((rule = head) != NULL) { 2913b074b7bbSAlexander V. Chernikov head = head->x_next; 2914b074b7bbSAlexander V. Chernikov ipfw_unbind_table_rule(chain, rule); 2915b074b7bbSAlexander V. Chernikov } 2916b074b7bbSAlexander V. Chernikov } 2917b074b7bbSAlexander V. Chernikov 2918b074b7bbSAlexander V. Chernikov 29193b3a8eb9SGleb Smirnoff /* end of file */ 2920