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 /* 62b074b7bbSAlexander V. Chernikov * Table has the following `type` concepts: 63b074b7bbSAlexander V. Chernikov * 649f7d47b0SAlexander V. Chernikov * `no.type` represents lookup key type (cidr, ifp, uid, etc..) 6535e1bbd0SAlexander V. Chernikov * `vtype` represents table value type (currently U32) 669490a627SAlexander V. Chernikov * `ftype` (at the moment )is pure userland field helping to properly 679490a627SAlexander V. Chernikov * format value data e.g. "value is IPv4 nexthop" or "value is DSCP" 689490a627SAlexander V. Chernikov * or "value is port". 69b074b7bbSAlexander V. Chernikov * 70b074b7bbSAlexander V. Chernikov */ 71b074b7bbSAlexander V. Chernikov struct table_config { 72b074b7bbSAlexander V. Chernikov struct named_object no; 73adf3b2b9SAlexander V. Chernikov uint8_t vtype; /* value type */ 74adf3b2b9SAlexander V. Chernikov uint8_t vftype; /* value format type */ 75914bffb6SAlexander V. Chernikov uint8_t tflags; /* type flags */ 764f43138aSAlexander V. Chernikov uint8_t locked; /* 1 if locked from changes */ 77b074b7bbSAlexander V. Chernikov uint32_t count; /* Number of records */ 784c0c07a5SAlexander V. Chernikov uint32_t limit; /* Max number of records */ 79adf3b2b9SAlexander V. Chernikov uint8_t linked; /* 1 if already linked */ 80adf3b2b9SAlexander V. Chernikov uint8_t ochanged; /* used by set swapping */ 81adf3b2b9SAlexander V. Chernikov uint16_t spare1; 82adf3b2b9SAlexander V. Chernikov uint32_t spare2; 83a73d728dSAlexander V. Chernikov uint32_t ocount; /* used by set swapping */ 84a73d728dSAlexander V. Chernikov uint64_t gencnt; /* generation count */ 85b074b7bbSAlexander V. Chernikov char tablename[64]; /* table name */ 869f7d47b0SAlexander V. Chernikov struct table_algo *ta; /* Callbacks for given algo */ 879f7d47b0SAlexander V. Chernikov void *astate; /* algorithm state */ 889f7d47b0SAlexander V. Chernikov struct table_info ti; /* data to put to table_info */ 89b074b7bbSAlexander V. Chernikov }; 90b074b7bbSAlexander V. Chernikov 91b074b7bbSAlexander V. Chernikov struct tables_config { 92b074b7bbSAlexander V. Chernikov struct namedobj_instance *namehash; 939f7d47b0SAlexander V. Chernikov int algo_count; 949f7d47b0SAlexander V. Chernikov struct table_algo *algo[256]; 9557a1cf95SAlexander V. Chernikov struct table_algo *def_algo[IPFW_TABLE_MAXTYPE + 1]; 96b074b7bbSAlexander V. Chernikov }; 97b074b7bbSAlexander V. Chernikov 98b074b7bbSAlexander V. Chernikov static struct table_config *find_table(struct namedobj_instance *ni, 99b074b7bbSAlexander V. Chernikov struct tid_info *ti); 10068394ec8SAlexander V. Chernikov static struct table_config *alloc_table_config(struct ip_fw_chain *ch, 101914bffb6SAlexander V. Chernikov struct tid_info *ti, struct table_algo *ta, char *adata, uint8_t tflags, 102914bffb6SAlexander V. Chernikov uint8_t vtype); 103b074b7bbSAlexander V. Chernikov static void free_table_config(struct namedobj_instance *ni, 104b074b7bbSAlexander V. Chernikov struct table_config *tc); 105db785d31SAlexander V. Chernikov static int create_table_internal(struct ip_fw_chain *ch, struct tid_info *ti, 1060468c5baSAlexander V. Chernikov char *aname, ipfw_xtable_info *i, struct table_config **ptc, 1070468c5baSAlexander V. Chernikov struct table_algo **pta, uint16_t *pkidx, int ref); 1080468c5baSAlexander V. Chernikov static void link_table(struct ip_fw_chain *ch, struct table_config *tc); 1090468c5baSAlexander V. Chernikov static void unlink_table(struct ip_fw_chain *ch, struct table_config *tc); 110b074b7bbSAlexander V. Chernikov static void free_table_state(void **state, void **xstate, uint8_t type); 1112d99a349SAlexander V. Chernikov static int export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh, 1122d99a349SAlexander V. Chernikov struct sockopt_data *sd); 113ac35ff17SAlexander V. Chernikov static void export_table_info(struct ip_fw_chain *ch, struct table_config *tc, 114ac35ff17SAlexander V. Chernikov ipfw_xtable_info *i); 11581d3153dSAlexander V. Chernikov static int dump_table_tentry(void *e, void *arg); 116f1220db8SAlexander V. Chernikov static int dump_table_xentry(void *e, void *arg); 117b074b7bbSAlexander V. Chernikov 1182d99a349SAlexander V. Chernikov static int ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt_data *sd); 1192d99a349SAlexander V. Chernikov static int ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt_data *sd); 120db785d31SAlexander V. Chernikov static int ipfw_manage_table_ent_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 121ac35ff17SAlexander V. Chernikov struct sockopt_data *sd); 122db785d31SAlexander V. Chernikov static int ipfw_manage_table_ent_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 123ac35ff17SAlexander V. Chernikov struct sockopt_data *sd); 124a73d728dSAlexander V. Chernikov static int swap_tables(struct ip_fw_chain *ch, struct tid_info *a, 12546d52008SAlexander V. Chernikov struct tid_info *b); 126ac35ff17SAlexander V. Chernikov 127b6ee846eSAlexander V. Chernikov static int check_table_space(struct ip_fw_chain *ch, struct table_config *tc, 128b6ee846eSAlexander V. Chernikov struct table_info *ti, uint32_t count); 129ac35ff17SAlexander V. Chernikov static int destroy_table(struct ip_fw_chain *ch, struct tid_info *ti); 130d3a4f924SAlexander V. Chernikov 1319f7d47b0SAlexander V. Chernikov static struct table_algo *find_table_algo(struct tables_config *tableconf, 1329490a627SAlexander V. Chernikov struct tid_info *ti, char *name); 133b074b7bbSAlexander V. Chernikov 13446d52008SAlexander V. Chernikov static void objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti); 13546d52008SAlexander V. Chernikov static void ntlv_to_ti(struct _ipfw_obj_ntlv *ntlv, struct tid_info *ti); 136a73d728dSAlexander V. Chernikov static int classify_table_opcode(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype); 13746d52008SAlexander V. Chernikov 138b074b7bbSAlexander V. Chernikov #define CHAIN_TO_TCFG(chain) ((struct tables_config *)(chain)->tblcfg) 139b074b7bbSAlexander V. Chernikov #define CHAIN_TO_NI(chain) (CHAIN_TO_TCFG(chain)->namehash) 1409f7d47b0SAlexander V. Chernikov #define KIDX_TO_TI(ch, k) (&(((struct table_info *)(ch)->tablestate)[k])) 141b074b7bbSAlexander V. Chernikov 142b6ee846eSAlexander V. Chernikov #define TA_BUF_SZ 128 /* On-stack buffer for add/delete state */ 143b6ee846eSAlexander V. Chernikov 1443a845e10SAlexander V. Chernikov /* 1453a845e10SAlexander V. Chernikov * Checks if we're able to insert/update entry @tei into table 1463a845e10SAlexander V. Chernikov * w.r.t @tc limits. 1473a845e10SAlexander V. Chernikov * May alter @tei to indicate insertion error / insert 1483a845e10SAlexander V. Chernikov * options. 1493a845e10SAlexander V. Chernikov * 1503a845e10SAlexander V. Chernikov * Returns 0 if operation can be performed/ 1513a845e10SAlexander V. Chernikov */ 1523a845e10SAlexander V. Chernikov static int 1533a845e10SAlexander V. Chernikov check_table_limit(struct table_config *tc, struct tentry_info *tei) 1543a845e10SAlexander V. Chernikov { 155b074b7bbSAlexander V. Chernikov 1563a845e10SAlexander V. Chernikov if (tc->limit == 0 || tc->count < tc->limit) 1573a845e10SAlexander V. Chernikov return (0); 1583a845e10SAlexander V. Chernikov 1593a845e10SAlexander V. Chernikov if ((tei->flags & TEI_FLAGS_UPDATE) == 0) { 1603a845e10SAlexander V. Chernikov /* Notify userland on error cause */ 1613a845e10SAlexander V. Chernikov tei->flags |= TEI_FLAGS_LIMIT; 1623a845e10SAlexander V. Chernikov return (EFBIG); 1633a845e10SAlexander V. Chernikov } 1643a845e10SAlexander V. Chernikov 1653a845e10SAlexander V. Chernikov /* 1663a845e10SAlexander V. Chernikov * We have UPDATE flag set. 1673a845e10SAlexander V. Chernikov * Permit updating record (if found), 1683a845e10SAlexander V. Chernikov * but restrict adding new one since we've 1693a845e10SAlexander V. Chernikov * already hit the limit. 1703a845e10SAlexander V. Chernikov */ 1713a845e10SAlexander V. Chernikov tei->flags |= TEI_FLAGS_DONTADD; 1723a845e10SAlexander V. Chernikov 1733a845e10SAlexander V. Chernikov return (0); 1743a845e10SAlexander V. Chernikov } 1753a845e10SAlexander V. Chernikov 1763a845e10SAlexander V. Chernikov /* 1771bc0d457SAlexander V. Chernikov * Convert algorithm callback return code into 1781bc0d457SAlexander V. Chernikov * one of pre-defined states known by userland. 1793a845e10SAlexander V. Chernikov */ 1801bc0d457SAlexander V. Chernikov static void 1811bc0d457SAlexander V. Chernikov store_tei_result(struct tentry_info *tei, int do_add, int error, uint32_t num) 1823b3a8eb9SGleb Smirnoff { 1831bc0d457SAlexander V. Chernikov int flag; 1843b3a8eb9SGleb Smirnoff 1851bc0d457SAlexander V. Chernikov flag = 0; 1861bc0d457SAlexander V. Chernikov 1871bc0d457SAlexander V. Chernikov switch (error) { 1881bc0d457SAlexander V. Chernikov case 0: 1891bc0d457SAlexander V. Chernikov if (do_add && num != 0) 1901bc0d457SAlexander V. Chernikov flag = TEI_FLAGS_ADDED; 1911bc0d457SAlexander V. Chernikov if (do_add == 0) 1921bc0d457SAlexander V. Chernikov flag = TEI_FLAGS_DELETED; 1931bc0d457SAlexander V. Chernikov break; 1941bc0d457SAlexander V. Chernikov case ENOENT: 1951bc0d457SAlexander V. Chernikov flag = TEI_FLAGS_NOTFOUND; 1961bc0d457SAlexander V. Chernikov break; 1971bc0d457SAlexander V. Chernikov case EEXIST: 1981bc0d457SAlexander V. Chernikov flag = TEI_FLAGS_EXISTS; 1991bc0d457SAlexander V. Chernikov break; 2001bc0d457SAlexander V. Chernikov default: 2011bc0d457SAlexander V. Chernikov flag = TEI_FLAGS_ERROR; 2021bc0d457SAlexander V. Chernikov } 2031bc0d457SAlexander V. Chernikov 2041bc0d457SAlexander V. Chernikov tei->flags |= flag; 2051bc0d457SAlexander V. Chernikov } 2069f7d47b0SAlexander V. Chernikov 2070468c5baSAlexander V. Chernikov static int 2080468c5baSAlexander V. Chernikov create_table_compat(struct ip_fw_chain *ch, struct tid_info *ti, 2090468c5baSAlexander V. Chernikov struct table_config **ptc, struct table_algo **pta, uint16_t *pkidx) 2100468c5baSAlexander V. Chernikov { 2110468c5baSAlexander V. Chernikov ipfw_xtable_info xi; 2120468c5baSAlexander V. Chernikov int error; 2130468c5baSAlexander V. Chernikov 2140468c5baSAlexander V. Chernikov memset(&xi, 0, sizeof(xi)); 2150468c5baSAlexander V. Chernikov xi.vtype = IPFW_VTYPE_U32; 2160468c5baSAlexander V. Chernikov 2170468c5baSAlexander V. Chernikov error = create_table_internal(ch, ti, NULL, &xi, ptc, pta, pkidx, 1); 2180468c5baSAlexander V. Chernikov if (error != 0) 2190468c5baSAlexander V. Chernikov return (error); 2200468c5baSAlexander V. Chernikov 2210468c5baSAlexander V. Chernikov return (0); 2220468c5baSAlexander V. Chernikov } 2230468c5baSAlexander V. Chernikov 2249f7d47b0SAlexander V. Chernikov /* 2251bc0d457SAlexander V. Chernikov * Find and reference existing table optionally 2261bc0d457SAlexander V. Chernikov * creating new one. 2271bc0d457SAlexander V. Chernikov * 2281bc0d457SAlexander V. Chernikov * Saves found table config/table algo into @ptc / @pta. 2291bc0d457SAlexander V. Chernikov * Returns 0 if table was found/created and referenced 2301bc0d457SAlexander V. Chernikov * or non-zero return code. 2319f7d47b0SAlexander V. Chernikov */ 2321bc0d457SAlexander V. Chernikov static int 2331bc0d457SAlexander V. Chernikov find_ref_table(struct ip_fw_chain *ch, struct tid_info *ti, 2341bc0d457SAlexander V. Chernikov struct tentry_info *tei, uint32_t count, int do_add, 2351bc0d457SAlexander V. Chernikov struct table_config **ptc, struct table_algo **pta) 2361bc0d457SAlexander V. Chernikov { 2371bc0d457SAlexander V. Chernikov struct namedobj_instance *ni; 2381bc0d457SAlexander V. Chernikov struct table_config *tc; 2391bc0d457SAlexander V. Chernikov struct table_algo *ta; 2401bc0d457SAlexander V. Chernikov int error; 2411bc0d457SAlexander V. Chernikov 2421bc0d457SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 2431bc0d457SAlexander V. Chernikov 2441bc0d457SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 2451bc0d457SAlexander V. Chernikov tc = NULL; 2469f7d47b0SAlexander V. Chernikov ta = NULL; 2479f7d47b0SAlexander V. Chernikov if ((tc = find_table(ni, ti)) != NULL) { 2489f7d47b0SAlexander V. Chernikov /* check table type */ 2499f7d47b0SAlexander V. Chernikov if (tc->no.type != ti->type) { 2509f7d47b0SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 2513b3a8eb9SGleb Smirnoff return (EINVAL); 2523b3a8eb9SGleb Smirnoff } 2533b3a8eb9SGleb Smirnoff 2544f43138aSAlexander V. Chernikov if (tc->locked != 0) { 2554f43138aSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 2564f43138aSAlexander V. Chernikov return (EACCES); 2574f43138aSAlexander V. Chernikov } 2584f43138aSAlexander V. Chernikov 2594c0c07a5SAlexander V. Chernikov /* Try to exit early on limit hit */ 2601bc0d457SAlexander V. Chernikov if (do_add != 0 && count == 1 && 2611bc0d457SAlexander V. Chernikov check_table_limit(tc, tei) != 0) { 2624c0c07a5SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 2634c0c07a5SAlexander V. Chernikov return (EFBIG); 2644c0c07a5SAlexander V. Chernikov } 2654c0c07a5SAlexander V. Chernikov 2669f7d47b0SAlexander V. Chernikov /* Reference and unlock */ 2679f7d47b0SAlexander V. Chernikov tc->no.refcnt++; 2689f7d47b0SAlexander V. Chernikov ta = tc->ta; 2699f7d47b0SAlexander V. Chernikov } 2709f7d47b0SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 2713b3a8eb9SGleb Smirnoff 272ac35ff17SAlexander V. Chernikov if (tc == NULL) { 2731bc0d457SAlexander V. Chernikov if (do_add == 0) 2741bc0d457SAlexander V. Chernikov return (ESRCH); 2751bc0d457SAlexander V. Chernikov 276db785d31SAlexander V. Chernikov /* Compability mode: create new table for old clients */ 277db785d31SAlexander V. Chernikov if ((tei->flags & TEI_FLAGS_COMPAT) == 0) 278db785d31SAlexander V. Chernikov return (ESRCH); 2793b3a8eb9SGleb Smirnoff 2800468c5baSAlexander V. Chernikov error = create_table_compat(ch, ti, &tc, &ta, NULL); 281db785d31SAlexander V. Chernikov if (error != 0) 282db785d31SAlexander V. Chernikov return (error); 283db785d31SAlexander V. Chernikov 2840468c5baSAlexander V. Chernikov /* OK, now we've got referenced table. */ 285db785d31SAlexander V. Chernikov } 286db785d31SAlexander V. Chernikov 2871bc0d457SAlexander V. Chernikov *ptc = tc; 2881bc0d457SAlexander V. Chernikov *pta = ta; 2891bc0d457SAlexander V. Chernikov return (0); 2901bc0d457SAlexander V. Chernikov } 2911bc0d457SAlexander V. Chernikov 2921bc0d457SAlexander V. Chernikov /* 2931bc0d457SAlexander V. Chernikov * Rolls back already @added to @tc entries using state arrat @ta_buf_m. 2941bc0d457SAlexander V. Chernikov * Assume the following layout: 2951bc0d457SAlexander V. Chernikov * 1) ADD state (ta_buf_m[0] ... t_buf_m[added - 1]) for handling update cases 2961bc0d457SAlexander V. Chernikov * 2) DEL state (ta_buf_m[count[ ... t_buf_m[count + added - 1]) 2971bc0d457SAlexander V. Chernikov * for storing deleted state 2981bc0d457SAlexander V. Chernikov */ 2991bc0d457SAlexander V. Chernikov static void 3001bc0d457SAlexander V. Chernikov rollback_added_entries(struct ip_fw_chain *ch, struct table_config *tc, 3011bc0d457SAlexander V. Chernikov struct table_info *tinfo, struct tentry_info *tei, caddr_t ta_buf_m, 3021bc0d457SAlexander V. Chernikov uint32_t count, uint32_t added) 3031bc0d457SAlexander V. Chernikov { 3041bc0d457SAlexander V. Chernikov struct table_algo *ta; 3051bc0d457SAlexander V. Chernikov struct tentry_info *ptei; 3061bc0d457SAlexander V. Chernikov caddr_t v, vv; 3071bc0d457SAlexander V. Chernikov size_t ta_buf_sz; 3081bc0d457SAlexander V. Chernikov int error, i; 3091bc0d457SAlexander V. Chernikov uint32_t num; 3101bc0d457SAlexander V. Chernikov 3111bc0d457SAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(ch); 3121bc0d457SAlexander V. Chernikov 3131bc0d457SAlexander V. Chernikov ta = tc->ta; 3143a845e10SAlexander V. Chernikov ta_buf_sz = ta->ta_buf_size; 3151bc0d457SAlexander V. Chernikov v = ta_buf_m; 3161bc0d457SAlexander V. Chernikov vv = v + count * ta_buf_sz; 3171bc0d457SAlexander V. Chernikov for (i = 0; i < added; i++, v += ta_buf_sz, vv += ta_buf_sz) { 3181bc0d457SAlexander V. Chernikov ptei = &tei[i]; 3191bc0d457SAlexander V. Chernikov if ((ptei->flags & TEI_FLAGS_UPDATED) != 0) { 3201bc0d457SAlexander V. Chernikov 3211bc0d457SAlexander V. Chernikov /* 3221bc0d457SAlexander V. Chernikov * We have old value stored by previous 3231bc0d457SAlexander V. Chernikov * call in @ptei->value. Do add once again 3241bc0d457SAlexander V. Chernikov * to restore it. 3251bc0d457SAlexander V. Chernikov */ 3261bc0d457SAlexander V. Chernikov error = ta->add(tc->astate, tinfo, ptei, v, &num); 3271bc0d457SAlexander V. Chernikov KASSERT(error == 0, ("rollback UPDATE fail")); 3281bc0d457SAlexander V. Chernikov KASSERT(num == 0, ("rollback UPDATE fail2")); 3291bc0d457SAlexander V. Chernikov continue; 3301bc0d457SAlexander V. Chernikov } 3311bc0d457SAlexander V. Chernikov 3321bc0d457SAlexander V. Chernikov error = ta->prepare_del(ch, ptei, vv); 3331bc0d457SAlexander V. Chernikov KASSERT(error == 0, ("pre-rollback INSERT failed")); 3341bc0d457SAlexander V. Chernikov error = ta->del(tc->astate, tinfo, ptei, vv, &num); 3351bc0d457SAlexander V. Chernikov KASSERT(error == 0, ("rollback INSERT failed")); 3361bc0d457SAlexander V. Chernikov tc->count -= num; 3371bc0d457SAlexander V. Chernikov } 3381bc0d457SAlexander V. Chernikov } 3391bc0d457SAlexander V. Chernikov 3401bc0d457SAlexander V. Chernikov /* 3411bc0d457SAlexander V. Chernikov * Prepares add/del state for all @count entries in @tei. 3421bc0d457SAlexander V. Chernikov * Uses either stack buffer (@ta_buf) or allocates a new one. 3431bc0d457SAlexander V. Chernikov * Stores pointer to allocated buffer back to @ta_buf. 3441bc0d457SAlexander V. Chernikov * 3451bc0d457SAlexander V. Chernikov * Returns 0 on success. 3461bc0d457SAlexander V. Chernikov */ 3471bc0d457SAlexander V. Chernikov static int 3481bc0d457SAlexander V. Chernikov prepare_batch_buffer(struct ip_fw_chain *ch, struct table_algo *ta, 3491bc0d457SAlexander V. Chernikov struct tentry_info *tei, uint32_t count, int do_add, caddr_t *ta_buf) 3501bc0d457SAlexander V. Chernikov { 3511bc0d457SAlexander V. Chernikov caddr_t ta_buf_m, v; 3521bc0d457SAlexander V. Chernikov size_t ta_buf_sz, sz; 3531bc0d457SAlexander V. Chernikov struct tentry_info *ptei; 3541bc0d457SAlexander V. Chernikov int error, i; 3551bc0d457SAlexander V. Chernikov 3561bc0d457SAlexander V. Chernikov error = 0; 3571bc0d457SAlexander V. Chernikov ta_buf_sz = ta->ta_buf_size; 3583a845e10SAlexander V. Chernikov if (count == 1) { 3591bc0d457SAlexander V. Chernikov /* Sigle add/delete, use on-stack buffer */ 3601bc0d457SAlexander V. Chernikov memset(*ta_buf, 0, TA_BUF_SZ); 3611bc0d457SAlexander V. Chernikov ta_buf_m = *ta_buf; 3623a845e10SAlexander V. Chernikov } else { 3633a845e10SAlexander V. Chernikov 3643a845e10SAlexander V. Chernikov /* 3651bc0d457SAlexander V. Chernikov * Multiple adds/deletes, allocate larger buffer 3661bc0d457SAlexander V. Chernikov * 3671bc0d457SAlexander V. Chernikov * Note we need 2xcount buffer for add case: 3681bc0d457SAlexander V. Chernikov * we have hold both ADD state 3693a845e10SAlexander V. Chernikov * and DELETE state (this may be needed 3703a845e10SAlexander V. Chernikov * if we need to rollback all changes) 3713a845e10SAlexander V. Chernikov */ 3721bc0d457SAlexander V. Chernikov sz = count * ta_buf_sz; 3731bc0d457SAlexander V. Chernikov ta_buf_m = malloc((do_add != 0) ? sz * 2 : sz, M_TEMP, 3743a845e10SAlexander V. Chernikov M_WAITOK | M_ZERO); 3753a845e10SAlexander V. Chernikov } 3761bc0d457SAlexander V. Chernikov 3773a845e10SAlexander V. Chernikov v = ta_buf_m; 3783a845e10SAlexander V. Chernikov for (i = 0; i < count; i++, v += ta_buf_sz) { 3791bc0d457SAlexander V. Chernikov ptei = &tei[i]; 3801bc0d457SAlexander V. Chernikov error = (do_add != 0) ? 3811bc0d457SAlexander V. Chernikov ta->prepare_add(ch, ptei, v) : ta->prepare_del(ch, ptei, v); 3823a845e10SAlexander V. Chernikov 3833a845e10SAlexander V. Chernikov /* 3843a845e10SAlexander V. Chernikov * Some syntax error (incorrect mask, or address, or 3853a845e10SAlexander V. Chernikov * anything). Return error regardless of atomicity 3863a845e10SAlexander V. Chernikov * settings. 3873a845e10SAlexander V. Chernikov */ 388db785d31SAlexander V. Chernikov if (error != 0) 3891bc0d457SAlexander V. Chernikov break; 3903a845e10SAlexander V. Chernikov } 3913b3a8eb9SGleb Smirnoff 3921bc0d457SAlexander V. Chernikov *ta_buf = ta_buf_m; 3931bc0d457SAlexander V. Chernikov return (error); 3941bc0d457SAlexander V. Chernikov } 3951bc0d457SAlexander V. Chernikov 3961bc0d457SAlexander V. Chernikov /* 3971bc0d457SAlexander V. Chernikov * Flushes allocated state for each @count entries in @tei. 3981bc0d457SAlexander V. Chernikov * Frees @ta_buf_m if differs from stack buffer @ta_buf. 3991bc0d457SAlexander V. Chernikov */ 4001bc0d457SAlexander V. Chernikov static void 4011bc0d457SAlexander V. Chernikov flush_batch_buffer(struct ip_fw_chain *ch, struct table_algo *ta, 4021bc0d457SAlexander V. Chernikov struct tentry_info *tei, uint32_t count, int do_add, int rollback, 4031bc0d457SAlexander V. Chernikov caddr_t ta_buf_m, caddr_t ta_buf) 4041bc0d457SAlexander V. Chernikov { 4051bc0d457SAlexander V. Chernikov caddr_t v; 4061bc0d457SAlexander V. Chernikov size_t ta_buf_sz; 4071bc0d457SAlexander V. Chernikov int i; 4081bc0d457SAlexander V. Chernikov 4091bc0d457SAlexander V. Chernikov ta_buf_sz = ta->ta_buf_size; 4101bc0d457SAlexander V. Chernikov 4111bc0d457SAlexander V. Chernikov /* Run cleaning callback anyway */ 4121bc0d457SAlexander V. Chernikov v = ta_buf_m; 4131bc0d457SAlexander V. Chernikov for (i = 0; i < count; i++, v += ta_buf_sz) 4141bc0d457SAlexander V. Chernikov ta->flush_entry(ch, &tei[i], v); 4151bc0d457SAlexander V. Chernikov 4161bc0d457SAlexander V. Chernikov /* Clean up "deleted" state in case of rollback */ 4171bc0d457SAlexander V. Chernikov if (rollback != 0) { 4181bc0d457SAlexander V. Chernikov v = ta_buf_m + count * ta_buf_sz; 4191bc0d457SAlexander V. Chernikov for (i = 0; i < count; i++, v += ta_buf_sz) 4201bc0d457SAlexander V. Chernikov ta->flush_entry(ch, &tei[i], v); 4211bc0d457SAlexander V. Chernikov } 4221bc0d457SAlexander V. Chernikov 4231bc0d457SAlexander V. Chernikov if (ta_buf_m != ta_buf) 4241bc0d457SAlexander V. Chernikov free(ta_buf_m, M_TEMP); 4251bc0d457SAlexander V. Chernikov } 4261bc0d457SAlexander V. Chernikov 4271bc0d457SAlexander V. Chernikov /* 4281bc0d457SAlexander V. Chernikov * Adds/updates one or more entries in table @ti. 4290468c5baSAlexander V. Chernikov * Function references @ti first to ensure table won't 4300468c5baSAlexander V. Chernikov * disappear or change its type. 4310468c5baSAlexander V. Chernikov * After that, prepare_add callback is called for each @tei entry. 4320468c5baSAlexander V. Chernikov * Next, we try to add each entry under UH+WHLOCK 4330468c5baSAlexander V. Chernikov * using add() callback. 4340468c5baSAlexander V. Chernikov * Finally, we free all state by calling flush_entry callback 4350468c5baSAlexander V. Chernikov * for each @tei. 4361bc0d457SAlexander V. Chernikov * 4371bc0d457SAlexander V. Chernikov * Returns 0 on success. 4381bc0d457SAlexander V. Chernikov */ 4391bc0d457SAlexander V. Chernikov int 4401bc0d457SAlexander V. Chernikov add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti, 4411bc0d457SAlexander V. Chernikov struct tentry_info *tei, uint8_t flags, uint32_t count) 4421bc0d457SAlexander V. Chernikov { 4431bc0d457SAlexander V. Chernikov struct table_config *tc; 4441bc0d457SAlexander V. Chernikov struct table_algo *ta; 4451bc0d457SAlexander V. Chernikov uint16_t kidx; 4461bc0d457SAlexander V. Chernikov int error, first_error, i, rollback; 4471bc0d457SAlexander V. Chernikov uint32_t num, numadd; 4481bc0d457SAlexander V. Chernikov struct tentry_info *ptei; 4491bc0d457SAlexander V. Chernikov char ta_buf[TA_BUF_SZ]; 4501bc0d457SAlexander V. Chernikov caddr_t ta_buf_m, v; 4511bc0d457SAlexander V. Chernikov 4521bc0d457SAlexander V. Chernikov /* 4531bc0d457SAlexander V. Chernikov * Find and reference existing table. 4541bc0d457SAlexander V. Chernikov */ 4551bc0d457SAlexander V. Chernikov if ((error = find_ref_table(ch, ti, tei, count, 1, &tc, &ta)) != 0) 4561bc0d457SAlexander V. Chernikov return (error); 4571bc0d457SAlexander V. Chernikov 4581bc0d457SAlexander V. Chernikov /* Allocate memory and prepare record(s) */ 4591bc0d457SAlexander V. Chernikov rollback = 0; 4601bc0d457SAlexander V. Chernikov /* Pass stack buffer by default */ 4611bc0d457SAlexander V. Chernikov ta_buf_m = ta_buf; 4621bc0d457SAlexander V. Chernikov error = prepare_batch_buffer(ch, ta, tei, count, 1, &ta_buf_m); 4631bc0d457SAlexander V. Chernikov if (error != 0) 4641bc0d457SAlexander V. Chernikov goto cleanup; 4651bc0d457SAlexander V. Chernikov 466b074b7bbSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 4673b3a8eb9SGleb Smirnoff 468b6ee846eSAlexander V. Chernikov /* 469b6ee846eSAlexander V. Chernikov * Ensure we are able to add all entries without additional 470b6ee846eSAlexander V. Chernikov * memory allocations. May release/reacquire UH_WLOCK. 471b6ee846eSAlexander V. Chernikov */ 472b6ee846eSAlexander V. Chernikov kidx = tc->no.kidx; 473b6ee846eSAlexander V. Chernikov error = check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), count); 474b6ee846eSAlexander V. Chernikov if (error != 0) { 475b6ee846eSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 4763a845e10SAlexander V. Chernikov goto cleanup; 477b6ee846eSAlexander V. Chernikov } 478b6ee846eSAlexander V. Chernikov 4799f7d47b0SAlexander V. Chernikov /* Drop reference we've used in first search */ 4809f7d47b0SAlexander V. Chernikov tc->no.refcnt--; 4813a845e10SAlexander V. Chernikov /* We've got valid table in @tc. Let's try to add data */ 4829f7d47b0SAlexander V. Chernikov kidx = tc->no.kidx; 4839f7d47b0SAlexander V. Chernikov ta = tc->ta; 4843a845e10SAlexander V. Chernikov numadd = 0; 4853a845e10SAlexander V. Chernikov first_error = 0; 4869f7d47b0SAlexander V. Chernikov 487b074b7bbSAlexander V. Chernikov IPFW_WLOCK(ch); 4883a845e10SAlexander V. Chernikov 4893a845e10SAlexander V. Chernikov v = ta_buf_m; 4901bc0d457SAlexander V. Chernikov for (i = 0; i < count; i++, v += ta->ta_buf_size) { 4913a845e10SAlexander V. Chernikov ptei = &tei[i]; 4923a845e10SAlexander V. Chernikov num = 0; 4933a845e10SAlexander V. Chernikov /* check limit before adding */ 4943a845e10SAlexander V. Chernikov if ((error = check_table_limit(tc, ptei)) == 0) { 4953a845e10SAlexander V. Chernikov error = ta->add(tc->astate, KIDX_TO_TI(ch, kidx), 4963a845e10SAlexander V. Chernikov ptei, v, &num); 4973a845e10SAlexander V. Chernikov /* Set status flag to inform userland */ 4981bc0d457SAlexander V. Chernikov store_tei_result(ptei, 1, error, num); 4993a845e10SAlexander V. Chernikov } 5003a845e10SAlexander V. Chernikov if (error == 0) { 5013a845e10SAlexander V. Chernikov /* Update number of records to ease limit checking */ 5023a845e10SAlexander V. Chernikov tc->count += num; 5033a845e10SAlexander V. Chernikov numadd += num; 5043a845e10SAlexander V. Chernikov continue; 5053a845e10SAlexander V. Chernikov } 5063a845e10SAlexander V. Chernikov 5073a845e10SAlexander V. Chernikov if (first_error == 0) 5083a845e10SAlexander V. Chernikov first_error = error; 5093a845e10SAlexander V. Chernikov 5103a845e10SAlexander V. Chernikov /* 5113a845e10SAlexander V. Chernikov * Some error have happened. Check our atomicity 5123a845e10SAlexander V. Chernikov * settings: continue if atomicity is not required, 5133a845e10SAlexander V. Chernikov * rollback changes otherwise. 5143a845e10SAlexander V. Chernikov */ 5153a845e10SAlexander V. Chernikov if ((flags & IPFW_CTF_ATOMIC) == 0) 5163a845e10SAlexander V. Chernikov continue; 5173a845e10SAlexander V. Chernikov 5181bc0d457SAlexander V. Chernikov rollback_added_entries(ch, tc, KIDX_TO_TI(ch, kidx), 5191bc0d457SAlexander V. Chernikov tei, ta_buf_m, count, i); 5203a845e10SAlexander V. Chernikov break; 5213a845e10SAlexander V. Chernikov } 5223a845e10SAlexander V. Chernikov 5233b3a8eb9SGleb Smirnoff IPFW_WUNLOCK(ch); 5249f7d47b0SAlexander V. Chernikov 525b6ee846eSAlexander V. Chernikov /* Permit post-add algorithm grow/rehash. */ 5263a845e10SAlexander V. Chernikov if (numadd != 0) 5273a845e10SAlexander V. Chernikov check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), 0); 528db785d31SAlexander V. Chernikov 529b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 530b074b7bbSAlexander V. Chernikov 5313a845e10SAlexander V. Chernikov /* Return first error to user, if any */ 5323a845e10SAlexander V. Chernikov error = first_error; 5333a845e10SAlexander V. Chernikov 5343a845e10SAlexander V. Chernikov cleanup: 5351bc0d457SAlexander V. Chernikov flush_batch_buffer(ch, ta, tei, count, 1, rollback, ta_buf_m, ta_buf); 536b074b7bbSAlexander V. Chernikov 5379f7d47b0SAlexander V. Chernikov return (error); 5383b3a8eb9SGleb Smirnoff } 5393b3a8eb9SGleb Smirnoff 5403a845e10SAlexander V. Chernikov /* 5413a845e10SAlexander V. Chernikov * Deletes one or more entries in table @ti. 5423a845e10SAlexander V. Chernikov * 5433a845e10SAlexander V. Chernikov * Returns 0 on success. 5443a845e10SAlexander V. Chernikov */ 5453b3a8eb9SGleb Smirnoff int 5461832a7b3SAlexander V. Chernikov del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti, 5473a845e10SAlexander V. Chernikov struct tentry_info *tei, uint8_t flags, uint32_t count) 5483b3a8eb9SGleb Smirnoff { 549b074b7bbSAlexander V. Chernikov struct table_config *tc; 5509f7d47b0SAlexander V. Chernikov struct table_algo *ta; 5513a845e10SAlexander V. Chernikov struct tentry_info *ptei; 552b074b7bbSAlexander V. Chernikov uint16_t kidx; 5533a845e10SAlexander V. Chernikov int error, first_error, i; 5543a845e10SAlexander V. Chernikov uint32_t num, numdel; 555b6ee846eSAlexander V. Chernikov char ta_buf[TA_BUF_SZ]; 5563a845e10SAlexander V. Chernikov caddr_t ta_buf_m, v; 5573b3a8eb9SGleb Smirnoff 558db785d31SAlexander V. Chernikov /* 5591bc0d457SAlexander V. Chernikov * Find and reference existing table. 560db785d31SAlexander V. Chernikov */ 5611bc0d457SAlexander V. Chernikov if ((error = find_ref_table(ch, ti, tei, count, 0, &tc, &ta)) != 0) 562db785d31SAlexander V. Chernikov return (error); 5633a845e10SAlexander V. Chernikov 56435e1bbd0SAlexander V. Chernikov /* Allocate memory and prepare record(s) */ 5651bc0d457SAlexander V. Chernikov /* Pass stack buffer by default */ 5663a845e10SAlexander V. Chernikov ta_buf_m = ta_buf; 5671bc0d457SAlexander V. Chernikov error = prepare_batch_buffer(ch, ta, tei, count, 0, &ta_buf_m); 5683a845e10SAlexander V. Chernikov if (error != 0) 5693a845e10SAlexander V. Chernikov goto cleanup; 5709f7d47b0SAlexander V. Chernikov 5713a845e10SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 5723a845e10SAlexander V. Chernikov 5733a845e10SAlexander V. Chernikov /* Drop reference we've used in first search */ 5743a845e10SAlexander V. Chernikov tc->no.refcnt--; 5753a845e10SAlexander V. Chernikov 576b074b7bbSAlexander V. Chernikov kidx = tc->no.kidx; 5773a845e10SAlexander V. Chernikov numdel = 0; 5783a845e10SAlexander V. Chernikov first_error = 0; 579b074b7bbSAlexander V. Chernikov 580b074b7bbSAlexander V. Chernikov IPFW_WLOCK(ch); 5813a845e10SAlexander V. Chernikov v = ta_buf_m; 5821bc0d457SAlexander V. Chernikov for (i = 0; i < count; i++, v += ta->ta_buf_size) { 5833a845e10SAlexander V. Chernikov ptei = &tei[i]; 5843a845e10SAlexander V. Chernikov num = 0; 5853a845e10SAlexander V. Chernikov error = ta->del(tc->astate, KIDX_TO_TI(ch, kidx), ptei, v, 5863a845e10SAlexander V. Chernikov &num); 5873a845e10SAlexander V. Chernikov /* Save state for userland */ 5881bc0d457SAlexander V. Chernikov store_tei_result(ptei, 0, error, num); 5893a845e10SAlexander V. Chernikov if (error != 0 && first_error == 0) 5903a845e10SAlexander V. Chernikov first_error = error; 5913a845e10SAlexander V. Chernikov tc->count -= num; 5923a845e10SAlexander V. Chernikov numdel += num; 5933a845e10SAlexander V. Chernikov } 5943b3a8eb9SGleb Smirnoff IPFW_WUNLOCK(ch); 5953b3a8eb9SGleb Smirnoff 5963a845e10SAlexander V. Chernikov if (numdel != 0) { 597b6ee846eSAlexander V. Chernikov /* Run post-del hook to permit shrinking */ 598b6ee846eSAlexander V. Chernikov error = check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), 0); 599b6ee846eSAlexander V. Chernikov } 600b074b7bbSAlexander V. Chernikov 6019f7d47b0SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 6023b3a8eb9SGleb Smirnoff 6033a845e10SAlexander V. Chernikov /* Return first error to user, if any */ 6043a845e10SAlexander V. Chernikov error = first_error; 6053a845e10SAlexander V. Chernikov 6063a845e10SAlexander V. Chernikov cleanup: 6071bc0d457SAlexander V. Chernikov flush_batch_buffer(ch, ta, tei, count, 0, 0, ta_buf_m, ta_buf); 608ac35ff17SAlexander V. Chernikov 609ac35ff17SAlexander V. Chernikov return (error); 610ac35ff17SAlexander V. Chernikov } 611ac35ff17SAlexander V. Chernikov 612db785d31SAlexander V. Chernikov /* 613b6ee846eSAlexander V. Chernikov * Ensure that table @tc has enough space to add @count entries without 614b6ee846eSAlexander V. Chernikov * need for reallocation. 615db785d31SAlexander V. Chernikov * 616db785d31SAlexander V. Chernikov * Callbacks order: 617b6ee846eSAlexander V. Chernikov * 0) has_space() (UH_WLOCK) - checks if @count items can be added w/o resize. 618b6ee846eSAlexander V. Chernikov * 619db785d31SAlexander V. Chernikov * 1) alloc_modify (no locks, M_WAITOK) - alloc new state based on @pflags. 620db785d31SAlexander V. Chernikov * 2) prepare_modifyt (UH_WLOCK) - copy old data into new storage 621db785d31SAlexander V. Chernikov * 3) modify (UH_WLOCK + WLOCK) - switch pointers 622b6ee846eSAlexander V. Chernikov * 4) flush_modify (UH_WLOCK) - free state, if needed 623b6ee846eSAlexander V. Chernikov * 624b6ee846eSAlexander V. Chernikov * Returns 0 on success. 625db785d31SAlexander V. Chernikov */ 626db785d31SAlexander V. Chernikov static int 627b6ee846eSAlexander V. Chernikov check_table_space(struct ip_fw_chain *ch, struct table_config *tc, 628b6ee846eSAlexander V. Chernikov struct table_info *ti, uint32_t count) 629db785d31SAlexander V. Chernikov { 630b6ee846eSAlexander V. Chernikov struct table_algo *ta; 631b6ee846eSAlexander V. Chernikov uint64_t pflags; 632b6ee846eSAlexander V. Chernikov char ta_buf[TA_BUF_SZ]; 633db785d31SAlexander V. Chernikov int error; 634db785d31SAlexander V. Chernikov 635b6ee846eSAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(ch); 636db785d31SAlexander V. Chernikov 637b6ee846eSAlexander V. Chernikov error = 0; 638b6ee846eSAlexander V. Chernikov ta = tc->ta; 639b6ee846eSAlexander V. Chernikov /* Acquire reference not to loose @tc between locks/unlocks */ 640b6ee846eSAlexander V. Chernikov tc->no.refcnt++; 641db785d31SAlexander V. Chernikov 642db785d31SAlexander V. Chernikov /* 643b6ee846eSAlexander V. Chernikov * TODO: think about avoiding race between large add/large delete 644b6ee846eSAlexander V. Chernikov * operation on algorithm which implements shrinking along with 645b6ee846eSAlexander V. Chernikov * growing. 646db785d31SAlexander V. Chernikov */ 647b6ee846eSAlexander V. Chernikov while (true) { 648b6ee846eSAlexander V. Chernikov pflags = 0; 649b6ee846eSAlexander V. Chernikov if (ta->has_space(tc->astate, ti, count, &pflags) != 0) { 65035e1bbd0SAlexander V. Chernikov error = 0; 65135e1bbd0SAlexander V. Chernikov break; 652b6ee846eSAlexander V. Chernikov } 653db785d31SAlexander V. Chernikov 654b6ee846eSAlexander V. Chernikov /* We have to shrink/grow table */ 655b6ee846eSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 656b6ee846eSAlexander V. Chernikov memset(&ta_buf, 0, sizeof(ta_buf)); 657b6ee846eSAlexander V. Chernikov 658b6ee846eSAlexander V. Chernikov if ((error = ta->prepare_mod(ta_buf, &pflags)) != 0) { 659b6ee846eSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 660b6ee846eSAlexander V. Chernikov break; 661b6ee846eSAlexander V. Chernikov } 662b6ee846eSAlexander V. Chernikov 663b6ee846eSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 664b6ee846eSAlexander V. Chernikov 665b6ee846eSAlexander V. Chernikov /* Check if we still need to alter table */ 666b6ee846eSAlexander V. Chernikov ti = KIDX_TO_TI(ch, tc->no.kidx); 667b6ee846eSAlexander V. Chernikov if (ta->has_space(tc->astate, ti, count, &pflags) != 0) { 668b6ee846eSAlexander V. Chernikov 669b6ee846eSAlexander V. Chernikov /* 67035e1bbd0SAlexander V. Chernikov * Other thread has already performed resize. 67135e1bbd0SAlexander V. Chernikov * Flush our state and return. 672b6ee846eSAlexander V. Chernikov */ 673b6ee846eSAlexander V. Chernikov ta->flush_mod(ta_buf); 674b6ee846eSAlexander V. Chernikov break; 675b6ee846eSAlexander V. Chernikov } 676b6ee846eSAlexander V. Chernikov 677b6ee846eSAlexander V. Chernikov error = ta->fill_mod(tc->astate, ti, ta_buf, &pflags); 678b6ee846eSAlexander V. Chernikov if (error == 0) { 679db785d31SAlexander V. Chernikov /* Do actual modification */ 680db785d31SAlexander V. Chernikov IPFW_WLOCK(ch); 68168394ec8SAlexander V. Chernikov ta->modify(tc->astate, ti, ta_buf, pflags); 682db785d31SAlexander V. Chernikov IPFW_WUNLOCK(ch); 683db785d31SAlexander V. Chernikov } 684db785d31SAlexander V. Chernikov 685b6ee846eSAlexander V. Chernikov /* Anyway, flush data and retry */ 686db785d31SAlexander V. Chernikov ta->flush_mod(ta_buf); 687b6ee846eSAlexander V. Chernikov } 688db785d31SAlexander V. Chernikov 689b6ee846eSAlexander V. Chernikov tc->no.refcnt--; 690db785d31SAlexander V. Chernikov return (error); 691db785d31SAlexander V. Chernikov } 692db785d31SAlexander V. Chernikov 6933a845e10SAlexander V. Chernikov /* 6943a845e10SAlexander V. Chernikov * Selects appropriate table operation handler 6953a845e10SAlexander V. Chernikov * depending on opcode version. 6963a845e10SAlexander V. Chernikov */ 697ac35ff17SAlexander V. Chernikov int 698db785d31SAlexander V. Chernikov ipfw_manage_table_ent(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 699ac35ff17SAlexander V. Chernikov struct sockopt_data *sd) 700ac35ff17SAlexander V. Chernikov { 701ac35ff17SAlexander V. Chernikov int error; 702ac35ff17SAlexander V. Chernikov 703ac35ff17SAlexander V. Chernikov switch (op3->version) { 704ac35ff17SAlexander V. Chernikov case 0: 705db785d31SAlexander V. Chernikov error = ipfw_manage_table_ent_v0(ch, op3, sd); 706ac35ff17SAlexander V. Chernikov break; 707ac35ff17SAlexander V. Chernikov case 1: 708db785d31SAlexander V. Chernikov error = ipfw_manage_table_ent_v1(ch, op3, sd); 709ac35ff17SAlexander V. Chernikov break; 710ac35ff17SAlexander V. Chernikov default: 711ac35ff17SAlexander V. Chernikov error = ENOTSUP; 712ac35ff17SAlexander V. Chernikov } 713ac35ff17SAlexander V. Chernikov 714ac35ff17SAlexander V. Chernikov return (error); 715ac35ff17SAlexander V. Chernikov } 716ac35ff17SAlexander V. Chernikov 717ac35ff17SAlexander V. Chernikov /* 718ac35ff17SAlexander V. Chernikov * Adds or deletes record in table. 719ac35ff17SAlexander V. Chernikov * Data layout (v0): 720ac35ff17SAlexander V. Chernikov * Request: [ ip_fw3_opheader ipfw_table_xentry ] 721ac35ff17SAlexander V. Chernikov * 722ac35ff17SAlexander V. Chernikov * Returns 0 on success 723ac35ff17SAlexander V. Chernikov */ 724ac35ff17SAlexander V. Chernikov static int 725db785d31SAlexander V. Chernikov ipfw_manage_table_ent_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 726ac35ff17SAlexander V. Chernikov struct sockopt_data *sd) 727ac35ff17SAlexander V. Chernikov { 728ac35ff17SAlexander V. Chernikov ipfw_table_xentry *xent; 729ac35ff17SAlexander V. Chernikov struct tentry_info tei; 730ac35ff17SAlexander V. Chernikov struct tid_info ti; 731ac35ff17SAlexander V. Chernikov int error, hdrlen, read; 732ac35ff17SAlexander V. Chernikov 733ac35ff17SAlexander V. Chernikov hdrlen = offsetof(ipfw_table_xentry, k); 734ac35ff17SAlexander V. Chernikov 735ac35ff17SAlexander V. Chernikov /* Check minimum header size */ 736ac35ff17SAlexander V. Chernikov if (sd->valsize < (sizeof(*op3) + hdrlen)) 737ac35ff17SAlexander V. Chernikov return (EINVAL); 738ac35ff17SAlexander V. Chernikov 739ac35ff17SAlexander V. Chernikov read = sizeof(ip_fw3_opheader); 740ac35ff17SAlexander V. Chernikov 741ac35ff17SAlexander V. Chernikov /* Check if xentry len field is valid */ 742ac35ff17SAlexander V. Chernikov xent = (ipfw_table_xentry *)(op3 + 1); 743ac35ff17SAlexander V. Chernikov if (xent->len < hdrlen || xent->len + read > sd->valsize) 744ac35ff17SAlexander V. Chernikov return (EINVAL); 745ac35ff17SAlexander V. Chernikov 746ac35ff17SAlexander V. Chernikov memset(&tei, 0, sizeof(tei)); 747ac35ff17SAlexander V. Chernikov tei.paddr = &xent->k; 748ac35ff17SAlexander V. Chernikov tei.masklen = xent->masklen; 749ac35ff17SAlexander V. Chernikov tei.value = xent->value; 750ac35ff17SAlexander V. Chernikov /* Old requests compability */ 751db785d31SAlexander V. Chernikov tei.flags = TEI_FLAGS_COMPAT; 752ac35ff17SAlexander V. Chernikov if (xent->type == IPFW_TABLE_CIDR) { 753ac35ff17SAlexander V. Chernikov if (xent->len - hdrlen == sizeof(in_addr_t)) 754ac35ff17SAlexander V. Chernikov tei.subtype = AF_INET; 755ac35ff17SAlexander V. Chernikov else 756ac35ff17SAlexander V. Chernikov tei.subtype = AF_INET6; 757ac35ff17SAlexander V. Chernikov } 758ac35ff17SAlexander V. Chernikov 759ac35ff17SAlexander V. Chernikov memset(&ti, 0, sizeof(ti)); 760ac35ff17SAlexander V. Chernikov ti.uidx = xent->tbl; 761ac35ff17SAlexander V. Chernikov ti.type = xent->type; 762ac35ff17SAlexander V. Chernikov 763ac35ff17SAlexander V. Chernikov error = (op3->opcode == IP_FW_TABLE_XADD) ? 7643a845e10SAlexander V. Chernikov add_table_entry(ch, &ti, &tei, 0, 1) : 7653a845e10SAlexander V. Chernikov del_table_entry(ch, &ti, &tei, 0, 1); 766ac35ff17SAlexander V. Chernikov 767ac35ff17SAlexander V. Chernikov return (error); 768ac35ff17SAlexander V. Chernikov } 769ac35ff17SAlexander V. Chernikov 770ac35ff17SAlexander V. Chernikov /* 771ac35ff17SAlexander V. Chernikov * Adds or deletes record in table. 772ac35ff17SAlexander V. Chernikov * Data layout (v1)(current): 773db785d31SAlexander V. Chernikov * Request: [ ipfw_obj_header 774db785d31SAlexander V. Chernikov * ipfw_obj_ctlv(IPFW_TLV_TBLENT_LIST) [ ipfw_obj_tentry x N ] 775db785d31SAlexander V. Chernikov * ] 776ac35ff17SAlexander V. Chernikov * 777ac35ff17SAlexander V. Chernikov * Returns 0 on success 778ac35ff17SAlexander V. Chernikov */ 779ac35ff17SAlexander V. Chernikov static int 780db785d31SAlexander V. Chernikov ipfw_manage_table_ent_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 781ac35ff17SAlexander V. Chernikov struct sockopt_data *sd) 782ac35ff17SAlexander V. Chernikov { 7833a845e10SAlexander V. Chernikov ipfw_obj_tentry *tent, *ptent; 784db785d31SAlexander V. Chernikov ipfw_obj_ctlv *ctlv; 785ac35ff17SAlexander V. Chernikov ipfw_obj_header *oh; 7863a845e10SAlexander V. Chernikov struct tentry_info *ptei, tei, *tei_buf; 787ac35ff17SAlexander V. Chernikov struct tid_info ti; 7883a845e10SAlexander V. Chernikov int error, i, kidx, read; 789ac35ff17SAlexander V. Chernikov 790ac35ff17SAlexander V. Chernikov /* Check minimum header size */ 791db785d31SAlexander V. Chernikov if (sd->valsize < (sizeof(*oh) + sizeof(*ctlv))) 792ac35ff17SAlexander V. Chernikov return (EINVAL); 793ac35ff17SAlexander V. Chernikov 794ac35ff17SAlexander V. Chernikov /* Check if passed data is too long */ 795ac35ff17SAlexander V. Chernikov if (sd->valsize != sd->kavail) 796ac35ff17SAlexander V. Chernikov return (EINVAL); 797ac35ff17SAlexander V. Chernikov 798ac35ff17SAlexander V. Chernikov oh = (ipfw_obj_header *)sd->kbuf; 799ac35ff17SAlexander V. Chernikov 800ac35ff17SAlexander V. Chernikov /* Basic length checks for TLVs */ 801ac35ff17SAlexander V. Chernikov if (oh->ntlv.head.length != sizeof(oh->ntlv)) 802ac35ff17SAlexander V. Chernikov return (EINVAL); 803ac35ff17SAlexander V. Chernikov 804ac35ff17SAlexander V. Chernikov read = sizeof(*oh); 805ac35ff17SAlexander V. Chernikov 806db785d31SAlexander V. Chernikov ctlv = (ipfw_obj_ctlv *)(oh + 1); 807db785d31SAlexander V. Chernikov if (ctlv->head.length + read != sd->valsize) 808db785d31SAlexander V. Chernikov return (EINVAL); 809db785d31SAlexander V. Chernikov 810db785d31SAlexander V. Chernikov read += sizeof(*ctlv); 811db785d31SAlexander V. Chernikov tent = (ipfw_obj_tentry *)(ctlv + 1); 8123a845e10SAlexander V. Chernikov if (ctlv->count * sizeof(*tent) + read != sd->valsize) 813ac35ff17SAlexander V. Chernikov return (EINVAL); 814ac35ff17SAlexander V. Chernikov 8153a845e10SAlexander V. Chernikov if (ctlv->count == 0) 8163a845e10SAlexander V. Chernikov return (0); 817ac35ff17SAlexander V. Chernikov 8183a845e10SAlexander V. Chernikov /* 8193a845e10SAlexander V. Chernikov * Mark entire buffer as "read". 82035e1bbd0SAlexander V. Chernikov * This instructs sopt api write it back 8213a845e10SAlexander V. Chernikov * after function return. 8223a845e10SAlexander V. Chernikov */ 8233a845e10SAlexander V. Chernikov ipfw_get_sopt_header(sd, sd->valsize); 8243a845e10SAlexander V. Chernikov 8253a845e10SAlexander V. Chernikov /* Perform basic checks for each entry */ 8263a845e10SAlexander V. Chernikov ptent = tent; 8273a845e10SAlexander V. Chernikov kidx = tent->idx; 8283a845e10SAlexander V. Chernikov for (i = 0; i < ctlv->count; i++, ptent++) { 8293a845e10SAlexander V. Chernikov if (ptent->head.length != sizeof(*ptent)) 8303a845e10SAlexander V. Chernikov return (EINVAL); 8313a845e10SAlexander V. Chernikov if (ptent->idx != kidx) 8323a845e10SAlexander V. Chernikov return (ENOTSUP); 8333a845e10SAlexander V. Chernikov } 8343a845e10SAlexander V. Chernikov 8353a845e10SAlexander V. Chernikov /* Convert data into kernel request objects */ 83681d3153dSAlexander V. Chernikov objheader_to_ti(oh, &ti); 837ac35ff17SAlexander V. Chernikov ti.type = oh->ntlv.type; 8383a845e10SAlexander V. Chernikov ti.uidx = kidx; 8393a845e10SAlexander V. Chernikov 8403a845e10SAlexander V. Chernikov /* Use on-stack buffer for single add/del */ 8413a845e10SAlexander V. Chernikov if (ctlv->count == 1) { 8423a845e10SAlexander V. Chernikov memset(&tei, 0, sizeof(tei)); 8433a845e10SAlexander V. Chernikov tei_buf = &tei; 8443a845e10SAlexander V. Chernikov } else 8453a845e10SAlexander V. Chernikov tei_buf = malloc(ctlv->count * sizeof(tei), M_TEMP, 8463a845e10SAlexander V. Chernikov M_WAITOK | M_ZERO); 8473a845e10SAlexander V. Chernikov 8483a845e10SAlexander V. Chernikov ptei = tei_buf; 8493a845e10SAlexander V. Chernikov ptent = tent; 8503a845e10SAlexander V. Chernikov for (i = 0; i < ctlv->count; i++, ptent++, ptei++) { 8513a845e10SAlexander V. Chernikov ptei->paddr = &ptent->k; 8523a845e10SAlexander V. Chernikov ptei->subtype = ptent->subtype; 8533a845e10SAlexander V. Chernikov ptei->masklen = ptent->masklen; 8543a845e10SAlexander V. Chernikov if (ptent->head.flags & IPFW_TF_UPDATE) 8553a845e10SAlexander V. Chernikov ptei->flags |= TEI_FLAGS_UPDATE; 8563a845e10SAlexander V. Chernikov ptei->value = ptent->value; 8573a845e10SAlexander V. Chernikov } 858ac35ff17SAlexander V. Chernikov 859ac35ff17SAlexander V. Chernikov error = (oh->opheader.opcode == IP_FW_TABLE_XADD) ? 8603a845e10SAlexander V. Chernikov add_table_entry(ch, &ti, tei_buf, ctlv->flags, ctlv->count) : 8613a845e10SAlexander V. Chernikov del_table_entry(ch, &ti, tei_buf, ctlv->flags, ctlv->count); 8623a845e10SAlexander V. Chernikov 8633a845e10SAlexander V. Chernikov /* Translate result back to userland */ 8643a845e10SAlexander V. Chernikov ptei = tei_buf; 8653a845e10SAlexander V. Chernikov ptent = tent; 8663a845e10SAlexander V. Chernikov for (i = 0; i < ctlv->count; i++, ptent++, ptei++) { 8673a845e10SAlexander V. Chernikov if (ptei->flags & TEI_FLAGS_ADDED) 8683a845e10SAlexander V. Chernikov ptent->result = IPFW_TR_ADDED; 8693a845e10SAlexander V. Chernikov else if (ptei->flags & TEI_FLAGS_DELETED) 8703a845e10SAlexander V. Chernikov ptent->result = IPFW_TR_DELETED; 8713a845e10SAlexander V. Chernikov else if (ptei->flags & TEI_FLAGS_UPDATED) 8723a845e10SAlexander V. Chernikov ptent->result = IPFW_TR_UPDATED; 8733a845e10SAlexander V. Chernikov else if (ptei->flags & TEI_FLAGS_LIMIT) 8743a845e10SAlexander V. Chernikov ptent->result = IPFW_TR_LIMIT; 8753a845e10SAlexander V. Chernikov else if (ptei->flags & TEI_FLAGS_ERROR) 8763a845e10SAlexander V. Chernikov ptent->result = IPFW_TR_ERROR; 8773a845e10SAlexander V. Chernikov else if (ptei->flags & TEI_FLAGS_NOTFOUND) 8783a845e10SAlexander V. Chernikov ptent->result = IPFW_TR_NOTFOUND; 8793a845e10SAlexander V. Chernikov else if (ptei->flags & TEI_FLAGS_EXISTS) 8803a845e10SAlexander V. Chernikov ptent->result = IPFW_TR_EXISTS; 8813a845e10SAlexander V. Chernikov } 8823a845e10SAlexander V. Chernikov 8833a845e10SAlexander V. Chernikov if (tei_buf != &tei) 8843a845e10SAlexander V. Chernikov free(tei_buf, M_TEMP); 885ac35ff17SAlexander V. Chernikov 886ac35ff17SAlexander V. Chernikov return (error); 887ac35ff17SAlexander V. Chernikov } 888ac35ff17SAlexander V. Chernikov 88981d3153dSAlexander V. Chernikov /* 89081d3153dSAlexander V. Chernikov * Looks up an entry in given table. 89181d3153dSAlexander V. Chernikov * Data layout (v0)(current): 89281d3153dSAlexander V. Chernikov * Request: [ ipfw_obj_header ipfw_obj_tentry ] 89381d3153dSAlexander V. Chernikov * Reply: [ ipfw_obj_header ipfw_obj_tentry ] 89481d3153dSAlexander V. Chernikov * 89581d3153dSAlexander V. Chernikov * Returns 0 on success 89681d3153dSAlexander V. Chernikov */ 89781d3153dSAlexander V. Chernikov int 89881d3153dSAlexander V. Chernikov ipfw_find_table_entry(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 89981d3153dSAlexander V. Chernikov struct sockopt_data *sd) 90081d3153dSAlexander V. Chernikov { 90181d3153dSAlexander V. Chernikov ipfw_obj_tentry *tent; 90281d3153dSAlexander V. Chernikov ipfw_obj_header *oh; 90381d3153dSAlexander V. Chernikov struct tid_info ti; 90481d3153dSAlexander V. Chernikov struct table_config *tc; 90581d3153dSAlexander V. Chernikov struct table_algo *ta; 90681d3153dSAlexander V. Chernikov struct table_info *kti; 90781d3153dSAlexander V. Chernikov struct namedobj_instance *ni; 908914bffb6SAlexander V. Chernikov int error; 90981d3153dSAlexander V. Chernikov size_t sz; 91081d3153dSAlexander V. Chernikov 91181d3153dSAlexander V. Chernikov /* Check minimum header size */ 91281d3153dSAlexander V. Chernikov sz = sizeof(*oh) + sizeof(*tent); 91381d3153dSAlexander V. Chernikov if (sd->valsize != sz) 91481d3153dSAlexander V. Chernikov return (EINVAL); 91581d3153dSAlexander V. Chernikov 91681d3153dSAlexander V. Chernikov oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 91781d3153dSAlexander V. Chernikov tent = (ipfw_obj_tentry *)(oh + 1); 91881d3153dSAlexander V. Chernikov 91981d3153dSAlexander V. Chernikov /* Basic length checks for TLVs */ 92081d3153dSAlexander V. Chernikov if (oh->ntlv.head.length != sizeof(oh->ntlv)) 92181d3153dSAlexander V. Chernikov return (EINVAL); 92281d3153dSAlexander V. Chernikov 92381d3153dSAlexander V. Chernikov objheader_to_ti(oh, &ti); 92481d3153dSAlexander V. Chernikov ti.type = oh->ntlv.type; 92581d3153dSAlexander V. Chernikov ti.uidx = tent->idx; 92681d3153dSAlexander V. Chernikov 92781d3153dSAlexander V. Chernikov IPFW_UH_RLOCK(ch); 92881d3153dSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 92981d3153dSAlexander V. Chernikov 93081d3153dSAlexander V. Chernikov /* 93181d3153dSAlexander V. Chernikov * Find existing table and check its type . 93281d3153dSAlexander V. Chernikov */ 93381d3153dSAlexander V. Chernikov ta = NULL; 93481d3153dSAlexander V. Chernikov if ((tc = find_table(ni, &ti)) == NULL) { 93581d3153dSAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 93681d3153dSAlexander V. Chernikov return (ESRCH); 93781d3153dSAlexander V. Chernikov } 93881d3153dSAlexander V. Chernikov 93981d3153dSAlexander V. Chernikov /* check table type */ 94081d3153dSAlexander V. Chernikov if (tc->no.type != ti.type) { 94181d3153dSAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 94281d3153dSAlexander V. Chernikov return (EINVAL); 94381d3153dSAlexander V. Chernikov } 94481d3153dSAlexander V. Chernikov 94581d3153dSAlexander V. Chernikov kti = KIDX_TO_TI(ch, tc->no.kidx); 94681d3153dSAlexander V. Chernikov ta = tc->ta; 94781d3153dSAlexander V. Chernikov 948914bffb6SAlexander V. Chernikov if (ta->find_tentry == NULL) 949914bffb6SAlexander V. Chernikov return (ENOTSUP); 950914bffb6SAlexander V. Chernikov 951914bffb6SAlexander V. Chernikov error = ta->find_tentry(tc->astate, kti, tent); 95281d3153dSAlexander V. Chernikov 95381d3153dSAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 95481d3153dSAlexander V. Chernikov 95581d3153dSAlexander V. Chernikov return (error); 95681d3153dSAlexander V. Chernikov } 95781d3153dSAlexander V. Chernikov 95846d52008SAlexander V. Chernikov /* 95946d52008SAlexander V. Chernikov * Flushes all entries or destroys given table. 96046d52008SAlexander V. Chernikov * Data layout (v0)(current): 96146d52008SAlexander V. Chernikov * Request: [ ipfw_obj_header ] 96246d52008SAlexander V. Chernikov * 96346d52008SAlexander V. Chernikov * Returns 0 on success 96446d52008SAlexander V. Chernikov */ 965ac35ff17SAlexander V. Chernikov int 966ac35ff17SAlexander V. Chernikov ipfw_flush_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 967ac35ff17SAlexander V. Chernikov struct sockopt_data *sd) 968ac35ff17SAlexander V. Chernikov { 969ac35ff17SAlexander V. Chernikov int error; 970ac35ff17SAlexander V. Chernikov struct _ipfw_obj_header *oh; 971ac35ff17SAlexander V. Chernikov struct tid_info ti; 972ac35ff17SAlexander V. Chernikov 973ac35ff17SAlexander V. Chernikov if (sd->valsize != sizeof(*oh)) 974ac35ff17SAlexander V. Chernikov return (EINVAL); 975ac35ff17SAlexander V. Chernikov 976ac35ff17SAlexander V. Chernikov oh = (struct _ipfw_obj_header *)op3; 977ac35ff17SAlexander V. Chernikov objheader_to_ti(oh, &ti); 978ac35ff17SAlexander V. Chernikov 9791832a7b3SAlexander V. Chernikov if (op3->opcode == IP_FW_TABLE_XDESTROY) 980ac35ff17SAlexander V. Chernikov error = destroy_table(ch, &ti); 9811832a7b3SAlexander V. Chernikov else if (op3->opcode == IP_FW_TABLE_XFLUSH) 982ac35ff17SAlexander V. Chernikov error = flush_table(ch, &ti); 983ac35ff17SAlexander V. Chernikov else 984ac35ff17SAlexander V. Chernikov return (ENOTSUP); 985ac35ff17SAlexander V. Chernikov 986ac35ff17SAlexander V. Chernikov return (error); 9873b3a8eb9SGleb Smirnoff } 9883b3a8eb9SGleb Smirnoff 98935e1bbd0SAlexander V. Chernikov /* 99035e1bbd0SAlexander V. Chernikov * Flushes given table. 99135e1bbd0SAlexander V. Chernikov * 99235e1bbd0SAlexander V. Chernikov * Function create new table instance with the same 99335e1bbd0SAlexander V. Chernikov * parameters, swaps it with old one and 99435e1bbd0SAlexander V. Chernikov * flushes state without holding any locks. 99535e1bbd0SAlexander V. Chernikov * 99635e1bbd0SAlexander V. Chernikov * Returns 0 on success. 99735e1bbd0SAlexander V. Chernikov */ 9981832a7b3SAlexander V. Chernikov int 999ac35ff17SAlexander V. Chernikov flush_table(struct ip_fw_chain *ch, struct tid_info *ti) 10003b3a8eb9SGleb Smirnoff { 1001b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 1002b074b7bbSAlexander V. Chernikov struct table_config *tc; 10039f7d47b0SAlexander V. Chernikov struct table_algo *ta; 10049f7d47b0SAlexander V. Chernikov struct table_info ti_old, ti_new, *tablestate; 10059f7d47b0SAlexander V. Chernikov void *astate_old, *astate_new; 1006914bffb6SAlexander V. Chernikov char algostate[64], *pstate; 1007b074b7bbSAlexander V. Chernikov int error; 1008b074b7bbSAlexander V. Chernikov uint16_t kidx; 1009914bffb6SAlexander V. Chernikov uint8_t tflags; 10103b3a8eb9SGleb Smirnoff 10113b3a8eb9SGleb Smirnoff /* 10129f7d47b0SAlexander V. Chernikov * Stage 1: save table algoritm. 1013b074b7bbSAlexander V. Chernikov * Reference found table to ensure it won't disappear. 10143b3a8eb9SGleb Smirnoff */ 1015b074b7bbSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 1016b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 1017b074b7bbSAlexander V. Chernikov if ((tc = find_table(ni, ti)) == NULL) { 1018b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1019b074b7bbSAlexander V. Chernikov return (ESRCH); 1020b074b7bbSAlexander V. Chernikov } 10219f7d47b0SAlexander V. Chernikov ta = tc->ta; 1022b074b7bbSAlexander V. Chernikov tc->no.refcnt++; 102335e1bbd0SAlexander V. Chernikov /* Save startup algo parameters */ 1024daabb523SAlexander V. Chernikov if (ta->print_config != NULL) { 1025daabb523SAlexander V. Chernikov ta->print_config(tc->astate, KIDX_TO_TI(ch, tc->no.kidx), 1026daabb523SAlexander V. Chernikov algostate, sizeof(algostate)); 1027daabb523SAlexander V. Chernikov pstate = algostate; 1028daabb523SAlexander V. Chernikov } else 1029daabb523SAlexander V. Chernikov pstate = NULL; 1030914bffb6SAlexander V. Chernikov tflags = tc->tflags; 1031b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 10323b3a8eb9SGleb Smirnoff 1033b074b7bbSAlexander V. Chernikov /* 10349f7d47b0SAlexander V. Chernikov * Stage 2: allocate new table instance using same algo. 1035b074b7bbSAlexander V. Chernikov */ 10369f7d47b0SAlexander V. Chernikov memset(&ti_new, 0, sizeof(struct table_info)); 1037914bffb6SAlexander V. Chernikov if ((error = ta->init(ch, &astate_new, &ti_new, pstate, tflags)) != 0) { 1038b074b7bbSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 1039b074b7bbSAlexander V. Chernikov tc->no.refcnt--; 1040b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1041b074b7bbSAlexander V. Chernikov return (error); 1042b074b7bbSAlexander V. Chernikov } 1043b074b7bbSAlexander V. Chernikov 1044b074b7bbSAlexander V. Chernikov /* 1045b074b7bbSAlexander V. Chernikov * Stage 3: swap old state pointers with newly-allocated ones. 1046b074b7bbSAlexander V. Chernikov * Decrease refcount. 1047b074b7bbSAlexander V. Chernikov */ 1048b074b7bbSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 1049b074b7bbSAlexander V. Chernikov 1050b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 1051b074b7bbSAlexander V. Chernikov kidx = tc->no.kidx; 10529f7d47b0SAlexander V. Chernikov tablestate = (struct table_info *)ch->tablestate; 1053b074b7bbSAlexander V. Chernikov 10549f7d47b0SAlexander V. Chernikov IPFW_WLOCK(ch); 10559f7d47b0SAlexander V. Chernikov ti_old = tablestate[kidx]; 10569f7d47b0SAlexander V. Chernikov tablestate[kidx] = ti_new; 10579f7d47b0SAlexander V. Chernikov IPFW_WUNLOCK(ch); 1058b074b7bbSAlexander V. Chernikov 10599f7d47b0SAlexander V. Chernikov astate_old = tc->astate; 10609f7d47b0SAlexander V. Chernikov tc->astate = astate_new; 10619f7d47b0SAlexander V. Chernikov tc->ti = ti_new; 10629f7d47b0SAlexander V. Chernikov tc->count = 0; 1063b074b7bbSAlexander V. Chernikov tc->no.refcnt--; 1064b074b7bbSAlexander V. Chernikov 1065b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 10663b3a8eb9SGleb Smirnoff 1067b074b7bbSAlexander V. Chernikov /* 1068b074b7bbSAlexander V. Chernikov * Stage 4: perform real flush. 1069b074b7bbSAlexander V. Chernikov */ 10709f7d47b0SAlexander V. Chernikov ta->destroy(astate_old, &ti_old); 10713b3a8eb9SGleb Smirnoff 10723b3a8eb9SGleb Smirnoff return (0); 10733b3a8eb9SGleb Smirnoff } 10743b3a8eb9SGleb Smirnoff 1075b074b7bbSAlexander V. Chernikov /* 107646d52008SAlexander V. Chernikov * Swaps two tables. 107746d52008SAlexander V. Chernikov * Data layout (v0)(current): 107846d52008SAlexander V. Chernikov * Request: [ ipfw_obj_header ipfw_obj_ntlv ] 107946d52008SAlexander V. Chernikov * 108046d52008SAlexander V. Chernikov * Returns 0 on success 108146d52008SAlexander V. Chernikov */ 108246d52008SAlexander V. Chernikov int 108346d52008SAlexander V. Chernikov ipfw_swap_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 108446d52008SAlexander V. Chernikov struct sockopt_data *sd) 108546d52008SAlexander V. Chernikov { 108646d52008SAlexander V. Chernikov int error; 108746d52008SAlexander V. Chernikov struct _ipfw_obj_header *oh; 108846d52008SAlexander V. Chernikov struct tid_info ti_a, ti_b; 108946d52008SAlexander V. Chernikov 109046d52008SAlexander V. Chernikov if (sd->valsize != sizeof(*oh) + sizeof(ipfw_obj_ntlv)) 109146d52008SAlexander V. Chernikov return (EINVAL); 109246d52008SAlexander V. Chernikov 109346d52008SAlexander V. Chernikov oh = (struct _ipfw_obj_header *)op3; 109446d52008SAlexander V. Chernikov ntlv_to_ti(&oh->ntlv, &ti_a); 109546d52008SAlexander V. Chernikov ntlv_to_ti((ipfw_obj_ntlv *)(oh + 1), &ti_b); 109646d52008SAlexander V. Chernikov 1097a73d728dSAlexander V. Chernikov error = swap_tables(ch, &ti_a, &ti_b); 109846d52008SAlexander V. Chernikov 109946d52008SAlexander V. Chernikov return (error); 110046d52008SAlexander V. Chernikov } 110146d52008SAlexander V. Chernikov 110235e1bbd0SAlexander V. Chernikov /* 110335e1bbd0SAlexander V. Chernikov * Swaps two tables of the same type/valtype. 110435e1bbd0SAlexander V. Chernikov * 110535e1bbd0SAlexander V. Chernikov * Checks if tables are compatible and limits 110635e1bbd0SAlexander V. Chernikov * permits swap, than actually perform swap 110735e1bbd0SAlexander V. Chernikov * by switching 110835e1bbd0SAlexander V. Chernikov * 1) runtime data (ch->tablestate) 110935e1bbd0SAlexander V. Chernikov * 2) runtime cache in @tc 111035e1bbd0SAlexander V. Chernikov * 3) algo-specific data (tc->astate) 111135e1bbd0SAlexander V. Chernikov * 4) number of items 111235e1bbd0SAlexander V. Chernikov * 111335e1bbd0SAlexander V. Chernikov * Since @ti has changed for each table, calls notification callbacks. 111435e1bbd0SAlexander V. Chernikov * 111535e1bbd0SAlexander V. Chernikov * Returns 0 on success. 111635e1bbd0SAlexander V. Chernikov */ 111746d52008SAlexander V. Chernikov static int 1118a73d728dSAlexander V. Chernikov swap_tables(struct ip_fw_chain *ch, struct tid_info *a, 111946d52008SAlexander V. Chernikov struct tid_info *b) 112046d52008SAlexander V. Chernikov { 112146d52008SAlexander V. Chernikov struct namedobj_instance *ni; 112246d52008SAlexander V. Chernikov struct table_config *tc_a, *tc_b; 112346d52008SAlexander V. Chernikov struct table_algo *ta; 112446d52008SAlexander V. Chernikov struct table_info ti, *tablestate; 112546d52008SAlexander V. Chernikov void *astate; 112646d52008SAlexander V. Chernikov uint32_t count; 112746d52008SAlexander V. Chernikov 112846d52008SAlexander V. Chernikov /* 112946d52008SAlexander V. Chernikov * Stage 1: find both tables and ensure they are of 113035e1bbd0SAlexander V. Chernikov * the same type. 113146d52008SAlexander V. Chernikov */ 113246d52008SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 113346d52008SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 113446d52008SAlexander V. Chernikov if ((tc_a = find_table(ni, a)) == NULL) { 113546d52008SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 113646d52008SAlexander V. Chernikov return (ESRCH); 113746d52008SAlexander V. Chernikov } 113846d52008SAlexander V. Chernikov if ((tc_b = find_table(ni, b)) == NULL) { 113946d52008SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 114046d52008SAlexander V. Chernikov return (ESRCH); 114146d52008SAlexander V. Chernikov } 114246d52008SAlexander V. Chernikov 114346d52008SAlexander V. Chernikov /* It is very easy to swap between the same table */ 114446d52008SAlexander V. Chernikov if (tc_a == tc_b) { 114546d52008SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 114646d52008SAlexander V. Chernikov return (0); 114746d52008SAlexander V. Chernikov } 114846d52008SAlexander V. Chernikov 114946d52008SAlexander V. Chernikov /* Check type and value are the same */ 115046d52008SAlexander V. Chernikov if (tc_a->no.type != tc_b->no.type || tc_a->tflags != tc_b->tflags || 115146d52008SAlexander V. Chernikov tc_a->vtype != tc_b->vtype) { 115246d52008SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 115346d52008SAlexander V. Chernikov return (EINVAL); 115446d52008SAlexander V. Chernikov } 115546d52008SAlexander V. Chernikov 115646d52008SAlexander V. Chernikov /* Check limits before swap */ 115746d52008SAlexander V. Chernikov if ((tc_a->limit != 0 && tc_b->count > tc_a->limit) || 115846d52008SAlexander V. Chernikov (tc_b->limit != 0 && tc_a->count > tc_b->limit)) { 115946d52008SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 116046d52008SAlexander V. Chernikov return (EFBIG); 116146d52008SAlexander V. Chernikov } 116246d52008SAlexander V. Chernikov 116346d52008SAlexander V. Chernikov /* Everything is fine, prepare to swap */ 116446d52008SAlexander V. Chernikov tablestate = (struct table_info *)ch->tablestate; 116546d52008SAlexander V. Chernikov ti = tablestate[tc_a->no.kidx]; 116646d52008SAlexander V. Chernikov ta = tc_a->ta; 116746d52008SAlexander V. Chernikov astate = tc_a->astate; 116846d52008SAlexander V. Chernikov count = tc_a->count; 116946d52008SAlexander V. Chernikov 117046d52008SAlexander V. Chernikov IPFW_WLOCK(ch); 117146d52008SAlexander V. Chernikov /* a <- b */ 117246d52008SAlexander V. Chernikov tablestate[tc_a->no.kidx] = tablestate[tc_b->no.kidx]; 117346d52008SAlexander V. Chernikov tc_a->ta = tc_b->ta; 117446d52008SAlexander V. Chernikov tc_a->astate = tc_b->astate; 117546d52008SAlexander V. Chernikov tc_a->count = tc_b->count; 117646d52008SAlexander V. Chernikov /* b <- a */ 117746d52008SAlexander V. Chernikov tablestate[tc_b->no.kidx] = ti; 117846d52008SAlexander V. Chernikov tc_b->ta = ta; 117946d52008SAlexander V. Chernikov tc_b->astate = astate; 118046d52008SAlexander V. Chernikov tc_b->count = count; 118146d52008SAlexander V. Chernikov IPFW_WUNLOCK(ch); 118246d52008SAlexander V. Chernikov 118346d52008SAlexander V. Chernikov /* Ensure tc.ti copies are in sync */ 118446d52008SAlexander V. Chernikov tc_a->ti = tablestate[tc_a->no.kidx]; 118546d52008SAlexander V. Chernikov tc_b->ti = tablestate[tc_b->no.kidx]; 118646d52008SAlexander V. Chernikov 118746d52008SAlexander V. Chernikov /* Notify both tables on @ti change */ 118846d52008SAlexander V. Chernikov if (tc_a->ta->change_ti != NULL) 118946d52008SAlexander V. Chernikov tc_a->ta->change_ti(tc_a->astate, &tablestate[tc_a->no.kidx]); 119046d52008SAlexander V. Chernikov if (tc_b->ta->change_ti != NULL) 119146d52008SAlexander V. Chernikov tc_b->ta->change_ti(tc_b->astate, &tablestate[tc_b->no.kidx]); 119246d52008SAlexander V. Chernikov 119346d52008SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 119446d52008SAlexander V. Chernikov 119546d52008SAlexander V. Chernikov return (0); 119646d52008SAlexander V. Chernikov } 119746d52008SAlexander V. Chernikov 119846d52008SAlexander V. Chernikov /* 11999f7d47b0SAlexander V. Chernikov * Destroys table specified by @ti. 1200ac35ff17SAlexander V. Chernikov * Data layout (v0)(current): 1201ac35ff17SAlexander V. Chernikov * Request: [ ip_fw3_opheader ] 1202ac35ff17SAlexander V. Chernikov * 1203ac35ff17SAlexander V. Chernikov * Returns 0 on success 1204b074b7bbSAlexander V. Chernikov */ 1205ac35ff17SAlexander V. Chernikov static int 1206ac35ff17SAlexander V. Chernikov destroy_table(struct ip_fw_chain *ch, struct tid_info *ti) 1207b074b7bbSAlexander V. Chernikov { 1208b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 1209b074b7bbSAlexander V. Chernikov struct table_config *tc; 1210b074b7bbSAlexander V. Chernikov 1211b074b7bbSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 1212b074b7bbSAlexander V. Chernikov 1213b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 1214b074b7bbSAlexander V. Chernikov if ((tc = find_table(ni, ti)) == NULL) { 1215b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1216b074b7bbSAlexander V. Chernikov return (ESRCH); 1217b074b7bbSAlexander V. Chernikov } 1218b074b7bbSAlexander V. Chernikov 12199f7d47b0SAlexander V. Chernikov /* Do not permit destroying referenced tables */ 12209f7d47b0SAlexander V. Chernikov if (tc->no.refcnt > 0) { 1221b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1222b074b7bbSAlexander V. Chernikov return (EBUSY); 1223b074b7bbSAlexander V. Chernikov } 1224b074b7bbSAlexander V. Chernikov 1225b074b7bbSAlexander V. Chernikov IPFW_WLOCK(ch); 1226b074b7bbSAlexander V. Chernikov unlink_table(ch, tc); 1227b074b7bbSAlexander V. Chernikov IPFW_WUNLOCK(ch); 1228b074b7bbSAlexander V. Chernikov 1229b074b7bbSAlexander V. Chernikov /* Free obj index */ 1230ac35ff17SAlexander V. Chernikov if (ipfw_objhash_free_idx(ni, tc->no.kidx) != 0) 1231b074b7bbSAlexander V. Chernikov printf("Error unlinking kidx %d from table %s\n", 1232b074b7bbSAlexander V. Chernikov tc->no.kidx, tc->tablename); 1233b074b7bbSAlexander V. Chernikov 1234b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1235b074b7bbSAlexander V. Chernikov 1236b074b7bbSAlexander V. Chernikov free_table_config(ni, tc); 1237b074b7bbSAlexander V. Chernikov 1238b074b7bbSAlexander V. Chernikov return (0); 1239b074b7bbSAlexander V. Chernikov } 1240b074b7bbSAlexander V. Chernikov 1241b074b7bbSAlexander V. Chernikov static void 1242b074b7bbSAlexander V. Chernikov destroy_table_locked(struct namedobj_instance *ni, struct named_object *no, 1243b074b7bbSAlexander V. Chernikov void *arg) 1244b074b7bbSAlexander V. Chernikov { 1245b074b7bbSAlexander V. Chernikov 1246b074b7bbSAlexander V. Chernikov unlink_table((struct ip_fw_chain *)arg, (struct table_config *)no); 1247ac35ff17SAlexander V. Chernikov if (ipfw_objhash_free_idx(ni, no->kidx) != 0) 1248b074b7bbSAlexander V. Chernikov printf("Error unlinking kidx %d from table %s\n", 1249b074b7bbSAlexander V. Chernikov no->kidx, no->name); 1250b074b7bbSAlexander V. Chernikov free_table_config(ni, (struct table_config *)no); 1251b074b7bbSAlexander V. Chernikov } 1252b074b7bbSAlexander V. Chernikov 125335e1bbd0SAlexander V. Chernikov /* 125435e1bbd0SAlexander V. Chernikov * Shuts tables module down. 125535e1bbd0SAlexander V. Chernikov */ 12563b3a8eb9SGleb Smirnoff void 12573b3a8eb9SGleb Smirnoff ipfw_destroy_tables(struct ip_fw_chain *ch) 12583b3a8eb9SGleb Smirnoff { 12593b3a8eb9SGleb Smirnoff 1260b074b7bbSAlexander V. Chernikov /* Remove all tables from working set */ 1261b074b7bbSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 1262b074b7bbSAlexander V. Chernikov IPFW_WLOCK(ch); 1263b074b7bbSAlexander V. Chernikov ipfw_objhash_foreach(CHAIN_TO_NI(ch), destroy_table_locked, ch); 1264b074b7bbSAlexander V. Chernikov IPFW_WUNLOCK(ch); 1265b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 12663b3a8eb9SGleb Smirnoff 12673b3a8eb9SGleb Smirnoff /* Free pointers itself */ 12689f7d47b0SAlexander V. Chernikov free(ch->tablestate, M_IPFW); 12699f7d47b0SAlexander V. Chernikov 12709f7d47b0SAlexander V. Chernikov ipfw_table_algo_destroy(ch); 1271b074b7bbSAlexander V. Chernikov 1272b074b7bbSAlexander V. Chernikov ipfw_objhash_destroy(CHAIN_TO_NI(ch)); 1273b074b7bbSAlexander V. Chernikov free(CHAIN_TO_TCFG(ch), M_IPFW); 12743b3a8eb9SGleb Smirnoff } 12753b3a8eb9SGleb Smirnoff 127635e1bbd0SAlexander V. Chernikov /* 127735e1bbd0SAlexander V. Chernikov * Starts tables module. 127835e1bbd0SAlexander V. Chernikov */ 12793b3a8eb9SGleb Smirnoff int 12803b3a8eb9SGleb Smirnoff ipfw_init_tables(struct ip_fw_chain *ch) 12813b3a8eb9SGleb Smirnoff { 1282b074b7bbSAlexander V. Chernikov struct tables_config *tcfg; 1283b074b7bbSAlexander V. Chernikov 12843b3a8eb9SGleb Smirnoff /* Allocate pointers */ 12859f7d47b0SAlexander V. Chernikov ch->tablestate = malloc(V_fw_tables_max * sizeof(struct table_info), 12869f7d47b0SAlexander V. Chernikov M_IPFW, M_WAITOK | M_ZERO); 1287b074b7bbSAlexander V. Chernikov 1288b074b7bbSAlexander V. Chernikov tcfg = malloc(sizeof(struct tables_config), M_IPFW, M_WAITOK | M_ZERO); 1289b074b7bbSAlexander V. Chernikov tcfg->namehash = ipfw_objhash_create(V_fw_tables_max); 1290b074b7bbSAlexander V. Chernikov ch->tblcfg = tcfg; 1291b074b7bbSAlexander V. Chernikov 12929f7d47b0SAlexander V. Chernikov ipfw_table_algo_init(ch); 12939f7d47b0SAlexander V. Chernikov 12943b3a8eb9SGleb Smirnoff return (0); 12953b3a8eb9SGleb Smirnoff } 12963b3a8eb9SGleb Smirnoff 129735e1bbd0SAlexander V. Chernikov /* 129835e1bbd0SAlexander V. Chernikov * Grow tables index. 129935e1bbd0SAlexander V. Chernikov * 130035e1bbd0SAlexander V. Chernikov * Returns 0 on success. 130135e1bbd0SAlexander V. Chernikov */ 13023b3a8eb9SGleb Smirnoff int 13033b3a8eb9SGleb Smirnoff ipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables) 13043b3a8eb9SGleb Smirnoff { 13053b3a8eb9SGleb Smirnoff unsigned int ntables_old, tbl; 1306b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 13079f7d47b0SAlexander V. Chernikov void *new_idx, *old_tablestate, *tablestate; 130868394ec8SAlexander V. Chernikov struct table_info *ti; 130968394ec8SAlexander V. Chernikov struct table_config *tc; 131068394ec8SAlexander V. Chernikov int i, new_blocks; 13113b3a8eb9SGleb Smirnoff 13123b3a8eb9SGleb Smirnoff /* Check new value for validity */ 13133b3a8eb9SGleb Smirnoff if (ntables > IPFW_TABLES_MAX) 13143b3a8eb9SGleb Smirnoff ntables = IPFW_TABLES_MAX; 13153b3a8eb9SGleb Smirnoff 13163b3a8eb9SGleb Smirnoff /* Allocate new pointers */ 13179f7d47b0SAlexander V. Chernikov tablestate = malloc(ntables * sizeof(struct table_info), 13189f7d47b0SAlexander V. Chernikov M_IPFW, M_WAITOK | M_ZERO); 13199f7d47b0SAlexander V. Chernikov 1320b074b7bbSAlexander V. Chernikov ipfw_objhash_bitmap_alloc(ntables, (void *)&new_idx, &new_blocks); 13213b3a8eb9SGleb Smirnoff 13229f7d47b0SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 13233b3a8eb9SGleb Smirnoff 13243b3a8eb9SGleb Smirnoff tbl = (ntables >= V_fw_tables_max) ? V_fw_tables_max : ntables; 1325b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 1326b074b7bbSAlexander V. Chernikov 13279f7d47b0SAlexander V. Chernikov /* Temporary restrict decreasing max_tables */ 13289f7d47b0SAlexander V. Chernikov if (ntables < V_fw_tables_max) { 13299f7d47b0SAlexander V. Chernikov 13309f7d47b0SAlexander V. Chernikov /* 13319f7d47b0SAlexander V. Chernikov * FIXME: Check if we really can shrink 13329f7d47b0SAlexander V. Chernikov */ 13339f7d47b0SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1334b074b7bbSAlexander V. Chernikov return (EINVAL); 1335b074b7bbSAlexander V. Chernikov } 13363b3a8eb9SGleb Smirnoff 13379f7d47b0SAlexander V. Chernikov /* Copy table info/indices */ 13389f7d47b0SAlexander V. Chernikov memcpy(tablestate, ch->tablestate, sizeof(struct table_info) * tbl); 13399f7d47b0SAlexander V. Chernikov ipfw_objhash_bitmap_merge(ni, &new_idx, &new_blocks); 13403b3a8eb9SGleb Smirnoff 13419f7d47b0SAlexander V. Chernikov IPFW_WLOCK(ch); 13429f7d47b0SAlexander V. Chernikov 13439f7d47b0SAlexander V. Chernikov /* Change pointers */ 13449f7d47b0SAlexander V. Chernikov old_tablestate = ch->tablestate; 13459f7d47b0SAlexander V. Chernikov ch->tablestate = tablestate; 13469f7d47b0SAlexander V. Chernikov ipfw_objhash_bitmap_swap(ni, &new_idx, &new_blocks); 13473b3a8eb9SGleb Smirnoff 13483b3a8eb9SGleb Smirnoff ntables_old = V_fw_tables_max; 13493b3a8eb9SGleb Smirnoff V_fw_tables_max = ntables; 13503b3a8eb9SGleb Smirnoff 13513b3a8eb9SGleb Smirnoff IPFW_WUNLOCK(ch); 135268394ec8SAlexander V. Chernikov 135368394ec8SAlexander V. Chernikov /* Notify all consumers that their @ti pointer has changed */ 135468394ec8SAlexander V. Chernikov ti = (struct table_info *)ch->tablestate; 135568394ec8SAlexander V. Chernikov for (i = 0; i < tbl; i++, ti++) { 135668394ec8SAlexander V. Chernikov if (ti->lookup == NULL) 135768394ec8SAlexander V. Chernikov continue; 135868394ec8SAlexander V. Chernikov tc = (struct table_config *)ipfw_objhash_lookup_kidx(ni, i); 135968394ec8SAlexander V. Chernikov if (tc == NULL || tc->ta->change_ti == NULL) 136068394ec8SAlexander V. Chernikov continue; 136168394ec8SAlexander V. Chernikov 136268394ec8SAlexander V. Chernikov tc->ta->change_ti(tc->astate, ti); 136368394ec8SAlexander V. Chernikov } 136468394ec8SAlexander V. Chernikov 13659f7d47b0SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 13663b3a8eb9SGleb Smirnoff 13673b3a8eb9SGleb Smirnoff /* Free old pointers */ 13689f7d47b0SAlexander V. Chernikov free(old_tablestate, M_IPFW); 1369b074b7bbSAlexander V. Chernikov ipfw_objhash_bitmap_free(new_idx, new_blocks); 13703b3a8eb9SGleb Smirnoff 13713b3a8eb9SGleb Smirnoff return (0); 13723b3a8eb9SGleb Smirnoff } 13733b3a8eb9SGleb Smirnoff 1374a73d728dSAlexander V. Chernikov /* 137535e1bbd0SAlexander V. Chernikov * Switch between "set 0" and "rule's set" table binding, 1376a73d728dSAlexander V. Chernikov * Check all ruleset bindings and permits changing 1377a73d728dSAlexander V. Chernikov * IFF each binding has both rule AND table in default set (set 0). 1378a73d728dSAlexander V. Chernikov * 1379a73d728dSAlexander V. Chernikov * Returns 0 on success. 1380a73d728dSAlexander V. Chernikov */ 1381a73d728dSAlexander V. Chernikov int 1382a73d728dSAlexander V. Chernikov ipfw_switch_tables_namespace(struct ip_fw_chain *ch, unsigned int sets) 1383a73d728dSAlexander V. Chernikov { 1384a73d728dSAlexander V. Chernikov struct namedobj_instance *ni; 1385a73d728dSAlexander V. Chernikov struct named_object *no; 1386a73d728dSAlexander V. Chernikov struct ip_fw *rule; 1387a73d728dSAlexander V. Chernikov ipfw_insn *cmd; 1388a73d728dSAlexander V. Chernikov int cmdlen, i, l; 1389a73d728dSAlexander V. Chernikov uint16_t kidx; 1390a73d728dSAlexander V. Chernikov uint8_t type; 1391a73d728dSAlexander V. Chernikov 1392a73d728dSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 1393a73d728dSAlexander V. Chernikov 1394a73d728dSAlexander V. Chernikov if (V_fw_tables_sets == sets) { 1395a73d728dSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1396a73d728dSAlexander V. Chernikov return (0); 1397a73d728dSAlexander V. Chernikov } 1398a73d728dSAlexander V. Chernikov 1399a73d728dSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 1400a73d728dSAlexander V. Chernikov 140135e1bbd0SAlexander V. Chernikov /* 140235e1bbd0SAlexander V. Chernikov * Scan all rules and examine tables opcodes. 140335e1bbd0SAlexander V. Chernikov */ 1404a73d728dSAlexander V. Chernikov for (i = 0; i < ch->n_rules; i++) { 1405a73d728dSAlexander V. Chernikov rule = ch->map[i]; 1406a73d728dSAlexander V. Chernikov 1407a73d728dSAlexander V. Chernikov l = rule->cmd_len; 1408a73d728dSAlexander V. Chernikov cmd = rule->cmd; 1409a73d728dSAlexander V. Chernikov cmdlen = 0; 1410a73d728dSAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 1411a73d728dSAlexander V. Chernikov cmdlen = F_LEN(cmd); 1412a73d728dSAlexander V. Chernikov 1413a73d728dSAlexander V. Chernikov if (classify_table_opcode(cmd, &kidx, &type) != 0) 1414a73d728dSAlexander V. Chernikov continue; 1415a73d728dSAlexander V. Chernikov 1416a73d728dSAlexander V. Chernikov no = ipfw_objhash_lookup_kidx(ni, kidx); 1417a73d728dSAlexander V. Chernikov 141835e1bbd0SAlexander V. Chernikov /* Check if both table object and rule has the set 0 */ 1419a73d728dSAlexander V. Chernikov if (no->set != 0 || rule->set != 0) { 1420a73d728dSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1421a73d728dSAlexander V. Chernikov return (EBUSY); 1422a73d728dSAlexander V. Chernikov } 1423a73d728dSAlexander V. Chernikov 1424a73d728dSAlexander V. Chernikov } 1425a73d728dSAlexander V. Chernikov } 1426a73d728dSAlexander V. Chernikov V_fw_tables_sets = sets; 1427a73d728dSAlexander V. Chernikov 1428a73d728dSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1429a73d728dSAlexander V. Chernikov 1430a73d728dSAlexander V. Chernikov return (0); 1431a73d728dSAlexander V. Chernikov } 1432a73d728dSAlexander V. Chernikov 14333b3a8eb9SGleb Smirnoff int 14343b3a8eb9SGleb Smirnoff ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, 14353b3a8eb9SGleb Smirnoff uint32_t *val) 14363b3a8eb9SGleb Smirnoff { 14379f7d47b0SAlexander V. Chernikov struct table_info *ti; 14383b3a8eb9SGleb Smirnoff 14399f7d47b0SAlexander V. Chernikov ti = &(((struct table_info *)ch->tablestate)[tbl]); 14409f7d47b0SAlexander V. Chernikov 14419f7d47b0SAlexander V. Chernikov return (ti->lookup(ti, &addr, sizeof(in_addr_t), val)); 14423b3a8eb9SGleb Smirnoff } 14439f7d47b0SAlexander V. Chernikov 14449f7d47b0SAlexander V. Chernikov int 14459f7d47b0SAlexander V. Chernikov ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, uint16_t plen, 14469f7d47b0SAlexander V. Chernikov void *paddr, uint32_t *val) 14479f7d47b0SAlexander V. Chernikov { 14489f7d47b0SAlexander V. Chernikov struct table_info *ti; 14499f7d47b0SAlexander V. Chernikov 14509f7d47b0SAlexander V. Chernikov ti = &(((struct table_info *)ch->tablestate)[tbl]); 14519f7d47b0SAlexander V. Chernikov 14529f7d47b0SAlexander V. Chernikov return (ti->lookup(ti, paddr, plen, val)); 14539f7d47b0SAlexander V. Chernikov } 14549f7d47b0SAlexander V. Chernikov 14559f7d47b0SAlexander V. Chernikov /* 14569f7d47b0SAlexander V. Chernikov * Info/List/dump support for tables. 14579f7d47b0SAlexander V. Chernikov * 14589f7d47b0SAlexander V. Chernikov */ 14599f7d47b0SAlexander V. Chernikov 1460f1220db8SAlexander V. Chernikov /* 1461d3a4f924SAlexander V. Chernikov * High-level 'get' cmds sysctl handlers 1462d3a4f924SAlexander V. Chernikov */ 1463d3a4f924SAlexander V. Chernikov 1464d3a4f924SAlexander V. Chernikov /* 1465d3a4f924SAlexander V. Chernikov * Lists all tables currently available in kernel. 1466ac35ff17SAlexander V. Chernikov * Data layout (v0)(current): 1467d3a4f924SAlexander V. Chernikov * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size 1468d3a4f924SAlexander V. Chernikov * Reply: [ ipfw_obj_lheader ipfw_xtable_info x N ] 1469d3a4f924SAlexander V. Chernikov * 1470d3a4f924SAlexander V. Chernikov * Returns 0 on success 1471d3a4f924SAlexander V. Chernikov */ 1472f1220db8SAlexander V. Chernikov int 14732d99a349SAlexander V. Chernikov ipfw_list_tables(struct ip_fw_chain *ch, struct sockopt_data *sd) 1474f1220db8SAlexander V. Chernikov { 1475f1220db8SAlexander V. Chernikov struct _ipfw_obj_lheader *olh; 1476f1220db8SAlexander V. Chernikov int error; 1477f1220db8SAlexander V. Chernikov 14782d99a349SAlexander V. Chernikov olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh)); 14792d99a349SAlexander V. Chernikov if (olh == NULL) 1480d3a4f924SAlexander V. Chernikov return (EINVAL); 148168394ec8SAlexander V. Chernikov if (sd->valsize < olh->size) 148268394ec8SAlexander V. Chernikov return (EINVAL); 1483d3a4f924SAlexander V. Chernikov 1484f1220db8SAlexander V. Chernikov IPFW_UH_RLOCK(ch); 14852d99a349SAlexander V. Chernikov error = export_tables(ch, olh, sd); 1486f1220db8SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 1487f1220db8SAlexander V. Chernikov 1488f1220db8SAlexander V. Chernikov return (error); 1489f1220db8SAlexander V. Chernikov } 1490f1220db8SAlexander V. Chernikov 1491f1220db8SAlexander V. Chernikov /* 14922d99a349SAlexander V. Chernikov * Store table info to buffer provided by @sd. 1493ac35ff17SAlexander V. Chernikov * Data layout (v0)(current): 1494d3a4f924SAlexander V. Chernikov * Request: [ ipfw_obj_header ipfw_xtable_info(empty)] 1495d3a4f924SAlexander V. Chernikov * Reply: [ ipfw_obj_header ipfw_xtable_info ] 1496d3a4f924SAlexander V. Chernikov * 1497d3a4f924SAlexander V. Chernikov * Returns 0 on success. 1498d3a4f924SAlexander V. Chernikov */ 1499d3a4f924SAlexander V. Chernikov int 15002d99a349SAlexander V. Chernikov ipfw_describe_table(struct ip_fw_chain *ch, struct sockopt_data *sd) 1501d3a4f924SAlexander V. Chernikov { 1502d3a4f924SAlexander V. Chernikov struct _ipfw_obj_header *oh; 1503d3a4f924SAlexander V. Chernikov struct table_config *tc; 1504d3a4f924SAlexander V. Chernikov struct tid_info ti; 1505d3a4f924SAlexander V. Chernikov size_t sz; 1506d3a4f924SAlexander V. Chernikov 1507d3a4f924SAlexander V. Chernikov sz = sizeof(*oh) + sizeof(ipfw_xtable_info); 15082d99a349SAlexander V. Chernikov oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 15092d99a349SAlexander V. Chernikov if (oh == NULL) 1510d3a4f924SAlexander V. Chernikov return (EINVAL); 1511d3a4f924SAlexander V. Chernikov 1512d3a4f924SAlexander V. Chernikov objheader_to_ti(oh, &ti); 1513d3a4f924SAlexander V. Chernikov 1514d3a4f924SAlexander V. Chernikov IPFW_UH_RLOCK(ch); 1515d3a4f924SAlexander V. Chernikov if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) { 1516d3a4f924SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 1517d3a4f924SAlexander V. Chernikov return (ESRCH); 1518d3a4f924SAlexander V. Chernikov } 1519d3a4f924SAlexander V. Chernikov 1520ac35ff17SAlexander V. Chernikov export_table_info(ch, tc, (ipfw_xtable_info *)(oh + 1)); 1521d3a4f924SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 1522d3a4f924SAlexander V. Chernikov 15232d99a349SAlexander V. Chernikov return (0); 1524d3a4f924SAlexander V. Chernikov } 1525d3a4f924SAlexander V. Chernikov 1526f1220db8SAlexander V. Chernikov struct dump_args { 1527f1220db8SAlexander V. Chernikov struct table_info *ti; 1528f1220db8SAlexander V. Chernikov struct table_config *tc; 15292d99a349SAlexander V. Chernikov struct sockopt_data *sd; 1530f1220db8SAlexander V. Chernikov uint32_t cnt; 1531f1220db8SAlexander V. Chernikov uint16_t uidx; 153281d3153dSAlexander V. Chernikov int error; 15332d99a349SAlexander V. Chernikov ipfw_table_entry *ent; 15342d99a349SAlexander V. Chernikov uint32_t size; 153581d3153dSAlexander V. Chernikov ipfw_obj_tentry tent; 1536f1220db8SAlexander V. Chernikov }; 1537f1220db8SAlexander V. Chernikov 1538f1220db8SAlexander V. Chernikov int 15392d99a349SAlexander V. Chernikov ipfw_dump_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 15402d99a349SAlexander V. Chernikov struct sockopt_data *sd) 1541f1220db8SAlexander V. Chernikov { 1542d3a4f924SAlexander V. Chernikov int error; 1543d3a4f924SAlexander V. Chernikov 1544d3a4f924SAlexander V. Chernikov switch (op3->version) { 1545d3a4f924SAlexander V. Chernikov case 0: 15462d99a349SAlexander V. Chernikov error = ipfw_dump_table_v0(ch, sd); 1547d3a4f924SAlexander V. Chernikov break; 1548d3a4f924SAlexander V. Chernikov case 1: 15492d99a349SAlexander V. Chernikov error = ipfw_dump_table_v1(ch, sd); 1550d3a4f924SAlexander V. Chernikov break; 1551d3a4f924SAlexander V. Chernikov default: 1552d3a4f924SAlexander V. Chernikov error = ENOTSUP; 1553d3a4f924SAlexander V. Chernikov } 1554d3a4f924SAlexander V. Chernikov 1555d3a4f924SAlexander V. Chernikov return (error); 1556d3a4f924SAlexander V. Chernikov } 1557d3a4f924SAlexander V. Chernikov 1558d3a4f924SAlexander V. Chernikov /* 1559d3a4f924SAlexander V. Chernikov * Dumps all table data 1560ac35ff17SAlexander V. Chernikov * Data layout (v1)(current): 15612d99a349SAlexander V. Chernikov * Request: [ ipfw_obj_header ], size = ipfw_xtable_info.size 156281d3153dSAlexander V. Chernikov * Reply: [ ipfw_obj_header ipfw_xtable_info ipfw_obj_tentry x N ] 1563d3a4f924SAlexander V. Chernikov * 1564d3a4f924SAlexander V. Chernikov * Returns 0 on success 1565d3a4f924SAlexander V. Chernikov */ 1566d3a4f924SAlexander V. Chernikov static int 15672d99a349SAlexander V. Chernikov ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt_data *sd) 1568d3a4f924SAlexander V. Chernikov { 1569f1220db8SAlexander V. Chernikov struct _ipfw_obj_header *oh; 1570f1220db8SAlexander V. Chernikov ipfw_xtable_info *i; 1571f1220db8SAlexander V. Chernikov struct tid_info ti; 1572f1220db8SAlexander V. Chernikov struct table_config *tc; 1573f1220db8SAlexander V. Chernikov struct table_algo *ta; 1574f1220db8SAlexander V. Chernikov struct dump_args da; 1575d3a4f924SAlexander V. Chernikov uint32_t sz; 1576f1220db8SAlexander V. Chernikov 15772d99a349SAlexander V. Chernikov sz = sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info); 15782d99a349SAlexander V. Chernikov oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 15792d99a349SAlexander V. Chernikov if (oh == NULL) 1580d3a4f924SAlexander V. Chernikov return (EINVAL); 1581d3a4f924SAlexander V. Chernikov 15822d99a349SAlexander V. Chernikov i = (ipfw_xtable_info *)(oh + 1); 1583d3a4f924SAlexander V. Chernikov objheader_to_ti(oh, &ti); 1584f1220db8SAlexander V. Chernikov 1585f1220db8SAlexander V. Chernikov IPFW_UH_RLOCK(ch); 1586f1220db8SAlexander V. Chernikov if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) { 1587f1220db8SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 1588f1220db8SAlexander V. Chernikov return (ESRCH); 1589f1220db8SAlexander V. Chernikov } 1590ac35ff17SAlexander V. Chernikov export_table_info(ch, tc, i); 15912d99a349SAlexander V. Chernikov 1592720ee730SAlexander V. Chernikov if (sd->valsize < i->size) { 15932d99a349SAlexander V. Chernikov 15942d99a349SAlexander V. Chernikov /* 15952d99a349SAlexander V. Chernikov * Submitted buffer size is not enough. 15962d99a349SAlexander V. Chernikov * WE've already filled in @i structure with 15972d99a349SAlexander V. Chernikov * relevant table info including size, so we 15982d99a349SAlexander V. Chernikov * can return. Buffer will be flushed automatically. 15992d99a349SAlexander V. Chernikov */ 1600f1220db8SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 16012d99a349SAlexander V. Chernikov return (ENOMEM); 1602f1220db8SAlexander V. Chernikov } 1603f1220db8SAlexander V. Chernikov 1604f1220db8SAlexander V. Chernikov /* 1605f1220db8SAlexander V. Chernikov * Do the actual dump in eXtended format 1606f1220db8SAlexander V. Chernikov */ 1607d3a4f924SAlexander V. Chernikov memset(&da, 0, sizeof(da)); 1608f1220db8SAlexander V. Chernikov da.ti = KIDX_TO_TI(ch, tc->no.kidx); 1609f1220db8SAlexander V. Chernikov da.tc = tc; 16102d99a349SAlexander V. Chernikov da.sd = sd; 1611f1220db8SAlexander V. Chernikov 1612f1220db8SAlexander V. Chernikov ta = tc->ta; 1613f1220db8SAlexander V. Chernikov 161481d3153dSAlexander V. Chernikov ta->foreach(tc->astate, da.ti, dump_table_tentry, &da); 1615f1220db8SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 1616f1220db8SAlexander V. Chernikov 161781d3153dSAlexander V. Chernikov return (da.error); 1618f1220db8SAlexander V. Chernikov } 1619f1220db8SAlexander V. Chernikov 1620d3a4f924SAlexander V. Chernikov /* 1621d3a4f924SAlexander V. Chernikov * Dumps all table data 16222d99a349SAlexander V. Chernikov * Data layout (version 0)(legacy): 1623d3a4f924SAlexander V. Chernikov * Request: [ ipfw_xtable ], size = IP_FW_TABLE_XGETSIZE() 1624d3a4f924SAlexander V. Chernikov * Reply: [ ipfw_xtable ipfw_table_xentry x N ] 1625d3a4f924SAlexander V. Chernikov * 1626d3a4f924SAlexander V. Chernikov * Returns 0 on success 1627d3a4f924SAlexander V. Chernikov */ 1628d3a4f924SAlexander V. Chernikov static int 16292d99a349SAlexander V. Chernikov ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt_data *sd) 1630d3a4f924SAlexander V. Chernikov { 1631d3a4f924SAlexander V. Chernikov ipfw_xtable *xtbl; 1632d3a4f924SAlexander V. Chernikov struct tid_info ti; 1633d3a4f924SAlexander V. Chernikov struct table_config *tc; 1634d3a4f924SAlexander V. Chernikov struct table_algo *ta; 1635d3a4f924SAlexander V. Chernikov struct dump_args da; 1636d3a4f924SAlexander V. Chernikov size_t sz; 1637d3a4f924SAlexander V. Chernikov 16382d99a349SAlexander V. Chernikov xtbl = (ipfw_xtable *)ipfw_get_sopt_header(sd, sizeof(ipfw_xtable)); 16392d99a349SAlexander V. Chernikov if (xtbl == NULL) 1640d3a4f924SAlexander V. Chernikov return (EINVAL); 1641d3a4f924SAlexander V. Chernikov 1642d3a4f924SAlexander V. Chernikov memset(&ti, 0, sizeof(ti)); 1643d3a4f924SAlexander V. Chernikov ti.uidx = xtbl->tbl; 1644d3a4f924SAlexander V. Chernikov 1645d3a4f924SAlexander V. Chernikov IPFW_UH_RLOCK(ch); 1646d3a4f924SAlexander V. Chernikov if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) { 1647d3a4f924SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 1648d3a4f924SAlexander V. Chernikov return (0); 1649d3a4f924SAlexander V. Chernikov } 1650d3a4f924SAlexander V. Chernikov sz = tc->count * sizeof(ipfw_table_xentry) + sizeof(ipfw_xtable); 16512d99a349SAlexander V. Chernikov 16522d99a349SAlexander V. Chernikov xtbl->cnt = tc->count; 16532d99a349SAlexander V. Chernikov xtbl->size = sz; 16542d99a349SAlexander V. Chernikov xtbl->type = tc->no.type; 16552d99a349SAlexander V. Chernikov xtbl->tbl = ti.uidx; 16562d99a349SAlexander V. Chernikov 16572d99a349SAlexander V. Chernikov if (sd->valsize < sz) { 16582d99a349SAlexander V. Chernikov 16592d99a349SAlexander V. Chernikov /* 16602d99a349SAlexander V. Chernikov * Submitted buffer size is not enough. 16612d99a349SAlexander V. Chernikov * WE've already filled in @i structure with 16622d99a349SAlexander V. Chernikov * relevant table info including size, so we 16632d99a349SAlexander V. Chernikov * can return. Buffer will be flushed automatically. 16642d99a349SAlexander V. Chernikov */ 1665d3a4f924SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 16662d99a349SAlexander V. Chernikov return (ENOMEM); 1667d3a4f924SAlexander V. Chernikov } 1668d3a4f924SAlexander V. Chernikov 1669d3a4f924SAlexander V. Chernikov /* Do the actual dump in eXtended format */ 1670d3a4f924SAlexander V. Chernikov memset(&da, 0, sizeof(da)); 1671d3a4f924SAlexander V. Chernikov da.ti = KIDX_TO_TI(ch, tc->no.kidx); 1672d3a4f924SAlexander V. Chernikov da.tc = tc; 16732d99a349SAlexander V. Chernikov da.sd = sd; 16742d99a349SAlexander V. Chernikov 1675d3a4f924SAlexander V. Chernikov ta = tc->ta; 1676d3a4f924SAlexander V. Chernikov 1677d3a4f924SAlexander V. Chernikov ta->foreach(tc->astate, da.ti, dump_table_xentry, &da); 1678d3a4f924SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 1679d3a4f924SAlexander V. Chernikov 16802d99a349SAlexander V. Chernikov return (0); 1681d3a4f924SAlexander V. Chernikov } 1682d3a4f924SAlexander V. Chernikov 1683d3a4f924SAlexander V. Chernikov /* 1684adf3b2b9SAlexander V. Chernikov * Modifies existing table. 1685adf3b2b9SAlexander V. Chernikov * Data layout (v0)(current): 1686adf3b2b9SAlexander V. Chernikov * Request: [ ipfw_obj_header ipfw_xtable_info ] 1687adf3b2b9SAlexander V. Chernikov * 1688adf3b2b9SAlexander V. Chernikov * Returns 0 on success 1689adf3b2b9SAlexander V. Chernikov */ 1690adf3b2b9SAlexander V. Chernikov int 1691adf3b2b9SAlexander V. Chernikov ipfw_modify_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 1692adf3b2b9SAlexander V. Chernikov struct sockopt_data *sd) 1693adf3b2b9SAlexander V. Chernikov { 1694adf3b2b9SAlexander V. Chernikov struct _ipfw_obj_header *oh; 1695adf3b2b9SAlexander V. Chernikov ipfw_xtable_info *i; 1696adf3b2b9SAlexander V. Chernikov char *tname; 1697adf3b2b9SAlexander V. Chernikov struct tid_info ti; 1698adf3b2b9SAlexander V. Chernikov struct namedobj_instance *ni; 1699adf3b2b9SAlexander V. Chernikov struct table_config *tc; 1700adf3b2b9SAlexander V. Chernikov 1701adf3b2b9SAlexander V. Chernikov if (sd->valsize != sizeof(*oh) + sizeof(ipfw_xtable_info)) 1702adf3b2b9SAlexander V. Chernikov return (EINVAL); 1703adf3b2b9SAlexander V. Chernikov 1704adf3b2b9SAlexander V. Chernikov oh = (struct _ipfw_obj_header *)sd->kbuf; 1705adf3b2b9SAlexander V. Chernikov i = (ipfw_xtable_info *)(oh + 1); 1706adf3b2b9SAlexander V. Chernikov 1707adf3b2b9SAlexander V. Chernikov /* 1708adf3b2b9SAlexander V. Chernikov * Verify user-supplied strings. 1709adf3b2b9SAlexander V. Chernikov * Check for null-terminated/zero-length strings/ 1710adf3b2b9SAlexander V. Chernikov */ 1711adf3b2b9SAlexander V. Chernikov tname = oh->ntlv.name; 1712adf3b2b9SAlexander V. Chernikov if (ipfw_check_table_name(tname) != 0) 1713adf3b2b9SAlexander V. Chernikov return (EINVAL); 1714adf3b2b9SAlexander V. Chernikov 1715adf3b2b9SAlexander V. Chernikov objheader_to_ti(oh, &ti); 1716adf3b2b9SAlexander V. Chernikov ti.type = i->type; 1717adf3b2b9SAlexander V. Chernikov 1718adf3b2b9SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 1719adf3b2b9SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 1720adf3b2b9SAlexander V. Chernikov if ((tc = find_table(ni, &ti)) == NULL) { 1721adf3b2b9SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1722adf3b2b9SAlexander V. Chernikov return (ESRCH); 1723adf3b2b9SAlexander V. Chernikov } 1724adf3b2b9SAlexander V. Chernikov if ((i->mflags & IPFW_TMFLAGS_FTYPE) != 0) 1725adf3b2b9SAlexander V. Chernikov tc->vftype = i->vftype; 1726adf3b2b9SAlexander V. Chernikov if ((i->mflags & IPFW_TMFLAGS_LIMIT) != 0) 1727adf3b2b9SAlexander V. Chernikov tc->limit = i->limit; 17284f43138aSAlexander V. Chernikov if ((i->mflags & IPFW_TMFLAGS_LOCK) != 0) 17294f43138aSAlexander V. Chernikov tc->locked = ((i->flags & IPFW_TGFLAGS_LOCKED) != 0); 1730adf3b2b9SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1731adf3b2b9SAlexander V. Chernikov 1732adf3b2b9SAlexander V. Chernikov return (0); 1733adf3b2b9SAlexander V. Chernikov } 1734adf3b2b9SAlexander V. Chernikov 1735adf3b2b9SAlexander V. Chernikov /* 17369490a627SAlexander V. Chernikov * Creates new table. 1737ac35ff17SAlexander V. Chernikov * Data layout (v0)(current): 17389490a627SAlexander V. Chernikov * Request: [ ipfw_obj_header ipfw_xtable_info ] 17399490a627SAlexander V. Chernikov * 17409490a627SAlexander V. Chernikov * Returns 0 on success 17419490a627SAlexander V. Chernikov */ 17429490a627SAlexander V. Chernikov int 1743ac35ff17SAlexander V. Chernikov ipfw_create_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 1744ac35ff17SAlexander V. Chernikov struct sockopt_data *sd) 17459490a627SAlexander V. Chernikov { 17469490a627SAlexander V. Chernikov struct _ipfw_obj_header *oh; 17479490a627SAlexander V. Chernikov ipfw_xtable_info *i; 17489490a627SAlexander V. Chernikov char *tname, *aname; 17499490a627SAlexander V. Chernikov struct tid_info ti; 17509490a627SAlexander V. Chernikov struct namedobj_instance *ni; 17519490a627SAlexander V. Chernikov struct table_config *tc; 17529490a627SAlexander V. Chernikov 1753ac35ff17SAlexander V. Chernikov if (sd->valsize != sizeof(*oh) + sizeof(ipfw_xtable_info)) 17549490a627SAlexander V. Chernikov return (EINVAL); 17559490a627SAlexander V. Chernikov 1756ac35ff17SAlexander V. Chernikov oh = (struct _ipfw_obj_header *)sd->kbuf; 17579490a627SAlexander V. Chernikov i = (ipfw_xtable_info *)(oh + 1); 17589490a627SAlexander V. Chernikov 17599490a627SAlexander V. Chernikov /* 17609490a627SAlexander V. Chernikov * Verify user-supplied strings. 17612d99a349SAlexander V. Chernikov * Check for null-terminated/zero-length strings/ 17629490a627SAlexander V. Chernikov */ 1763ac35ff17SAlexander V. Chernikov tname = oh->ntlv.name; 17649490a627SAlexander V. Chernikov aname = i->algoname; 1765ac35ff17SAlexander V. Chernikov if (ipfw_check_table_name(tname) != 0 || 17669490a627SAlexander V. Chernikov strnlen(aname, sizeof(i->algoname)) == sizeof(i->algoname)) 17679490a627SAlexander V. Chernikov return (EINVAL); 17689490a627SAlexander V. Chernikov 17699490a627SAlexander V. Chernikov if (aname[0] == '\0') { 17709490a627SAlexander V. Chernikov /* Use default algorithm */ 17719490a627SAlexander V. Chernikov aname = NULL; 17729490a627SAlexander V. Chernikov } 17739490a627SAlexander V. Chernikov 17749490a627SAlexander V. Chernikov objheader_to_ti(oh, &ti); 1775ac35ff17SAlexander V. Chernikov ti.type = i->type; 17769490a627SAlexander V. Chernikov 17779490a627SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 17789490a627SAlexander V. Chernikov 17799490a627SAlexander V. Chernikov IPFW_UH_RLOCK(ch); 17809490a627SAlexander V. Chernikov if ((tc = find_table(ni, &ti)) != NULL) { 17819490a627SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 17829490a627SAlexander V. Chernikov return (EEXIST); 17839490a627SAlexander V. Chernikov } 17849490a627SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 17859490a627SAlexander V. Chernikov 17860468c5baSAlexander V. Chernikov return (create_table_internal(ch, &ti, aname, i, NULL, NULL, NULL, 0)); 1787db785d31SAlexander V. Chernikov } 1788db785d31SAlexander V. Chernikov 1789db785d31SAlexander V. Chernikov /* 1790db785d31SAlexander V. Chernikov * Creates new table based on @ti and @aname. 1791db785d31SAlexander V. Chernikov * 1792db785d31SAlexander V. Chernikov * Relies on table name checking inside find_name_tlv() 1793db785d31SAlexander V. Chernikov * Assume @aname to be checked and valid. 17940468c5baSAlexander V. Chernikov * Stores allocated table config, used algo and kidx 17950468c5baSAlexander V. Chernikov * inside @ptc, @pta and @pkidx (if non-NULL). 17960468c5baSAlexander V. Chernikov * Reference created table if @compat is non-zero. 1797db785d31SAlexander V. Chernikov * 1798db785d31SAlexander V. Chernikov * Returns 0 on success. 1799db785d31SAlexander V. Chernikov */ 1800db785d31SAlexander V. Chernikov static int 1801db785d31SAlexander V. Chernikov create_table_internal(struct ip_fw_chain *ch, struct tid_info *ti, 18020468c5baSAlexander V. Chernikov char *aname, ipfw_xtable_info *i, struct table_config **ptc, 18030468c5baSAlexander V. Chernikov struct table_algo **pta, uint16_t *pkidx, int compat) 1804db785d31SAlexander V. Chernikov { 1805db785d31SAlexander V. Chernikov struct namedobj_instance *ni; 18060468c5baSAlexander V. Chernikov struct table_config *tc, *tc_new, *tmp;; 1807db785d31SAlexander V. Chernikov struct table_algo *ta; 1808db785d31SAlexander V. Chernikov uint16_t kidx; 1809db785d31SAlexander V. Chernikov 1810db785d31SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 1811db785d31SAlexander V. Chernikov 1812db785d31SAlexander V. Chernikov ta = find_table_algo(CHAIN_TO_TCFG(ch), ti, aname); 18139490a627SAlexander V. Chernikov if (ta == NULL) 18149490a627SAlexander V. Chernikov return (ENOTSUP); 18159490a627SAlexander V. Chernikov 18164c0c07a5SAlexander V. Chernikov tc = alloc_table_config(ch, ti, ta, aname, i->tflags, i->vtype); 18174c0c07a5SAlexander V. Chernikov if (tc == NULL) 18189490a627SAlexander V. Chernikov return (ENOMEM); 18199490a627SAlexander V. Chernikov 1820adf3b2b9SAlexander V. Chernikov tc->vftype = i->vftype; 18214c0c07a5SAlexander V. Chernikov tc->limit = i->limit; 18224f43138aSAlexander V. Chernikov tc->locked = (i->flags & IPFW_TGFLAGS_LOCKED) != 0; 18234c0c07a5SAlexander V. Chernikov 18249490a627SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 1825db785d31SAlexander V. Chernikov 1826db785d31SAlexander V. Chernikov /* Check if table has been already created */ 18270468c5baSAlexander V. Chernikov tc_new = find_table(ni, ti); 18280468c5baSAlexander V. Chernikov if (tc_new != NULL) { 18290468c5baSAlexander V. Chernikov 18300468c5baSAlexander V. Chernikov /* 18310468c5baSAlexander V. Chernikov * Compat: do not fail if we're 18320468c5baSAlexander V. Chernikov * requesting to create existing table 18330468c5baSAlexander V. Chernikov * which has the same type / vtype 18340468c5baSAlexander V. Chernikov */ 18350468c5baSAlexander V. Chernikov if (compat == 0 || tc_new->no.type != tc->no.type || 18360468c5baSAlexander V. Chernikov tc_new->vtype != tc->vtype) { 1837db785d31SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1838db785d31SAlexander V. Chernikov free_table_config(ni, tc); 1839db785d31SAlexander V. Chernikov return (EEXIST); 1840db785d31SAlexander V. Chernikov } 1841db785d31SAlexander V. Chernikov 18420468c5baSAlexander V. Chernikov /* Exchange tc and tc_new for proper refcounting & freeing */ 18430468c5baSAlexander V. Chernikov tmp = tc; 18440468c5baSAlexander V. Chernikov tc = tc_new; 18450468c5baSAlexander V. Chernikov tc_new = tmp; 18460468c5baSAlexander V. Chernikov } else { 18470468c5baSAlexander V. Chernikov /* New table */ 1848ac35ff17SAlexander V. Chernikov if (ipfw_objhash_alloc_idx(ni, &kidx) != 0) { 18499490a627SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1850db785d31SAlexander V. Chernikov printf("Unable to allocate table index." 1851db785d31SAlexander V. Chernikov " Consider increasing net.inet.ip.fw.tables_max"); 18529490a627SAlexander V. Chernikov free_table_config(ni, tc); 18539490a627SAlexander V. Chernikov return (EBUSY); 18549490a627SAlexander V. Chernikov } 18559490a627SAlexander V. Chernikov tc->no.kidx = kidx; 18569490a627SAlexander V. Chernikov 18579490a627SAlexander V. Chernikov IPFW_WLOCK(ch); 18589490a627SAlexander V. Chernikov link_table(ch, tc); 18599490a627SAlexander V. Chernikov IPFW_WUNLOCK(ch); 18600468c5baSAlexander V. Chernikov } 18610468c5baSAlexander V. Chernikov 18620468c5baSAlexander V. Chernikov if (compat != 0) 18630468c5baSAlexander V. Chernikov tc->no.refcnt++; 18640468c5baSAlexander V. Chernikov if (ptc != NULL) 18650468c5baSAlexander V. Chernikov *ptc = tc; 18660468c5baSAlexander V. Chernikov if (pta != NULL) 18670468c5baSAlexander V. Chernikov *pta = ta; 18680468c5baSAlexander V. Chernikov if (pkidx != NULL) 18690468c5baSAlexander V. Chernikov *pkidx = tc->no.kidx; 18709490a627SAlexander V. Chernikov 18719490a627SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 18729490a627SAlexander V. Chernikov 18730468c5baSAlexander V. Chernikov if (tc_new != NULL) 18740468c5baSAlexander V. Chernikov free_table_config(ni, tc_new); 18750468c5baSAlexander V. Chernikov 18769490a627SAlexander V. Chernikov return (0); 18779490a627SAlexander V. Chernikov } 18789490a627SAlexander V. Chernikov 187946d52008SAlexander V. Chernikov static void 188046d52008SAlexander V. Chernikov ntlv_to_ti(ipfw_obj_ntlv *ntlv, struct tid_info *ti) 1881d3a4f924SAlexander V. Chernikov { 1882d3a4f924SAlexander V. Chernikov 1883d3a4f924SAlexander V. Chernikov memset(ti, 0, sizeof(struct tid_info)); 188446d52008SAlexander V. Chernikov ti->set = ntlv->set; 188546d52008SAlexander V. Chernikov ti->uidx = ntlv->idx; 188646d52008SAlexander V. Chernikov ti->tlvs = ntlv; 188746d52008SAlexander V. Chernikov ti->tlen = ntlv->head.length; 188846d52008SAlexander V. Chernikov } 188946d52008SAlexander V. Chernikov 189046d52008SAlexander V. Chernikov static void 189146d52008SAlexander V. Chernikov objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti) 189246d52008SAlexander V. Chernikov { 189346d52008SAlexander V. Chernikov 189446d52008SAlexander V. Chernikov ntlv_to_ti(&oh->ntlv, ti); 1895d3a4f924SAlexander V. Chernikov } 1896d3a4f924SAlexander V. Chernikov 189735e1bbd0SAlexander V. Chernikov /* 189835e1bbd0SAlexander V. Chernikov * Exports basic table info as name TLV. 189935e1bbd0SAlexander V. Chernikov * Used inside dump_static_rules() to provide info 190035e1bbd0SAlexander V. Chernikov * about all tables referenced by current ruleset. 190135e1bbd0SAlexander V. Chernikov * 190235e1bbd0SAlexander V. Chernikov * Returns 0 on success. 190335e1bbd0SAlexander V. Chernikov */ 1904563b5ab1SAlexander V. Chernikov int 1905563b5ab1SAlexander V. Chernikov ipfw_export_table_ntlv(struct ip_fw_chain *ch, uint16_t kidx, 1906563b5ab1SAlexander V. Chernikov struct sockopt_data *sd) 1907563b5ab1SAlexander V. Chernikov { 1908563b5ab1SAlexander V. Chernikov struct namedobj_instance *ni; 1909563b5ab1SAlexander V. Chernikov struct named_object *no; 1910563b5ab1SAlexander V. Chernikov ipfw_obj_ntlv *ntlv; 1911563b5ab1SAlexander V. Chernikov 1912563b5ab1SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 1913563b5ab1SAlexander V. Chernikov 1914ac35ff17SAlexander V. Chernikov no = ipfw_objhash_lookup_kidx(ni, kidx); 1915563b5ab1SAlexander V. Chernikov KASSERT(no != NULL, ("invalid table kidx passed")); 1916563b5ab1SAlexander V. Chernikov 1917563b5ab1SAlexander V. Chernikov ntlv = (ipfw_obj_ntlv *)ipfw_get_sopt_space(sd, sizeof(*ntlv)); 1918563b5ab1SAlexander V. Chernikov if (ntlv == NULL) 1919563b5ab1SAlexander V. Chernikov return (ENOMEM); 1920563b5ab1SAlexander V. Chernikov 1921563b5ab1SAlexander V. Chernikov ntlv->head.type = IPFW_TLV_TBL_NAME; 1922563b5ab1SAlexander V. Chernikov ntlv->head.length = sizeof(*ntlv); 1923563b5ab1SAlexander V. Chernikov ntlv->idx = no->kidx; 1924563b5ab1SAlexander V. Chernikov strlcpy(ntlv->name, no->name, sizeof(ntlv->name)); 1925563b5ab1SAlexander V. Chernikov 1926563b5ab1SAlexander V. Chernikov return (0); 1927563b5ab1SAlexander V. Chernikov } 1928563b5ab1SAlexander V. Chernikov 192935e1bbd0SAlexander V. Chernikov /* 193035e1bbd0SAlexander V. Chernikov * Exports table @tc info into standard ipfw_xtable_info format. 193135e1bbd0SAlexander V. Chernikov */ 19329f7d47b0SAlexander V. Chernikov static void 1933ac35ff17SAlexander V. Chernikov export_table_info(struct ip_fw_chain *ch, struct table_config *tc, 1934ac35ff17SAlexander V. Chernikov ipfw_xtable_info *i) 19359f7d47b0SAlexander V. Chernikov { 1936ac35ff17SAlexander V. Chernikov struct table_info *ti; 19375f379342SAlexander V. Chernikov struct table_algo *ta; 19389f7d47b0SAlexander V. Chernikov 19399f7d47b0SAlexander V. Chernikov i->type = tc->no.type; 1940914bffb6SAlexander V. Chernikov i->tflags = tc->tflags; 1941ac35ff17SAlexander V. Chernikov i->vtype = tc->vtype; 1942adf3b2b9SAlexander V. Chernikov i->vftype = tc->vftype; 19439f7d47b0SAlexander V. Chernikov i->set = tc->no.set; 19449f7d47b0SAlexander V. Chernikov i->kidx = tc->no.kidx; 19459f7d47b0SAlexander V. Chernikov i->refcnt = tc->no.refcnt; 19469f7d47b0SAlexander V. Chernikov i->count = tc->count; 19474c0c07a5SAlexander V. Chernikov i->limit = tc->limit; 19484f43138aSAlexander V. Chernikov i->flags |= (tc->locked != 0) ? IPFW_TGFLAGS_LOCKED : 0; 194981d3153dSAlexander V. Chernikov i->size = tc->count * sizeof(ipfw_obj_tentry); 1950f1220db8SAlexander V. Chernikov i->size += sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info); 19519f7d47b0SAlexander V. Chernikov strlcpy(i->tablename, tc->tablename, sizeof(i->tablename)); 1952ac35ff17SAlexander V. Chernikov ti = KIDX_TO_TI(ch, tc->no.kidx); 19535f379342SAlexander V. Chernikov ta = tc->ta; 19545f379342SAlexander V. Chernikov if (ta->print_config != NULL) { 19555f379342SAlexander V. Chernikov /* Use algo function to print table config to string */ 19565f379342SAlexander V. Chernikov ta->print_config(tc->astate, ti, i->algoname, 1957ac35ff17SAlexander V. Chernikov sizeof(i->algoname)); 1958ac35ff17SAlexander V. Chernikov } else 19595f379342SAlexander V. Chernikov strlcpy(i->algoname, ta->name, sizeof(i->algoname)); 19605f379342SAlexander V. Chernikov /* Dump algo-specific data, if possible */ 19615f379342SAlexander V. Chernikov if (ta->dump_tinfo != NULL) { 19625f379342SAlexander V. Chernikov ta->dump_tinfo(tc->astate, ti, &i->ta_info); 19635f379342SAlexander V. Chernikov i->ta_info.flags |= IPFW_TATFLAGS_DATA; 19645f379342SAlexander V. Chernikov } 19659f7d47b0SAlexander V. Chernikov } 19669f7d47b0SAlexander V. Chernikov 1967ac35ff17SAlexander V. Chernikov struct dump_table_args { 1968ac35ff17SAlexander V. Chernikov struct ip_fw_chain *ch; 1969ac35ff17SAlexander V. Chernikov struct sockopt_data *sd; 1970ac35ff17SAlexander V. Chernikov }; 1971ac35ff17SAlexander V. Chernikov 19729f7d47b0SAlexander V. Chernikov static void 19739f7d47b0SAlexander V. Chernikov export_table_internal(struct namedobj_instance *ni, struct named_object *no, 19749f7d47b0SAlexander V. Chernikov void *arg) 19753b3a8eb9SGleb Smirnoff { 19769f7d47b0SAlexander V. Chernikov ipfw_xtable_info *i; 1977ac35ff17SAlexander V. Chernikov struct dump_table_args *dta; 19783b3a8eb9SGleb Smirnoff 1979ac35ff17SAlexander V. Chernikov dta = (struct dump_table_args *)arg; 1980ac35ff17SAlexander V. Chernikov 1981ac35ff17SAlexander V. Chernikov i = (ipfw_xtable_info *)ipfw_get_sopt_space(dta->sd, sizeof(*i)); 198268394ec8SAlexander V. Chernikov KASSERT(i != 0, ("previously checked buffer is not enough")); 19839f7d47b0SAlexander V. Chernikov 1984ac35ff17SAlexander V. Chernikov export_table_info(dta->ch, (struct table_config *)no, i); 19859f7d47b0SAlexander V. Chernikov } 19869f7d47b0SAlexander V. Chernikov 1987f1220db8SAlexander V. Chernikov /* 1988f1220db8SAlexander V. Chernikov * Export all tables as ipfw_xtable_info structures to 19892d99a349SAlexander V. Chernikov * storage provided by @sd. 199028ea4fa3SAlexander V. Chernikov * 199128ea4fa3SAlexander V. Chernikov * If supplied buffer is too small, fills in required size 199228ea4fa3SAlexander V. Chernikov * and returns ENOMEM. 1993f1220db8SAlexander V. Chernikov * Returns 0 on success. 1994f1220db8SAlexander V. Chernikov */ 1995f1220db8SAlexander V. Chernikov static int 19962d99a349SAlexander V. Chernikov export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh, 19972d99a349SAlexander V. Chernikov struct sockopt_data *sd) 19989f7d47b0SAlexander V. Chernikov { 19999f7d47b0SAlexander V. Chernikov uint32_t size; 20009f7d47b0SAlexander V. Chernikov uint32_t count; 2001ac35ff17SAlexander V. Chernikov struct dump_table_args dta; 20029f7d47b0SAlexander V. Chernikov 20039f7d47b0SAlexander V. Chernikov count = ipfw_objhash_count(CHAIN_TO_NI(ch)); 20049f7d47b0SAlexander V. Chernikov size = count * sizeof(ipfw_xtable_info) + sizeof(ipfw_obj_lheader); 20052d99a349SAlexander V. Chernikov 20062d99a349SAlexander V. Chernikov /* Fill in header regadless of buffer size */ 2007f1220db8SAlexander V. Chernikov olh->count = count; 2008f1220db8SAlexander V. Chernikov olh->objsize = sizeof(ipfw_xtable_info); 20092d99a349SAlexander V. Chernikov 20102d99a349SAlexander V. Chernikov if (size > olh->size) { 20112d99a349SAlexander V. Chernikov olh->size = size; 20129f7d47b0SAlexander V. Chernikov return (ENOMEM); 2013f1220db8SAlexander V. Chernikov } 201468394ec8SAlexander V. Chernikov 20159f7d47b0SAlexander V. Chernikov olh->size = size; 20162d99a349SAlexander V. Chernikov 2017ac35ff17SAlexander V. Chernikov dta.ch = ch; 2018ac35ff17SAlexander V. Chernikov dta.sd = sd; 2019ac35ff17SAlexander V. Chernikov 2020ac35ff17SAlexander V. Chernikov ipfw_objhash_foreach(CHAIN_TO_NI(ch), export_table_internal, &dta); 20219f7d47b0SAlexander V. Chernikov 20223b3a8eb9SGleb Smirnoff return (0); 20233b3a8eb9SGleb Smirnoff } 20243b3a8eb9SGleb Smirnoff 202581d3153dSAlexander V. Chernikov /* 202681d3153dSAlexander V. Chernikov * Legacy IP_FW_TABLE_GETSIZE handler 202781d3153dSAlexander V. Chernikov */ 20283b3a8eb9SGleb Smirnoff int 2029b074b7bbSAlexander V. Chernikov ipfw_count_table(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt) 20303b3a8eb9SGleb Smirnoff { 2031b074b7bbSAlexander V. Chernikov struct table_config *tc; 20323b3a8eb9SGleb Smirnoff 2033b074b7bbSAlexander V. Chernikov if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL) 2034b074b7bbSAlexander V. Chernikov return (ESRCH); 20359f7d47b0SAlexander V. Chernikov *cnt = tc->count; 20363b3a8eb9SGleb Smirnoff return (0); 20373b3a8eb9SGleb Smirnoff } 20383b3a8eb9SGleb Smirnoff 203981d3153dSAlexander V. Chernikov /* 204081d3153dSAlexander V. Chernikov * Legacy IP_FW_TABLE_XGETSIZE handler 204181d3153dSAlexander V. Chernikov */ 20429f7d47b0SAlexander V. Chernikov int 20439f7d47b0SAlexander V. Chernikov ipfw_count_xtable(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt) 20443b3a8eb9SGleb Smirnoff { 20459f7d47b0SAlexander V. Chernikov struct table_config *tc; 20469f7d47b0SAlexander V. Chernikov 2047d3a4f924SAlexander V. Chernikov if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL) { 2048d3a4f924SAlexander V. Chernikov *cnt = 0; 20499f7d47b0SAlexander V. Chernikov return (0); /* 'table all list' requires success */ 2050d3a4f924SAlexander V. Chernikov } 20519f7d47b0SAlexander V. Chernikov *cnt = tc->count * sizeof(ipfw_table_xentry); 20529f7d47b0SAlexander V. Chernikov if (tc->count > 0) 20539f7d47b0SAlexander V. Chernikov *cnt += sizeof(ipfw_xtable); 20549f7d47b0SAlexander V. Chernikov return (0); 20559f7d47b0SAlexander V. Chernikov } 20569f7d47b0SAlexander V. Chernikov 20579f7d47b0SAlexander V. Chernikov static int 20589f7d47b0SAlexander V. Chernikov dump_table_entry(void *e, void *arg) 20599f7d47b0SAlexander V. Chernikov { 20609f7d47b0SAlexander V. Chernikov struct dump_args *da; 20619f7d47b0SAlexander V. Chernikov struct table_config *tc; 20629f7d47b0SAlexander V. Chernikov struct table_algo *ta; 20633b3a8eb9SGleb Smirnoff ipfw_table_entry *ent; 206481d3153dSAlexander V. Chernikov int error; 20653b3a8eb9SGleb Smirnoff 20669f7d47b0SAlexander V. Chernikov da = (struct dump_args *)arg; 20679f7d47b0SAlexander V. Chernikov 20689f7d47b0SAlexander V. Chernikov tc = da->tc; 20699f7d47b0SAlexander V. Chernikov ta = tc->ta; 20709f7d47b0SAlexander V. Chernikov 20719f7d47b0SAlexander V. Chernikov /* Out of memory, returning */ 2072f1220db8SAlexander V. Chernikov if (da->cnt == da->size) 20733b3a8eb9SGleb Smirnoff return (1); 2074f1220db8SAlexander V. Chernikov ent = da->ent++; 2075f1220db8SAlexander V. Chernikov ent->tbl = da->uidx; 2076f1220db8SAlexander V. Chernikov da->cnt++; 20779f7d47b0SAlexander V. Chernikov 207881d3153dSAlexander V. Chernikov error = ta->dump_tentry(tc->astate, da->ti, e, &da->tent); 207981d3153dSAlexander V. Chernikov if (error != 0) 208081d3153dSAlexander V. Chernikov return (error); 208181d3153dSAlexander V. Chernikov 208281d3153dSAlexander V. Chernikov ent->addr = da->tent.k.addr.s_addr; 208381d3153dSAlexander V. Chernikov ent->masklen = da->tent.masklen; 208481d3153dSAlexander V. Chernikov ent->value = da->tent.value; 208581d3153dSAlexander V. Chernikov 208681d3153dSAlexander V. Chernikov return (0); 20873b3a8eb9SGleb Smirnoff } 20883b3a8eb9SGleb Smirnoff 2089f1220db8SAlexander V. Chernikov /* 2090f1220db8SAlexander V. Chernikov * Dumps table in pre-8.1 legacy format. 2091f1220db8SAlexander V. Chernikov */ 20923b3a8eb9SGleb Smirnoff int 2093f1220db8SAlexander V. Chernikov ipfw_dump_table_legacy(struct ip_fw_chain *ch, struct tid_info *ti, 2094f1220db8SAlexander V. Chernikov ipfw_table *tbl) 20953b3a8eb9SGleb Smirnoff { 2096b074b7bbSAlexander V. Chernikov struct table_config *tc; 20979f7d47b0SAlexander V. Chernikov struct table_algo *ta; 20989f7d47b0SAlexander V. Chernikov struct dump_args da; 20993b3a8eb9SGleb Smirnoff 21003b3a8eb9SGleb Smirnoff tbl->cnt = 0; 21013b3a8eb9SGleb Smirnoff 2102b074b7bbSAlexander V. Chernikov if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL) 2103b074b7bbSAlexander V. Chernikov return (0); /* XXX: We should return ESRCH */ 21049f7d47b0SAlexander V. Chernikov 21059f7d47b0SAlexander V. Chernikov ta = tc->ta; 21069f7d47b0SAlexander V. Chernikov 210781d3153dSAlexander V. Chernikov /* This dump format supports IPv4 only */ 210881d3153dSAlexander V. Chernikov if (tc->no.type != IPFW_TABLE_CIDR) 210981d3153dSAlexander V. Chernikov return (0); 21109f7d47b0SAlexander V. Chernikov 2111d3a4f924SAlexander V. Chernikov memset(&da, 0, sizeof(da)); 21129f7d47b0SAlexander V. Chernikov da.ti = KIDX_TO_TI(ch, tc->no.kidx); 21139f7d47b0SAlexander V. Chernikov da.tc = tc; 2114f1220db8SAlexander V. Chernikov da.ent = &tbl->ent[0]; 2115f1220db8SAlexander V. Chernikov da.size = tbl->size; 21169f7d47b0SAlexander V. Chernikov 21179f7d47b0SAlexander V. Chernikov tbl->cnt = 0; 21189f7d47b0SAlexander V. Chernikov ta->foreach(tc->astate, da.ti, dump_table_entry, &da); 2119f1220db8SAlexander V. Chernikov tbl->cnt = da.cnt; 21209f7d47b0SAlexander V. Chernikov 21213b3a8eb9SGleb Smirnoff return (0); 21223b3a8eb9SGleb Smirnoff } 21233b3a8eb9SGleb Smirnoff 21249490a627SAlexander V. Chernikov /* 212581d3153dSAlexander V. Chernikov * Dumps table entry in eXtended format (v1)(current). 212681d3153dSAlexander V. Chernikov */ 212781d3153dSAlexander V. Chernikov static int 212881d3153dSAlexander V. Chernikov dump_table_tentry(void *e, void *arg) 212981d3153dSAlexander V. Chernikov { 213081d3153dSAlexander V. Chernikov struct dump_args *da; 213181d3153dSAlexander V. Chernikov struct table_config *tc; 213281d3153dSAlexander V. Chernikov struct table_algo *ta; 213381d3153dSAlexander V. Chernikov ipfw_obj_tentry *tent; 213481d3153dSAlexander V. Chernikov 213581d3153dSAlexander V. Chernikov da = (struct dump_args *)arg; 213681d3153dSAlexander V. Chernikov 213781d3153dSAlexander V. Chernikov tc = da->tc; 213881d3153dSAlexander V. Chernikov ta = tc->ta; 213981d3153dSAlexander V. Chernikov 214081d3153dSAlexander V. Chernikov tent = (ipfw_obj_tentry *)ipfw_get_sopt_space(da->sd, sizeof(*tent)); 214181d3153dSAlexander V. Chernikov /* Out of memory, returning */ 214281d3153dSAlexander V. Chernikov if (tent == NULL) { 214381d3153dSAlexander V. Chernikov da->error = ENOMEM; 214481d3153dSAlexander V. Chernikov return (1); 214581d3153dSAlexander V. Chernikov } 214681d3153dSAlexander V. Chernikov tent->head.length = sizeof(ipfw_obj_tentry); 214781d3153dSAlexander V. Chernikov tent->idx = da->uidx; 214881d3153dSAlexander V. Chernikov 214981d3153dSAlexander V. Chernikov return (ta->dump_tentry(tc->astate, da->ti, e, tent)); 215081d3153dSAlexander V. Chernikov } 215181d3153dSAlexander V. Chernikov 215281d3153dSAlexander V. Chernikov /* 215381d3153dSAlexander V. Chernikov * Dumps table entry in eXtended format (v0). 21549490a627SAlexander V. Chernikov */ 21553b3a8eb9SGleb Smirnoff static int 21569f7d47b0SAlexander V. Chernikov dump_table_xentry(void *e, void *arg) 21573b3a8eb9SGleb Smirnoff { 21589f7d47b0SAlexander V. Chernikov struct dump_args *da; 21599f7d47b0SAlexander V. Chernikov struct table_config *tc; 21609f7d47b0SAlexander V. Chernikov struct table_algo *ta; 21613b3a8eb9SGleb Smirnoff ipfw_table_xentry *xent; 216281d3153dSAlexander V. Chernikov ipfw_obj_tentry *tent; 216381d3153dSAlexander V. Chernikov int error; 21643b3a8eb9SGleb Smirnoff 21659f7d47b0SAlexander V. Chernikov da = (struct dump_args *)arg; 21669f7d47b0SAlexander V. Chernikov 21679f7d47b0SAlexander V. Chernikov tc = da->tc; 21689f7d47b0SAlexander V. Chernikov ta = tc->ta; 21699f7d47b0SAlexander V. Chernikov 21702d99a349SAlexander V. Chernikov xent = (ipfw_table_xentry *)ipfw_get_sopt_space(da->sd, sizeof(*xent)); 21713b3a8eb9SGleb Smirnoff /* Out of memory, returning */ 21722d99a349SAlexander V. Chernikov if (xent == NULL) 21733b3a8eb9SGleb Smirnoff return (1); 21743b3a8eb9SGleb Smirnoff xent->len = sizeof(ipfw_table_xentry); 2175f1220db8SAlexander V. Chernikov xent->tbl = da->uidx; 21769f7d47b0SAlexander V. Chernikov 217781d3153dSAlexander V. Chernikov memset(&da->tent, 0, sizeof(da->tent)); 217881d3153dSAlexander V. Chernikov tent = &da->tent; 217981d3153dSAlexander V. Chernikov error = ta->dump_tentry(tc->astate, da->ti, e, tent); 218081d3153dSAlexander V. Chernikov if (error != 0) 218181d3153dSAlexander V. Chernikov return (error); 218281d3153dSAlexander V. Chernikov 218381d3153dSAlexander V. Chernikov /* Convert current format to previous one */ 218481d3153dSAlexander V. Chernikov xent->masklen = tent->masklen; 218581d3153dSAlexander V. Chernikov xent->value = tent->value; 218681d3153dSAlexander V. Chernikov /* Apply some hacks */ 218781d3153dSAlexander V. Chernikov if (tc->no.type == IPFW_TABLE_CIDR && tent->subtype == AF_INET) { 218881d3153dSAlexander V. Chernikov xent->k.addr6.s6_addr32[3] = tent->k.addr.s_addr; 218981d3153dSAlexander V. Chernikov xent->flags = IPFW_TCF_INET; 219081d3153dSAlexander V. Chernikov } else 219181d3153dSAlexander V. Chernikov memcpy(&xent->k, &tent->k, sizeof(xent->k)); 219281d3153dSAlexander V. Chernikov 219381d3153dSAlexander V. Chernikov return (0); 21949f7d47b0SAlexander V. Chernikov } 21959f7d47b0SAlexander V. Chernikov 21969f7d47b0SAlexander V. Chernikov /* 21979f7d47b0SAlexander V. Chernikov * Table algorithms 21989f7d47b0SAlexander V. Chernikov */ 21993b3a8eb9SGleb Smirnoff 22009f7d47b0SAlexander V. Chernikov /* 220135e1bbd0SAlexander V. Chernikov * Finds algoritm by index, table type or supplied name. 220235e1bbd0SAlexander V. Chernikov * 220335e1bbd0SAlexander V. Chernikov * Returns pointer to algo or NULL. 22049f7d47b0SAlexander V. Chernikov */ 22059f7d47b0SAlexander V. Chernikov static struct table_algo * 22069490a627SAlexander V. Chernikov find_table_algo(struct tables_config *tcfg, struct tid_info *ti, char *name) 22079f7d47b0SAlexander V. Chernikov { 22089490a627SAlexander V. Chernikov int i, l; 22099490a627SAlexander V. Chernikov struct table_algo *ta; 22109f7d47b0SAlexander V. Chernikov 221157a1cf95SAlexander V. Chernikov if (ti->type > IPFW_TABLE_MAXTYPE) 221257a1cf95SAlexander V. Chernikov return (NULL); 221357a1cf95SAlexander V. Chernikov 22149f7d47b0SAlexander V. Chernikov /* Search by index */ 22159f7d47b0SAlexander V. Chernikov if (ti->atype != 0) { 22169f7d47b0SAlexander V. Chernikov if (ti->atype > tcfg->algo_count) 22179f7d47b0SAlexander V. Chernikov return (NULL); 22189f7d47b0SAlexander V. Chernikov return (tcfg->algo[ti->atype]); 22199f7d47b0SAlexander V. Chernikov } 22209f7d47b0SAlexander V. Chernikov 22219490a627SAlexander V. Chernikov /* Search by name if supplied */ 22229490a627SAlexander V. Chernikov if (name != NULL) { 22239490a627SAlexander V. Chernikov /* TODO: better search */ 22249490a627SAlexander V. Chernikov for (i = 1; i <= tcfg->algo_count; i++) { 22259490a627SAlexander V. Chernikov ta = tcfg->algo[i]; 22269490a627SAlexander V. Chernikov 22279490a627SAlexander V. Chernikov /* 22289490a627SAlexander V. Chernikov * One can supply additional algorithm 22299490a627SAlexander V. Chernikov * parameters so we compare only the first word 22309490a627SAlexander V. Chernikov * of supplied name: 22319490a627SAlexander V. Chernikov * 'hash_cidr hsize=32' 22329490a627SAlexander V. Chernikov * '^^^^^^^^^' 22339490a627SAlexander V. Chernikov * 22349490a627SAlexander V. Chernikov */ 22359490a627SAlexander V. Chernikov l = strlen(ta->name); 22369490a627SAlexander V. Chernikov if (strncmp(name, ta->name, l) == 0) { 22379490a627SAlexander V. Chernikov if (name[l] == '\0' || name[l] == ' ') 22389490a627SAlexander V. Chernikov return (ta); 22399490a627SAlexander V. Chernikov } 22409490a627SAlexander V. Chernikov } 22419490a627SAlexander V. Chernikov 22429490a627SAlexander V. Chernikov return (NULL); 22439490a627SAlexander V. Chernikov } 22449490a627SAlexander V. Chernikov 224557a1cf95SAlexander V. Chernikov /* Return default algorithm for given type if set */ 224657a1cf95SAlexander V. Chernikov return (tcfg->def_algo[ti->type]); 22473b3a8eb9SGleb Smirnoff } 22483b3a8eb9SGleb Smirnoff 2249b6ee846eSAlexander V. Chernikov /* 2250b6ee846eSAlexander V. Chernikov * Register new table algo @ta. 225135e1bbd0SAlexander V. Chernikov * Stores algo id inside @idx. 2252b6ee846eSAlexander V. Chernikov * 2253b6ee846eSAlexander V. Chernikov * Returns 0 on success. 2254b6ee846eSAlexander V. Chernikov */ 22550b565ac0SAlexander V. Chernikov int 22560b565ac0SAlexander V. Chernikov ipfw_add_table_algo(struct ip_fw_chain *ch, struct table_algo *ta, size_t size, 22570b565ac0SAlexander V. Chernikov int *idx) 22583b3a8eb9SGleb Smirnoff { 22599f7d47b0SAlexander V. Chernikov struct tables_config *tcfg; 22600b565ac0SAlexander V. Chernikov struct table_algo *ta_new; 2261b6ee846eSAlexander V. Chernikov size_t sz; 22620b565ac0SAlexander V. Chernikov 22630b565ac0SAlexander V. Chernikov if (size > sizeof(struct table_algo)) 22640b565ac0SAlexander V. Chernikov return (EINVAL); 22650b565ac0SAlexander V. Chernikov 2266b6ee846eSAlexander V. Chernikov /* Check for the required on-stack size for add/del */ 2267b6ee846eSAlexander V. Chernikov sz = roundup2(ta->ta_buf_size, sizeof(void *)); 2268b6ee846eSAlexander V. Chernikov if (sz > TA_BUF_SZ) 2269b6ee846eSAlexander V. Chernikov return (EINVAL); 2270b6ee846eSAlexander V. Chernikov 227157a1cf95SAlexander V. Chernikov KASSERT(ta->type >= IPFW_TABLE_MAXTYPE,("Increase IPFW_TABLE_MAXTYPE")); 227257a1cf95SAlexander V. Chernikov 227335e1bbd0SAlexander V. Chernikov /* Copy algorithm data to stable storage. */ 22740b565ac0SAlexander V. Chernikov ta_new = malloc(sizeof(struct table_algo), M_IPFW, M_WAITOK | M_ZERO); 22750b565ac0SAlexander V. Chernikov memcpy(ta_new, ta, size); 22763b3a8eb9SGleb Smirnoff 22779f7d47b0SAlexander V. Chernikov tcfg = CHAIN_TO_TCFG(ch); 2278b074b7bbSAlexander V. Chernikov 22799f7d47b0SAlexander V. Chernikov KASSERT(tcfg->algo_count < 255, ("Increase algo array size")); 22809f7d47b0SAlexander V. Chernikov 22810b565ac0SAlexander V. Chernikov tcfg->algo[++tcfg->algo_count] = ta_new; 22820b565ac0SAlexander V. Chernikov ta_new->idx = tcfg->algo_count; 22830b565ac0SAlexander V. Chernikov 228457a1cf95SAlexander V. Chernikov /* Set algorithm as default one for given type */ 228557a1cf95SAlexander V. Chernikov if ((ta_new->flags & TA_FLAG_DEFAULT) != 0 && 228657a1cf95SAlexander V. Chernikov tcfg->def_algo[ta_new->type] == NULL) 228757a1cf95SAlexander V. Chernikov tcfg->def_algo[ta_new->type] = ta_new; 228857a1cf95SAlexander V. Chernikov 22890b565ac0SAlexander V. Chernikov *idx = ta_new->idx; 22900b565ac0SAlexander V. Chernikov 22910b565ac0SAlexander V. Chernikov return (0); 22920b565ac0SAlexander V. Chernikov } 22930b565ac0SAlexander V. Chernikov 2294b6ee846eSAlexander V. Chernikov /* 2295b6ee846eSAlexander V. Chernikov * Unregisters table algo using @idx as id. 2296b6ee846eSAlexander V. Chernikov */ 22970b565ac0SAlexander V. Chernikov void 22980b565ac0SAlexander V. Chernikov ipfw_del_table_algo(struct ip_fw_chain *ch, int idx) 22990b565ac0SAlexander V. Chernikov { 23000b565ac0SAlexander V. Chernikov struct tables_config *tcfg; 23010b565ac0SAlexander V. Chernikov struct table_algo *ta; 23020b565ac0SAlexander V. Chernikov 23030b565ac0SAlexander V. Chernikov tcfg = CHAIN_TO_TCFG(ch); 23040b565ac0SAlexander V. Chernikov 2305b6ee846eSAlexander V. Chernikov KASSERT(idx <= tcfg->algo_count, ("algo idx %d out of range 1..%d", 2306b6ee846eSAlexander V. Chernikov idx, tcfg->algo_count)); 23070b565ac0SAlexander V. Chernikov 23080b565ac0SAlexander V. Chernikov ta = tcfg->algo[idx]; 23090b565ac0SAlexander V. Chernikov KASSERT(ta != NULL, ("algo idx %d is NULL", idx)); 231057a1cf95SAlexander V. Chernikov 231157a1cf95SAlexander V. Chernikov if (tcfg->def_algo[ta->type] == ta) 231257a1cf95SAlexander V. Chernikov tcfg->def_algo[ta->type] = NULL; 231357a1cf95SAlexander V. Chernikov 23140b565ac0SAlexander V. Chernikov free(ta, M_IPFW); 23153b3a8eb9SGleb Smirnoff } 23163b3a8eb9SGleb Smirnoff 23179d099b4fSAlexander V. Chernikov /* 23189d099b4fSAlexander V. Chernikov * Lists all table algorithms currently available. 23199d099b4fSAlexander V. Chernikov * Data layout (v0)(current): 23209d099b4fSAlexander V. Chernikov * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size 23219d099b4fSAlexander V. Chernikov * Reply: [ ipfw_obj_lheader ipfw_ta_info x N ] 23229d099b4fSAlexander V. Chernikov * 23239d099b4fSAlexander V. Chernikov * Returns 0 on success 23249d099b4fSAlexander V. Chernikov */ 23259d099b4fSAlexander V. Chernikov int 23269d099b4fSAlexander V. Chernikov ipfw_list_table_algo(struct ip_fw_chain *ch, struct sockopt_data *sd) 23279d099b4fSAlexander V. Chernikov { 23289d099b4fSAlexander V. Chernikov struct _ipfw_obj_lheader *olh; 23299d099b4fSAlexander V. Chernikov struct tables_config *tcfg; 23309d099b4fSAlexander V. Chernikov ipfw_ta_info *i; 23319d099b4fSAlexander V. Chernikov struct table_algo *ta; 23329d099b4fSAlexander V. Chernikov uint32_t count, n, size; 23339d099b4fSAlexander V. Chernikov 23349d099b4fSAlexander V. Chernikov olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh)); 23359d099b4fSAlexander V. Chernikov if (olh == NULL) 23369d099b4fSAlexander V. Chernikov return (EINVAL); 23379d099b4fSAlexander V. Chernikov if (sd->valsize < olh->size) 23389d099b4fSAlexander V. Chernikov return (EINVAL); 23399d099b4fSAlexander V. Chernikov 23409d099b4fSAlexander V. Chernikov IPFW_UH_RLOCK(ch); 23419d099b4fSAlexander V. Chernikov tcfg = CHAIN_TO_TCFG(ch); 23429d099b4fSAlexander V. Chernikov count = tcfg->algo_count; 23439d099b4fSAlexander V. Chernikov size = count * sizeof(ipfw_ta_info) + sizeof(ipfw_obj_lheader); 23449d099b4fSAlexander V. Chernikov 23459d099b4fSAlexander V. Chernikov /* Fill in header regadless of buffer size */ 23469d099b4fSAlexander V. Chernikov olh->count = count; 23479d099b4fSAlexander V. Chernikov olh->objsize = sizeof(ipfw_ta_info); 23489d099b4fSAlexander V. Chernikov 23499d099b4fSAlexander V. Chernikov if (size > olh->size) { 23509d099b4fSAlexander V. Chernikov olh->size = size; 23519d099b4fSAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 23529d099b4fSAlexander V. Chernikov return (ENOMEM); 23539d099b4fSAlexander V. Chernikov } 23549d099b4fSAlexander V. Chernikov olh->size = size; 23559d099b4fSAlexander V. Chernikov 23569d099b4fSAlexander V. Chernikov for (n = 1; n <= count; n++) { 23579d099b4fSAlexander V. Chernikov i = (ipfw_ta_info *)ipfw_get_sopt_space(sd, sizeof(*i)); 23589d099b4fSAlexander V. Chernikov KASSERT(i != 0, ("previously checked buffer is not enough")); 23599d099b4fSAlexander V. Chernikov ta = tcfg->algo[n]; 23609d099b4fSAlexander V. Chernikov strlcpy(i->algoname, ta->name, sizeof(i->algoname)); 23619d099b4fSAlexander V. Chernikov i->type = ta->type; 23629d099b4fSAlexander V. Chernikov i->refcnt = ta->refcnt; 23639d099b4fSAlexander V. Chernikov } 23649d099b4fSAlexander V. Chernikov 23659d099b4fSAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 23669d099b4fSAlexander V. Chernikov 23679d099b4fSAlexander V. Chernikov return (0); 23689d099b4fSAlexander V. Chernikov } 23699d099b4fSAlexander V. Chernikov 23709f7d47b0SAlexander V. Chernikov 2371b074b7bbSAlexander V. Chernikov /* 2372b074b7bbSAlexander V. Chernikov * Tables rewriting code 2373b074b7bbSAlexander V. Chernikov * 2374b074b7bbSAlexander V. Chernikov */ 2375b074b7bbSAlexander V. Chernikov 2376b074b7bbSAlexander V. Chernikov /* 2377b074b7bbSAlexander V. Chernikov * Determine table number and lookup type for @cmd. 2378b074b7bbSAlexander V. Chernikov * Fill @tbl and @type with appropriate values. 2379b074b7bbSAlexander V. Chernikov * Returns 0 for relevant opcodes, 1 otherwise. 2380b074b7bbSAlexander V. Chernikov */ 2381b074b7bbSAlexander V. Chernikov static int 2382b074b7bbSAlexander V. Chernikov classify_table_opcode(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) 2383b074b7bbSAlexander V. Chernikov { 2384b074b7bbSAlexander V. Chernikov ipfw_insn_if *cmdif; 2385b074b7bbSAlexander V. Chernikov int skip; 2386b074b7bbSAlexander V. Chernikov uint16_t v; 2387b074b7bbSAlexander V. Chernikov 2388b074b7bbSAlexander V. Chernikov skip = 1; 2389b074b7bbSAlexander V. Chernikov 2390b074b7bbSAlexander V. Chernikov switch (cmd->opcode) { 2391b074b7bbSAlexander V. Chernikov case O_IP_SRC_LOOKUP: 2392b074b7bbSAlexander V. Chernikov case O_IP_DST_LOOKUP: 2393b074b7bbSAlexander V. Chernikov /* Basic IPv4/IPv6 or u32 lookups */ 2394b074b7bbSAlexander V. Chernikov *puidx = cmd->arg1; 2395b074b7bbSAlexander V. Chernikov /* Assume CIDR by default */ 2396b074b7bbSAlexander V. Chernikov *ptype = IPFW_TABLE_CIDR; 2397b074b7bbSAlexander V. Chernikov skip = 0; 2398b074b7bbSAlexander V. Chernikov 2399b074b7bbSAlexander V. Chernikov if (F_LEN(cmd) > F_INSN_SIZE(ipfw_insn_u32)) { 2400b074b7bbSAlexander V. Chernikov /* 2401b074b7bbSAlexander V. Chernikov * generic lookup. The key must be 2402b074b7bbSAlexander V. Chernikov * in 32bit big-endian format. 2403b074b7bbSAlexander V. Chernikov */ 2404b074b7bbSAlexander V. Chernikov v = ((ipfw_insn_u32 *)cmd)->d[1]; 2405b074b7bbSAlexander V. Chernikov switch (v) { 2406b074b7bbSAlexander V. Chernikov case 0: 2407b074b7bbSAlexander V. Chernikov case 1: 2408b074b7bbSAlexander V. Chernikov /* IPv4 src/dst */ 2409b074b7bbSAlexander V. Chernikov break; 2410b074b7bbSAlexander V. Chernikov case 2: 2411b074b7bbSAlexander V. Chernikov case 3: 2412b074b7bbSAlexander V. Chernikov /* src/dst port */ 2413b23d5de9SAlexander V. Chernikov *ptype = IPFW_TABLE_NUMBER; 2414b074b7bbSAlexander V. Chernikov break; 2415b074b7bbSAlexander V. Chernikov case 4: 2416b074b7bbSAlexander V. Chernikov /* uid/gid */ 2417b23d5de9SAlexander V. Chernikov *ptype = IPFW_TABLE_NUMBER; 2418b23d5de9SAlexander V. Chernikov break; 2419b074b7bbSAlexander V. Chernikov case 5: 2420b074b7bbSAlexander V. Chernikov /* jid */ 2421b23d5de9SAlexander V. Chernikov *ptype = IPFW_TABLE_NUMBER; 2422b23d5de9SAlexander V. Chernikov break; 2423b074b7bbSAlexander V. Chernikov case 6: 2424b074b7bbSAlexander V. Chernikov /* dscp */ 2425b23d5de9SAlexander V. Chernikov *ptype = IPFW_TABLE_NUMBER; 2426b074b7bbSAlexander V. Chernikov break; 2427b074b7bbSAlexander V. Chernikov } 2428b074b7bbSAlexander V. Chernikov } 2429b074b7bbSAlexander V. Chernikov break; 2430b074b7bbSAlexander V. Chernikov case O_XMIT: 2431b074b7bbSAlexander V. Chernikov case O_RECV: 2432b074b7bbSAlexander V. Chernikov case O_VIA: 2433b074b7bbSAlexander V. Chernikov /* Interface table, possibly */ 2434b074b7bbSAlexander V. Chernikov cmdif = (ipfw_insn_if *)cmd; 2435b074b7bbSAlexander V. Chernikov if (cmdif->name[0] != '\1') 2436b074b7bbSAlexander V. Chernikov break; 2437b074b7bbSAlexander V. Chernikov 2438b074b7bbSAlexander V. Chernikov *ptype = IPFW_TABLE_INTERFACE; 24398bd19212SAlexander V. Chernikov *puidx = cmdif->p.glob; 2440b074b7bbSAlexander V. Chernikov skip = 0; 2441b074b7bbSAlexander V. Chernikov break; 2442914bffb6SAlexander V. Chernikov case O_IP_FLOW_LOOKUP: 2443914bffb6SAlexander V. Chernikov *puidx = cmd->arg1; 2444914bffb6SAlexander V. Chernikov *ptype = IPFW_TABLE_FLOW; 2445914bffb6SAlexander V. Chernikov skip = 0; 2446914bffb6SAlexander V. Chernikov break; 2447b074b7bbSAlexander V. Chernikov } 2448b074b7bbSAlexander V. Chernikov 2449b074b7bbSAlexander V. Chernikov return (skip); 2450b074b7bbSAlexander V. Chernikov } 2451b074b7bbSAlexander V. Chernikov 2452b074b7bbSAlexander V. Chernikov /* 2453b074b7bbSAlexander V. Chernikov * Sets new table value for given opcode. 2454b074b7bbSAlexander V. Chernikov * Assume the same opcodes as classify_table_opcode() 2455b074b7bbSAlexander V. Chernikov */ 2456b074b7bbSAlexander V. Chernikov static void 2457b074b7bbSAlexander V. Chernikov update_table_opcode(ipfw_insn *cmd, uint16_t idx) 2458b074b7bbSAlexander V. Chernikov { 2459b074b7bbSAlexander V. Chernikov ipfw_insn_if *cmdif; 2460b074b7bbSAlexander V. Chernikov 2461b074b7bbSAlexander V. Chernikov switch (cmd->opcode) { 2462b074b7bbSAlexander V. Chernikov case O_IP_SRC_LOOKUP: 2463b074b7bbSAlexander V. Chernikov case O_IP_DST_LOOKUP: 2464b074b7bbSAlexander V. Chernikov /* Basic IPv4/IPv6 or u32 lookups */ 2465b074b7bbSAlexander V. Chernikov cmd->arg1 = idx; 2466b074b7bbSAlexander V. Chernikov break; 2467b074b7bbSAlexander V. Chernikov case O_XMIT: 2468b074b7bbSAlexander V. Chernikov case O_RECV: 2469b074b7bbSAlexander V. Chernikov case O_VIA: 2470b074b7bbSAlexander V. Chernikov /* Interface table, possibly */ 2471b074b7bbSAlexander V. Chernikov cmdif = (ipfw_insn_if *)cmd; 24728bd19212SAlexander V. Chernikov cmdif->p.glob = idx; 2473b074b7bbSAlexander V. Chernikov break; 2474914bffb6SAlexander V. Chernikov case O_IP_FLOW_LOOKUP: 2475914bffb6SAlexander V. Chernikov cmd->arg1 = idx; 2476914bffb6SAlexander V. Chernikov break; 2477b074b7bbSAlexander V. Chernikov } 2478b074b7bbSAlexander V. Chernikov } 2479b074b7bbSAlexander V. Chernikov 2480ac35ff17SAlexander V. Chernikov /* 2481ac35ff17SAlexander V. Chernikov * Checks table name for validity. 2482ac35ff17SAlexander V. Chernikov * Enforce basic length checks, the rest 2483ac35ff17SAlexander V. Chernikov * should be done in userland. 2484ac35ff17SAlexander V. Chernikov * 2485ac35ff17SAlexander V. Chernikov * Returns 0 if name is considered valid. 2486ac35ff17SAlexander V. Chernikov */ 2487ac35ff17SAlexander V. Chernikov int 2488ac35ff17SAlexander V. Chernikov ipfw_check_table_name(char *name) 2489ac35ff17SAlexander V. Chernikov { 2490ac35ff17SAlexander V. Chernikov int nsize; 2491ac35ff17SAlexander V. Chernikov ipfw_obj_ntlv *ntlv = NULL; 2492ac35ff17SAlexander V. Chernikov 2493ac35ff17SAlexander V. Chernikov nsize = sizeof(ntlv->name); 2494ac35ff17SAlexander V. Chernikov 2495ac35ff17SAlexander V. Chernikov if (strnlen(name, nsize) == nsize) 2496ac35ff17SAlexander V. Chernikov return (EINVAL); 2497ac35ff17SAlexander V. Chernikov 2498ac35ff17SAlexander V. Chernikov if (name[0] == '\0') 2499ac35ff17SAlexander V. Chernikov return (EINVAL); 2500ac35ff17SAlexander V. Chernikov 2501ac35ff17SAlexander V. Chernikov /* 2502ac35ff17SAlexander V. Chernikov * TODO: do some more complicated checks 2503ac35ff17SAlexander V. Chernikov */ 2504ac35ff17SAlexander V. Chernikov 2505ac35ff17SAlexander V. Chernikov return (0); 2506ac35ff17SAlexander V. Chernikov } 2507ac35ff17SAlexander V. Chernikov 2508ac35ff17SAlexander V. Chernikov /* 2509ac35ff17SAlexander V. Chernikov * Find tablename TLV by @uid. 2510ac35ff17SAlexander V. Chernikov * Check @tlvs for valid data inside. 2511ac35ff17SAlexander V. Chernikov * 2512ac35ff17SAlexander V. Chernikov * Returns pointer to found TLV or NULL. 2513ac35ff17SAlexander V. Chernikov */ 2514ac35ff17SAlexander V. Chernikov static ipfw_obj_ntlv * 2515b074b7bbSAlexander V. Chernikov find_name_tlv(void *tlvs, int len, uint16_t uidx) 2516b074b7bbSAlexander V. Chernikov { 25179f7d47b0SAlexander V. Chernikov ipfw_obj_ntlv *ntlv; 2518b074b7bbSAlexander V. Chernikov uintptr_t pa, pe; 2519b074b7bbSAlexander V. Chernikov int l; 2520b074b7bbSAlexander V. Chernikov 2521b074b7bbSAlexander V. Chernikov pa = (uintptr_t)tlvs; 2522b074b7bbSAlexander V. Chernikov pe = pa + len; 2523b074b7bbSAlexander V. Chernikov l = 0; 2524b074b7bbSAlexander V. Chernikov for (; pa < pe; pa += l) { 25259f7d47b0SAlexander V. Chernikov ntlv = (ipfw_obj_ntlv *)pa; 2526b074b7bbSAlexander V. Chernikov l = ntlv->head.length; 2527ac35ff17SAlexander V. Chernikov 2528ac35ff17SAlexander V. Chernikov if (l != sizeof(*ntlv)) 2529ac35ff17SAlexander V. Chernikov return (NULL); 2530ac35ff17SAlexander V. Chernikov 2531563b5ab1SAlexander V. Chernikov if (ntlv->head.type != IPFW_TLV_TBL_NAME) 2532b074b7bbSAlexander V. Chernikov continue; 2533ac35ff17SAlexander V. Chernikov 2534b074b7bbSAlexander V. Chernikov if (ntlv->idx != uidx) 2535b074b7bbSAlexander V. Chernikov continue; 2536b074b7bbSAlexander V. Chernikov 2537ac35ff17SAlexander V. Chernikov if (ipfw_check_table_name(ntlv->name) != 0) 2538ac35ff17SAlexander V. Chernikov return (NULL); 2539ac35ff17SAlexander V. Chernikov 2540ac35ff17SAlexander V. Chernikov return (ntlv); 2541b074b7bbSAlexander V. Chernikov } 2542b074b7bbSAlexander V. Chernikov 2543b074b7bbSAlexander V. Chernikov return (NULL); 2544b074b7bbSAlexander V. Chernikov } 2545b074b7bbSAlexander V. Chernikov 2546ac35ff17SAlexander V. Chernikov /* 2547ac35ff17SAlexander V. Chernikov * Finds table config based on either legacy index 2548ac35ff17SAlexander V. Chernikov * or name in ntlv. 2549ac35ff17SAlexander V. Chernikov * Note @ti structure contains unchecked data from userland. 2550ac35ff17SAlexander V. Chernikov * 2551ac35ff17SAlexander V. Chernikov * Returns pointer to table_config or NULL. 2552ac35ff17SAlexander V. Chernikov */ 2553b074b7bbSAlexander V. Chernikov static struct table_config * 2554b074b7bbSAlexander V. Chernikov find_table(struct namedobj_instance *ni, struct tid_info *ti) 2555b074b7bbSAlexander V. Chernikov { 2556b074b7bbSAlexander V. Chernikov char *name, bname[16]; 2557b074b7bbSAlexander V. Chernikov struct named_object *no; 2558ac35ff17SAlexander V. Chernikov ipfw_obj_ntlv *ntlv; 2559ac35ff17SAlexander V. Chernikov uint32_t set; 2560b074b7bbSAlexander V. Chernikov 2561b074b7bbSAlexander V. Chernikov if (ti->tlvs != NULL) { 2562ac35ff17SAlexander V. Chernikov ntlv = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx); 2563ac35ff17SAlexander V. Chernikov if (ntlv == NULL) 2564b074b7bbSAlexander V. Chernikov return (NULL); 2565ac35ff17SAlexander V. Chernikov name = ntlv->name; 2566ac35ff17SAlexander V. Chernikov set = ntlv->set; 2567b074b7bbSAlexander V. Chernikov } else { 2568b074b7bbSAlexander V. Chernikov snprintf(bname, sizeof(bname), "%d", ti->uidx); 2569b074b7bbSAlexander V. Chernikov name = bname; 2570ac35ff17SAlexander V. Chernikov set = 0; 2571b074b7bbSAlexander V. Chernikov } 2572b074b7bbSAlexander V. Chernikov 2573ac35ff17SAlexander V. Chernikov no = ipfw_objhash_lookup_name(ni, set, name); 2574b074b7bbSAlexander V. Chernikov 2575b074b7bbSAlexander V. Chernikov return ((struct table_config *)no); 2576b074b7bbSAlexander V. Chernikov } 2577b074b7bbSAlexander V. Chernikov 257835e1bbd0SAlexander V. Chernikov /* 257935e1bbd0SAlexander V. Chernikov * Allocate new table config structure using 258035e1bbd0SAlexander V. Chernikov * specified @algo and @aname. 258135e1bbd0SAlexander V. Chernikov * 258235e1bbd0SAlexander V. Chernikov * Returns pointer to config or NULL. 258335e1bbd0SAlexander V. Chernikov */ 2584b074b7bbSAlexander V. Chernikov static struct table_config * 258568394ec8SAlexander V. Chernikov alloc_table_config(struct ip_fw_chain *ch, struct tid_info *ti, 2586914bffb6SAlexander V. Chernikov struct table_algo *ta, char *aname, uint8_t tflags, uint8_t vtype) 2587b074b7bbSAlexander V. Chernikov { 2588b074b7bbSAlexander V. Chernikov char *name, bname[16]; 2589b074b7bbSAlexander V. Chernikov struct table_config *tc; 2590b074b7bbSAlexander V. Chernikov int error; 2591ac35ff17SAlexander V. Chernikov ipfw_obj_ntlv *ntlv; 2592ac35ff17SAlexander V. Chernikov uint32_t set; 2593b074b7bbSAlexander V. Chernikov 2594b074b7bbSAlexander V. Chernikov if (ti->tlvs != NULL) { 2595ac35ff17SAlexander V. Chernikov ntlv = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx); 2596ac35ff17SAlexander V. Chernikov if (ntlv == NULL) 2597b074b7bbSAlexander V. Chernikov return (NULL); 2598ac35ff17SAlexander V. Chernikov name = ntlv->name; 2599ac35ff17SAlexander V. Chernikov set = ntlv->set; 2600b074b7bbSAlexander V. Chernikov } else { 2601b074b7bbSAlexander V. Chernikov snprintf(bname, sizeof(bname), "%d", ti->uidx); 2602b074b7bbSAlexander V. Chernikov name = bname; 2603ac35ff17SAlexander V. Chernikov set = 0; 2604b074b7bbSAlexander V. Chernikov } 2605b074b7bbSAlexander V. Chernikov 2606b074b7bbSAlexander V. Chernikov tc = malloc(sizeof(struct table_config), M_IPFW, M_WAITOK | M_ZERO); 2607b074b7bbSAlexander V. Chernikov tc->no.name = tc->tablename; 2608b074b7bbSAlexander V. Chernikov tc->no.type = ti->type; 2609ac35ff17SAlexander V. Chernikov tc->no.set = set; 2610914bffb6SAlexander V. Chernikov tc->tflags = tflags; 26119f7d47b0SAlexander V. Chernikov tc->ta = ta; 2612b074b7bbSAlexander V. Chernikov strlcpy(tc->tablename, name, sizeof(tc->tablename)); 2613ac35ff17SAlexander V. Chernikov /* Set default value type to u32 for compability reasons */ 2614db785d31SAlexander V. Chernikov if (vtype == 0) 2615ac35ff17SAlexander V. Chernikov tc->vtype = IPFW_VTYPE_U32; 2616db785d31SAlexander V. Chernikov else 2617db785d31SAlexander V. Chernikov tc->vtype = vtype; 2618b074b7bbSAlexander V. Chernikov 2619b074b7bbSAlexander V. Chernikov if (ti->tlvs == NULL) { 2620b074b7bbSAlexander V. Chernikov tc->no.compat = 1; 2621b074b7bbSAlexander V. Chernikov tc->no.uidx = ti->uidx; 2622b074b7bbSAlexander V. Chernikov } 2623b074b7bbSAlexander V. Chernikov 2624b074b7bbSAlexander V. Chernikov /* Preallocate data structures for new tables */ 2625914bffb6SAlexander V. Chernikov error = ta->init(ch, &tc->astate, &tc->ti, aname, tflags); 2626b074b7bbSAlexander V. Chernikov if (error != 0) { 2627b074b7bbSAlexander V. Chernikov free(tc, M_IPFW); 2628b074b7bbSAlexander V. Chernikov return (NULL); 2629b074b7bbSAlexander V. Chernikov } 2630b074b7bbSAlexander V. Chernikov 2631b074b7bbSAlexander V. Chernikov return (tc); 2632b074b7bbSAlexander V. Chernikov } 2633b074b7bbSAlexander V. Chernikov 263435e1bbd0SAlexander V. Chernikov /* 263535e1bbd0SAlexander V. Chernikov * Destroys table state and config. 263635e1bbd0SAlexander V. Chernikov */ 2637b074b7bbSAlexander V. Chernikov static void 2638b074b7bbSAlexander V. Chernikov free_table_config(struct namedobj_instance *ni, struct table_config *tc) 2639b074b7bbSAlexander V. Chernikov { 2640b074b7bbSAlexander V. Chernikov 264135e1bbd0SAlexander V. Chernikov KASSERT(tc->linked == 0, ("free() on linked config")); 2642b074b7bbSAlexander V. Chernikov 26430468c5baSAlexander V. Chernikov /* 26440468c5baSAlexander V. Chernikov * We're using ta without any locking/referencing. 26450468c5baSAlexander V. Chernikov * TODO: fix this if we're going to use unloadable algos. 26460468c5baSAlexander V. Chernikov */ 264735e1bbd0SAlexander V. Chernikov tc->ta->destroy(tc->astate, &tc->ti); 2648b074b7bbSAlexander V. Chernikov free(tc, M_IPFW); 2649b074b7bbSAlexander V. Chernikov } 2650b074b7bbSAlexander V. Chernikov 2651b074b7bbSAlexander V. Chernikov /* 2652b074b7bbSAlexander V. Chernikov * Links @tc to @chain table named instance. 2653b074b7bbSAlexander V. Chernikov * Sets appropriate type/states in @chain table info. 2654b074b7bbSAlexander V. Chernikov */ 2655b074b7bbSAlexander V. Chernikov static void 26569f7d47b0SAlexander V. Chernikov link_table(struct ip_fw_chain *ch, struct table_config *tc) 2657b074b7bbSAlexander V. Chernikov { 2658b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 26599f7d47b0SAlexander V. Chernikov struct table_info *ti; 2660b074b7bbSAlexander V. Chernikov uint16_t kidx; 2661b074b7bbSAlexander V. Chernikov 26629f7d47b0SAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(ch); 26639f7d47b0SAlexander V. Chernikov IPFW_WLOCK_ASSERT(ch); 2664b074b7bbSAlexander V. Chernikov 26659f7d47b0SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 2666b074b7bbSAlexander V. Chernikov kidx = tc->no.kidx; 2667b074b7bbSAlexander V. Chernikov 2668b074b7bbSAlexander V. Chernikov ipfw_objhash_add(ni, &tc->no); 26699f7d47b0SAlexander V. Chernikov 26709f7d47b0SAlexander V. Chernikov ti = KIDX_TO_TI(ch, kidx); 26719f7d47b0SAlexander V. Chernikov *ti = tc->ti; 2672b074b7bbSAlexander V. Chernikov 267368394ec8SAlexander V. Chernikov /* Notify algo on real @ti address */ 267468394ec8SAlexander V. Chernikov if (tc->ta->change_ti != NULL) 267568394ec8SAlexander V. Chernikov tc->ta->change_ti(tc->astate, ti); 267668394ec8SAlexander V. Chernikov 2677b074b7bbSAlexander V. Chernikov tc->linked = 1; 26789d099b4fSAlexander V. Chernikov tc->ta->refcnt++; 2679b074b7bbSAlexander V. Chernikov } 2680b074b7bbSAlexander V. Chernikov 2681b074b7bbSAlexander V. Chernikov /* 2682b074b7bbSAlexander V. Chernikov * Unlinks @tc from @chain table named instance. 2683b074b7bbSAlexander V. Chernikov * Zeroes states in @chain and stores them in @tc. 2684b074b7bbSAlexander V. Chernikov */ 2685b074b7bbSAlexander V. Chernikov static void 26869f7d47b0SAlexander V. Chernikov unlink_table(struct ip_fw_chain *ch, struct table_config *tc) 2687b074b7bbSAlexander V. Chernikov { 2688b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 26899f7d47b0SAlexander V. Chernikov struct table_info *ti; 2690b074b7bbSAlexander V. Chernikov uint16_t kidx; 2691b074b7bbSAlexander V. Chernikov 26929f7d47b0SAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(ch); 26939f7d47b0SAlexander V. Chernikov IPFW_WLOCK_ASSERT(ch); 2694b074b7bbSAlexander V. Chernikov 26959f7d47b0SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 2696b074b7bbSAlexander V. Chernikov kidx = tc->no.kidx; 2697b074b7bbSAlexander V. Chernikov 26989f7d47b0SAlexander V. Chernikov /* Clear state. @ti copy is already saved inside @tc */ 2699b074b7bbSAlexander V. Chernikov ipfw_objhash_del(ni, &tc->no); 27009f7d47b0SAlexander V. Chernikov ti = KIDX_TO_TI(ch, kidx); 27019f7d47b0SAlexander V. Chernikov memset(ti, 0, sizeof(struct table_info)); 2702b074b7bbSAlexander V. Chernikov tc->linked = 0; 27039d099b4fSAlexander V. Chernikov tc->ta->refcnt--; 270468394ec8SAlexander V. Chernikov 270568394ec8SAlexander V. Chernikov /* Notify algo on real @ti address */ 270668394ec8SAlexander V. Chernikov if (tc->ta->change_ti != NULL) 270768394ec8SAlexander V. Chernikov tc->ta->change_ti(tc->astate, NULL); 2708b074b7bbSAlexander V. Chernikov } 2709b074b7bbSAlexander V. Chernikov 2710b074b7bbSAlexander V. Chernikov /* 271135e1bbd0SAlexander V. Chernikov * Finds and bumps refcount for tables referenced by given @rule. 271235e1bbd0SAlexander V. Chernikov * Allocates new indexes for non-existing tables. 271335e1bbd0SAlexander V. Chernikov * Fills in @oib array with userland/kernel indexes. 27149490a627SAlexander V. Chernikov * First free oidx pointer is saved back in @oib. 2715b074b7bbSAlexander V. Chernikov * 2716b074b7bbSAlexander V. Chernikov * Returns 0 on success. 2717b074b7bbSAlexander V. Chernikov */ 2718b074b7bbSAlexander V. Chernikov static int 27199490a627SAlexander V. Chernikov bind_table_rule(struct ip_fw_chain *ch, struct ip_fw *rule, 27209490a627SAlexander V. Chernikov struct rule_check_info *ci, struct obj_idx **oib, struct tid_info *ti) 2721b074b7bbSAlexander V. Chernikov { 2722b074b7bbSAlexander V. Chernikov struct table_config *tc; 27239490a627SAlexander V. Chernikov struct namedobj_instance *ni; 27249490a627SAlexander V. Chernikov struct named_object *no; 27250468c5baSAlexander V. Chernikov int cmdlen, error, l, numnew; 27260468c5baSAlexander V. Chernikov uint16_t kidx; 27279490a627SAlexander V. Chernikov ipfw_insn *cmd; 27280468c5baSAlexander V. Chernikov struct obj_idx *pidx, *pidx_first, *p; 27299490a627SAlexander V. Chernikov 27300468c5baSAlexander V. Chernikov pidx_first = *oib; 27310468c5baSAlexander V. Chernikov pidx = pidx_first; 27329490a627SAlexander V. Chernikov l = rule->cmd_len; 27339490a627SAlexander V. Chernikov cmd = rule->cmd; 27349490a627SAlexander V. Chernikov cmdlen = 0; 27359490a627SAlexander V. Chernikov error = 0; 27360468c5baSAlexander V. Chernikov numnew = 0; 27379490a627SAlexander V. Chernikov 27389490a627SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 27399490a627SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 27409490a627SAlexander V. Chernikov 274135e1bbd0SAlexander V. Chernikov /* 274235e1bbd0SAlexander V. Chernikov * Increase refcount on each referenced table. 274335e1bbd0SAlexander V. Chernikov * Allocate table indexes for non-existing tables. 274435e1bbd0SAlexander V. Chernikov */ 27459490a627SAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 27469490a627SAlexander V. Chernikov cmdlen = F_LEN(cmd); 27479490a627SAlexander V. Chernikov 27489490a627SAlexander V. Chernikov if (classify_table_opcode(cmd, &ti->uidx, &ti->type) != 0) 27499490a627SAlexander V. Chernikov continue; 2750b074b7bbSAlexander V. Chernikov 2751b074b7bbSAlexander V. Chernikov pidx->uidx = ti->uidx; 2752b074b7bbSAlexander V. Chernikov pidx->type = ti->type; 2753b074b7bbSAlexander V. Chernikov 27549490a627SAlexander V. Chernikov if ((tc = find_table(ni, ti)) != NULL) { 27559490a627SAlexander V. Chernikov if (tc->no.type != ti->type) { 27569490a627SAlexander V. Chernikov /* Incompatible types */ 27579490a627SAlexander V. Chernikov error = EINVAL; 27589490a627SAlexander V. Chernikov break; 27599490a627SAlexander V. Chernikov } 27609f7d47b0SAlexander V. Chernikov 27619490a627SAlexander V. Chernikov /* Reference found table and save kidx */ 27629490a627SAlexander V. Chernikov tc->no.refcnt++; 27639490a627SAlexander V. Chernikov pidx->kidx = tc->no.kidx; 27649490a627SAlexander V. Chernikov pidx++; 27659490a627SAlexander V. Chernikov continue; 27669490a627SAlexander V. Chernikov } 27679490a627SAlexander V. Chernikov 27680468c5baSAlexander V. Chernikov /* 27690468c5baSAlexander V. Chernikov * Compability stuff for old clients: 27700468c5baSAlexander V. Chernikov * prepare to manually create non-existing tables. 27710468c5baSAlexander V. Chernikov */ 27729490a627SAlexander V. Chernikov pidx++; 27730468c5baSAlexander V. Chernikov numnew++; 2774b074b7bbSAlexander V. Chernikov } 2775b074b7bbSAlexander V. Chernikov 27769490a627SAlexander V. Chernikov if (error != 0) { 27779490a627SAlexander V. Chernikov /* Unref everything we have already done */ 27789490a627SAlexander V. Chernikov for (p = *oib; p < pidx; p++) { 27790468c5baSAlexander V. Chernikov if (p->kidx == 0) 27809490a627SAlexander V. Chernikov continue; 2781b074b7bbSAlexander V. Chernikov 27829490a627SAlexander V. Chernikov /* Find & unref by existing idx */ 2783ac35ff17SAlexander V. Chernikov no = ipfw_objhash_lookup_kidx(ni, p->kidx); 27849490a627SAlexander V. Chernikov KASSERT(no != NULL, ("Ref'd table %d disappeared", 27859490a627SAlexander V. Chernikov p->kidx)); 2786b074b7bbSAlexander V. Chernikov 27879490a627SAlexander V. Chernikov no->refcnt--; 27889490a627SAlexander V. Chernikov } 27899490a627SAlexander V. Chernikov } 27900468c5baSAlexander V. Chernikov 27919490a627SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 2792b074b7bbSAlexander V. Chernikov 27930468c5baSAlexander V. Chernikov if (numnew == 0) { 27940468c5baSAlexander V. Chernikov *oib = pidx; 27950468c5baSAlexander V. Chernikov return (error); 27960468c5baSAlexander V. Chernikov } 27970468c5baSAlexander V. Chernikov 27980468c5baSAlexander V. Chernikov /* 27990468c5baSAlexander V. Chernikov * Compatibility stuff: do actual creation for non-existing, 28000468c5baSAlexander V. Chernikov * but referenced tables. 28010468c5baSAlexander V. Chernikov */ 28020468c5baSAlexander V. Chernikov for (p = pidx_first; p < pidx; p++) { 28030468c5baSAlexander V. Chernikov if (p->kidx != 0) 28040468c5baSAlexander V. Chernikov continue; 28050468c5baSAlexander V. Chernikov 28060468c5baSAlexander V. Chernikov ti->uidx = p->uidx; 28070468c5baSAlexander V. Chernikov ti->type = p->type; 28080468c5baSAlexander V. Chernikov ti->atype = 0; 28090468c5baSAlexander V. Chernikov 28100468c5baSAlexander V. Chernikov error = create_table_compat(ch, ti, NULL, NULL, &kidx); 28110468c5baSAlexander V. Chernikov if (error == 0) { 28120468c5baSAlexander V. Chernikov p->kidx = kidx; 28130468c5baSAlexander V. Chernikov continue; 28140468c5baSAlexander V. Chernikov } 28150468c5baSAlexander V. Chernikov 28160468c5baSAlexander V. Chernikov /* Error. We have to drop references */ 28170468c5baSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 28180468c5baSAlexander V. Chernikov for (p = pidx_first; p < pidx; p++) { 28190468c5baSAlexander V. Chernikov if (p->kidx == 0) 28200468c5baSAlexander V. Chernikov continue; 28210468c5baSAlexander V. Chernikov 28220468c5baSAlexander V. Chernikov /* Find & unref by existing idx */ 28230468c5baSAlexander V. Chernikov no = ipfw_objhash_lookup_kidx(ni, p->kidx); 28240468c5baSAlexander V. Chernikov KASSERT(no != NULL, ("Ref'd table %d disappeared", 28250468c5baSAlexander V. Chernikov p->kidx)); 28260468c5baSAlexander V. Chernikov 28270468c5baSAlexander V. Chernikov no->refcnt--; 28280468c5baSAlexander V. Chernikov } 28290468c5baSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 28300468c5baSAlexander V. Chernikov 28310468c5baSAlexander V. Chernikov return (error); 28320468c5baSAlexander V. Chernikov } 28330468c5baSAlexander V. Chernikov 28349490a627SAlexander V. Chernikov *oib = pidx; 28359490a627SAlexander V. Chernikov 28369490a627SAlexander V. Chernikov return (error); 2837b074b7bbSAlexander V. Chernikov } 2838b074b7bbSAlexander V. Chernikov 2839a73d728dSAlexander V. Chernikov struct swap_table_args { 2840a73d728dSAlexander V. Chernikov int set; 2841a73d728dSAlexander V. Chernikov int new_set; 2842a73d728dSAlexander V. Chernikov int mv; 2843a73d728dSAlexander V. Chernikov }; 2844a73d728dSAlexander V. Chernikov 2845a73d728dSAlexander V. Chernikov /* 2846a73d728dSAlexander V. Chernikov * Change set for each matching table. 2847a73d728dSAlexander V. Chernikov * 2848a73d728dSAlexander V. Chernikov * Ensure we dispatch each table once by setting/checking ochange 2849a73d728dSAlexander V. Chernikov * fields. 2850a73d728dSAlexander V. Chernikov */ 2851a73d728dSAlexander V. Chernikov static void 2852a73d728dSAlexander V. Chernikov swap_table_set(struct namedobj_instance *ni, struct named_object *no, 2853a73d728dSAlexander V. Chernikov void *arg) 2854a73d728dSAlexander V. Chernikov { 2855a73d728dSAlexander V. Chernikov struct table_config *tc; 2856a73d728dSAlexander V. Chernikov struct swap_table_args *sta; 2857a73d728dSAlexander V. Chernikov 2858a73d728dSAlexander V. Chernikov tc = (struct table_config *)no; 2859a73d728dSAlexander V. Chernikov sta = (struct swap_table_args *)arg; 2860a73d728dSAlexander V. Chernikov 2861a73d728dSAlexander V. Chernikov if (no->set != sta->set && (no->set != sta->new_set || sta->mv != 0)) 2862a73d728dSAlexander V. Chernikov return; 2863a73d728dSAlexander V. Chernikov 2864a73d728dSAlexander V. Chernikov if (tc->ochanged != 0) 2865a73d728dSAlexander V. Chernikov return; 2866a73d728dSAlexander V. Chernikov 2867a73d728dSAlexander V. Chernikov tc->ochanged = 1; 2868a73d728dSAlexander V. Chernikov ipfw_objhash_del(ni, no); 2869a73d728dSAlexander V. Chernikov if (no->set == sta->set) 2870a73d728dSAlexander V. Chernikov no->set = sta->new_set; 2871a73d728dSAlexander V. Chernikov else 2872a73d728dSAlexander V. Chernikov no->set = sta->set; 2873a73d728dSAlexander V. Chernikov ipfw_objhash_add(ni, no); 2874a73d728dSAlexander V. Chernikov } 2875a73d728dSAlexander V. Chernikov 2876a73d728dSAlexander V. Chernikov /* 2877a73d728dSAlexander V. Chernikov * Cleans up ochange field for all tables. 2878a73d728dSAlexander V. Chernikov */ 2879a73d728dSAlexander V. Chernikov static void 2880a73d728dSAlexander V. Chernikov clean_table_set_data(struct namedobj_instance *ni, struct named_object *no, 2881a73d728dSAlexander V. Chernikov void *arg) 2882a73d728dSAlexander V. Chernikov { 2883a73d728dSAlexander V. Chernikov struct table_config *tc; 2884a73d728dSAlexander V. Chernikov struct swap_table_args *sta; 2885a73d728dSAlexander V. Chernikov 2886a73d728dSAlexander V. Chernikov tc = (struct table_config *)no; 2887a73d728dSAlexander V. Chernikov sta = (struct swap_table_args *)arg; 2888a73d728dSAlexander V. Chernikov 2889a73d728dSAlexander V. Chernikov tc->ochanged = 0; 2890a73d728dSAlexander V. Chernikov } 2891a73d728dSAlexander V. Chernikov 2892a73d728dSAlexander V. Chernikov /* 2893a73d728dSAlexander V. Chernikov * Swaps tables within two sets. 2894a73d728dSAlexander V. Chernikov */ 2895a73d728dSAlexander V. Chernikov void 2896a73d728dSAlexander V. Chernikov ipfw_swap_tables_sets(struct ip_fw_chain *ch, uint32_t set, 2897a73d728dSAlexander V. Chernikov uint32_t new_set, int mv) 2898a73d728dSAlexander V. Chernikov { 2899a73d728dSAlexander V. Chernikov struct swap_table_args sta; 2900a73d728dSAlexander V. Chernikov 2901a73d728dSAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(ch); 2902a73d728dSAlexander V. Chernikov 2903a73d728dSAlexander V. Chernikov sta.set = set; 2904a73d728dSAlexander V. Chernikov sta.new_set = new_set; 2905a73d728dSAlexander V. Chernikov sta.mv = mv; 2906a73d728dSAlexander V. Chernikov 2907a73d728dSAlexander V. Chernikov ipfw_objhash_foreach(CHAIN_TO_NI(ch), swap_table_set, &sta); 2908a73d728dSAlexander V. Chernikov ipfw_objhash_foreach(CHAIN_TO_NI(ch), clean_table_set_data, &sta); 2909a73d728dSAlexander V. Chernikov } 2910a73d728dSAlexander V. Chernikov 2911a73d728dSAlexander V. Chernikov /* 2912a73d728dSAlexander V. Chernikov * Move all tables which are reference by rules in @rr to set @new_set. 2913a73d728dSAlexander V. Chernikov * Makes sure that all relevant tables are referenced ONLLY by given rules. 2914a73d728dSAlexander V. Chernikov * 2915a73d728dSAlexander V. Chernikov * Retuns 0 on success, 2916a73d728dSAlexander V. Chernikov */ 2917a73d728dSAlexander V. Chernikov int 2918a73d728dSAlexander V. Chernikov ipfw_move_tables_sets(struct ip_fw_chain *ch, ipfw_range_tlv *rt, 2919a73d728dSAlexander V. Chernikov uint32_t new_set) 2920a73d728dSAlexander V. Chernikov { 2921a73d728dSAlexander V. Chernikov struct ip_fw *rule; 2922a73d728dSAlexander V. Chernikov struct table_config *tc; 2923a73d728dSAlexander V. Chernikov struct named_object *no; 2924a73d728dSAlexander V. Chernikov struct namedobj_instance *ni; 2925a73d728dSAlexander V. Chernikov int bad, i, l, cmdlen; 2926a73d728dSAlexander V. Chernikov uint16_t kidx; 2927a73d728dSAlexander V. Chernikov uint8_t type; 2928a73d728dSAlexander V. Chernikov ipfw_insn *cmd; 2929a73d728dSAlexander V. Chernikov 2930a73d728dSAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(ch); 2931a73d728dSAlexander V. Chernikov 2932a73d728dSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 2933a73d728dSAlexander V. Chernikov 2934a73d728dSAlexander V. Chernikov /* Stage 1: count number of references by given rules */ 2935a73d728dSAlexander V. Chernikov for (i = 0; i < ch->n_rules - 1; i++) { 2936a73d728dSAlexander V. Chernikov rule = ch->map[i]; 2937a73d728dSAlexander V. Chernikov if (ipfw_match_range(rule, rt) == 0) 2938a73d728dSAlexander V. Chernikov continue; 2939a73d728dSAlexander V. Chernikov 2940a73d728dSAlexander V. Chernikov l = rule->cmd_len; 2941a73d728dSAlexander V. Chernikov cmd = rule->cmd; 2942a73d728dSAlexander V. Chernikov cmdlen = 0; 2943a73d728dSAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 2944a73d728dSAlexander V. Chernikov cmdlen = F_LEN(cmd); 2945a73d728dSAlexander V. Chernikov if (classify_table_opcode(cmd, &kidx, &type) != 0) 2946a73d728dSAlexander V. Chernikov continue; 2947a73d728dSAlexander V. Chernikov no = ipfw_objhash_lookup_kidx(ni, kidx); 2948a73d728dSAlexander V. Chernikov KASSERT(no != NULL, 2949a73d728dSAlexander V. Chernikov ("objhash lookup failed on index %d", kidx)); 2950a73d728dSAlexander V. Chernikov tc = (struct table_config *)no; 2951a73d728dSAlexander V. Chernikov tc->ocount++; 2952a73d728dSAlexander V. Chernikov } 2953a73d728dSAlexander V. Chernikov 2954a73d728dSAlexander V. Chernikov } 2955a73d728dSAlexander V. Chernikov 2956a73d728dSAlexander V. Chernikov /* Stage 2: verify "ownership" */ 2957a73d728dSAlexander V. Chernikov bad = 0; 2958a73d728dSAlexander V. Chernikov for (i = 0; i < ch->n_rules - 1; i++) { 2959a73d728dSAlexander V. Chernikov rule = ch->map[i]; 2960a73d728dSAlexander V. Chernikov if (ipfw_match_range(rule, rt) == 0) 2961a73d728dSAlexander V. Chernikov continue; 2962a73d728dSAlexander V. Chernikov 2963a73d728dSAlexander V. Chernikov l = rule->cmd_len; 2964a73d728dSAlexander V. Chernikov cmd = rule->cmd; 2965a73d728dSAlexander V. Chernikov cmdlen = 0; 2966a73d728dSAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 2967a73d728dSAlexander V. Chernikov cmdlen = F_LEN(cmd); 2968a73d728dSAlexander V. Chernikov if (classify_table_opcode(cmd, &kidx, &type) != 0) 2969a73d728dSAlexander V. Chernikov continue; 2970a73d728dSAlexander V. Chernikov no = ipfw_objhash_lookup_kidx(ni, kidx); 2971a73d728dSAlexander V. Chernikov KASSERT(no != NULL, 2972a73d728dSAlexander V. Chernikov ("objhash lookup failed on index %d", kidx)); 2973a73d728dSAlexander V. Chernikov tc = (struct table_config *)no; 2974a73d728dSAlexander V. Chernikov if (tc->no.refcnt != tc->ocount) { 2975a73d728dSAlexander V. Chernikov 2976a73d728dSAlexander V. Chernikov /* 2977a73d728dSAlexander V. Chernikov * Number of references differ: 2978a73d728dSAlexander V. Chernikov * Other rule(s) are holding reference to given 2979a73d728dSAlexander V. Chernikov * table, so it is not possible to change its set. 2980a73d728dSAlexander V. Chernikov * 2981a73d728dSAlexander V. Chernikov * Note that refcnt may account 2982a73d728dSAlexander V. Chernikov * references to some going-to-be-added rules. 2983a73d728dSAlexander V. Chernikov * Since we don't know their numbers (and event 2984a73d728dSAlexander V. Chernikov * if they will be added) it is perfectly OK 2985a73d728dSAlexander V. Chernikov * to return error here. 2986a73d728dSAlexander V. Chernikov */ 2987a73d728dSAlexander V. Chernikov bad = 1; 2988a73d728dSAlexander V. Chernikov break; 2989a73d728dSAlexander V. Chernikov } 2990a73d728dSAlexander V. Chernikov } 2991a73d728dSAlexander V. Chernikov 2992a73d728dSAlexander V. Chernikov if (bad != 0) 2993a73d728dSAlexander V. Chernikov break; 2994a73d728dSAlexander V. Chernikov } 2995a73d728dSAlexander V. Chernikov 2996a73d728dSAlexander V. Chernikov /* Stage 3: change set or cleanup */ 2997a73d728dSAlexander V. Chernikov for (i = 0; i < ch->n_rules - 1; i++) { 2998a73d728dSAlexander V. Chernikov rule = ch->map[i]; 2999a73d728dSAlexander V. Chernikov if (ipfw_match_range(rule, rt) == 0) 3000a73d728dSAlexander V. Chernikov continue; 3001a73d728dSAlexander V. Chernikov 3002a73d728dSAlexander V. Chernikov l = rule->cmd_len; 3003a73d728dSAlexander V. Chernikov cmd = rule->cmd; 3004a73d728dSAlexander V. Chernikov cmdlen = 0; 3005a73d728dSAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 3006a73d728dSAlexander V. Chernikov cmdlen = F_LEN(cmd); 3007a73d728dSAlexander V. Chernikov if (classify_table_opcode(cmd, &kidx, &type) != 0) 3008a73d728dSAlexander V. Chernikov continue; 3009a73d728dSAlexander V. Chernikov no = ipfw_objhash_lookup_kidx(ni, kidx); 3010a73d728dSAlexander V. Chernikov KASSERT(no != NULL, 3011a73d728dSAlexander V. Chernikov ("objhash lookup failed on index %d", kidx)); 3012a73d728dSAlexander V. Chernikov tc = (struct table_config *)no; 3013a73d728dSAlexander V. Chernikov 3014a73d728dSAlexander V. Chernikov tc->ocount = 0; 3015a73d728dSAlexander V. Chernikov if (bad != 0) 3016a73d728dSAlexander V. Chernikov continue; 3017a73d728dSAlexander V. Chernikov 3018a73d728dSAlexander V. Chernikov /* Actually change set. */ 3019a73d728dSAlexander V. Chernikov ipfw_objhash_del(ni, no); 3020a73d728dSAlexander V. Chernikov no->set = new_set; 3021a73d728dSAlexander V. Chernikov ipfw_objhash_add(ni, no); 3022a73d728dSAlexander V. Chernikov } 3023a73d728dSAlexander V. Chernikov } 3024a73d728dSAlexander V. Chernikov 3025a73d728dSAlexander V. Chernikov return (bad); 3026a73d728dSAlexander V. Chernikov } 3027a73d728dSAlexander V. Chernikov 3028b074b7bbSAlexander V. Chernikov /* 3029b074b7bbSAlexander V. Chernikov * Compatibility function for old ipfw(8) binaries. 3030b074b7bbSAlexander V. Chernikov * Rewrites table kernel indices with userland ones. 303135e1bbd0SAlexander V. Chernikov * Convert tables matching '/^\d+$/' to their atoi() value. 303235e1bbd0SAlexander V. Chernikov * Use number 65535 for other tables. 3033b074b7bbSAlexander V. Chernikov * 3034b074b7bbSAlexander V. Chernikov * Returns 0 on success. 3035b074b7bbSAlexander V. Chernikov */ 3036b074b7bbSAlexander V. Chernikov int 30377e767c79SAlexander V. Chernikov ipfw_rewrite_table_kidx(struct ip_fw_chain *chain, struct ip_fw_rule0 *rule) 3038b074b7bbSAlexander V. Chernikov { 30391832a7b3SAlexander V. Chernikov int cmdlen, error, l; 3040b074b7bbSAlexander V. Chernikov ipfw_insn *cmd; 30411832a7b3SAlexander V. Chernikov uint16_t kidx, uidx; 3042b074b7bbSAlexander V. Chernikov uint8_t type; 3043b074b7bbSAlexander V. Chernikov struct named_object *no; 3044b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 3045b074b7bbSAlexander V. Chernikov 3046b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(chain); 30471832a7b3SAlexander V. Chernikov error = 0; 3048b074b7bbSAlexander V. Chernikov 3049b074b7bbSAlexander V. Chernikov l = rule->cmd_len; 3050b074b7bbSAlexander V. Chernikov cmd = rule->cmd; 3051b074b7bbSAlexander V. Chernikov cmdlen = 0; 3052b074b7bbSAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 3053b074b7bbSAlexander V. Chernikov cmdlen = F_LEN(cmd); 3054b074b7bbSAlexander V. Chernikov 3055b074b7bbSAlexander V. Chernikov if (classify_table_opcode(cmd, &kidx, &type) != 0) 3056b074b7bbSAlexander V. Chernikov continue; 3057b074b7bbSAlexander V. Chernikov 3058ac35ff17SAlexander V. Chernikov if ((no = ipfw_objhash_lookup_kidx(ni, kidx)) == NULL) 3059b074b7bbSAlexander V. Chernikov return (1); 3060b074b7bbSAlexander V. Chernikov 30611832a7b3SAlexander V. Chernikov uidx = no->uidx; 30621832a7b3SAlexander V. Chernikov if (no->compat == 0) { 3063b074b7bbSAlexander V. Chernikov 30641832a7b3SAlexander V. Chernikov /* 30651832a7b3SAlexander V. Chernikov * We are called via legacy opcode. 30661832a7b3SAlexander V. Chernikov * Save error and show table as fake number 30671832a7b3SAlexander V. Chernikov * not to make ipfw(8) hang. 30681832a7b3SAlexander V. Chernikov */ 30691832a7b3SAlexander V. Chernikov uidx = 65535; 30701832a7b3SAlexander V. Chernikov error = 2; 3071b074b7bbSAlexander V. Chernikov } 3072b074b7bbSAlexander V. Chernikov 30731832a7b3SAlexander V. Chernikov update_table_opcode(cmd, uidx); 30741832a7b3SAlexander V. Chernikov } 30751832a7b3SAlexander V. Chernikov 30761832a7b3SAlexander V. Chernikov return (error); 3077b074b7bbSAlexander V. Chernikov } 3078b074b7bbSAlexander V. Chernikov 3079563b5ab1SAlexander V. Chernikov /* 308035e1bbd0SAlexander V. Chernikov * Marks every table kidx used in @rule with bit in @bmask. 308135e1bbd0SAlexander V. Chernikov * Used to generate bitmask of referenced tables for given ruleset. 3082563b5ab1SAlexander V. Chernikov * 3083563b5ab1SAlexander V. Chernikov * Returns number of newly-referenced tables. 3084563b5ab1SAlexander V. Chernikov */ 3085563b5ab1SAlexander V. Chernikov int 3086563b5ab1SAlexander V. Chernikov ipfw_mark_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule, 3087563b5ab1SAlexander V. Chernikov uint32_t *bmask) 3088563b5ab1SAlexander V. Chernikov { 3089563b5ab1SAlexander V. Chernikov int cmdlen, l, count; 3090563b5ab1SAlexander V. Chernikov ipfw_insn *cmd; 3091563b5ab1SAlexander V. Chernikov uint16_t kidx; 3092563b5ab1SAlexander V. Chernikov uint8_t type; 3093563b5ab1SAlexander V. Chernikov 3094563b5ab1SAlexander V. Chernikov l = rule->cmd_len; 3095563b5ab1SAlexander V. Chernikov cmd = rule->cmd; 3096563b5ab1SAlexander V. Chernikov cmdlen = 0; 3097563b5ab1SAlexander V. Chernikov count = 0; 3098563b5ab1SAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 3099563b5ab1SAlexander V. Chernikov cmdlen = F_LEN(cmd); 3100563b5ab1SAlexander V. Chernikov 3101563b5ab1SAlexander V. Chernikov if (classify_table_opcode(cmd, &kidx, &type) != 0) 3102563b5ab1SAlexander V. Chernikov continue; 3103563b5ab1SAlexander V. Chernikov 3104563b5ab1SAlexander V. Chernikov if ((bmask[kidx / 32] & (1 << (kidx % 32))) == 0) 3105563b5ab1SAlexander V. Chernikov count++; 3106563b5ab1SAlexander V. Chernikov 3107563b5ab1SAlexander V. Chernikov bmask[kidx / 32] |= 1 << (kidx % 32); 3108563b5ab1SAlexander V. Chernikov } 3109563b5ab1SAlexander V. Chernikov 3110563b5ab1SAlexander V. Chernikov return (count); 3111563b5ab1SAlexander V. Chernikov } 3112563b5ab1SAlexander V. Chernikov 3113b074b7bbSAlexander V. Chernikov /* 3114b074b7bbSAlexander V. Chernikov * Checks is opcode is referencing table of appropriate type. 3115b074b7bbSAlexander V. Chernikov * Adds reference count for found table if true. 3116b074b7bbSAlexander V. Chernikov * Rewrites user-supplied opcode values with kernel ones. 3117b074b7bbSAlexander V. Chernikov * 3118b074b7bbSAlexander V. Chernikov * Returns 0 on success and appropriate error code otherwise. 3119b074b7bbSAlexander V. Chernikov */ 3120b074b7bbSAlexander V. Chernikov int 3121b074b7bbSAlexander V. Chernikov ipfw_rewrite_table_uidx(struct ip_fw_chain *chain, 3122b074b7bbSAlexander V. Chernikov struct rule_check_info *ci) 3123b074b7bbSAlexander V. Chernikov { 31240468c5baSAlexander V. Chernikov int cmdlen, error, l; 3125b074b7bbSAlexander V. Chernikov ipfw_insn *cmd; 3126b074b7bbSAlexander V. Chernikov uint16_t uidx; 3127b074b7bbSAlexander V. Chernikov uint8_t type; 3128b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 31299490a627SAlexander V. Chernikov struct obj_idx *p, *pidx_first, *pidx_last; 3130b074b7bbSAlexander V. Chernikov struct tid_info ti; 3131b074b7bbSAlexander V. Chernikov 3132b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(chain); 3133b074b7bbSAlexander V. Chernikov 3134b074b7bbSAlexander V. Chernikov /* 3135b074b7bbSAlexander V. Chernikov * Prepare an array for storing opcode indices. 3136b074b7bbSAlexander V. Chernikov * Use stack allocation by default. 3137b074b7bbSAlexander V. Chernikov */ 3138b074b7bbSAlexander V. Chernikov if (ci->table_opcodes <= (sizeof(ci->obuf)/sizeof(ci->obuf[0]))) { 3139b074b7bbSAlexander V. Chernikov /* Stack */ 31409490a627SAlexander V. Chernikov pidx_first = ci->obuf; 3141b074b7bbSAlexander V. Chernikov } else 31429490a627SAlexander V. Chernikov pidx_first = malloc(ci->table_opcodes * sizeof(struct obj_idx), 3143b074b7bbSAlexander V. Chernikov M_IPFW, M_WAITOK | M_ZERO); 3144b074b7bbSAlexander V. Chernikov 31459490a627SAlexander V. Chernikov pidx_last = pidx_first; 3146b074b7bbSAlexander V. Chernikov error = 0; 3147b074b7bbSAlexander V. Chernikov type = 0; 3148b074b7bbSAlexander V. Chernikov memset(&ti, 0, sizeof(ti)); 31491832a7b3SAlexander V. Chernikov 31501832a7b3SAlexander V. Chernikov /* 31511832a7b3SAlexander V. Chernikov * Use default set for looking up tables (old way) or 31521832a7b3SAlexander V. Chernikov * use set rule is assigned to (new way). 31531832a7b3SAlexander V. Chernikov */ 31541832a7b3SAlexander V. Chernikov ti.set = (V_fw_tables_sets != 0) ? ci->krule->set : 0; 31556c2997ffSAlexander V. Chernikov if (ci->ctlv != NULL) { 31566c2997ffSAlexander V. Chernikov ti.tlvs = (void *)(ci->ctlv + 1); 31576c2997ffSAlexander V. Chernikov ti.tlen = ci->ctlv->head.length - sizeof(ipfw_obj_ctlv); 31586c2997ffSAlexander V. Chernikov } 3159b074b7bbSAlexander V. Chernikov 31600468c5baSAlexander V. Chernikov /* Reference all used tables */ 31619490a627SAlexander V. Chernikov error = bind_table_rule(chain, ci->krule, ci, &pidx_last, &ti); 31620468c5baSAlexander V. Chernikov if (error != 0) 31639f7d47b0SAlexander V. Chernikov goto free; 3164b074b7bbSAlexander V. Chernikov 31659f7d47b0SAlexander V. Chernikov IPFW_UH_WLOCK(chain); 31669f7d47b0SAlexander V. Chernikov 3167b074b7bbSAlexander V. Chernikov /* Perform rule rewrite */ 3168b074b7bbSAlexander V. Chernikov l = ci->krule->cmd_len; 3169b074b7bbSAlexander V. Chernikov cmd = ci->krule->cmd; 3170b074b7bbSAlexander V. Chernikov cmdlen = 0; 31719490a627SAlexander V. Chernikov p = pidx_first; 3172b074b7bbSAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 3173b074b7bbSAlexander V. Chernikov cmdlen = F_LEN(cmd); 3174b074b7bbSAlexander V. Chernikov if (classify_table_opcode(cmd, &uidx, &type) != 0) 3175b074b7bbSAlexander V. Chernikov continue; 31769490a627SAlexander V. Chernikov update_table_opcode(cmd, p->kidx); 31779490a627SAlexander V. Chernikov p++; 3178b074b7bbSAlexander V. Chernikov } 3179b074b7bbSAlexander V. Chernikov 3180b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 3181b074b7bbSAlexander V. Chernikov 3182b074b7bbSAlexander V. Chernikov free: 31839490a627SAlexander V. Chernikov if (pidx_first != ci->obuf) 31849490a627SAlexander V. Chernikov free(pidx_first, M_IPFW); 3185b074b7bbSAlexander V. Chernikov 3186b074b7bbSAlexander V. Chernikov return (error); 3187b074b7bbSAlexander V. Chernikov } 3188b074b7bbSAlexander V. Chernikov 3189b074b7bbSAlexander V. Chernikov /* 3190b074b7bbSAlexander V. Chernikov * Remove references from every table used in @rule. 3191b074b7bbSAlexander V. Chernikov */ 3192b074b7bbSAlexander V. Chernikov void 3193b074b7bbSAlexander V. Chernikov ipfw_unbind_table_rule(struct ip_fw_chain *chain, struct ip_fw *rule) 3194b074b7bbSAlexander V. Chernikov { 3195b074b7bbSAlexander V. Chernikov int cmdlen, l; 3196b074b7bbSAlexander V. Chernikov ipfw_insn *cmd; 3197b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 3198b074b7bbSAlexander V. Chernikov struct named_object *no; 3199b074b7bbSAlexander V. Chernikov uint16_t kidx; 3200b074b7bbSAlexander V. Chernikov uint8_t type; 3201b074b7bbSAlexander V. Chernikov 3202030b184fSAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(chain); 3203b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(chain); 3204b074b7bbSAlexander V. Chernikov 3205b074b7bbSAlexander V. Chernikov l = rule->cmd_len; 3206b074b7bbSAlexander V. Chernikov cmd = rule->cmd; 3207b074b7bbSAlexander V. Chernikov cmdlen = 0; 3208b074b7bbSAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 3209b074b7bbSAlexander V. Chernikov cmdlen = F_LEN(cmd); 3210b074b7bbSAlexander V. Chernikov 3211b074b7bbSAlexander V. Chernikov if (classify_table_opcode(cmd, &kidx, &type) != 0) 3212b074b7bbSAlexander V. Chernikov continue; 3213b074b7bbSAlexander V. Chernikov 3214ac35ff17SAlexander V. Chernikov no = ipfw_objhash_lookup_kidx(ni, kidx); 3215b074b7bbSAlexander V. Chernikov 3216b074b7bbSAlexander V. Chernikov KASSERT(no != NULL, ("table id %d not found", kidx)); 3217b074b7bbSAlexander V. Chernikov KASSERT(no->type == type, ("wrong type %d (%d) for table id %d", 3218b074b7bbSAlexander V. Chernikov no->type, type, kidx)); 3219b074b7bbSAlexander V. Chernikov KASSERT(no->refcnt > 0, ("refcount for table %d is %d", 3220b074b7bbSAlexander V. Chernikov kidx, no->refcnt)); 3221b074b7bbSAlexander V. Chernikov 3222b074b7bbSAlexander V. Chernikov no->refcnt--; 3223b074b7bbSAlexander V. Chernikov } 3224b074b7bbSAlexander V. Chernikov } 3225b074b7bbSAlexander V. Chernikov 3226