13b3a8eb9SGleb Smirnoff /*- 23b3a8eb9SGleb Smirnoff * Copyright (c) 2004 Ruslan Ermilov and Vsevolod Lobko. 33b3a8eb9SGleb Smirnoff * 43b3a8eb9SGleb Smirnoff * Redistribution and use in source and binary forms, with or without 53b3a8eb9SGleb Smirnoff * modification, are permitted provided that the following conditions 63b3a8eb9SGleb Smirnoff * are met: 73b3a8eb9SGleb Smirnoff * 1. Redistributions of source code must retain the above copyright 83b3a8eb9SGleb Smirnoff * notice, this list of conditions and the following disclaimer. 93b3a8eb9SGleb Smirnoff * 2. Redistributions in binary form must reproduce the above copyright 103b3a8eb9SGleb Smirnoff * notice, this list of conditions and the following disclaimer in the 113b3a8eb9SGleb Smirnoff * documentation and/or other materials provided with the distribution. 123b3a8eb9SGleb Smirnoff * 133b3a8eb9SGleb Smirnoff * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 143b3a8eb9SGleb Smirnoff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 153b3a8eb9SGleb Smirnoff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 163b3a8eb9SGleb Smirnoff * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 173b3a8eb9SGleb Smirnoff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 183b3a8eb9SGleb Smirnoff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 193b3a8eb9SGleb Smirnoff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 203b3a8eb9SGleb Smirnoff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 213b3a8eb9SGleb Smirnoff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 223b3a8eb9SGleb Smirnoff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 233b3a8eb9SGleb Smirnoff * SUCH DAMAGE. 243b3a8eb9SGleb Smirnoff */ 253b3a8eb9SGleb Smirnoff 263b3a8eb9SGleb Smirnoff #include <sys/cdefs.h> 273b3a8eb9SGleb Smirnoff __FBSDID("$FreeBSD$"); 283b3a8eb9SGleb Smirnoff 293b3a8eb9SGleb Smirnoff /* 30563b5ab1SAlexander V. Chernikov * Lookup table support for ipfw. 313b3a8eb9SGleb Smirnoff * 32ac35ff17SAlexander V. Chernikov * This file contains handlers for all generic tables' operations: 33563b5ab1SAlexander V. Chernikov * add/del/flush entries, list/dump tables etc.. 343b3a8eb9SGleb Smirnoff * 35563b5ab1SAlexander V. Chernikov * Table data modification is protected by both UH and runtimg lock 36563b5ab1SAlexander V. Chernikov * while reading configuration/data is protected by UH lock. 37563b5ab1SAlexander V. Chernikov * 38563b5ab1SAlexander V. Chernikov * Lookup algorithms for all table types are located in ip_fw_table_algo.c 393b3a8eb9SGleb Smirnoff */ 403b3a8eb9SGleb Smirnoff 413b3a8eb9SGleb Smirnoff #include "opt_ipfw.h" 423b3a8eb9SGleb Smirnoff 433b3a8eb9SGleb Smirnoff #include <sys/param.h> 443b3a8eb9SGleb Smirnoff #include <sys/systm.h> 453b3a8eb9SGleb Smirnoff #include <sys/malloc.h> 463b3a8eb9SGleb Smirnoff #include <sys/kernel.h> 473b3a8eb9SGleb Smirnoff #include <sys/lock.h> 483b3a8eb9SGleb Smirnoff #include <sys/rwlock.h> 493b3a8eb9SGleb Smirnoff #include <sys/socket.h> 50f1220db8SAlexander V. Chernikov #include <sys/socketvar.h> 513b3a8eb9SGleb Smirnoff #include <sys/queue.h> 523b3a8eb9SGleb Smirnoff #include <net/if.h> /* ip_fw.h requires IFNAMSIZ */ 533b3a8eb9SGleb Smirnoff 543b3a8eb9SGleb Smirnoff #include <netinet/in.h> 553b3a8eb9SGleb Smirnoff #include <netinet/ip_var.h> /* struct ipfw_rule_ref */ 563b3a8eb9SGleb Smirnoff #include <netinet/ip_fw.h> 573b3a8eb9SGleb Smirnoff 583b3a8eb9SGleb Smirnoff #include <netpfil/ipfw/ip_fw_private.h> 59ea761a5dSAlexander V. Chernikov #include <netpfil/ipfw/ip_fw_table.h> 603b3a8eb9SGleb Smirnoff 613b3a8eb9SGleb Smirnoff 623b3a8eb9SGleb Smirnoff /* 63b074b7bbSAlexander V. Chernikov * Table has the following `type` concepts: 64b074b7bbSAlexander V. Chernikov * 659f7d47b0SAlexander V. Chernikov * `no.type` represents lookup key type (cidr, ifp, uid, etc..) 669f7d47b0SAlexander V. Chernikov * `ta->atype` represents exact lookup algorithm. 67b074b7bbSAlexander V. Chernikov * For example, we can use more efficient search schemes if we plan 68b074b7bbSAlexander V. Chernikov * to use some specific table for storing host-routes only. 699490a627SAlexander V. Chernikov * `ftype` (at the moment )is pure userland field helping to properly 709490a627SAlexander V. Chernikov * format value data e.g. "value is IPv4 nexthop" or "value is DSCP" 719490a627SAlexander V. Chernikov * or "value is port". 72b074b7bbSAlexander V. Chernikov * 73b074b7bbSAlexander V. Chernikov */ 74b074b7bbSAlexander V. Chernikov struct table_config { 75b074b7bbSAlexander V. Chernikov struct named_object no; 76adf3b2b9SAlexander V. Chernikov uint8_t vtype; /* value type */ 77adf3b2b9SAlexander V. Chernikov uint8_t vftype; /* value format type */ 78914bffb6SAlexander V. Chernikov uint8_t tflags; /* type flags */ 794f43138aSAlexander V. Chernikov uint8_t locked; /* 1 if locked from changes */ 80b074b7bbSAlexander V. Chernikov uint32_t count; /* Number of records */ 814c0c07a5SAlexander V. Chernikov uint32_t limit; /* Max number of records */ 82adf3b2b9SAlexander V. Chernikov uint8_t linked; /* 1 if already linked */ 83adf3b2b9SAlexander V. Chernikov uint8_t ochanged; /* used by set swapping */ 84adf3b2b9SAlexander V. Chernikov uint16_t spare1; 85adf3b2b9SAlexander V. Chernikov uint32_t spare2; 86a73d728dSAlexander V. Chernikov uint32_t ocount; /* used by set swapping */ 87a73d728dSAlexander V. Chernikov uint64_t gencnt; /* generation count */ 88b074b7bbSAlexander V. Chernikov char tablename[64]; /* table name */ 899f7d47b0SAlexander V. Chernikov struct table_algo *ta; /* Callbacks for given algo */ 909f7d47b0SAlexander V. Chernikov void *astate; /* algorithm state */ 919f7d47b0SAlexander V. Chernikov struct table_info ti; /* data to put to table_info */ 92b074b7bbSAlexander V. Chernikov }; 93b074b7bbSAlexander V. Chernikov 94b074b7bbSAlexander V. Chernikov struct tables_config { 95b074b7bbSAlexander V. Chernikov struct namedobj_instance *namehash; 969f7d47b0SAlexander V. Chernikov int algo_count; 979f7d47b0SAlexander V. Chernikov struct table_algo *algo[256]; 9857a1cf95SAlexander V. Chernikov struct table_algo *def_algo[IPFW_TABLE_MAXTYPE + 1]; 99b074b7bbSAlexander V. Chernikov }; 100b074b7bbSAlexander V. Chernikov 101b074b7bbSAlexander V. Chernikov static struct table_config *find_table(struct namedobj_instance *ni, 102b074b7bbSAlexander V. Chernikov struct tid_info *ti); 10368394ec8SAlexander V. Chernikov static struct table_config *alloc_table_config(struct ip_fw_chain *ch, 104914bffb6SAlexander V. Chernikov struct tid_info *ti, struct table_algo *ta, char *adata, uint8_t tflags, 105914bffb6SAlexander V. Chernikov uint8_t vtype); 106b074b7bbSAlexander V. Chernikov static void free_table_config(struct namedobj_instance *ni, 107b074b7bbSAlexander V. Chernikov struct table_config *tc); 108db785d31SAlexander V. Chernikov static int create_table_internal(struct ip_fw_chain *ch, struct tid_info *ti, 1094c0c07a5SAlexander V. Chernikov char *aname, ipfw_xtable_info *i); 110b074b7bbSAlexander V. Chernikov static void link_table(struct ip_fw_chain *chain, struct table_config *tc); 111b074b7bbSAlexander V. Chernikov static void unlink_table(struct ip_fw_chain *chain, struct table_config *tc); 112b074b7bbSAlexander V. Chernikov static void free_table_state(void **state, void **xstate, uint8_t type); 1132d99a349SAlexander V. Chernikov static int export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh, 1142d99a349SAlexander V. Chernikov struct sockopt_data *sd); 115ac35ff17SAlexander V. Chernikov static void export_table_info(struct ip_fw_chain *ch, struct table_config *tc, 116ac35ff17SAlexander V. Chernikov ipfw_xtable_info *i); 11781d3153dSAlexander V. Chernikov static int dump_table_tentry(void *e, void *arg); 118f1220db8SAlexander V. Chernikov static int dump_table_xentry(void *e, void *arg); 119b074b7bbSAlexander V. Chernikov 1202d99a349SAlexander V. Chernikov static int ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt_data *sd); 1212d99a349SAlexander V. Chernikov static int ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt_data *sd); 122db785d31SAlexander V. Chernikov static int ipfw_manage_table_ent_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 123ac35ff17SAlexander V. Chernikov struct sockopt_data *sd); 124db785d31SAlexander V. Chernikov static int ipfw_manage_table_ent_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 125ac35ff17SAlexander V. Chernikov struct sockopt_data *sd); 126a73d728dSAlexander V. Chernikov static int swap_tables(struct ip_fw_chain *ch, struct tid_info *a, 12746d52008SAlexander V. Chernikov struct tid_info *b); 128ac35ff17SAlexander V. Chernikov 129b6ee846eSAlexander V. Chernikov static int check_table_space(struct ip_fw_chain *ch, struct table_config *tc, 130b6ee846eSAlexander V. Chernikov struct table_info *ti, uint32_t count); 131ac35ff17SAlexander V. Chernikov static int destroy_table(struct ip_fw_chain *ch, struct tid_info *ti); 132d3a4f924SAlexander V. Chernikov 1339f7d47b0SAlexander V. Chernikov static struct table_algo *find_table_algo(struct tables_config *tableconf, 1349490a627SAlexander V. Chernikov struct tid_info *ti, char *name); 135b074b7bbSAlexander V. Chernikov 13646d52008SAlexander V. Chernikov static void objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti); 13746d52008SAlexander V. Chernikov static void ntlv_to_ti(struct _ipfw_obj_ntlv *ntlv, struct tid_info *ti); 138a73d728dSAlexander V. Chernikov static int classify_table_opcode(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype); 13946d52008SAlexander V. Chernikov 140b074b7bbSAlexander V. Chernikov #define CHAIN_TO_TCFG(chain) ((struct tables_config *)(chain)->tblcfg) 141b074b7bbSAlexander V. Chernikov #define CHAIN_TO_NI(chain) (CHAIN_TO_TCFG(chain)->namehash) 1429f7d47b0SAlexander V. Chernikov #define KIDX_TO_TI(ch, k) (&(((struct table_info *)(ch)->tablestate)[k])) 143b074b7bbSAlexander V. Chernikov 144b6ee846eSAlexander V. Chernikov #define TA_BUF_SZ 128 /* On-stack buffer for add/delete state */ 145b6ee846eSAlexander V. Chernikov 1463a845e10SAlexander V. Chernikov /* 1473a845e10SAlexander V. Chernikov * Checks if we're able to insert/update entry @tei into table 1483a845e10SAlexander V. Chernikov * w.r.t @tc limits. 1493a845e10SAlexander V. Chernikov * May alter @tei to indicate insertion error / insert 1503a845e10SAlexander V. Chernikov * options. 1513a845e10SAlexander V. Chernikov * 1523a845e10SAlexander V. Chernikov * Returns 0 if operation can be performed/ 1533a845e10SAlexander V. Chernikov */ 1543a845e10SAlexander V. Chernikov static int 1553a845e10SAlexander V. Chernikov check_table_limit(struct table_config *tc, struct tentry_info *tei) 1563a845e10SAlexander V. Chernikov { 157b074b7bbSAlexander V. Chernikov 1583a845e10SAlexander V. Chernikov if (tc->limit == 0 || tc->count < tc->limit) 1593a845e10SAlexander V. Chernikov return (0); 1603a845e10SAlexander V. Chernikov 1613a845e10SAlexander V. Chernikov if ((tei->flags & TEI_FLAGS_UPDATE) == 0) { 1623a845e10SAlexander V. Chernikov /* Notify userland on error cause */ 1633a845e10SAlexander V. Chernikov tei->flags |= TEI_FLAGS_LIMIT; 1643a845e10SAlexander V. Chernikov return (EFBIG); 1653a845e10SAlexander V. Chernikov } 1663a845e10SAlexander V. Chernikov 1673a845e10SAlexander V. Chernikov /* 1683a845e10SAlexander V. Chernikov * We have UPDATE flag set. 1693a845e10SAlexander V. Chernikov * Permit updating record (if found), 1703a845e10SAlexander V. Chernikov * but restrict adding new one since we've 1713a845e10SAlexander V. Chernikov * already hit the limit. 1723a845e10SAlexander V. Chernikov */ 1733a845e10SAlexander V. Chernikov tei->flags |= TEI_FLAGS_DONTADD; 1743a845e10SAlexander V. Chernikov 1753a845e10SAlexander V. Chernikov return (0); 1763a845e10SAlexander V. Chernikov } 1773a845e10SAlexander V. Chernikov 1783a845e10SAlexander V. Chernikov /* 1793a845e10SAlexander V. Chernikov * Adds/updates one or more entries in table @ti. 1803a845e10SAlexander V. Chernikov * 1813a845e10SAlexander V. Chernikov * Returns 0 on success. 1823a845e10SAlexander V. Chernikov */ 1833b3a8eb9SGleb Smirnoff int 1841832a7b3SAlexander V. Chernikov add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti, 1853a845e10SAlexander V. Chernikov struct tentry_info *tei, uint8_t flags, uint32_t count) 1863b3a8eb9SGleb Smirnoff { 187db785d31SAlexander V. Chernikov struct table_config *tc; 1889f7d47b0SAlexander V. Chernikov struct table_algo *ta; 189b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 190b074b7bbSAlexander V. Chernikov uint16_t kidx; 1913a845e10SAlexander V. Chernikov int error, first_error, i, j, rerror, rollback; 1923a845e10SAlexander V. Chernikov uint32_t num, numadd; 193b6ee846eSAlexander V. Chernikov ipfw_xtable_info *xi; 1943a845e10SAlexander V. Chernikov struct tentry_info *ptei; 195b6ee846eSAlexander V. Chernikov char ta_buf[TA_BUF_SZ]; 1963a845e10SAlexander V. Chernikov size_t ta_buf_sz; 1973a845e10SAlexander V. Chernikov caddr_t ta_buf_m, v, vv; 1983b3a8eb9SGleb Smirnoff 1999f7d47b0SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 2009f7d47b0SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 2019f7d47b0SAlexander V. Chernikov 2029f7d47b0SAlexander V. Chernikov /* 2039f7d47b0SAlexander V. Chernikov * Find and reference existing table. 2049f7d47b0SAlexander V. Chernikov */ 2059f7d47b0SAlexander V. Chernikov ta = NULL; 2069f7d47b0SAlexander V. Chernikov if ((tc = find_table(ni, ti)) != NULL) { 2079f7d47b0SAlexander V. Chernikov /* check table type */ 2089f7d47b0SAlexander V. Chernikov if (tc->no.type != ti->type) { 2099f7d47b0SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 2103b3a8eb9SGleb Smirnoff return (EINVAL); 2113b3a8eb9SGleb Smirnoff } 2123b3a8eb9SGleb Smirnoff 2134f43138aSAlexander V. Chernikov if (tc->locked != 0) { 2144f43138aSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 2154f43138aSAlexander V. Chernikov return (EACCES); 2164f43138aSAlexander V. Chernikov } 2174f43138aSAlexander V. Chernikov 2184c0c07a5SAlexander V. Chernikov /* Try to exit early on limit hit */ 2193a845e10SAlexander V. Chernikov if ((error = check_table_limit(tc, tei)) != 0 && count == 1) { 2204c0c07a5SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 2214c0c07a5SAlexander V. Chernikov return (EFBIG); 2224c0c07a5SAlexander V. Chernikov } 2234c0c07a5SAlexander V. Chernikov 2249f7d47b0SAlexander V. Chernikov /* Reference and unlock */ 2259f7d47b0SAlexander V. Chernikov tc->no.refcnt++; 2269f7d47b0SAlexander V. Chernikov ta = tc->ta; 2279f7d47b0SAlexander V. Chernikov } 2289f7d47b0SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 2293b3a8eb9SGleb Smirnoff 230ac35ff17SAlexander V. Chernikov if (tc == NULL) { 231db785d31SAlexander V. Chernikov /* Compability mode: create new table for old clients */ 232db785d31SAlexander V. Chernikov if ((tei->flags & TEI_FLAGS_COMPAT) == 0) 233db785d31SAlexander V. Chernikov return (ESRCH); 2343b3a8eb9SGleb Smirnoff 235b6ee846eSAlexander V. Chernikov xi = malloc(sizeof(ipfw_xtable_info), M_TEMP, M_WAITOK|M_ZERO); 236b6ee846eSAlexander V. Chernikov xi->vtype = IPFW_VTYPE_U32; 2374c0c07a5SAlexander V. Chernikov 238b6ee846eSAlexander V. Chernikov error = create_table_internal(ch, ti, NULL, xi); 239b6ee846eSAlexander V. Chernikov free(xi, M_TEMP); 240db785d31SAlexander V. Chernikov 241db785d31SAlexander V. Chernikov if (error != 0) 242db785d31SAlexander V. Chernikov return (error); 243db785d31SAlexander V. Chernikov 244db785d31SAlexander V. Chernikov /* Let's try to find & reference another time */ 245db785d31SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 246db785d31SAlexander V. Chernikov if ((tc = find_table(ni, ti)) == NULL) { 247db785d31SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 248db785d31SAlexander V. Chernikov return (EINVAL); 249db785d31SAlexander V. Chernikov } 250db785d31SAlexander V. Chernikov 251db785d31SAlexander V. Chernikov if (tc->no.type != ti->type) { 252db785d31SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 253db785d31SAlexander V. Chernikov return (EINVAL); 254db785d31SAlexander V. Chernikov } 255db785d31SAlexander V. Chernikov 256db785d31SAlexander V. Chernikov /* Reference and unlock */ 257db785d31SAlexander V. Chernikov tc->no.refcnt++; 258db785d31SAlexander V. Chernikov ta = tc->ta; 259db785d31SAlexander V. Chernikov 260db785d31SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 261db785d31SAlexander V. Chernikov } 262db785d31SAlexander V. Chernikov 2639f7d47b0SAlexander V. Chernikov /* Prepare record (allocate memory) */ 2643a845e10SAlexander V. Chernikov ta_buf_sz = ta->ta_buf_size; 2653a845e10SAlexander V. Chernikov rollback = 0; 2663a845e10SAlexander V. Chernikov if (count == 1) { 2679f7d47b0SAlexander V. Chernikov memset(&ta_buf, 0, sizeof(ta_buf)); 2683a845e10SAlexander V. Chernikov ta_buf_m = ta_buf; 2693a845e10SAlexander V. Chernikov } else { 2703a845e10SAlexander V. Chernikov 2713a845e10SAlexander V. Chernikov /* 2723a845e10SAlexander V. Chernikov * Multiple adds, allocate larger buffer 2733a845e10SAlexander V. Chernikov * sufficient to hold both ADD state 2743a845e10SAlexander V. Chernikov * and DELETE state (this may be needed 2753a845e10SAlexander V. Chernikov * if we need to rollback all changes) 2763a845e10SAlexander V. Chernikov */ 2773a845e10SAlexander V. Chernikov ta_buf_m = malloc(2 * count * ta_buf_sz, M_TEMP, 2783a845e10SAlexander V. Chernikov M_WAITOK | M_ZERO); 2793a845e10SAlexander V. Chernikov } 2803a845e10SAlexander V. Chernikov v = ta_buf_m; 2813a845e10SAlexander V. Chernikov for (i = 0; i < count; i++, v += ta_buf_sz) { 2823a845e10SAlexander V. Chernikov error = ta->prepare_add(ch, &tei[i], v); 2833a845e10SAlexander V. Chernikov 2843a845e10SAlexander V. Chernikov /* 2853a845e10SAlexander V. Chernikov * Some syntax error (incorrect mask, or address, or 2863a845e10SAlexander V. Chernikov * anything). Return error regardless of atomicity 2873a845e10SAlexander V. Chernikov * settings. 2883a845e10SAlexander V. Chernikov */ 289db785d31SAlexander V. Chernikov if (error != 0) 2903a845e10SAlexander V. Chernikov goto cleanup; 2913a845e10SAlexander V. Chernikov } 2923b3a8eb9SGleb Smirnoff 293b074b7bbSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 2943b3a8eb9SGleb Smirnoff 295b6ee846eSAlexander V. Chernikov /* 296b6ee846eSAlexander V. Chernikov * Ensure we are able to add all entries without additional 297b6ee846eSAlexander V. Chernikov * memory allocations. May release/reacquire UH_WLOCK. 298b6ee846eSAlexander V. Chernikov */ 299b6ee846eSAlexander V. Chernikov kidx = tc->no.kidx; 300b6ee846eSAlexander V. Chernikov error = check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), count); 301b6ee846eSAlexander V. Chernikov if (error != 0) { 302b6ee846eSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 3033a845e10SAlexander V. Chernikov goto cleanup; 304b6ee846eSAlexander V. Chernikov } 305b6ee846eSAlexander V. Chernikov 306b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 3073b3a8eb9SGleb Smirnoff 3089f7d47b0SAlexander V. Chernikov /* Drop reference we've used in first search */ 3099f7d47b0SAlexander V. Chernikov tc->no.refcnt--; 3103a845e10SAlexander V. Chernikov /* We've got valid table in @tc. Let's try to add data */ 3119f7d47b0SAlexander V. Chernikov kidx = tc->no.kidx; 3129f7d47b0SAlexander V. Chernikov ta = tc->ta; 3133a845e10SAlexander V. Chernikov numadd = 0; 3143a845e10SAlexander V. Chernikov first_error = 0; 3159f7d47b0SAlexander V. Chernikov 316b074b7bbSAlexander V. Chernikov IPFW_WLOCK(ch); 3173a845e10SAlexander V. Chernikov 3183a845e10SAlexander V. Chernikov v = ta_buf_m; 3193a845e10SAlexander V. Chernikov for (i = 0; i < count; i++, v += ta_buf_sz) { 3203a845e10SAlexander V. Chernikov ptei = &tei[i]; 3213a845e10SAlexander V. Chernikov num = 0; 3223a845e10SAlexander V. Chernikov /* check limit before adding */ 3233a845e10SAlexander V. Chernikov if ((error = check_table_limit(tc, ptei)) == 0) { 3243a845e10SAlexander V. Chernikov error = ta->add(tc->astate, KIDX_TO_TI(ch, kidx), 3253a845e10SAlexander V. Chernikov ptei, v, &num); 3263a845e10SAlexander V. Chernikov /* Set status flag to inform userland */ 3273a845e10SAlexander V. Chernikov if (error == 0 && num != 0) 3283a845e10SAlexander V. Chernikov ptei->flags |= TEI_FLAGS_ADDED; 3293a845e10SAlexander V. Chernikov else if (error == ENOENT) 3303a845e10SAlexander V. Chernikov ptei->flags |= TEI_FLAGS_NOTFOUND; 3313a845e10SAlexander V. Chernikov else if (error == EEXIST) 3323a845e10SAlexander V. Chernikov ptei->flags |= TEI_FLAGS_EXISTS; 3333a845e10SAlexander V. Chernikov else 3343a845e10SAlexander V. Chernikov ptei->flags |= TEI_FLAGS_ERROR; 3353a845e10SAlexander V. Chernikov } 3363a845e10SAlexander V. Chernikov if (error == 0) { 3373a845e10SAlexander V. Chernikov /* Update number of records to ease limit checking */ 3383a845e10SAlexander V. Chernikov tc->count += num; 3393a845e10SAlexander V. Chernikov numadd += num; 3403a845e10SAlexander V. Chernikov continue; 3413a845e10SAlexander V. Chernikov } 3423a845e10SAlexander V. Chernikov 3433a845e10SAlexander V. Chernikov if (first_error == 0) 3443a845e10SAlexander V. Chernikov first_error = error; 3453a845e10SAlexander V. Chernikov 3463a845e10SAlexander V. Chernikov /* 3473a845e10SAlexander V. Chernikov * Some error have happened. Check our atomicity 3483a845e10SAlexander V. Chernikov * settings: continue if atomicity is not required, 3493a845e10SAlexander V. Chernikov * rollback changes otherwise. 3503a845e10SAlexander V. Chernikov */ 3513a845e10SAlexander V. Chernikov if ((flags & IPFW_CTF_ATOMIC) == 0) 3523a845e10SAlexander V. Chernikov continue; 3533a845e10SAlexander V. Chernikov 3543a845e10SAlexander V. Chernikov /* 3553a845e10SAlexander V. Chernikov * We need to rollback changes. 3563a845e10SAlexander V. Chernikov * This is tricky since some entries may have been 3573a845e10SAlexander V. Chernikov * updated, so we need to change their value back 3583a845e10SAlexander V. Chernikov * instead of deletion. 3593a845e10SAlexander V. Chernikov */ 3603a845e10SAlexander V. Chernikov rollback = 1; 3613a845e10SAlexander V. Chernikov v = ta_buf_m; 3623a845e10SAlexander V. Chernikov vv = v + count * ta_buf_sz; 3633a845e10SAlexander V. Chernikov for (j = 0; j < i; j++, v += ta_buf_sz, vv += ta_buf_sz) { 3643a845e10SAlexander V. Chernikov ptei = &tei[j]; 3653a845e10SAlexander V. Chernikov if ((ptei->flags & TEI_FLAGS_UPDATED) != 0) { 3663a845e10SAlexander V. Chernikov 3673a845e10SAlexander V. Chernikov /* 3683a845e10SAlexander V. Chernikov * We have old value stored by previous 3693a845e10SAlexander V. Chernikov * call in @ptei->value. Do add once again 3703a845e10SAlexander V. Chernikov * to restore it. 3713a845e10SAlexander V. Chernikov */ 3723a845e10SAlexander V. Chernikov rerror = ta->add(tc->astate, 3733a845e10SAlexander V. Chernikov KIDX_TO_TI(ch, kidx), ptei, v, &num); 3743a845e10SAlexander V. Chernikov KASSERT(rerror == 0, ("rollback UPDATE fail")); 3753a845e10SAlexander V. Chernikov KASSERT(num == 0, ("rollback UPDATE fail2")); 3763a845e10SAlexander V. Chernikov continue; 3773a845e10SAlexander V. Chernikov } 3783a845e10SAlexander V. Chernikov 3793a845e10SAlexander V. Chernikov rerror = ta->prepare_del(ch, ptei, vv); 3803a845e10SAlexander V. Chernikov KASSERT(rerror == 0, ("pre-rollback INSERT failed")); 3813a845e10SAlexander V. Chernikov rerror = ta->del(tc->astate, KIDX_TO_TI(ch, kidx), ptei, 3823a845e10SAlexander V. Chernikov vv, &num); 3833a845e10SAlexander V. Chernikov KASSERT(rerror == 0, ("rollback INSERT failed")); 3843a845e10SAlexander V. Chernikov tc->count -= num; 3853a845e10SAlexander V. Chernikov } 3863a845e10SAlexander V. Chernikov 3873a845e10SAlexander V. Chernikov break; 3883a845e10SAlexander V. Chernikov } 3893a845e10SAlexander V. Chernikov 3903b3a8eb9SGleb Smirnoff IPFW_WUNLOCK(ch); 3919f7d47b0SAlexander V. Chernikov 392b6ee846eSAlexander V. Chernikov /* Permit post-add algorithm grow/rehash. */ 3933a845e10SAlexander V. Chernikov if (numadd != 0) 3943a845e10SAlexander V. Chernikov check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), 0); 395db785d31SAlexander V. Chernikov 396b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 397b074b7bbSAlexander V. Chernikov 3983a845e10SAlexander V. Chernikov /* Return first error to user, if any */ 3993a845e10SAlexander V. Chernikov error = first_error; 4003a845e10SAlexander V. Chernikov 4013a845e10SAlexander V. Chernikov cleanup: 402ac35ff17SAlexander V. Chernikov /* Run cleaning callback anyway */ 4033a845e10SAlexander V. Chernikov v = ta_buf_m; 4043a845e10SAlexander V. Chernikov for (i = 0; i < count; i++, v += ta_buf_sz) 4053a845e10SAlexander V. Chernikov ta->flush_entry(ch, &tei[i], v); 4063a845e10SAlexander V. Chernikov 4073a845e10SAlexander V. Chernikov /* Clean up "deleted" state in case of rollback */ 4083a845e10SAlexander V. Chernikov if (rollback != 0) { 4093a845e10SAlexander V. Chernikov vv = ta_buf_m + count * ta_buf_sz; 4103a845e10SAlexander V. Chernikov for (i = 0; i < count; i++, vv += ta_buf_sz) 4113a845e10SAlexander V. Chernikov ta->flush_entry(ch, &tei[i], vv); 4123a845e10SAlexander V. Chernikov } 4133a845e10SAlexander V. Chernikov 4143a845e10SAlexander V. Chernikov if (ta_buf_m != ta_buf) 4153a845e10SAlexander V. Chernikov free(ta_buf_m, M_TEMP); 416b074b7bbSAlexander V. Chernikov 4179f7d47b0SAlexander V. Chernikov return (error); 4183b3a8eb9SGleb Smirnoff } 4193b3a8eb9SGleb Smirnoff 4203a845e10SAlexander V. Chernikov /* 4213a845e10SAlexander V. Chernikov * Deletes one or more entries in table @ti. 4223a845e10SAlexander V. Chernikov * 4233a845e10SAlexander V. Chernikov * Returns 0 on success. 4243a845e10SAlexander V. Chernikov */ 4253b3a8eb9SGleb Smirnoff int 4261832a7b3SAlexander V. Chernikov del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti, 4273a845e10SAlexander V. Chernikov struct tentry_info *tei, uint8_t flags, uint32_t count) 4283b3a8eb9SGleb Smirnoff { 429b074b7bbSAlexander V. Chernikov struct table_config *tc; 4309f7d47b0SAlexander V. Chernikov struct table_algo *ta; 431b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 4323a845e10SAlexander V. Chernikov struct tentry_info *ptei; 433b074b7bbSAlexander V. Chernikov uint16_t kidx; 4343a845e10SAlexander V. Chernikov int error, first_error, i; 4353a845e10SAlexander V. Chernikov uint32_t num, numdel; 436b6ee846eSAlexander V. Chernikov char ta_buf[TA_BUF_SZ]; 4373a845e10SAlexander V. Chernikov size_t ta_buf_sz; 4383a845e10SAlexander V. Chernikov caddr_t ta_buf_m, v; 4393b3a8eb9SGleb Smirnoff 4409f7d47b0SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 441b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 442b074b7bbSAlexander V. Chernikov if ((tc = find_table(ni, ti)) == NULL) { 4439f7d47b0SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 4443b3a8eb9SGleb Smirnoff return (ESRCH); 4453b3a8eb9SGleb Smirnoff } 4463b3a8eb9SGleb Smirnoff 4474f43138aSAlexander V. Chernikov if (tc->locked != 0) { 4484f43138aSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 4494f43138aSAlexander V. Chernikov return (EACCES); 4504f43138aSAlexander V. Chernikov } 4514f43138aSAlexander V. Chernikov 452b074b7bbSAlexander V. Chernikov if (tc->no.type != ti->type) { 4539f7d47b0SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 4543b3a8eb9SGleb Smirnoff return (EINVAL); 4553b3a8eb9SGleb Smirnoff } 4569f7d47b0SAlexander V. Chernikov 457db785d31SAlexander V. Chernikov /* 458b6ee846eSAlexander V. Chernikov * Give a chance for algorithm to shrink. 459b6ee846eSAlexander V. Chernikov * May release/reacquire UH_WLOCK. 460db785d31SAlexander V. Chernikov */ 461b6ee846eSAlexander V. Chernikov kidx = tc->no.kidx; 462b6ee846eSAlexander V. Chernikov error = check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), 0); 463db785d31SAlexander V. Chernikov if (error != 0) { 464db785d31SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 465db785d31SAlexander V. Chernikov return (error); 466db785d31SAlexander V. Chernikov } 467db785d31SAlexander V. Chernikov 4683a845e10SAlexander V. Chernikov /* Reference and unlock */ 4693a845e10SAlexander V. Chernikov tc->no.refcnt++; 4703a845e10SAlexander V. Chernikov ta = tc->ta; 4713a845e10SAlexander V. Chernikov 4723a845e10SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 4733a845e10SAlexander V. Chernikov 4743a845e10SAlexander V. Chernikov /* Prepare record (allocate memory) */ 4753a845e10SAlexander V. Chernikov ta_buf_sz = ta->ta_buf_size; 4763a845e10SAlexander V. Chernikov if (count == 1) { 4773a845e10SAlexander V. Chernikov memset(&ta_buf, 0, sizeof(ta_buf)); 4783a845e10SAlexander V. Chernikov ta_buf_m = ta_buf; 4793a845e10SAlexander V. Chernikov } else { 4803a845e10SAlexander V. Chernikov 481db785d31SAlexander V. Chernikov /* 4823a845e10SAlexander V. Chernikov * Multiple deletes, allocate larger buffer 4833a845e10SAlexander V. Chernikov * sufficient to hold delete state. 484db785d31SAlexander V. Chernikov */ 4853a845e10SAlexander V. Chernikov ta_buf_m = malloc(count * ta_buf_sz, M_TEMP, 4863a845e10SAlexander V. Chernikov M_WAITOK | M_ZERO); 4873a845e10SAlexander V. Chernikov } 4883a845e10SAlexander V. Chernikov v = ta_buf_m; 4893a845e10SAlexander V. Chernikov for (i = 0; i < count; i++, v += ta_buf_sz) { 4903a845e10SAlexander V. Chernikov error = ta->prepare_del(ch, &tei[i], v); 4913a845e10SAlexander V. Chernikov 4923a845e10SAlexander V. Chernikov /* 4933a845e10SAlexander V. Chernikov * Some syntax error (incorrect mask, or address, or 4943a845e10SAlexander V. Chernikov * anything). Return error immediately. 4953a845e10SAlexander V. Chernikov */ 4963a845e10SAlexander V. Chernikov if (error != 0) 4973a845e10SAlexander V. Chernikov goto cleanup; 4989f7d47b0SAlexander V. Chernikov } 4999f7d47b0SAlexander V. Chernikov 5003a845e10SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 5013a845e10SAlexander V. Chernikov 5023a845e10SAlexander V. Chernikov /* Drop reference we've used in first search */ 5033a845e10SAlexander V. Chernikov tc->no.refcnt--; 5043a845e10SAlexander V. Chernikov 505b074b7bbSAlexander V. Chernikov kidx = tc->no.kidx; 5063a845e10SAlexander V. Chernikov numdel = 0; 5073a845e10SAlexander V. Chernikov first_error = 0; 508b074b7bbSAlexander V. Chernikov 509b074b7bbSAlexander V. Chernikov IPFW_WLOCK(ch); 5103a845e10SAlexander V. Chernikov v = ta_buf_m; 5113a845e10SAlexander V. Chernikov for (i = 0; i < count; i++, v += ta_buf_sz) { 5123a845e10SAlexander V. Chernikov ptei = &tei[i]; 5133a845e10SAlexander V. Chernikov num = 0; 5143a845e10SAlexander V. Chernikov error = ta->del(tc->astate, KIDX_TO_TI(ch, kidx), ptei, v, 5153a845e10SAlexander V. Chernikov &num); 5163a845e10SAlexander V. Chernikov /* Save state for userland */ 5173a845e10SAlexander V. Chernikov if (error == 0) 5183a845e10SAlexander V. Chernikov ptei->flags |= TEI_FLAGS_DELETED; 5193a845e10SAlexander V. Chernikov else if (error == ENOENT) 5203a845e10SAlexander V. Chernikov ptei->flags |= TEI_FLAGS_NOTFOUND; 5213a845e10SAlexander V. Chernikov else 5223a845e10SAlexander V. Chernikov ptei->flags |= TEI_FLAGS_ERROR; 5233a845e10SAlexander V. Chernikov if (error != 0 && first_error == 0) 5243a845e10SAlexander V. Chernikov first_error = error; 5253a845e10SAlexander V. Chernikov tc->count -= num; 5263a845e10SAlexander V. Chernikov numdel += num; 5273a845e10SAlexander V. Chernikov } 5283b3a8eb9SGleb Smirnoff IPFW_WUNLOCK(ch); 5293b3a8eb9SGleb Smirnoff 5303a845e10SAlexander V. Chernikov if (numdel != 0) { 531b6ee846eSAlexander V. Chernikov /* Run post-del hook to permit shrinking */ 532b6ee846eSAlexander V. Chernikov error = check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), 0); 533b6ee846eSAlexander V. Chernikov } 534b074b7bbSAlexander V. Chernikov 5359f7d47b0SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 5363b3a8eb9SGleb Smirnoff 5373a845e10SAlexander V. Chernikov /* Return first error to user, if any */ 5383a845e10SAlexander V. Chernikov error = first_error; 5393a845e10SAlexander V. Chernikov 5403a845e10SAlexander V. Chernikov cleanup: 5413a845e10SAlexander V. Chernikov /* Run cleaning callback anyway */ 5423a845e10SAlexander V. Chernikov v = ta_buf_m; 5433a845e10SAlexander V. Chernikov for (i = 0; i < count; i++, v += ta_buf_sz) 5443a845e10SAlexander V. Chernikov ta->flush_entry(ch, &tei[i], v); 5453a845e10SAlexander V. Chernikov 5463a845e10SAlexander V. Chernikov if (ta_buf_m != ta_buf) 5473a845e10SAlexander V. Chernikov free(ta_buf_m, M_TEMP); 548ac35ff17SAlexander V. Chernikov 549ac35ff17SAlexander V. Chernikov return (error); 550ac35ff17SAlexander V. Chernikov } 551ac35ff17SAlexander V. Chernikov 552db785d31SAlexander V. Chernikov /* 553b6ee846eSAlexander V. Chernikov * Ensure that table @tc has enough space to add @count entries without 554b6ee846eSAlexander V. Chernikov * need for reallocation. 555db785d31SAlexander V. Chernikov * 556db785d31SAlexander V. Chernikov * Callbacks order: 557b6ee846eSAlexander V. Chernikov * 0) has_space() (UH_WLOCK) - checks if @count items can be added w/o resize. 558b6ee846eSAlexander V. Chernikov * 559db785d31SAlexander V. Chernikov * 1) alloc_modify (no locks, M_WAITOK) - alloc new state based on @pflags. 560db785d31SAlexander V. Chernikov * 2) prepare_modifyt (UH_WLOCK) - copy old data into new storage 561db785d31SAlexander V. Chernikov * 3) modify (UH_WLOCK + WLOCK) - switch pointers 562b6ee846eSAlexander V. Chernikov * 4) flush_modify (UH_WLOCK) - free state, if needed 563b6ee846eSAlexander V. Chernikov * 564b6ee846eSAlexander V. Chernikov * Returns 0 on success. 565db785d31SAlexander V. Chernikov */ 566db785d31SAlexander V. Chernikov static int 567b6ee846eSAlexander V. Chernikov check_table_space(struct ip_fw_chain *ch, struct table_config *tc, 568b6ee846eSAlexander V. Chernikov struct table_info *ti, uint32_t count) 569db785d31SAlexander V. Chernikov { 570b6ee846eSAlexander V. Chernikov struct table_algo *ta; 571b6ee846eSAlexander V. Chernikov uint64_t pflags; 572b6ee846eSAlexander V. Chernikov char ta_buf[TA_BUF_SZ]; 573db785d31SAlexander V. Chernikov int error; 574db785d31SAlexander V. Chernikov 575b6ee846eSAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(ch); 576db785d31SAlexander V. Chernikov 577b6ee846eSAlexander V. Chernikov error = 0; 578b6ee846eSAlexander V. Chernikov ta = tc->ta; 579b6ee846eSAlexander V. Chernikov /* Acquire reference not to loose @tc between locks/unlocks */ 580b6ee846eSAlexander V. Chernikov tc->no.refcnt++; 581db785d31SAlexander V. Chernikov 582db785d31SAlexander V. Chernikov /* 583b6ee846eSAlexander V. Chernikov * TODO: think about avoiding race between large add/large delete 584b6ee846eSAlexander V. Chernikov * operation on algorithm which implements shrinking along with 585b6ee846eSAlexander V. Chernikov * growing. 586db785d31SAlexander V. Chernikov */ 587b6ee846eSAlexander V. Chernikov while (true) { 588b6ee846eSAlexander V. Chernikov pflags = 0; 589b6ee846eSAlexander V. Chernikov if (ta->has_space(tc->astate, ti, count, &pflags) != 0) { 590b6ee846eSAlexander V. Chernikov tc->no.refcnt--; 591b6ee846eSAlexander V. Chernikov return (0); 592b6ee846eSAlexander V. Chernikov } 593db785d31SAlexander V. Chernikov 594b6ee846eSAlexander V. Chernikov /* We have to shrink/grow table */ 595b6ee846eSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 596b6ee846eSAlexander V. Chernikov memset(&ta_buf, 0, sizeof(ta_buf)); 597b6ee846eSAlexander V. Chernikov 598b6ee846eSAlexander V. Chernikov if ((error = ta->prepare_mod(ta_buf, &pflags)) != 0) { 599b6ee846eSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 600b6ee846eSAlexander V. Chernikov break; 601b6ee846eSAlexander V. Chernikov } 602b6ee846eSAlexander V. Chernikov 603b6ee846eSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 604b6ee846eSAlexander V. Chernikov 605b6ee846eSAlexander V. Chernikov /* Check if we still need to alter table */ 606b6ee846eSAlexander V. Chernikov ti = KIDX_TO_TI(ch, tc->no.kidx); 607b6ee846eSAlexander V. Chernikov if (ta->has_space(tc->astate, ti, count, &pflags) != 0) { 608b6ee846eSAlexander V. Chernikov 609b6ee846eSAlexander V. Chernikov /* 610b6ee846eSAlexander V. Chernikov * Other threads has already performed resize. 611b6ee846eSAlexander V. Chernikov * Flush our state and return/ 612b6ee846eSAlexander V. Chernikov */ 613b6ee846eSAlexander V. Chernikov ta->flush_mod(ta_buf); 614b6ee846eSAlexander V. Chernikov break; 615b6ee846eSAlexander V. Chernikov } 616b6ee846eSAlexander V. Chernikov 617b6ee846eSAlexander V. Chernikov error = ta->fill_mod(tc->astate, ti, ta_buf, &pflags); 618b6ee846eSAlexander V. Chernikov if (error == 0) { 619db785d31SAlexander V. Chernikov /* Do actual modification */ 620db785d31SAlexander V. Chernikov IPFW_WLOCK(ch); 62168394ec8SAlexander V. Chernikov ta->modify(tc->astate, ti, ta_buf, pflags); 622db785d31SAlexander V. Chernikov IPFW_WUNLOCK(ch); 623db785d31SAlexander V. Chernikov } 624db785d31SAlexander V. Chernikov 625b6ee846eSAlexander V. Chernikov /* Anyway, flush data and retry */ 626db785d31SAlexander V. Chernikov ta->flush_mod(ta_buf); 627b6ee846eSAlexander V. Chernikov } 628db785d31SAlexander V. Chernikov 629b6ee846eSAlexander V. Chernikov tc->no.refcnt--; 630db785d31SAlexander V. Chernikov return (error); 631db785d31SAlexander V. Chernikov } 632db785d31SAlexander V. Chernikov 6333a845e10SAlexander V. Chernikov /* 6343a845e10SAlexander V. Chernikov * Selects appropriate table operation handler 6353a845e10SAlexander V. Chernikov * depending on opcode version. 6363a845e10SAlexander V. Chernikov */ 637ac35ff17SAlexander V. Chernikov int 638db785d31SAlexander V. Chernikov ipfw_manage_table_ent(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 639ac35ff17SAlexander V. Chernikov struct sockopt_data *sd) 640ac35ff17SAlexander V. Chernikov { 641ac35ff17SAlexander V. Chernikov int error; 642ac35ff17SAlexander V. Chernikov 643ac35ff17SAlexander V. Chernikov switch (op3->version) { 644ac35ff17SAlexander V. Chernikov case 0: 645db785d31SAlexander V. Chernikov error = ipfw_manage_table_ent_v0(ch, op3, sd); 646ac35ff17SAlexander V. Chernikov break; 647ac35ff17SAlexander V. Chernikov case 1: 648db785d31SAlexander V. Chernikov error = ipfw_manage_table_ent_v1(ch, op3, sd); 649ac35ff17SAlexander V. Chernikov break; 650ac35ff17SAlexander V. Chernikov default: 651ac35ff17SAlexander V. Chernikov error = ENOTSUP; 652ac35ff17SAlexander V. Chernikov } 653ac35ff17SAlexander V. Chernikov 654ac35ff17SAlexander V. Chernikov return (error); 655ac35ff17SAlexander V. Chernikov } 656ac35ff17SAlexander V. Chernikov 657ac35ff17SAlexander V. Chernikov /* 658ac35ff17SAlexander V. Chernikov * Adds or deletes record in table. 659ac35ff17SAlexander V. Chernikov * Data layout (v0): 660ac35ff17SAlexander V. Chernikov * Request: [ ip_fw3_opheader ipfw_table_xentry ] 661ac35ff17SAlexander V. Chernikov * 662ac35ff17SAlexander V. Chernikov * Returns 0 on success 663ac35ff17SAlexander V. Chernikov */ 664ac35ff17SAlexander V. Chernikov static int 665db785d31SAlexander V. Chernikov ipfw_manage_table_ent_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 666ac35ff17SAlexander V. Chernikov struct sockopt_data *sd) 667ac35ff17SAlexander V. Chernikov { 668ac35ff17SAlexander V. Chernikov ipfw_table_xentry *xent; 669ac35ff17SAlexander V. Chernikov struct tentry_info tei; 670ac35ff17SAlexander V. Chernikov struct tid_info ti; 671ac35ff17SAlexander V. Chernikov int error, hdrlen, read; 672ac35ff17SAlexander V. Chernikov 673ac35ff17SAlexander V. Chernikov hdrlen = offsetof(ipfw_table_xentry, k); 674ac35ff17SAlexander V. Chernikov 675ac35ff17SAlexander V. Chernikov /* Check minimum header size */ 676ac35ff17SAlexander V. Chernikov if (sd->valsize < (sizeof(*op3) + hdrlen)) 677ac35ff17SAlexander V. Chernikov return (EINVAL); 678ac35ff17SAlexander V. Chernikov 679ac35ff17SAlexander V. Chernikov read = sizeof(ip_fw3_opheader); 680ac35ff17SAlexander V. Chernikov 681ac35ff17SAlexander V. Chernikov /* Check if xentry len field is valid */ 682ac35ff17SAlexander V. Chernikov xent = (ipfw_table_xentry *)(op3 + 1); 683ac35ff17SAlexander V. Chernikov if (xent->len < hdrlen || xent->len + read > sd->valsize) 684ac35ff17SAlexander V. Chernikov return (EINVAL); 685ac35ff17SAlexander V. Chernikov 686ac35ff17SAlexander V. Chernikov memset(&tei, 0, sizeof(tei)); 687ac35ff17SAlexander V. Chernikov tei.paddr = &xent->k; 688ac35ff17SAlexander V. Chernikov tei.masklen = xent->masklen; 689ac35ff17SAlexander V. Chernikov tei.value = xent->value; 690ac35ff17SAlexander V. Chernikov /* Old requests compability */ 691db785d31SAlexander V. Chernikov tei.flags = TEI_FLAGS_COMPAT; 692ac35ff17SAlexander V. Chernikov if (xent->type == IPFW_TABLE_CIDR) { 693ac35ff17SAlexander V. Chernikov if (xent->len - hdrlen == sizeof(in_addr_t)) 694ac35ff17SAlexander V. Chernikov tei.subtype = AF_INET; 695ac35ff17SAlexander V. Chernikov else 696ac35ff17SAlexander V. Chernikov tei.subtype = AF_INET6; 697ac35ff17SAlexander V. Chernikov } 698ac35ff17SAlexander V. Chernikov 699ac35ff17SAlexander V. Chernikov memset(&ti, 0, sizeof(ti)); 700ac35ff17SAlexander V. Chernikov ti.uidx = xent->tbl; 701ac35ff17SAlexander V. Chernikov ti.type = xent->type; 702ac35ff17SAlexander V. Chernikov 703ac35ff17SAlexander V. Chernikov error = (op3->opcode == IP_FW_TABLE_XADD) ? 7043a845e10SAlexander V. Chernikov add_table_entry(ch, &ti, &tei, 0, 1) : 7053a845e10SAlexander V. Chernikov del_table_entry(ch, &ti, &tei, 0, 1); 706ac35ff17SAlexander V. Chernikov 707ac35ff17SAlexander V. Chernikov return (error); 708ac35ff17SAlexander V. Chernikov } 709ac35ff17SAlexander V. Chernikov 710ac35ff17SAlexander V. Chernikov /* 711ac35ff17SAlexander V. Chernikov * Adds or deletes record in table. 712ac35ff17SAlexander V. Chernikov * Data layout (v1)(current): 713db785d31SAlexander V. Chernikov * Request: [ ipfw_obj_header 714db785d31SAlexander V. Chernikov * ipfw_obj_ctlv(IPFW_TLV_TBLENT_LIST) [ ipfw_obj_tentry x N ] 715db785d31SAlexander V. Chernikov * ] 716ac35ff17SAlexander V. Chernikov * 717ac35ff17SAlexander V. Chernikov * Returns 0 on success 718ac35ff17SAlexander V. Chernikov */ 719ac35ff17SAlexander V. Chernikov static int 720db785d31SAlexander V. Chernikov ipfw_manage_table_ent_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 721ac35ff17SAlexander V. Chernikov struct sockopt_data *sd) 722ac35ff17SAlexander V. Chernikov { 7233a845e10SAlexander V. Chernikov ipfw_obj_tentry *tent, *ptent; 724db785d31SAlexander V. Chernikov ipfw_obj_ctlv *ctlv; 725ac35ff17SAlexander V. Chernikov ipfw_obj_header *oh; 7263a845e10SAlexander V. Chernikov struct tentry_info *ptei, tei, *tei_buf; 727ac35ff17SAlexander V. Chernikov struct tid_info ti; 7283a845e10SAlexander V. Chernikov int error, i, kidx, read; 729ac35ff17SAlexander V. Chernikov 730ac35ff17SAlexander V. Chernikov /* Check minimum header size */ 731db785d31SAlexander V. Chernikov if (sd->valsize < (sizeof(*oh) + sizeof(*ctlv))) 732ac35ff17SAlexander V. Chernikov return (EINVAL); 733ac35ff17SAlexander V. Chernikov 734ac35ff17SAlexander V. Chernikov /* Check if passed data is too long */ 735ac35ff17SAlexander V. Chernikov if (sd->valsize != sd->kavail) 736ac35ff17SAlexander V. Chernikov return (EINVAL); 737ac35ff17SAlexander V. Chernikov 738ac35ff17SAlexander V. Chernikov oh = (ipfw_obj_header *)sd->kbuf; 739ac35ff17SAlexander V. Chernikov 740ac35ff17SAlexander V. Chernikov /* Basic length checks for TLVs */ 741ac35ff17SAlexander V. Chernikov if (oh->ntlv.head.length != sizeof(oh->ntlv)) 742ac35ff17SAlexander V. Chernikov return (EINVAL); 743ac35ff17SAlexander V. Chernikov 744ac35ff17SAlexander V. Chernikov read = sizeof(*oh); 745ac35ff17SAlexander V. Chernikov 746db785d31SAlexander V. Chernikov ctlv = (ipfw_obj_ctlv *)(oh + 1); 747db785d31SAlexander V. Chernikov if (ctlv->head.length + read != sd->valsize) 748db785d31SAlexander V. Chernikov return (EINVAL); 749db785d31SAlexander V. Chernikov 750db785d31SAlexander V. Chernikov read += sizeof(*ctlv); 751db785d31SAlexander V. Chernikov tent = (ipfw_obj_tentry *)(ctlv + 1); 7523a845e10SAlexander V. Chernikov if (ctlv->count * sizeof(*tent) + read != sd->valsize) 753ac35ff17SAlexander V. Chernikov return (EINVAL); 754ac35ff17SAlexander V. Chernikov 7553a845e10SAlexander V. Chernikov if (ctlv->count == 0) 7563a845e10SAlexander V. Chernikov return (0); 757ac35ff17SAlexander V. Chernikov 7583a845e10SAlexander V. Chernikov /* 7593a845e10SAlexander V. Chernikov * Mark entire buffer as "read". 7603a845e10SAlexander V. Chernikov * This makes sopt api write it back 7613a845e10SAlexander V. Chernikov * after function return. 7623a845e10SAlexander V. Chernikov */ 7633a845e10SAlexander V. Chernikov ipfw_get_sopt_header(sd, sd->valsize); 7643a845e10SAlexander V. Chernikov 7653a845e10SAlexander V. Chernikov /* Perform basic checks for each entry */ 7663a845e10SAlexander V. Chernikov ptent = tent; 7673a845e10SAlexander V. Chernikov kidx = tent->idx; 7683a845e10SAlexander V. Chernikov for (i = 0; i < ctlv->count; i++, ptent++) { 7693a845e10SAlexander V. Chernikov if (ptent->head.length != sizeof(*ptent)) 7703a845e10SAlexander V. Chernikov return (EINVAL); 7713a845e10SAlexander V. Chernikov if (ptent->idx != kidx) 7723a845e10SAlexander V. Chernikov return (ENOTSUP); 7733a845e10SAlexander V. Chernikov } 7743a845e10SAlexander V. Chernikov 7753a845e10SAlexander V. Chernikov /* Convert data into kernel request objects */ 77681d3153dSAlexander V. Chernikov objheader_to_ti(oh, &ti); 777ac35ff17SAlexander V. Chernikov ti.type = oh->ntlv.type; 7783a845e10SAlexander V. Chernikov ti.uidx = kidx; 7793a845e10SAlexander V. Chernikov 7803a845e10SAlexander V. Chernikov /* Use on-stack buffer for single add/del */ 7813a845e10SAlexander V. Chernikov if (ctlv->count == 1) { 7823a845e10SAlexander V. Chernikov memset(&tei, 0, sizeof(tei)); 7833a845e10SAlexander V. Chernikov tei_buf = &tei; 7843a845e10SAlexander V. Chernikov } else 7853a845e10SAlexander V. Chernikov tei_buf = malloc(ctlv->count * sizeof(tei), M_TEMP, 7863a845e10SAlexander V. Chernikov M_WAITOK | M_ZERO); 7873a845e10SAlexander V. Chernikov 7883a845e10SAlexander V. Chernikov ptei = tei_buf; 7893a845e10SAlexander V. Chernikov ptent = tent; 7903a845e10SAlexander V. Chernikov for (i = 0; i < ctlv->count; i++, ptent++, ptei++) { 7913a845e10SAlexander V. Chernikov ptei->paddr = &ptent->k; 7923a845e10SAlexander V. Chernikov ptei->subtype = ptent->subtype; 7933a845e10SAlexander V. Chernikov ptei->masklen = ptent->masklen; 7943a845e10SAlexander V. Chernikov if (ptent->head.flags & IPFW_TF_UPDATE) 7953a845e10SAlexander V. Chernikov ptei->flags |= TEI_FLAGS_UPDATE; 7963a845e10SAlexander V. Chernikov ptei->value = ptent->value; 7973a845e10SAlexander V. Chernikov } 798ac35ff17SAlexander V. Chernikov 799ac35ff17SAlexander V. Chernikov error = (oh->opheader.opcode == IP_FW_TABLE_XADD) ? 8003a845e10SAlexander V. Chernikov add_table_entry(ch, &ti, tei_buf, ctlv->flags, ctlv->count) : 8013a845e10SAlexander V. Chernikov del_table_entry(ch, &ti, tei_buf, ctlv->flags, ctlv->count); 8023a845e10SAlexander V. Chernikov 8033a845e10SAlexander V. Chernikov /* Translate result back to userland */ 8043a845e10SAlexander V. Chernikov ptei = tei_buf; 8053a845e10SAlexander V. Chernikov ptent = tent; 8063a845e10SAlexander V. Chernikov for (i = 0; i < ctlv->count; i++, ptent++, ptei++) { 8073a845e10SAlexander V. Chernikov if (ptei->flags & TEI_FLAGS_ADDED) 8083a845e10SAlexander V. Chernikov ptent->result = IPFW_TR_ADDED; 8093a845e10SAlexander V. Chernikov else if (ptei->flags & TEI_FLAGS_DELETED) 8103a845e10SAlexander V. Chernikov ptent->result = IPFW_TR_DELETED; 8113a845e10SAlexander V. Chernikov else if (ptei->flags & TEI_FLAGS_UPDATED) 8123a845e10SAlexander V. Chernikov ptent->result = IPFW_TR_UPDATED; 8133a845e10SAlexander V. Chernikov else if (ptei->flags & TEI_FLAGS_LIMIT) 8143a845e10SAlexander V. Chernikov ptent->result = IPFW_TR_LIMIT; 8153a845e10SAlexander V. Chernikov else if (ptei->flags & TEI_FLAGS_ERROR) 8163a845e10SAlexander V. Chernikov ptent->result = IPFW_TR_ERROR; 8173a845e10SAlexander V. Chernikov else if (ptei->flags & TEI_FLAGS_NOTFOUND) 8183a845e10SAlexander V. Chernikov ptent->result = IPFW_TR_NOTFOUND; 8193a845e10SAlexander V. Chernikov else if (ptei->flags & TEI_FLAGS_EXISTS) 8203a845e10SAlexander V. Chernikov ptent->result = IPFW_TR_EXISTS; 8213a845e10SAlexander V. Chernikov } 8223a845e10SAlexander V. Chernikov 8233a845e10SAlexander V. Chernikov if (tei_buf != &tei) 8243a845e10SAlexander V. Chernikov free(tei_buf, M_TEMP); 825ac35ff17SAlexander V. Chernikov 826ac35ff17SAlexander V. Chernikov return (error); 827ac35ff17SAlexander V. Chernikov } 828ac35ff17SAlexander V. Chernikov 82981d3153dSAlexander V. Chernikov /* 83081d3153dSAlexander V. Chernikov * Looks up an entry in given table. 83181d3153dSAlexander V. Chernikov * Data layout (v0)(current): 83281d3153dSAlexander V. Chernikov * Request: [ ipfw_obj_header ipfw_obj_tentry ] 83381d3153dSAlexander V. Chernikov * Reply: [ ipfw_obj_header ipfw_obj_tentry ] 83481d3153dSAlexander V. Chernikov * 83581d3153dSAlexander V. Chernikov * Returns 0 on success 83681d3153dSAlexander V. Chernikov */ 83781d3153dSAlexander V. Chernikov int 83881d3153dSAlexander V. Chernikov ipfw_find_table_entry(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 83981d3153dSAlexander V. Chernikov struct sockopt_data *sd) 84081d3153dSAlexander V. Chernikov { 84181d3153dSAlexander V. Chernikov ipfw_obj_tentry *tent; 84281d3153dSAlexander V. Chernikov ipfw_obj_header *oh; 84381d3153dSAlexander V. Chernikov struct tid_info ti; 84481d3153dSAlexander V. Chernikov struct table_config *tc; 84581d3153dSAlexander V. Chernikov struct table_algo *ta; 84681d3153dSAlexander V. Chernikov struct table_info *kti; 84781d3153dSAlexander V. Chernikov struct namedobj_instance *ni; 848914bffb6SAlexander V. Chernikov int error; 84981d3153dSAlexander V. Chernikov size_t sz; 85081d3153dSAlexander V. Chernikov 85181d3153dSAlexander V. Chernikov /* Check minimum header size */ 85281d3153dSAlexander V. Chernikov sz = sizeof(*oh) + sizeof(*tent); 85381d3153dSAlexander V. Chernikov if (sd->valsize != sz) 85481d3153dSAlexander V. Chernikov return (EINVAL); 85581d3153dSAlexander V. Chernikov 85681d3153dSAlexander V. Chernikov oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 85781d3153dSAlexander V. Chernikov tent = (ipfw_obj_tentry *)(oh + 1); 85881d3153dSAlexander V. Chernikov 85981d3153dSAlexander V. Chernikov /* Basic length checks for TLVs */ 86081d3153dSAlexander V. Chernikov if (oh->ntlv.head.length != sizeof(oh->ntlv)) 86181d3153dSAlexander V. Chernikov return (EINVAL); 86281d3153dSAlexander V. Chernikov 86381d3153dSAlexander V. Chernikov objheader_to_ti(oh, &ti); 86481d3153dSAlexander V. Chernikov ti.type = oh->ntlv.type; 86581d3153dSAlexander V. Chernikov ti.uidx = tent->idx; 86681d3153dSAlexander V. Chernikov 86781d3153dSAlexander V. Chernikov IPFW_UH_RLOCK(ch); 86881d3153dSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 86981d3153dSAlexander V. Chernikov 87081d3153dSAlexander V. Chernikov /* 87181d3153dSAlexander V. Chernikov * Find existing table and check its type . 87281d3153dSAlexander V. Chernikov */ 87381d3153dSAlexander V. Chernikov ta = NULL; 87481d3153dSAlexander V. Chernikov if ((tc = find_table(ni, &ti)) == NULL) { 87581d3153dSAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 87681d3153dSAlexander V. Chernikov return (ESRCH); 87781d3153dSAlexander V. Chernikov } 87881d3153dSAlexander V. Chernikov 87981d3153dSAlexander V. Chernikov /* check table type */ 88081d3153dSAlexander V. Chernikov if (tc->no.type != ti.type) { 88181d3153dSAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 88281d3153dSAlexander V. Chernikov return (EINVAL); 88381d3153dSAlexander V. Chernikov } 88481d3153dSAlexander V. Chernikov 88581d3153dSAlexander V. Chernikov kti = KIDX_TO_TI(ch, tc->no.kidx); 88681d3153dSAlexander V. Chernikov ta = tc->ta; 88781d3153dSAlexander V. Chernikov 888914bffb6SAlexander V. Chernikov if (ta->find_tentry == NULL) 889914bffb6SAlexander V. Chernikov return (ENOTSUP); 890914bffb6SAlexander V. Chernikov 891914bffb6SAlexander V. Chernikov error = ta->find_tentry(tc->astate, kti, tent); 89281d3153dSAlexander V. Chernikov 89381d3153dSAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 89481d3153dSAlexander V. Chernikov 89581d3153dSAlexander V. Chernikov return (error); 89681d3153dSAlexander V. Chernikov } 89781d3153dSAlexander V. Chernikov 89846d52008SAlexander V. Chernikov /* 89946d52008SAlexander V. Chernikov * Flushes all entries or destroys given table. 90046d52008SAlexander V. Chernikov * Data layout (v0)(current): 90146d52008SAlexander V. Chernikov * Request: [ ipfw_obj_header ] 90246d52008SAlexander V. Chernikov * 90346d52008SAlexander V. Chernikov * Returns 0 on success 90446d52008SAlexander V. Chernikov */ 905ac35ff17SAlexander V. Chernikov int 906ac35ff17SAlexander V. Chernikov ipfw_flush_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 907ac35ff17SAlexander V. Chernikov struct sockopt_data *sd) 908ac35ff17SAlexander V. Chernikov { 909ac35ff17SAlexander V. Chernikov int error; 910ac35ff17SAlexander V. Chernikov struct _ipfw_obj_header *oh; 911ac35ff17SAlexander V. Chernikov struct tid_info ti; 912ac35ff17SAlexander V. Chernikov 913ac35ff17SAlexander V. Chernikov if (sd->valsize != sizeof(*oh)) 914ac35ff17SAlexander V. Chernikov return (EINVAL); 915ac35ff17SAlexander V. Chernikov 916ac35ff17SAlexander V. Chernikov oh = (struct _ipfw_obj_header *)op3; 917ac35ff17SAlexander V. Chernikov objheader_to_ti(oh, &ti); 918ac35ff17SAlexander V. Chernikov 9191832a7b3SAlexander V. Chernikov if (op3->opcode == IP_FW_TABLE_XDESTROY) 920ac35ff17SAlexander V. Chernikov error = destroy_table(ch, &ti); 9211832a7b3SAlexander V. Chernikov else if (op3->opcode == IP_FW_TABLE_XFLUSH) 922ac35ff17SAlexander V. Chernikov error = flush_table(ch, &ti); 923ac35ff17SAlexander V. Chernikov else 924ac35ff17SAlexander V. Chernikov return (ENOTSUP); 925ac35ff17SAlexander V. Chernikov 926ac35ff17SAlexander V. Chernikov return (error); 9273b3a8eb9SGleb Smirnoff } 9283b3a8eb9SGleb Smirnoff 9291832a7b3SAlexander V. Chernikov int 930ac35ff17SAlexander V. Chernikov flush_table(struct ip_fw_chain *ch, struct tid_info *ti) 9313b3a8eb9SGleb Smirnoff { 932b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 933b074b7bbSAlexander V. Chernikov struct table_config *tc; 9349f7d47b0SAlexander V. Chernikov struct table_algo *ta; 9359f7d47b0SAlexander V. Chernikov struct table_info ti_old, ti_new, *tablestate; 9369f7d47b0SAlexander V. Chernikov void *astate_old, *astate_new; 937914bffb6SAlexander V. Chernikov char algostate[64], *pstate; 938b074b7bbSAlexander V. Chernikov int error; 939b074b7bbSAlexander V. Chernikov uint16_t kidx; 940914bffb6SAlexander V. Chernikov uint8_t tflags; 9413b3a8eb9SGleb Smirnoff 9423b3a8eb9SGleb Smirnoff /* 9439f7d47b0SAlexander V. Chernikov * Stage 1: save table algoritm. 944b074b7bbSAlexander V. Chernikov * Reference found table to ensure it won't disappear. 9453b3a8eb9SGleb Smirnoff */ 946b074b7bbSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 947b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 948b074b7bbSAlexander V. Chernikov if ((tc = find_table(ni, ti)) == NULL) { 949b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 950b074b7bbSAlexander V. Chernikov return (ESRCH); 951b074b7bbSAlexander V. Chernikov } 9529f7d47b0SAlexander V. Chernikov ta = tc->ta; 953b074b7bbSAlexander V. Chernikov tc->no.refcnt++; 954daabb523SAlexander V. Chernikov /* Save statup algo parameters */ 955daabb523SAlexander V. Chernikov if (ta->print_config != NULL) { 956daabb523SAlexander V. Chernikov ta->print_config(tc->astate, KIDX_TO_TI(ch, tc->no.kidx), 957daabb523SAlexander V. Chernikov algostate, sizeof(algostate)); 958daabb523SAlexander V. Chernikov pstate = algostate; 959daabb523SAlexander V. Chernikov } else 960daabb523SAlexander V. Chernikov pstate = NULL; 961914bffb6SAlexander V. Chernikov tflags = tc->tflags; 962b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 9633b3a8eb9SGleb Smirnoff 964b074b7bbSAlexander V. Chernikov /* 9659f7d47b0SAlexander V. Chernikov * Stage 2: allocate new table instance using same algo. 966b074b7bbSAlexander V. Chernikov */ 9679f7d47b0SAlexander V. Chernikov memset(&ti_new, 0, sizeof(struct table_info)); 968914bffb6SAlexander V. Chernikov if ((error = ta->init(ch, &astate_new, &ti_new, pstate, tflags)) != 0) { 969b074b7bbSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 970b074b7bbSAlexander V. Chernikov tc->no.refcnt--; 971b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 972b074b7bbSAlexander V. Chernikov return (error); 973b074b7bbSAlexander V. Chernikov } 974b074b7bbSAlexander V. Chernikov 975b074b7bbSAlexander V. Chernikov /* 976b074b7bbSAlexander V. Chernikov * Stage 3: swap old state pointers with newly-allocated ones. 977b074b7bbSAlexander V. Chernikov * Decrease refcount. 978b074b7bbSAlexander V. Chernikov */ 979b074b7bbSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 980b074b7bbSAlexander V. Chernikov 981b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 982b074b7bbSAlexander V. Chernikov kidx = tc->no.kidx; 9839f7d47b0SAlexander V. Chernikov tablestate = (struct table_info *)ch->tablestate; 984b074b7bbSAlexander V. Chernikov 9859f7d47b0SAlexander V. Chernikov IPFW_WLOCK(ch); 9869f7d47b0SAlexander V. Chernikov ti_old = tablestate[kidx]; 9879f7d47b0SAlexander V. Chernikov tablestate[kidx] = ti_new; 9889f7d47b0SAlexander V. Chernikov IPFW_WUNLOCK(ch); 989b074b7bbSAlexander V. Chernikov 9909f7d47b0SAlexander V. Chernikov astate_old = tc->astate; 9919f7d47b0SAlexander V. Chernikov tc->astate = astate_new; 9929f7d47b0SAlexander V. Chernikov tc->ti = ti_new; 9939f7d47b0SAlexander V. Chernikov tc->count = 0; 994b074b7bbSAlexander V. Chernikov tc->no.refcnt--; 995b074b7bbSAlexander V. Chernikov 996b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 9973b3a8eb9SGleb Smirnoff 998b074b7bbSAlexander V. Chernikov /* 999b074b7bbSAlexander V. Chernikov * Stage 4: perform real flush. 1000b074b7bbSAlexander V. Chernikov */ 10019f7d47b0SAlexander V. Chernikov ta->destroy(astate_old, &ti_old); 10023b3a8eb9SGleb Smirnoff 10033b3a8eb9SGleb Smirnoff return (0); 10043b3a8eb9SGleb Smirnoff } 10053b3a8eb9SGleb Smirnoff 1006b074b7bbSAlexander V. Chernikov /* 100746d52008SAlexander V. Chernikov * Swaps two tables. 100846d52008SAlexander V. Chernikov * Data layout (v0)(current): 100946d52008SAlexander V. Chernikov * Request: [ ipfw_obj_header ipfw_obj_ntlv ] 101046d52008SAlexander V. Chernikov * 101146d52008SAlexander V. Chernikov * Returns 0 on success 101246d52008SAlexander V. Chernikov */ 101346d52008SAlexander V. Chernikov int 101446d52008SAlexander V. Chernikov ipfw_swap_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 101546d52008SAlexander V. Chernikov struct sockopt_data *sd) 101646d52008SAlexander V. Chernikov { 101746d52008SAlexander V. Chernikov int error; 101846d52008SAlexander V. Chernikov struct _ipfw_obj_header *oh; 101946d52008SAlexander V. Chernikov struct tid_info ti_a, ti_b; 102046d52008SAlexander V. Chernikov 102146d52008SAlexander V. Chernikov if (sd->valsize != sizeof(*oh) + sizeof(ipfw_obj_ntlv)) 102246d52008SAlexander V. Chernikov return (EINVAL); 102346d52008SAlexander V. Chernikov 102446d52008SAlexander V. Chernikov oh = (struct _ipfw_obj_header *)op3; 102546d52008SAlexander V. Chernikov ntlv_to_ti(&oh->ntlv, &ti_a); 102646d52008SAlexander V. Chernikov ntlv_to_ti((ipfw_obj_ntlv *)(oh + 1), &ti_b); 102746d52008SAlexander V. Chernikov 1028a73d728dSAlexander V. Chernikov error = swap_tables(ch, &ti_a, &ti_b); 102946d52008SAlexander V. Chernikov 103046d52008SAlexander V. Chernikov return (error); 103146d52008SAlexander V. Chernikov } 103246d52008SAlexander V. Chernikov 103346d52008SAlexander V. Chernikov static int 1034a73d728dSAlexander V. Chernikov swap_tables(struct ip_fw_chain *ch, struct tid_info *a, 103546d52008SAlexander V. Chernikov struct tid_info *b) 103646d52008SAlexander V. Chernikov { 103746d52008SAlexander V. Chernikov struct namedobj_instance *ni; 103846d52008SAlexander V. Chernikov struct table_config *tc_a, *tc_b; 103946d52008SAlexander V. Chernikov struct table_algo *ta; 104046d52008SAlexander V. Chernikov struct table_info ti, *tablestate; 104146d52008SAlexander V. Chernikov void *astate; 104246d52008SAlexander V. Chernikov uint32_t count; 104346d52008SAlexander V. Chernikov 104446d52008SAlexander V. Chernikov /* 104546d52008SAlexander V. Chernikov * Stage 1: find both tables and ensure they are of 104646d52008SAlexander V. Chernikov * the same type and algo. 104746d52008SAlexander V. Chernikov */ 104846d52008SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 104946d52008SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 105046d52008SAlexander V. Chernikov if ((tc_a = find_table(ni, a)) == NULL) { 105146d52008SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 105246d52008SAlexander V. Chernikov return (ESRCH); 105346d52008SAlexander V. Chernikov } 105446d52008SAlexander V. Chernikov if ((tc_b = find_table(ni, b)) == NULL) { 105546d52008SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 105646d52008SAlexander V. Chernikov return (ESRCH); 105746d52008SAlexander V. Chernikov } 105846d52008SAlexander V. Chernikov 105946d52008SAlexander V. Chernikov /* It is very easy to swap between the same table */ 106046d52008SAlexander V. Chernikov if (tc_a == tc_b) { 106146d52008SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 106246d52008SAlexander V. Chernikov return (0); 106346d52008SAlexander V. Chernikov } 106446d52008SAlexander V. Chernikov 106546d52008SAlexander V. Chernikov /* Check type and value are the same */ 106646d52008SAlexander V. Chernikov if (tc_a->no.type != tc_b->no.type || tc_a->tflags != tc_b->tflags || 106746d52008SAlexander V. Chernikov tc_a->vtype != tc_b->vtype) { 106846d52008SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 106946d52008SAlexander V. Chernikov return (EINVAL); 107046d52008SAlexander V. Chernikov } 107146d52008SAlexander V. Chernikov 107246d52008SAlexander V. Chernikov /* Check limits before swap */ 107346d52008SAlexander V. Chernikov if ((tc_a->limit != 0 && tc_b->count > tc_a->limit) || 107446d52008SAlexander V. Chernikov (tc_b->limit != 0 && tc_a->count > tc_b->limit)) { 107546d52008SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 107646d52008SAlexander V. Chernikov return (EFBIG); 107746d52008SAlexander V. Chernikov } 107846d52008SAlexander V. Chernikov 107946d52008SAlexander V. Chernikov /* Everything is fine, prepare to swap */ 108046d52008SAlexander V. Chernikov tablestate = (struct table_info *)ch->tablestate; 108146d52008SAlexander V. Chernikov ti = tablestate[tc_a->no.kidx]; 108246d52008SAlexander V. Chernikov ta = tc_a->ta; 108346d52008SAlexander V. Chernikov astate = tc_a->astate; 108446d52008SAlexander V. Chernikov count = tc_a->count; 108546d52008SAlexander V. Chernikov 108646d52008SAlexander V. Chernikov IPFW_WLOCK(ch); 108746d52008SAlexander V. Chernikov /* a <- b */ 108846d52008SAlexander V. Chernikov tablestate[tc_a->no.kidx] = tablestate[tc_b->no.kidx]; 108946d52008SAlexander V. Chernikov tc_a->ta = tc_b->ta; 109046d52008SAlexander V. Chernikov tc_a->astate = tc_b->astate; 109146d52008SAlexander V. Chernikov tc_a->count = tc_b->count; 109246d52008SAlexander V. Chernikov /* b <- a */ 109346d52008SAlexander V. Chernikov tablestate[tc_b->no.kidx] = ti; 109446d52008SAlexander V. Chernikov tc_b->ta = ta; 109546d52008SAlexander V. Chernikov tc_b->astate = astate; 109646d52008SAlexander V. Chernikov tc_b->count = count; 109746d52008SAlexander V. Chernikov IPFW_WUNLOCK(ch); 109846d52008SAlexander V. Chernikov 109946d52008SAlexander V. Chernikov /* Ensure tc.ti copies are in sync */ 110046d52008SAlexander V. Chernikov tc_a->ti = tablestate[tc_a->no.kidx]; 110146d52008SAlexander V. Chernikov tc_b->ti = tablestate[tc_b->no.kidx]; 110246d52008SAlexander V. Chernikov 110346d52008SAlexander V. Chernikov /* Notify both tables on @ti change */ 110446d52008SAlexander V. Chernikov if (tc_a->ta->change_ti != NULL) 110546d52008SAlexander V. Chernikov tc_a->ta->change_ti(tc_a->astate, &tablestate[tc_a->no.kidx]); 110646d52008SAlexander V. Chernikov if (tc_b->ta->change_ti != NULL) 110746d52008SAlexander V. Chernikov tc_b->ta->change_ti(tc_b->astate, &tablestate[tc_b->no.kidx]); 110846d52008SAlexander V. Chernikov 110946d52008SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 111046d52008SAlexander V. Chernikov 111146d52008SAlexander V. Chernikov return (0); 111246d52008SAlexander V. Chernikov } 111346d52008SAlexander V. Chernikov 111446d52008SAlexander V. Chernikov /* 11159f7d47b0SAlexander V. Chernikov * Destroys table specified by @ti. 1116ac35ff17SAlexander V. Chernikov * Data layout (v0)(current): 1117ac35ff17SAlexander V. Chernikov * Request: [ ip_fw3_opheader ] 1118ac35ff17SAlexander V. Chernikov * 1119ac35ff17SAlexander V. Chernikov * Returns 0 on success 1120b074b7bbSAlexander V. Chernikov */ 1121ac35ff17SAlexander V. Chernikov static int 1122ac35ff17SAlexander V. Chernikov destroy_table(struct ip_fw_chain *ch, struct tid_info *ti) 1123b074b7bbSAlexander V. Chernikov { 1124b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 1125b074b7bbSAlexander V. Chernikov struct table_config *tc; 1126b074b7bbSAlexander V. Chernikov 1127b074b7bbSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 1128b074b7bbSAlexander V. Chernikov 1129b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 1130b074b7bbSAlexander V. Chernikov if ((tc = find_table(ni, ti)) == NULL) { 1131b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1132b074b7bbSAlexander V. Chernikov return (ESRCH); 1133b074b7bbSAlexander V. Chernikov } 1134b074b7bbSAlexander V. Chernikov 11359f7d47b0SAlexander V. Chernikov /* Do not permit destroying referenced tables */ 11369f7d47b0SAlexander V. Chernikov if (tc->no.refcnt > 0) { 1137b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1138b074b7bbSAlexander V. Chernikov return (EBUSY); 1139b074b7bbSAlexander V. Chernikov } 1140b074b7bbSAlexander V. Chernikov 1141b074b7bbSAlexander V. Chernikov IPFW_WLOCK(ch); 1142b074b7bbSAlexander V. Chernikov unlink_table(ch, tc); 1143b074b7bbSAlexander V. Chernikov IPFW_WUNLOCK(ch); 1144b074b7bbSAlexander V. Chernikov 1145b074b7bbSAlexander V. Chernikov /* Free obj index */ 1146ac35ff17SAlexander V. Chernikov if (ipfw_objhash_free_idx(ni, tc->no.kidx) != 0) 1147b074b7bbSAlexander V. Chernikov printf("Error unlinking kidx %d from table %s\n", 1148b074b7bbSAlexander V. Chernikov tc->no.kidx, tc->tablename); 1149b074b7bbSAlexander V. Chernikov 1150b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1151b074b7bbSAlexander V. Chernikov 1152b074b7bbSAlexander V. Chernikov free_table_config(ni, tc); 1153b074b7bbSAlexander V. Chernikov 1154b074b7bbSAlexander V. Chernikov return (0); 1155b074b7bbSAlexander V. Chernikov } 1156b074b7bbSAlexander V. Chernikov 1157b074b7bbSAlexander V. Chernikov static void 1158b074b7bbSAlexander V. Chernikov destroy_table_locked(struct namedobj_instance *ni, struct named_object *no, 1159b074b7bbSAlexander V. Chernikov void *arg) 1160b074b7bbSAlexander V. Chernikov { 1161b074b7bbSAlexander V. Chernikov 1162b074b7bbSAlexander V. Chernikov unlink_table((struct ip_fw_chain *)arg, (struct table_config *)no); 1163ac35ff17SAlexander V. Chernikov if (ipfw_objhash_free_idx(ni, no->kidx) != 0) 1164b074b7bbSAlexander V. Chernikov printf("Error unlinking kidx %d from table %s\n", 1165b074b7bbSAlexander V. Chernikov no->kidx, no->name); 1166b074b7bbSAlexander V. Chernikov free_table_config(ni, (struct table_config *)no); 1167b074b7bbSAlexander V. Chernikov } 1168b074b7bbSAlexander V. Chernikov 11693b3a8eb9SGleb Smirnoff void 11703b3a8eb9SGleb Smirnoff ipfw_destroy_tables(struct ip_fw_chain *ch) 11713b3a8eb9SGleb Smirnoff { 11723b3a8eb9SGleb Smirnoff 1173b074b7bbSAlexander V. Chernikov /* Remove all tables from working set */ 1174b074b7bbSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 1175b074b7bbSAlexander V. Chernikov IPFW_WLOCK(ch); 1176b074b7bbSAlexander V. Chernikov ipfw_objhash_foreach(CHAIN_TO_NI(ch), destroy_table_locked, ch); 1177b074b7bbSAlexander V. Chernikov IPFW_WUNLOCK(ch); 1178b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 11793b3a8eb9SGleb Smirnoff 11803b3a8eb9SGleb Smirnoff /* Free pointers itself */ 11819f7d47b0SAlexander V. Chernikov free(ch->tablestate, M_IPFW); 11829f7d47b0SAlexander V. Chernikov 11839f7d47b0SAlexander V. Chernikov ipfw_table_algo_destroy(ch); 1184b074b7bbSAlexander V. Chernikov 1185b074b7bbSAlexander V. Chernikov ipfw_objhash_destroy(CHAIN_TO_NI(ch)); 1186b074b7bbSAlexander V. Chernikov free(CHAIN_TO_TCFG(ch), M_IPFW); 11873b3a8eb9SGleb Smirnoff } 11883b3a8eb9SGleb Smirnoff 11893b3a8eb9SGleb Smirnoff int 11903b3a8eb9SGleb Smirnoff ipfw_init_tables(struct ip_fw_chain *ch) 11913b3a8eb9SGleb Smirnoff { 1192b074b7bbSAlexander V. Chernikov struct tables_config *tcfg; 1193b074b7bbSAlexander V. Chernikov 11943b3a8eb9SGleb Smirnoff /* Allocate pointers */ 11959f7d47b0SAlexander V. Chernikov ch->tablestate = malloc(V_fw_tables_max * sizeof(struct table_info), 11969f7d47b0SAlexander V. Chernikov M_IPFW, M_WAITOK | M_ZERO); 1197b074b7bbSAlexander V. Chernikov 1198b074b7bbSAlexander V. Chernikov tcfg = malloc(sizeof(struct tables_config), M_IPFW, M_WAITOK | M_ZERO); 1199b074b7bbSAlexander V. Chernikov tcfg->namehash = ipfw_objhash_create(V_fw_tables_max); 1200b074b7bbSAlexander V. Chernikov ch->tblcfg = tcfg; 1201b074b7bbSAlexander V. Chernikov 12029f7d47b0SAlexander V. Chernikov ipfw_table_algo_init(ch); 12039f7d47b0SAlexander V. Chernikov 12043b3a8eb9SGleb Smirnoff return (0); 12053b3a8eb9SGleb Smirnoff } 12063b3a8eb9SGleb Smirnoff 12073b3a8eb9SGleb Smirnoff int 12083b3a8eb9SGleb Smirnoff ipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables) 12093b3a8eb9SGleb Smirnoff { 12103b3a8eb9SGleb Smirnoff unsigned int ntables_old, tbl; 1211b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 12129f7d47b0SAlexander V. Chernikov void *new_idx, *old_tablestate, *tablestate; 121368394ec8SAlexander V. Chernikov struct table_info *ti; 121468394ec8SAlexander V. Chernikov struct table_config *tc; 121568394ec8SAlexander V. Chernikov int i, new_blocks; 12163b3a8eb9SGleb Smirnoff 12173b3a8eb9SGleb Smirnoff /* Check new value for validity */ 12183b3a8eb9SGleb Smirnoff if (ntables > IPFW_TABLES_MAX) 12193b3a8eb9SGleb Smirnoff ntables = IPFW_TABLES_MAX; 12203b3a8eb9SGleb Smirnoff 12213b3a8eb9SGleb Smirnoff /* Allocate new pointers */ 12229f7d47b0SAlexander V. Chernikov tablestate = malloc(ntables * sizeof(struct table_info), 12239f7d47b0SAlexander V. Chernikov M_IPFW, M_WAITOK | M_ZERO); 12249f7d47b0SAlexander V. Chernikov 1225b074b7bbSAlexander V. Chernikov ipfw_objhash_bitmap_alloc(ntables, (void *)&new_idx, &new_blocks); 12263b3a8eb9SGleb Smirnoff 12279f7d47b0SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 12283b3a8eb9SGleb Smirnoff 12293b3a8eb9SGleb Smirnoff tbl = (ntables >= V_fw_tables_max) ? V_fw_tables_max : ntables; 1230b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 1231b074b7bbSAlexander V. Chernikov 12329f7d47b0SAlexander V. Chernikov /* Temporary restrict decreasing max_tables */ 12339f7d47b0SAlexander V. Chernikov if (ntables < V_fw_tables_max) { 12349f7d47b0SAlexander V. Chernikov 12359f7d47b0SAlexander V. Chernikov /* 12369f7d47b0SAlexander V. Chernikov * FIXME: Check if we really can shrink 12379f7d47b0SAlexander V. Chernikov */ 12389f7d47b0SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1239b074b7bbSAlexander V. Chernikov return (EINVAL); 1240b074b7bbSAlexander V. Chernikov } 12413b3a8eb9SGleb Smirnoff 12429f7d47b0SAlexander V. Chernikov /* Copy table info/indices */ 12439f7d47b0SAlexander V. Chernikov memcpy(tablestate, ch->tablestate, sizeof(struct table_info) * tbl); 12449f7d47b0SAlexander V. Chernikov ipfw_objhash_bitmap_merge(ni, &new_idx, &new_blocks); 12453b3a8eb9SGleb Smirnoff 12469f7d47b0SAlexander V. Chernikov IPFW_WLOCK(ch); 12479f7d47b0SAlexander V. Chernikov 12489f7d47b0SAlexander V. Chernikov /* Change pointers */ 12499f7d47b0SAlexander V. Chernikov old_tablestate = ch->tablestate; 12509f7d47b0SAlexander V. Chernikov ch->tablestate = tablestate; 12519f7d47b0SAlexander V. Chernikov ipfw_objhash_bitmap_swap(ni, &new_idx, &new_blocks); 12523b3a8eb9SGleb Smirnoff 12533b3a8eb9SGleb Smirnoff ntables_old = V_fw_tables_max; 12543b3a8eb9SGleb Smirnoff V_fw_tables_max = ntables; 12553b3a8eb9SGleb Smirnoff 12563b3a8eb9SGleb Smirnoff IPFW_WUNLOCK(ch); 125768394ec8SAlexander V. Chernikov 125868394ec8SAlexander V. Chernikov /* Notify all consumers that their @ti pointer has changed */ 125968394ec8SAlexander V. Chernikov ti = (struct table_info *)ch->tablestate; 126068394ec8SAlexander V. Chernikov for (i = 0; i < tbl; i++, ti++) { 126168394ec8SAlexander V. Chernikov if (ti->lookup == NULL) 126268394ec8SAlexander V. Chernikov continue; 126368394ec8SAlexander V. Chernikov tc = (struct table_config *)ipfw_objhash_lookup_kidx(ni, i); 126468394ec8SAlexander V. Chernikov if (tc == NULL || tc->ta->change_ti == NULL) 126568394ec8SAlexander V. Chernikov continue; 126668394ec8SAlexander V. Chernikov 126768394ec8SAlexander V. Chernikov tc->ta->change_ti(tc->astate, ti); 126868394ec8SAlexander V. Chernikov } 126968394ec8SAlexander V. Chernikov 12709f7d47b0SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 12713b3a8eb9SGleb Smirnoff 12723b3a8eb9SGleb Smirnoff /* Free old pointers */ 12739f7d47b0SAlexander V. Chernikov free(old_tablestate, M_IPFW); 1274b074b7bbSAlexander V. Chernikov ipfw_objhash_bitmap_free(new_idx, new_blocks); 12753b3a8eb9SGleb Smirnoff 12763b3a8eb9SGleb Smirnoff return (0); 12773b3a8eb9SGleb Smirnoff } 12783b3a8eb9SGleb Smirnoff 1279a73d728dSAlexander V. Chernikov /* 1280a73d728dSAlexander V. Chernikov * Switch between "set 0" and "rule set" table binding, 1281a73d728dSAlexander V. Chernikov * Check all ruleset bindings and permits changing 1282a73d728dSAlexander V. Chernikov * IFF each binding has both rule AND table in default set (set 0). 1283a73d728dSAlexander V. Chernikov * 1284a73d728dSAlexander V. Chernikov * Returns 0 on success. 1285a73d728dSAlexander V. Chernikov */ 1286a73d728dSAlexander V. Chernikov int 1287a73d728dSAlexander V. Chernikov ipfw_switch_tables_namespace(struct ip_fw_chain *ch, unsigned int sets) 1288a73d728dSAlexander V. Chernikov { 1289a73d728dSAlexander V. Chernikov struct namedobj_instance *ni; 1290a73d728dSAlexander V. Chernikov struct named_object *no; 1291a73d728dSAlexander V. Chernikov struct ip_fw *rule; 1292a73d728dSAlexander V. Chernikov ipfw_insn *cmd; 1293a73d728dSAlexander V. Chernikov int cmdlen, i, l; 1294a73d728dSAlexander V. Chernikov uint16_t kidx; 1295a73d728dSAlexander V. Chernikov uint8_t type; 1296a73d728dSAlexander V. Chernikov 1297a73d728dSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 1298a73d728dSAlexander V. Chernikov 1299a73d728dSAlexander V. Chernikov if (V_fw_tables_sets == sets) { 1300a73d728dSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1301a73d728dSAlexander V. Chernikov return (0); 1302a73d728dSAlexander V. Chernikov } 1303a73d728dSAlexander V. Chernikov 1304a73d728dSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 1305a73d728dSAlexander V. Chernikov 1306a73d728dSAlexander V. Chernikov for (i = 0; i < ch->n_rules; i++) { 1307a73d728dSAlexander V. Chernikov rule = ch->map[i]; 1308a73d728dSAlexander V. Chernikov 1309a73d728dSAlexander V. Chernikov l = rule->cmd_len; 1310a73d728dSAlexander V. Chernikov cmd = rule->cmd; 1311a73d728dSAlexander V. Chernikov cmdlen = 0; 1312a73d728dSAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 1313a73d728dSAlexander V. Chernikov cmdlen = F_LEN(cmd); 1314a73d728dSAlexander V. Chernikov 1315a73d728dSAlexander V. Chernikov if (classify_table_opcode(cmd, &kidx, &type) != 0) 1316a73d728dSAlexander V. Chernikov continue; 1317a73d728dSAlexander V. Chernikov 1318a73d728dSAlexander V. Chernikov no = ipfw_objhash_lookup_kidx(ni, kidx); 1319a73d728dSAlexander V. Chernikov 1320a73d728dSAlexander V. Chernikov if (no->set != 0 || rule->set != 0) { 1321a73d728dSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1322a73d728dSAlexander V. Chernikov return (EBUSY); 1323a73d728dSAlexander V. Chernikov } 1324a73d728dSAlexander V. Chernikov 1325a73d728dSAlexander V. Chernikov } 1326a73d728dSAlexander V. Chernikov } 1327a73d728dSAlexander V. Chernikov V_fw_tables_sets = sets; 1328a73d728dSAlexander V. Chernikov 1329a73d728dSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1330a73d728dSAlexander V. Chernikov 1331a73d728dSAlexander V. Chernikov return (0); 1332a73d728dSAlexander V. Chernikov } 1333a73d728dSAlexander V. Chernikov 13343b3a8eb9SGleb Smirnoff int 13353b3a8eb9SGleb Smirnoff ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, 13363b3a8eb9SGleb Smirnoff uint32_t *val) 13373b3a8eb9SGleb Smirnoff { 13389f7d47b0SAlexander V. Chernikov struct table_info *ti; 13393b3a8eb9SGleb Smirnoff 13409f7d47b0SAlexander V. Chernikov ti = &(((struct table_info *)ch->tablestate)[tbl]); 13419f7d47b0SAlexander V. Chernikov 13429f7d47b0SAlexander V. Chernikov return (ti->lookup(ti, &addr, sizeof(in_addr_t), val)); 13433b3a8eb9SGleb Smirnoff } 13449f7d47b0SAlexander V. Chernikov 13459f7d47b0SAlexander V. Chernikov int 13469f7d47b0SAlexander V. Chernikov ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, uint16_t plen, 13479f7d47b0SAlexander V. Chernikov void *paddr, uint32_t *val) 13489f7d47b0SAlexander V. Chernikov { 13499f7d47b0SAlexander V. Chernikov struct table_info *ti; 13509f7d47b0SAlexander V. Chernikov 13519f7d47b0SAlexander V. Chernikov ti = &(((struct table_info *)ch->tablestate)[tbl]); 13529f7d47b0SAlexander V. Chernikov 13539f7d47b0SAlexander V. Chernikov return (ti->lookup(ti, paddr, plen, val)); 13549f7d47b0SAlexander V. Chernikov } 13559f7d47b0SAlexander V. Chernikov 13569f7d47b0SAlexander V. Chernikov /* 13579f7d47b0SAlexander V. Chernikov * Info/List/dump support for tables. 13589f7d47b0SAlexander V. Chernikov * 13599f7d47b0SAlexander V. Chernikov */ 13609f7d47b0SAlexander V. Chernikov 1361f1220db8SAlexander V. Chernikov /* 1362d3a4f924SAlexander V. Chernikov * High-level 'get' cmds sysctl handlers 1363d3a4f924SAlexander V. Chernikov */ 1364d3a4f924SAlexander V. Chernikov 1365d3a4f924SAlexander V. Chernikov /* 1366d3a4f924SAlexander V. Chernikov * Lists all tables currently available in kernel. 1367ac35ff17SAlexander V. Chernikov * Data layout (v0)(current): 1368d3a4f924SAlexander V. Chernikov * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size 1369d3a4f924SAlexander V. Chernikov * Reply: [ ipfw_obj_lheader ipfw_xtable_info x N ] 1370d3a4f924SAlexander V. Chernikov * 1371d3a4f924SAlexander V. Chernikov * Returns 0 on success 1372d3a4f924SAlexander V. Chernikov */ 1373f1220db8SAlexander V. Chernikov int 13742d99a349SAlexander V. Chernikov ipfw_list_tables(struct ip_fw_chain *ch, struct sockopt_data *sd) 1375f1220db8SAlexander V. Chernikov { 1376f1220db8SAlexander V. Chernikov struct _ipfw_obj_lheader *olh; 1377f1220db8SAlexander V. Chernikov int error; 1378f1220db8SAlexander V. Chernikov 13792d99a349SAlexander V. Chernikov olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh)); 13802d99a349SAlexander V. Chernikov if (olh == NULL) 1381d3a4f924SAlexander V. Chernikov return (EINVAL); 138268394ec8SAlexander V. Chernikov if (sd->valsize < olh->size) 138368394ec8SAlexander V. Chernikov return (EINVAL); 1384d3a4f924SAlexander V. Chernikov 1385f1220db8SAlexander V. Chernikov IPFW_UH_RLOCK(ch); 13862d99a349SAlexander V. Chernikov error = export_tables(ch, olh, sd); 1387f1220db8SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 1388f1220db8SAlexander V. Chernikov 1389f1220db8SAlexander V. Chernikov return (error); 1390f1220db8SAlexander V. Chernikov } 1391f1220db8SAlexander V. Chernikov 1392f1220db8SAlexander V. Chernikov /* 13932d99a349SAlexander V. Chernikov * Store table info to buffer provided by @sd. 1394ac35ff17SAlexander V. Chernikov * Data layout (v0)(current): 1395d3a4f924SAlexander V. Chernikov * Request: [ ipfw_obj_header ipfw_xtable_info(empty)] 1396d3a4f924SAlexander V. Chernikov * Reply: [ ipfw_obj_header ipfw_xtable_info ] 1397d3a4f924SAlexander V. Chernikov * 1398d3a4f924SAlexander V. Chernikov * Returns 0 on success. 1399d3a4f924SAlexander V. Chernikov */ 1400d3a4f924SAlexander V. Chernikov int 14012d99a349SAlexander V. Chernikov ipfw_describe_table(struct ip_fw_chain *ch, struct sockopt_data *sd) 1402d3a4f924SAlexander V. Chernikov { 1403d3a4f924SAlexander V. Chernikov struct _ipfw_obj_header *oh; 1404d3a4f924SAlexander V. Chernikov struct table_config *tc; 1405d3a4f924SAlexander V. Chernikov struct tid_info ti; 1406d3a4f924SAlexander V. Chernikov size_t sz; 1407d3a4f924SAlexander V. Chernikov 1408d3a4f924SAlexander V. Chernikov sz = sizeof(*oh) + sizeof(ipfw_xtable_info); 14092d99a349SAlexander V. Chernikov oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 14102d99a349SAlexander V. Chernikov if (oh == NULL) 1411d3a4f924SAlexander V. Chernikov return (EINVAL); 1412d3a4f924SAlexander V. Chernikov 1413d3a4f924SAlexander V. Chernikov objheader_to_ti(oh, &ti); 1414d3a4f924SAlexander V. Chernikov 1415d3a4f924SAlexander V. Chernikov IPFW_UH_RLOCK(ch); 1416d3a4f924SAlexander V. Chernikov if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) { 1417d3a4f924SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 1418d3a4f924SAlexander V. Chernikov return (ESRCH); 1419d3a4f924SAlexander V. Chernikov } 1420d3a4f924SAlexander V. Chernikov 1421ac35ff17SAlexander V. Chernikov export_table_info(ch, tc, (ipfw_xtable_info *)(oh + 1)); 1422d3a4f924SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 1423d3a4f924SAlexander V. Chernikov 14242d99a349SAlexander V. Chernikov return (0); 1425d3a4f924SAlexander V. Chernikov } 1426d3a4f924SAlexander V. Chernikov 1427f1220db8SAlexander V. Chernikov struct dump_args { 1428f1220db8SAlexander V. Chernikov struct table_info *ti; 1429f1220db8SAlexander V. Chernikov struct table_config *tc; 14302d99a349SAlexander V. Chernikov struct sockopt_data *sd; 1431f1220db8SAlexander V. Chernikov uint32_t cnt; 1432f1220db8SAlexander V. Chernikov uint16_t uidx; 143381d3153dSAlexander V. Chernikov int error; 14342d99a349SAlexander V. Chernikov ipfw_table_entry *ent; 14352d99a349SAlexander V. Chernikov uint32_t size; 143681d3153dSAlexander V. Chernikov ipfw_obj_tentry tent; 1437f1220db8SAlexander V. Chernikov }; 1438f1220db8SAlexander V. Chernikov 1439f1220db8SAlexander V. Chernikov int 14402d99a349SAlexander V. Chernikov ipfw_dump_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 14412d99a349SAlexander V. Chernikov struct sockopt_data *sd) 1442f1220db8SAlexander V. Chernikov { 1443d3a4f924SAlexander V. Chernikov int error; 1444d3a4f924SAlexander V. Chernikov 1445d3a4f924SAlexander V. Chernikov switch (op3->version) { 1446d3a4f924SAlexander V. Chernikov case 0: 14472d99a349SAlexander V. Chernikov error = ipfw_dump_table_v0(ch, sd); 1448d3a4f924SAlexander V. Chernikov break; 1449d3a4f924SAlexander V. Chernikov case 1: 14502d99a349SAlexander V. Chernikov error = ipfw_dump_table_v1(ch, sd); 1451d3a4f924SAlexander V. Chernikov break; 1452d3a4f924SAlexander V. Chernikov default: 1453d3a4f924SAlexander V. Chernikov error = ENOTSUP; 1454d3a4f924SAlexander V. Chernikov } 1455d3a4f924SAlexander V. Chernikov 1456d3a4f924SAlexander V. Chernikov return (error); 1457d3a4f924SAlexander V. Chernikov } 1458d3a4f924SAlexander V. Chernikov 1459d3a4f924SAlexander V. Chernikov /* 1460d3a4f924SAlexander V. Chernikov * Dumps all table data 1461ac35ff17SAlexander V. Chernikov * Data layout (v1)(current): 14622d99a349SAlexander V. Chernikov * Request: [ ipfw_obj_header ], size = ipfw_xtable_info.size 146381d3153dSAlexander V. Chernikov * Reply: [ ipfw_obj_header ipfw_xtable_info ipfw_obj_tentry x N ] 1464d3a4f924SAlexander V. Chernikov * 1465d3a4f924SAlexander V. Chernikov * Returns 0 on success 1466d3a4f924SAlexander V. Chernikov */ 1467d3a4f924SAlexander V. Chernikov static int 14682d99a349SAlexander V. Chernikov ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt_data *sd) 1469d3a4f924SAlexander V. Chernikov { 1470f1220db8SAlexander V. Chernikov struct _ipfw_obj_header *oh; 1471f1220db8SAlexander V. Chernikov ipfw_xtable_info *i; 1472f1220db8SAlexander V. Chernikov struct tid_info ti; 1473f1220db8SAlexander V. Chernikov struct table_config *tc; 1474f1220db8SAlexander V. Chernikov struct table_algo *ta; 1475f1220db8SAlexander V. Chernikov struct dump_args da; 1476d3a4f924SAlexander V. Chernikov uint32_t sz; 1477f1220db8SAlexander V. Chernikov 14782d99a349SAlexander V. Chernikov sz = sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info); 14792d99a349SAlexander V. Chernikov oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 14802d99a349SAlexander V. Chernikov if (oh == NULL) 1481d3a4f924SAlexander V. Chernikov return (EINVAL); 1482d3a4f924SAlexander V. Chernikov 14832d99a349SAlexander V. Chernikov i = (ipfw_xtable_info *)(oh + 1); 1484d3a4f924SAlexander V. Chernikov objheader_to_ti(oh, &ti); 1485f1220db8SAlexander V. Chernikov 1486f1220db8SAlexander V. Chernikov IPFW_UH_RLOCK(ch); 1487f1220db8SAlexander V. Chernikov if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) { 1488f1220db8SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 1489f1220db8SAlexander V. Chernikov return (ESRCH); 1490f1220db8SAlexander V. Chernikov } 1491ac35ff17SAlexander V. Chernikov export_table_info(ch, tc, i); 14922d99a349SAlexander V. Chernikov 1493720ee730SAlexander V. Chernikov if (sd->valsize < i->size) { 14942d99a349SAlexander V. Chernikov 14952d99a349SAlexander V. Chernikov /* 14962d99a349SAlexander V. Chernikov * Submitted buffer size is not enough. 14972d99a349SAlexander V. Chernikov * WE've already filled in @i structure with 14982d99a349SAlexander V. Chernikov * relevant table info including size, so we 14992d99a349SAlexander V. Chernikov * can return. Buffer will be flushed automatically. 15002d99a349SAlexander V. Chernikov */ 1501f1220db8SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 15022d99a349SAlexander V. Chernikov return (ENOMEM); 1503f1220db8SAlexander V. Chernikov } 1504f1220db8SAlexander V. Chernikov 1505f1220db8SAlexander V. Chernikov /* 1506f1220db8SAlexander V. Chernikov * Do the actual dump in eXtended format 1507f1220db8SAlexander V. Chernikov */ 1508d3a4f924SAlexander V. Chernikov memset(&da, 0, sizeof(da)); 1509f1220db8SAlexander V. Chernikov da.ti = KIDX_TO_TI(ch, tc->no.kidx); 1510f1220db8SAlexander V. Chernikov da.tc = tc; 15112d99a349SAlexander V. Chernikov da.sd = sd; 1512f1220db8SAlexander V. Chernikov 1513f1220db8SAlexander V. Chernikov ta = tc->ta; 1514f1220db8SAlexander V. Chernikov 151581d3153dSAlexander V. Chernikov ta->foreach(tc->astate, da.ti, dump_table_tentry, &da); 1516f1220db8SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 1517f1220db8SAlexander V. Chernikov 151881d3153dSAlexander V. Chernikov return (da.error); 1519f1220db8SAlexander V. Chernikov } 1520f1220db8SAlexander V. Chernikov 1521d3a4f924SAlexander V. Chernikov /* 1522d3a4f924SAlexander V. Chernikov * Dumps all table data 15232d99a349SAlexander V. Chernikov * Data layout (version 0)(legacy): 1524d3a4f924SAlexander V. Chernikov * Request: [ ipfw_xtable ], size = IP_FW_TABLE_XGETSIZE() 1525d3a4f924SAlexander V. Chernikov * Reply: [ ipfw_xtable ipfw_table_xentry x N ] 1526d3a4f924SAlexander V. Chernikov * 1527d3a4f924SAlexander V. Chernikov * Returns 0 on success 1528d3a4f924SAlexander V. Chernikov */ 1529d3a4f924SAlexander V. Chernikov static int 15302d99a349SAlexander V. Chernikov ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt_data *sd) 1531d3a4f924SAlexander V. Chernikov { 1532d3a4f924SAlexander V. Chernikov ipfw_xtable *xtbl; 1533d3a4f924SAlexander V. Chernikov struct tid_info ti; 1534d3a4f924SAlexander V. Chernikov struct table_config *tc; 1535d3a4f924SAlexander V. Chernikov struct table_algo *ta; 1536d3a4f924SAlexander V. Chernikov struct dump_args da; 1537d3a4f924SAlexander V. Chernikov size_t sz; 1538d3a4f924SAlexander V. Chernikov 15392d99a349SAlexander V. Chernikov xtbl = (ipfw_xtable *)ipfw_get_sopt_header(sd, sizeof(ipfw_xtable)); 15402d99a349SAlexander V. Chernikov if (xtbl == NULL) 1541d3a4f924SAlexander V. Chernikov return (EINVAL); 1542d3a4f924SAlexander V. Chernikov 1543d3a4f924SAlexander V. Chernikov memset(&ti, 0, sizeof(ti)); 1544d3a4f924SAlexander V. Chernikov ti.uidx = xtbl->tbl; 1545d3a4f924SAlexander V. Chernikov 1546d3a4f924SAlexander V. Chernikov IPFW_UH_RLOCK(ch); 1547d3a4f924SAlexander V. Chernikov if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) { 1548d3a4f924SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 1549d3a4f924SAlexander V. Chernikov return (0); 1550d3a4f924SAlexander V. Chernikov } 1551d3a4f924SAlexander V. Chernikov sz = tc->count * sizeof(ipfw_table_xentry) + sizeof(ipfw_xtable); 15522d99a349SAlexander V. Chernikov 15532d99a349SAlexander V. Chernikov xtbl->cnt = tc->count; 15542d99a349SAlexander V. Chernikov xtbl->size = sz; 15552d99a349SAlexander V. Chernikov xtbl->type = tc->no.type; 15562d99a349SAlexander V. Chernikov xtbl->tbl = ti.uidx; 15572d99a349SAlexander V. Chernikov 15582d99a349SAlexander V. Chernikov if (sd->valsize < sz) { 15592d99a349SAlexander V. Chernikov 15602d99a349SAlexander V. Chernikov /* 15612d99a349SAlexander V. Chernikov * Submitted buffer size is not enough. 15622d99a349SAlexander V. Chernikov * WE've already filled in @i structure with 15632d99a349SAlexander V. Chernikov * relevant table info including size, so we 15642d99a349SAlexander V. Chernikov * can return. Buffer will be flushed automatically. 15652d99a349SAlexander V. Chernikov */ 1566d3a4f924SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 15672d99a349SAlexander V. Chernikov return (ENOMEM); 1568d3a4f924SAlexander V. Chernikov } 1569d3a4f924SAlexander V. Chernikov 1570d3a4f924SAlexander V. Chernikov /* Do the actual dump in eXtended format */ 1571d3a4f924SAlexander V. Chernikov memset(&da, 0, sizeof(da)); 1572d3a4f924SAlexander V. Chernikov da.ti = KIDX_TO_TI(ch, tc->no.kidx); 1573d3a4f924SAlexander V. Chernikov da.tc = tc; 15742d99a349SAlexander V. Chernikov da.sd = sd; 15752d99a349SAlexander V. Chernikov 1576d3a4f924SAlexander V. Chernikov ta = tc->ta; 1577d3a4f924SAlexander V. Chernikov 1578d3a4f924SAlexander V. Chernikov ta->foreach(tc->astate, da.ti, dump_table_xentry, &da); 1579d3a4f924SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 1580d3a4f924SAlexander V. Chernikov 15812d99a349SAlexander V. Chernikov return (0); 1582d3a4f924SAlexander V. Chernikov } 1583d3a4f924SAlexander V. Chernikov 1584d3a4f924SAlexander V. Chernikov /* 1585adf3b2b9SAlexander V. Chernikov * Modifies existing table. 1586adf3b2b9SAlexander V. Chernikov * Data layout (v0)(current): 1587adf3b2b9SAlexander V. Chernikov * Request: [ ipfw_obj_header ipfw_xtable_info ] 1588adf3b2b9SAlexander V. Chernikov * 1589adf3b2b9SAlexander V. Chernikov * Returns 0 on success 1590adf3b2b9SAlexander V. Chernikov */ 1591adf3b2b9SAlexander V. Chernikov int 1592adf3b2b9SAlexander V. Chernikov ipfw_modify_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 1593adf3b2b9SAlexander V. Chernikov struct sockopt_data *sd) 1594adf3b2b9SAlexander V. Chernikov { 1595adf3b2b9SAlexander V. Chernikov struct _ipfw_obj_header *oh; 1596adf3b2b9SAlexander V. Chernikov ipfw_xtable_info *i; 1597adf3b2b9SAlexander V. Chernikov char *tname; 1598adf3b2b9SAlexander V. Chernikov struct tid_info ti; 1599adf3b2b9SAlexander V. Chernikov struct namedobj_instance *ni; 1600adf3b2b9SAlexander V. Chernikov struct table_config *tc; 1601adf3b2b9SAlexander V. Chernikov 1602adf3b2b9SAlexander V. Chernikov if (sd->valsize != sizeof(*oh) + sizeof(ipfw_xtable_info)) 1603adf3b2b9SAlexander V. Chernikov return (EINVAL); 1604adf3b2b9SAlexander V. Chernikov 1605adf3b2b9SAlexander V. Chernikov oh = (struct _ipfw_obj_header *)sd->kbuf; 1606adf3b2b9SAlexander V. Chernikov i = (ipfw_xtable_info *)(oh + 1); 1607adf3b2b9SAlexander V. Chernikov 1608adf3b2b9SAlexander V. Chernikov /* 1609adf3b2b9SAlexander V. Chernikov * Verify user-supplied strings. 1610adf3b2b9SAlexander V. Chernikov * Check for null-terminated/zero-length strings/ 1611adf3b2b9SAlexander V. Chernikov */ 1612adf3b2b9SAlexander V. Chernikov tname = oh->ntlv.name; 1613adf3b2b9SAlexander V. Chernikov if (ipfw_check_table_name(tname) != 0) 1614adf3b2b9SAlexander V. Chernikov return (EINVAL); 1615adf3b2b9SAlexander V. Chernikov 1616adf3b2b9SAlexander V. Chernikov objheader_to_ti(oh, &ti); 1617adf3b2b9SAlexander V. Chernikov ti.type = i->type; 1618adf3b2b9SAlexander V. Chernikov 1619adf3b2b9SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 1620adf3b2b9SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 1621adf3b2b9SAlexander V. Chernikov if ((tc = find_table(ni, &ti)) == NULL) { 1622adf3b2b9SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1623adf3b2b9SAlexander V. Chernikov return (ESRCH); 1624adf3b2b9SAlexander V. Chernikov } 1625adf3b2b9SAlexander V. Chernikov if ((i->mflags & IPFW_TMFLAGS_FTYPE) != 0) 1626adf3b2b9SAlexander V. Chernikov tc->vftype = i->vftype; 1627adf3b2b9SAlexander V. Chernikov if ((i->mflags & IPFW_TMFLAGS_LIMIT) != 0) 1628adf3b2b9SAlexander V. Chernikov tc->limit = i->limit; 16294f43138aSAlexander V. Chernikov if ((i->mflags & IPFW_TMFLAGS_LOCK) != 0) 16304f43138aSAlexander V. Chernikov tc->locked = ((i->flags & IPFW_TGFLAGS_LOCKED) != 0); 1631adf3b2b9SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1632adf3b2b9SAlexander V. Chernikov 1633adf3b2b9SAlexander V. Chernikov return (0); 1634adf3b2b9SAlexander V. Chernikov } 1635adf3b2b9SAlexander V. Chernikov 1636adf3b2b9SAlexander V. Chernikov /* 16379490a627SAlexander V. Chernikov * Creates new table. 1638ac35ff17SAlexander V. Chernikov * Data layout (v0)(current): 16399490a627SAlexander V. Chernikov * Request: [ ipfw_obj_header ipfw_xtable_info ] 16409490a627SAlexander V. Chernikov * 16419490a627SAlexander V. Chernikov * Returns 0 on success 16429490a627SAlexander V. Chernikov */ 16439490a627SAlexander V. Chernikov int 1644ac35ff17SAlexander V. Chernikov ipfw_create_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 1645ac35ff17SAlexander V. Chernikov struct sockopt_data *sd) 16469490a627SAlexander V. Chernikov { 16479490a627SAlexander V. Chernikov struct _ipfw_obj_header *oh; 16489490a627SAlexander V. Chernikov ipfw_xtable_info *i; 16499490a627SAlexander V. Chernikov char *tname, *aname; 16509490a627SAlexander V. Chernikov struct tid_info ti; 16519490a627SAlexander V. Chernikov struct namedobj_instance *ni; 16529490a627SAlexander V. Chernikov struct table_config *tc; 16539490a627SAlexander V. Chernikov 1654ac35ff17SAlexander V. Chernikov if (sd->valsize != sizeof(*oh) + sizeof(ipfw_xtable_info)) 16559490a627SAlexander V. Chernikov return (EINVAL); 16569490a627SAlexander V. Chernikov 1657ac35ff17SAlexander V. Chernikov oh = (struct _ipfw_obj_header *)sd->kbuf; 16589490a627SAlexander V. Chernikov i = (ipfw_xtable_info *)(oh + 1); 16599490a627SAlexander V. Chernikov 16609490a627SAlexander V. Chernikov /* 16619490a627SAlexander V. Chernikov * Verify user-supplied strings. 16622d99a349SAlexander V. Chernikov * Check for null-terminated/zero-length strings/ 16639490a627SAlexander V. Chernikov */ 1664ac35ff17SAlexander V. Chernikov tname = oh->ntlv.name; 16659490a627SAlexander V. Chernikov aname = i->algoname; 1666ac35ff17SAlexander V. Chernikov if (ipfw_check_table_name(tname) != 0 || 16679490a627SAlexander V. Chernikov strnlen(aname, sizeof(i->algoname)) == sizeof(i->algoname)) 16689490a627SAlexander V. Chernikov return (EINVAL); 16699490a627SAlexander V. Chernikov 16709490a627SAlexander V. Chernikov if (aname[0] == '\0') { 16719490a627SAlexander V. Chernikov /* Use default algorithm */ 16729490a627SAlexander V. Chernikov aname = NULL; 16739490a627SAlexander V. Chernikov } 16749490a627SAlexander V. Chernikov 16759490a627SAlexander V. Chernikov objheader_to_ti(oh, &ti); 1676ac35ff17SAlexander V. Chernikov ti.type = i->type; 16779490a627SAlexander V. Chernikov 16789490a627SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 16799490a627SAlexander V. Chernikov 16809490a627SAlexander V. Chernikov IPFW_UH_RLOCK(ch); 16819490a627SAlexander V. Chernikov if ((tc = find_table(ni, &ti)) != NULL) { 16829490a627SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 16839490a627SAlexander V. Chernikov return (EEXIST); 16849490a627SAlexander V. Chernikov } 16859490a627SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 16869490a627SAlexander V. Chernikov 16874c0c07a5SAlexander V. Chernikov return (create_table_internal(ch, &ti, aname, i)); 1688db785d31SAlexander V. Chernikov } 1689db785d31SAlexander V. Chernikov 1690db785d31SAlexander V. Chernikov /* 1691db785d31SAlexander V. Chernikov * Creates new table based on @ti and @aname. 1692db785d31SAlexander V. Chernikov * 1693db785d31SAlexander V. Chernikov * Relies on table name checking inside find_name_tlv() 1694db785d31SAlexander V. Chernikov * Assume @aname to be checked and valid. 1695db785d31SAlexander V. Chernikov * 1696db785d31SAlexander V. Chernikov * Returns 0 on success. 1697db785d31SAlexander V. Chernikov */ 1698db785d31SAlexander V. Chernikov static int 1699db785d31SAlexander V. Chernikov create_table_internal(struct ip_fw_chain *ch, struct tid_info *ti, 17004c0c07a5SAlexander V. Chernikov char *aname, ipfw_xtable_info *i) 1701db785d31SAlexander V. Chernikov { 1702db785d31SAlexander V. Chernikov struct namedobj_instance *ni; 1703db785d31SAlexander V. Chernikov struct table_config *tc; 1704db785d31SAlexander V. Chernikov struct table_algo *ta; 1705db785d31SAlexander V. Chernikov uint16_t kidx; 1706db785d31SAlexander V. Chernikov 1707db785d31SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 1708db785d31SAlexander V. Chernikov 1709db785d31SAlexander V. Chernikov ta = find_table_algo(CHAIN_TO_TCFG(ch), ti, aname); 17109490a627SAlexander V. Chernikov if (ta == NULL) 17119490a627SAlexander V. Chernikov return (ENOTSUP); 17129490a627SAlexander V. Chernikov 17134c0c07a5SAlexander V. Chernikov tc = alloc_table_config(ch, ti, ta, aname, i->tflags, i->vtype); 17144c0c07a5SAlexander V. Chernikov if (tc == NULL) 17159490a627SAlexander V. Chernikov return (ENOMEM); 17169490a627SAlexander V. Chernikov 1717adf3b2b9SAlexander V. Chernikov tc->vftype = i->vftype; 17184c0c07a5SAlexander V. Chernikov tc->limit = i->limit; 17194f43138aSAlexander V. Chernikov tc->locked = (i->flags & IPFW_TGFLAGS_LOCKED) != 0; 17204c0c07a5SAlexander V. Chernikov 17219490a627SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 1722db785d31SAlexander V. Chernikov 1723db785d31SAlexander V. Chernikov /* Check if table has been already created */ 1724db785d31SAlexander V. Chernikov if (find_table(ni, ti) != NULL) { 1725db785d31SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1726db785d31SAlexander V. Chernikov free_table_config(ni, tc); 1727db785d31SAlexander V. Chernikov return (EEXIST); 1728db785d31SAlexander V. Chernikov } 1729db785d31SAlexander V. Chernikov 1730ac35ff17SAlexander V. Chernikov if (ipfw_objhash_alloc_idx(ni, &kidx) != 0) { 17319490a627SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1732db785d31SAlexander V. Chernikov printf("Unable to allocate table index." 1733db785d31SAlexander V. Chernikov " Consider increasing net.inet.ip.fw.tables_max"); 17349490a627SAlexander V. Chernikov free_table_config(ni, tc); 17359490a627SAlexander V. Chernikov return (EBUSY); 17369490a627SAlexander V. Chernikov } 17379490a627SAlexander V. Chernikov 17389490a627SAlexander V. Chernikov tc->no.kidx = kidx; 17399490a627SAlexander V. Chernikov 17409490a627SAlexander V. Chernikov IPFW_WLOCK(ch); 17419490a627SAlexander V. Chernikov link_table(ch, tc); 17429490a627SAlexander V. Chernikov IPFW_WUNLOCK(ch); 17439490a627SAlexander V. Chernikov 17449490a627SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 17459490a627SAlexander V. Chernikov 17469490a627SAlexander V. Chernikov return (0); 17479490a627SAlexander V. Chernikov } 17489490a627SAlexander V. Chernikov 174946d52008SAlexander V. Chernikov static void 175046d52008SAlexander V. Chernikov ntlv_to_ti(ipfw_obj_ntlv *ntlv, struct tid_info *ti) 1751d3a4f924SAlexander V. Chernikov { 1752d3a4f924SAlexander V. Chernikov 1753d3a4f924SAlexander V. Chernikov memset(ti, 0, sizeof(struct tid_info)); 175446d52008SAlexander V. Chernikov ti->set = ntlv->set; 175546d52008SAlexander V. Chernikov ti->uidx = ntlv->idx; 175646d52008SAlexander V. Chernikov ti->tlvs = ntlv; 175746d52008SAlexander V. Chernikov ti->tlen = ntlv->head.length; 175846d52008SAlexander V. Chernikov } 175946d52008SAlexander V. Chernikov 176046d52008SAlexander V. Chernikov static void 176146d52008SAlexander V. Chernikov objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti) 176246d52008SAlexander V. Chernikov { 176346d52008SAlexander V. Chernikov 176446d52008SAlexander V. Chernikov ntlv_to_ti(&oh->ntlv, ti); 1765d3a4f924SAlexander V. Chernikov } 1766d3a4f924SAlexander V. Chernikov 1767563b5ab1SAlexander V. Chernikov int 1768563b5ab1SAlexander V. Chernikov ipfw_export_table_ntlv(struct ip_fw_chain *ch, uint16_t kidx, 1769563b5ab1SAlexander V. Chernikov struct sockopt_data *sd) 1770563b5ab1SAlexander V. Chernikov { 1771563b5ab1SAlexander V. Chernikov struct namedobj_instance *ni; 1772563b5ab1SAlexander V. Chernikov struct named_object *no; 1773563b5ab1SAlexander V. Chernikov ipfw_obj_ntlv *ntlv; 1774563b5ab1SAlexander V. Chernikov 1775563b5ab1SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 1776563b5ab1SAlexander V. Chernikov 1777ac35ff17SAlexander V. Chernikov no = ipfw_objhash_lookup_kidx(ni, kidx); 1778563b5ab1SAlexander V. Chernikov KASSERT(no != NULL, ("invalid table kidx passed")); 1779563b5ab1SAlexander V. Chernikov 1780563b5ab1SAlexander V. Chernikov ntlv = (ipfw_obj_ntlv *)ipfw_get_sopt_space(sd, sizeof(*ntlv)); 1781563b5ab1SAlexander V. Chernikov if (ntlv == NULL) 1782563b5ab1SAlexander V. Chernikov return (ENOMEM); 1783563b5ab1SAlexander V. Chernikov 1784563b5ab1SAlexander V. Chernikov ntlv->head.type = IPFW_TLV_TBL_NAME; 1785563b5ab1SAlexander V. Chernikov ntlv->head.length = sizeof(*ntlv); 1786563b5ab1SAlexander V. Chernikov ntlv->idx = no->kidx; 1787563b5ab1SAlexander V. Chernikov strlcpy(ntlv->name, no->name, sizeof(ntlv->name)); 1788563b5ab1SAlexander V. Chernikov 1789563b5ab1SAlexander V. Chernikov return (0); 1790563b5ab1SAlexander V. Chernikov } 1791563b5ab1SAlexander V. Chernikov 17929f7d47b0SAlexander V. Chernikov static void 1793ac35ff17SAlexander V. Chernikov export_table_info(struct ip_fw_chain *ch, struct table_config *tc, 1794ac35ff17SAlexander V. Chernikov ipfw_xtable_info *i) 17959f7d47b0SAlexander V. Chernikov { 1796ac35ff17SAlexander V. Chernikov struct table_info *ti; 17975f379342SAlexander V. Chernikov struct table_algo *ta; 17989f7d47b0SAlexander V. Chernikov 17999f7d47b0SAlexander V. Chernikov i->type = tc->no.type; 1800914bffb6SAlexander V. Chernikov i->tflags = tc->tflags; 1801ac35ff17SAlexander V. Chernikov i->vtype = tc->vtype; 1802adf3b2b9SAlexander V. Chernikov i->vftype = tc->vftype; 18039f7d47b0SAlexander V. Chernikov i->set = tc->no.set; 18049f7d47b0SAlexander V. Chernikov i->kidx = tc->no.kidx; 18059f7d47b0SAlexander V. Chernikov i->refcnt = tc->no.refcnt; 18069f7d47b0SAlexander V. Chernikov i->count = tc->count; 18074c0c07a5SAlexander V. Chernikov i->limit = tc->limit; 18084f43138aSAlexander V. Chernikov i->flags |= (tc->locked != 0) ? IPFW_TGFLAGS_LOCKED : 0; 180981d3153dSAlexander V. Chernikov i->size = tc->count * sizeof(ipfw_obj_tentry); 1810f1220db8SAlexander V. Chernikov i->size += sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info); 18119f7d47b0SAlexander V. Chernikov strlcpy(i->tablename, tc->tablename, sizeof(i->tablename)); 1812ac35ff17SAlexander V. Chernikov ti = KIDX_TO_TI(ch, tc->no.kidx); 18135f379342SAlexander V. Chernikov ta = tc->ta; 18145f379342SAlexander V. Chernikov if (ta->print_config != NULL) { 18155f379342SAlexander V. Chernikov /* Use algo function to print table config to string */ 18165f379342SAlexander V. Chernikov ta->print_config(tc->astate, ti, i->algoname, 1817ac35ff17SAlexander V. Chernikov sizeof(i->algoname)); 1818ac35ff17SAlexander V. Chernikov } else 18195f379342SAlexander V. Chernikov strlcpy(i->algoname, ta->name, sizeof(i->algoname)); 18205f379342SAlexander V. Chernikov /* Dump algo-specific data, if possible */ 18215f379342SAlexander V. Chernikov if (ta->dump_tinfo != NULL) { 18225f379342SAlexander V. Chernikov ta->dump_tinfo(tc->astate, ti, &i->ta_info); 18235f379342SAlexander V. Chernikov i->ta_info.flags |= IPFW_TATFLAGS_DATA; 18245f379342SAlexander V. Chernikov } 18259f7d47b0SAlexander V. Chernikov } 18269f7d47b0SAlexander V. Chernikov 1827ac35ff17SAlexander V. Chernikov struct dump_table_args { 1828ac35ff17SAlexander V. Chernikov struct ip_fw_chain *ch; 1829ac35ff17SAlexander V. Chernikov struct sockopt_data *sd; 1830ac35ff17SAlexander V. Chernikov }; 1831ac35ff17SAlexander V. Chernikov 18329f7d47b0SAlexander V. Chernikov static void 18339f7d47b0SAlexander V. Chernikov export_table_internal(struct namedobj_instance *ni, struct named_object *no, 18349f7d47b0SAlexander V. Chernikov void *arg) 18353b3a8eb9SGleb Smirnoff { 18369f7d47b0SAlexander V. Chernikov ipfw_xtable_info *i; 1837ac35ff17SAlexander V. Chernikov struct dump_table_args *dta; 18383b3a8eb9SGleb Smirnoff 1839ac35ff17SAlexander V. Chernikov dta = (struct dump_table_args *)arg; 1840ac35ff17SAlexander V. Chernikov 1841ac35ff17SAlexander V. Chernikov i = (ipfw_xtable_info *)ipfw_get_sopt_space(dta->sd, sizeof(*i)); 184268394ec8SAlexander V. Chernikov KASSERT(i != 0, ("previously checked buffer is not enough")); 18439f7d47b0SAlexander V. Chernikov 1844ac35ff17SAlexander V. Chernikov export_table_info(dta->ch, (struct table_config *)no, i); 18459f7d47b0SAlexander V. Chernikov } 18469f7d47b0SAlexander V. Chernikov 1847f1220db8SAlexander V. Chernikov /* 1848f1220db8SAlexander V. Chernikov * Export all tables as ipfw_xtable_info structures to 18492d99a349SAlexander V. Chernikov * storage provided by @sd. 185028ea4fa3SAlexander V. Chernikov * 185128ea4fa3SAlexander V. Chernikov * If supplied buffer is too small, fills in required size 185228ea4fa3SAlexander V. Chernikov * and returns ENOMEM. 1853f1220db8SAlexander V. Chernikov * Returns 0 on success. 1854f1220db8SAlexander V. Chernikov */ 1855f1220db8SAlexander V. Chernikov static int 18562d99a349SAlexander V. Chernikov export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh, 18572d99a349SAlexander V. Chernikov struct sockopt_data *sd) 18589f7d47b0SAlexander V. Chernikov { 18599f7d47b0SAlexander V. Chernikov uint32_t size; 18609f7d47b0SAlexander V. Chernikov uint32_t count; 1861ac35ff17SAlexander V. Chernikov struct dump_table_args dta; 18629f7d47b0SAlexander V. Chernikov 18639f7d47b0SAlexander V. Chernikov count = ipfw_objhash_count(CHAIN_TO_NI(ch)); 18649f7d47b0SAlexander V. Chernikov size = count * sizeof(ipfw_xtable_info) + sizeof(ipfw_obj_lheader); 18652d99a349SAlexander V. Chernikov 18662d99a349SAlexander V. Chernikov /* Fill in header regadless of buffer size */ 1867f1220db8SAlexander V. Chernikov olh->count = count; 1868f1220db8SAlexander V. Chernikov olh->objsize = sizeof(ipfw_xtable_info); 18692d99a349SAlexander V. Chernikov 18702d99a349SAlexander V. Chernikov if (size > olh->size) { 18712d99a349SAlexander V. Chernikov olh->size = size; 18729f7d47b0SAlexander V. Chernikov return (ENOMEM); 1873f1220db8SAlexander V. Chernikov } 187468394ec8SAlexander V. Chernikov 18759f7d47b0SAlexander V. Chernikov olh->size = size; 18762d99a349SAlexander V. Chernikov 1877ac35ff17SAlexander V. Chernikov dta.ch = ch; 1878ac35ff17SAlexander V. Chernikov dta.sd = sd; 1879ac35ff17SAlexander V. Chernikov 1880ac35ff17SAlexander V. Chernikov ipfw_objhash_foreach(CHAIN_TO_NI(ch), export_table_internal, &dta); 18819f7d47b0SAlexander V. Chernikov 18823b3a8eb9SGleb Smirnoff return (0); 18833b3a8eb9SGleb Smirnoff } 18843b3a8eb9SGleb Smirnoff 188581d3153dSAlexander V. Chernikov /* 188681d3153dSAlexander V. Chernikov * Legacy IP_FW_TABLE_GETSIZE handler 188781d3153dSAlexander V. Chernikov */ 18883b3a8eb9SGleb Smirnoff int 1889b074b7bbSAlexander V. Chernikov ipfw_count_table(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt) 18903b3a8eb9SGleb Smirnoff { 1891b074b7bbSAlexander V. Chernikov struct table_config *tc; 18923b3a8eb9SGleb Smirnoff 1893b074b7bbSAlexander V. Chernikov if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL) 1894b074b7bbSAlexander V. Chernikov return (ESRCH); 18959f7d47b0SAlexander V. Chernikov *cnt = tc->count; 18963b3a8eb9SGleb Smirnoff return (0); 18973b3a8eb9SGleb Smirnoff } 18983b3a8eb9SGleb Smirnoff 18999f7d47b0SAlexander V. Chernikov 190081d3153dSAlexander V. Chernikov /* 190181d3153dSAlexander V. Chernikov * Legacy IP_FW_TABLE_XGETSIZE handler 190281d3153dSAlexander V. Chernikov */ 19039f7d47b0SAlexander V. Chernikov int 19049f7d47b0SAlexander V. Chernikov ipfw_count_xtable(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt) 19053b3a8eb9SGleb Smirnoff { 19069f7d47b0SAlexander V. Chernikov struct table_config *tc; 19079f7d47b0SAlexander V. Chernikov 1908d3a4f924SAlexander V. Chernikov if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL) { 1909d3a4f924SAlexander V. Chernikov *cnt = 0; 19109f7d47b0SAlexander V. Chernikov return (0); /* 'table all list' requires success */ 1911d3a4f924SAlexander V. Chernikov } 19129f7d47b0SAlexander V. Chernikov *cnt = tc->count * sizeof(ipfw_table_xentry); 19139f7d47b0SAlexander V. Chernikov if (tc->count > 0) 19149f7d47b0SAlexander V. Chernikov *cnt += sizeof(ipfw_xtable); 19159f7d47b0SAlexander V. Chernikov return (0); 19169f7d47b0SAlexander V. Chernikov } 19179f7d47b0SAlexander V. Chernikov 19189f7d47b0SAlexander V. Chernikov static int 19199f7d47b0SAlexander V. Chernikov dump_table_entry(void *e, void *arg) 19209f7d47b0SAlexander V. Chernikov { 19219f7d47b0SAlexander V. Chernikov struct dump_args *da; 19229f7d47b0SAlexander V. Chernikov struct table_config *tc; 19239f7d47b0SAlexander V. Chernikov struct table_algo *ta; 19243b3a8eb9SGleb Smirnoff ipfw_table_entry *ent; 192581d3153dSAlexander V. Chernikov int error; 19263b3a8eb9SGleb Smirnoff 19279f7d47b0SAlexander V. Chernikov da = (struct dump_args *)arg; 19289f7d47b0SAlexander V. Chernikov 19299f7d47b0SAlexander V. Chernikov tc = da->tc; 19309f7d47b0SAlexander V. Chernikov ta = tc->ta; 19319f7d47b0SAlexander V. Chernikov 19329f7d47b0SAlexander V. Chernikov /* Out of memory, returning */ 1933f1220db8SAlexander V. Chernikov if (da->cnt == da->size) 19343b3a8eb9SGleb Smirnoff return (1); 1935f1220db8SAlexander V. Chernikov ent = da->ent++; 1936f1220db8SAlexander V. Chernikov ent->tbl = da->uidx; 1937f1220db8SAlexander V. Chernikov da->cnt++; 19389f7d47b0SAlexander V. Chernikov 193981d3153dSAlexander V. Chernikov error = ta->dump_tentry(tc->astate, da->ti, e, &da->tent); 194081d3153dSAlexander V. Chernikov if (error != 0) 194181d3153dSAlexander V. Chernikov return (error); 194281d3153dSAlexander V. Chernikov 194381d3153dSAlexander V. Chernikov ent->addr = da->tent.k.addr.s_addr; 194481d3153dSAlexander V. Chernikov ent->masklen = da->tent.masklen; 194581d3153dSAlexander V. Chernikov ent->value = da->tent.value; 194681d3153dSAlexander V. Chernikov 194781d3153dSAlexander V. Chernikov return (0); 19483b3a8eb9SGleb Smirnoff } 19493b3a8eb9SGleb Smirnoff 1950f1220db8SAlexander V. Chernikov /* 1951f1220db8SAlexander V. Chernikov * Dumps table in pre-8.1 legacy format. 1952f1220db8SAlexander V. Chernikov */ 19533b3a8eb9SGleb Smirnoff int 1954f1220db8SAlexander V. Chernikov ipfw_dump_table_legacy(struct ip_fw_chain *ch, struct tid_info *ti, 1955f1220db8SAlexander V. Chernikov ipfw_table *tbl) 19563b3a8eb9SGleb Smirnoff { 1957b074b7bbSAlexander V. Chernikov struct table_config *tc; 19589f7d47b0SAlexander V. Chernikov struct table_algo *ta; 19599f7d47b0SAlexander V. Chernikov struct dump_args da; 19603b3a8eb9SGleb Smirnoff 19613b3a8eb9SGleb Smirnoff tbl->cnt = 0; 19623b3a8eb9SGleb Smirnoff 1963b074b7bbSAlexander V. Chernikov if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL) 1964b074b7bbSAlexander V. Chernikov return (0); /* XXX: We should return ESRCH */ 19659f7d47b0SAlexander V. Chernikov 19669f7d47b0SAlexander V. Chernikov ta = tc->ta; 19679f7d47b0SAlexander V. Chernikov 196881d3153dSAlexander V. Chernikov /* This dump format supports IPv4 only */ 196981d3153dSAlexander V. Chernikov if (tc->no.type != IPFW_TABLE_CIDR) 197081d3153dSAlexander V. Chernikov return (0); 19719f7d47b0SAlexander V. Chernikov 1972d3a4f924SAlexander V. Chernikov memset(&da, 0, sizeof(da)); 19739f7d47b0SAlexander V. Chernikov da.ti = KIDX_TO_TI(ch, tc->no.kidx); 19749f7d47b0SAlexander V. Chernikov da.tc = tc; 1975f1220db8SAlexander V. Chernikov da.ent = &tbl->ent[0]; 1976f1220db8SAlexander V. Chernikov da.size = tbl->size; 19779f7d47b0SAlexander V. Chernikov 19789f7d47b0SAlexander V. Chernikov tbl->cnt = 0; 19799f7d47b0SAlexander V. Chernikov ta->foreach(tc->astate, da.ti, dump_table_entry, &da); 1980f1220db8SAlexander V. Chernikov tbl->cnt = da.cnt; 19819f7d47b0SAlexander V. Chernikov 19823b3a8eb9SGleb Smirnoff return (0); 19833b3a8eb9SGleb Smirnoff } 19843b3a8eb9SGleb Smirnoff 19859490a627SAlexander V. Chernikov /* 198681d3153dSAlexander V. Chernikov * Dumps table entry in eXtended format (v1)(current). 198781d3153dSAlexander V. Chernikov */ 198881d3153dSAlexander V. Chernikov static int 198981d3153dSAlexander V. Chernikov dump_table_tentry(void *e, void *arg) 199081d3153dSAlexander V. Chernikov { 199181d3153dSAlexander V. Chernikov struct dump_args *da; 199281d3153dSAlexander V. Chernikov struct table_config *tc; 199381d3153dSAlexander V. Chernikov struct table_algo *ta; 199481d3153dSAlexander V. Chernikov ipfw_obj_tentry *tent; 199581d3153dSAlexander V. Chernikov 199681d3153dSAlexander V. Chernikov da = (struct dump_args *)arg; 199781d3153dSAlexander V. Chernikov 199881d3153dSAlexander V. Chernikov tc = da->tc; 199981d3153dSAlexander V. Chernikov ta = tc->ta; 200081d3153dSAlexander V. Chernikov 200181d3153dSAlexander V. Chernikov tent = (ipfw_obj_tentry *)ipfw_get_sopt_space(da->sd, sizeof(*tent)); 200281d3153dSAlexander V. Chernikov /* Out of memory, returning */ 200381d3153dSAlexander V. Chernikov if (tent == NULL) { 200481d3153dSAlexander V. Chernikov da->error = ENOMEM; 200581d3153dSAlexander V. Chernikov return (1); 200681d3153dSAlexander V. Chernikov } 200781d3153dSAlexander V. Chernikov tent->head.length = sizeof(ipfw_obj_tentry); 200881d3153dSAlexander V. Chernikov tent->idx = da->uidx; 200981d3153dSAlexander V. Chernikov 201081d3153dSAlexander V. Chernikov return (ta->dump_tentry(tc->astate, da->ti, e, tent)); 201181d3153dSAlexander V. Chernikov } 201281d3153dSAlexander V. Chernikov 201381d3153dSAlexander V. Chernikov /* 201481d3153dSAlexander V. Chernikov * Dumps table entry in eXtended format (v0). 20159490a627SAlexander V. Chernikov */ 20163b3a8eb9SGleb Smirnoff static int 20179f7d47b0SAlexander V. Chernikov dump_table_xentry(void *e, void *arg) 20183b3a8eb9SGleb Smirnoff { 20199f7d47b0SAlexander V. Chernikov struct dump_args *da; 20209f7d47b0SAlexander V. Chernikov struct table_config *tc; 20219f7d47b0SAlexander V. Chernikov struct table_algo *ta; 20223b3a8eb9SGleb Smirnoff ipfw_table_xentry *xent; 202381d3153dSAlexander V. Chernikov ipfw_obj_tentry *tent; 202481d3153dSAlexander V. Chernikov int error; 20253b3a8eb9SGleb Smirnoff 20269f7d47b0SAlexander V. Chernikov da = (struct dump_args *)arg; 20279f7d47b0SAlexander V. Chernikov 20289f7d47b0SAlexander V. Chernikov tc = da->tc; 20299f7d47b0SAlexander V. Chernikov ta = tc->ta; 20309f7d47b0SAlexander V. Chernikov 20312d99a349SAlexander V. Chernikov xent = (ipfw_table_xentry *)ipfw_get_sopt_space(da->sd, sizeof(*xent)); 20323b3a8eb9SGleb Smirnoff /* Out of memory, returning */ 20332d99a349SAlexander V. Chernikov if (xent == NULL) 20343b3a8eb9SGleb Smirnoff return (1); 20353b3a8eb9SGleb Smirnoff xent->len = sizeof(ipfw_table_xentry); 2036f1220db8SAlexander V. Chernikov xent->tbl = da->uidx; 20379f7d47b0SAlexander V. Chernikov 203881d3153dSAlexander V. Chernikov memset(&da->tent, 0, sizeof(da->tent)); 203981d3153dSAlexander V. Chernikov tent = &da->tent; 204081d3153dSAlexander V. Chernikov error = ta->dump_tentry(tc->astate, da->ti, e, tent); 204181d3153dSAlexander V. Chernikov if (error != 0) 204281d3153dSAlexander V. Chernikov return (error); 204381d3153dSAlexander V. Chernikov 204481d3153dSAlexander V. Chernikov /* Convert current format to previous one */ 204581d3153dSAlexander V. Chernikov xent->masklen = tent->masklen; 204681d3153dSAlexander V. Chernikov xent->value = tent->value; 204781d3153dSAlexander V. Chernikov /* Apply some hacks */ 204881d3153dSAlexander V. Chernikov if (tc->no.type == IPFW_TABLE_CIDR && tent->subtype == AF_INET) { 204981d3153dSAlexander V. Chernikov xent->k.addr6.s6_addr32[3] = tent->k.addr.s_addr; 205081d3153dSAlexander V. Chernikov xent->flags = IPFW_TCF_INET; 205181d3153dSAlexander V. Chernikov } else 205281d3153dSAlexander V. Chernikov memcpy(&xent->k, &tent->k, sizeof(xent->k)); 205381d3153dSAlexander V. Chernikov 205481d3153dSAlexander V. Chernikov return (0); 20559f7d47b0SAlexander V. Chernikov } 20569f7d47b0SAlexander V. Chernikov 20579f7d47b0SAlexander V. Chernikov /* 20589f7d47b0SAlexander V. Chernikov * Table algorithms 20599f7d47b0SAlexander V. Chernikov */ 20603b3a8eb9SGleb Smirnoff 20619f7d47b0SAlexander V. Chernikov /* 20629490a627SAlexander V. Chernikov * Finds algoritm by index, table type or supplied name 20639f7d47b0SAlexander V. Chernikov */ 20649f7d47b0SAlexander V. Chernikov static struct table_algo * 20659490a627SAlexander V. Chernikov find_table_algo(struct tables_config *tcfg, struct tid_info *ti, char *name) 20669f7d47b0SAlexander V. Chernikov { 20679490a627SAlexander V. Chernikov int i, l; 20689490a627SAlexander V. Chernikov struct table_algo *ta; 20699f7d47b0SAlexander V. Chernikov 207057a1cf95SAlexander V. Chernikov if (ti->type > IPFW_TABLE_MAXTYPE) 207157a1cf95SAlexander V. Chernikov return (NULL); 207257a1cf95SAlexander V. Chernikov 20739f7d47b0SAlexander V. Chernikov /* Search by index */ 20749f7d47b0SAlexander V. Chernikov if (ti->atype != 0) { 20759f7d47b0SAlexander V. Chernikov if (ti->atype > tcfg->algo_count) 20769f7d47b0SAlexander V. Chernikov return (NULL); 20779f7d47b0SAlexander V. Chernikov return (tcfg->algo[ti->atype]); 20789f7d47b0SAlexander V. Chernikov } 20799f7d47b0SAlexander V. Chernikov 20809490a627SAlexander V. Chernikov /* Search by name if supplied */ 20819490a627SAlexander V. Chernikov if (name != NULL) { 20829490a627SAlexander V. Chernikov /* TODO: better search */ 20839490a627SAlexander V. Chernikov for (i = 1; i <= tcfg->algo_count; i++) { 20849490a627SAlexander V. Chernikov ta = tcfg->algo[i]; 20859490a627SAlexander V. Chernikov 20869490a627SAlexander V. Chernikov /* 20879490a627SAlexander V. Chernikov * One can supply additional algorithm 20889490a627SAlexander V. Chernikov * parameters so we compare only the first word 20899490a627SAlexander V. Chernikov * of supplied name: 20909490a627SAlexander V. Chernikov * 'hash_cidr hsize=32' 20919490a627SAlexander V. Chernikov * '^^^^^^^^^' 20929490a627SAlexander V. Chernikov * 20939490a627SAlexander V. Chernikov */ 20949490a627SAlexander V. Chernikov l = strlen(ta->name); 20959490a627SAlexander V. Chernikov if (strncmp(name, ta->name, l) == 0) { 20969490a627SAlexander V. Chernikov if (name[l] == '\0' || name[l] == ' ') 20979490a627SAlexander V. Chernikov return (ta); 20989490a627SAlexander V. Chernikov } 20999490a627SAlexander V. Chernikov } 21009490a627SAlexander V. Chernikov 21019490a627SAlexander V. Chernikov return (NULL); 21029490a627SAlexander V. Chernikov } 21039490a627SAlexander V. Chernikov 210457a1cf95SAlexander V. Chernikov /* Return default algorithm for given type if set */ 210557a1cf95SAlexander V. Chernikov return (tcfg->def_algo[ti->type]); 21063b3a8eb9SGleb Smirnoff } 21073b3a8eb9SGleb Smirnoff 2108b6ee846eSAlexander V. Chernikov /* 2109b6ee846eSAlexander V. Chernikov * Register new table algo @ta. 2110b6ee846eSAlexander V. Chernikov * Stores algo id iside @idx.<F2> 2111b6ee846eSAlexander V. Chernikov * 2112b6ee846eSAlexander V. Chernikov * Returns 0 on success. 2113b6ee846eSAlexander V. Chernikov */ 21140b565ac0SAlexander V. Chernikov int 21150b565ac0SAlexander V. Chernikov ipfw_add_table_algo(struct ip_fw_chain *ch, struct table_algo *ta, size_t size, 21160b565ac0SAlexander V. Chernikov int *idx) 21173b3a8eb9SGleb Smirnoff { 21189f7d47b0SAlexander V. Chernikov struct tables_config *tcfg; 21190b565ac0SAlexander V. Chernikov struct table_algo *ta_new; 2120b6ee846eSAlexander V. Chernikov size_t sz; 21210b565ac0SAlexander V. Chernikov 21220b565ac0SAlexander V. Chernikov if (size > sizeof(struct table_algo)) 21230b565ac0SAlexander V. Chernikov return (EINVAL); 21240b565ac0SAlexander V. Chernikov 2125b6ee846eSAlexander V. Chernikov /* Check for the required on-stack size for add/del */ 2126b6ee846eSAlexander V. Chernikov sz = roundup2(ta->ta_buf_size, sizeof(void *)); 2127b6ee846eSAlexander V. Chernikov if (sz > TA_BUF_SZ) 2128b6ee846eSAlexander V. Chernikov return (EINVAL); 2129b6ee846eSAlexander V. Chernikov 213057a1cf95SAlexander V. Chernikov KASSERT(ta->type >= IPFW_TABLE_MAXTYPE,("Increase IPFW_TABLE_MAXTYPE")); 213157a1cf95SAlexander V. Chernikov 21320b565ac0SAlexander V. Chernikov ta_new = malloc(sizeof(struct table_algo), M_IPFW, M_WAITOK | M_ZERO); 21330b565ac0SAlexander V. Chernikov memcpy(ta_new, ta, size); 21343b3a8eb9SGleb Smirnoff 21359f7d47b0SAlexander V. Chernikov tcfg = CHAIN_TO_TCFG(ch); 2136b074b7bbSAlexander V. Chernikov 21379f7d47b0SAlexander V. Chernikov KASSERT(tcfg->algo_count < 255, ("Increase algo array size")); 21389f7d47b0SAlexander V. Chernikov 21390b565ac0SAlexander V. Chernikov tcfg->algo[++tcfg->algo_count] = ta_new; 21400b565ac0SAlexander V. Chernikov ta_new->idx = tcfg->algo_count; 21410b565ac0SAlexander V. Chernikov 214257a1cf95SAlexander V. Chernikov /* Set algorithm as default one for given type */ 214357a1cf95SAlexander V. Chernikov if ((ta_new->flags & TA_FLAG_DEFAULT) != 0 && 214457a1cf95SAlexander V. Chernikov tcfg->def_algo[ta_new->type] == NULL) 214557a1cf95SAlexander V. Chernikov tcfg->def_algo[ta_new->type] = ta_new; 214657a1cf95SAlexander V. Chernikov 21470b565ac0SAlexander V. Chernikov *idx = ta_new->idx; 21480b565ac0SAlexander V. Chernikov 21490b565ac0SAlexander V. Chernikov return (0); 21500b565ac0SAlexander V. Chernikov } 21510b565ac0SAlexander V. Chernikov 2152b6ee846eSAlexander V. Chernikov /* 2153b6ee846eSAlexander V. Chernikov * Unregisters table algo using @idx as id. 2154b6ee846eSAlexander V. Chernikov */ 21550b565ac0SAlexander V. Chernikov void 21560b565ac0SAlexander V. Chernikov ipfw_del_table_algo(struct ip_fw_chain *ch, int idx) 21570b565ac0SAlexander V. Chernikov { 21580b565ac0SAlexander V. Chernikov struct tables_config *tcfg; 21590b565ac0SAlexander V. Chernikov struct table_algo *ta; 21600b565ac0SAlexander V. Chernikov 21610b565ac0SAlexander V. Chernikov tcfg = CHAIN_TO_TCFG(ch); 21620b565ac0SAlexander V. Chernikov 2163b6ee846eSAlexander V. Chernikov KASSERT(idx <= tcfg->algo_count, ("algo idx %d out of range 1..%d", 2164b6ee846eSAlexander V. Chernikov idx, tcfg->algo_count)); 21650b565ac0SAlexander V. Chernikov 21660b565ac0SAlexander V. Chernikov ta = tcfg->algo[idx]; 21670b565ac0SAlexander V. Chernikov KASSERT(ta != NULL, ("algo idx %d is NULL", idx)); 216857a1cf95SAlexander V. Chernikov 216957a1cf95SAlexander V. Chernikov if (tcfg->def_algo[ta->type] == ta) 217057a1cf95SAlexander V. Chernikov tcfg->def_algo[ta->type] = NULL; 217157a1cf95SAlexander V. Chernikov 21720b565ac0SAlexander V. Chernikov free(ta, M_IPFW); 21733b3a8eb9SGleb Smirnoff } 21743b3a8eb9SGleb Smirnoff 21759d099b4fSAlexander V. Chernikov /* 21769d099b4fSAlexander V. Chernikov * Lists all table algorithms currently available. 21779d099b4fSAlexander V. Chernikov * Data layout (v0)(current): 21789d099b4fSAlexander V. Chernikov * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size 21799d099b4fSAlexander V. Chernikov * Reply: [ ipfw_obj_lheader ipfw_ta_info x N ] 21809d099b4fSAlexander V. Chernikov * 21819d099b4fSAlexander V. Chernikov * Returns 0 on success 21829d099b4fSAlexander V. Chernikov */ 21839d099b4fSAlexander V. Chernikov int 21849d099b4fSAlexander V. Chernikov ipfw_list_table_algo(struct ip_fw_chain *ch, struct sockopt_data *sd) 21859d099b4fSAlexander V. Chernikov { 21869d099b4fSAlexander V. Chernikov struct _ipfw_obj_lheader *olh; 21879d099b4fSAlexander V. Chernikov struct tables_config *tcfg; 21889d099b4fSAlexander V. Chernikov ipfw_ta_info *i; 21899d099b4fSAlexander V. Chernikov struct table_algo *ta; 21909d099b4fSAlexander V. Chernikov uint32_t count, n, size; 21919d099b4fSAlexander V. Chernikov 21929d099b4fSAlexander V. Chernikov olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh)); 21939d099b4fSAlexander V. Chernikov if (olh == NULL) 21949d099b4fSAlexander V. Chernikov return (EINVAL); 21959d099b4fSAlexander V. Chernikov if (sd->valsize < olh->size) 21969d099b4fSAlexander V. Chernikov return (EINVAL); 21979d099b4fSAlexander V. Chernikov 21989d099b4fSAlexander V. Chernikov IPFW_UH_RLOCK(ch); 21999d099b4fSAlexander V. Chernikov tcfg = CHAIN_TO_TCFG(ch); 22009d099b4fSAlexander V. Chernikov count = tcfg->algo_count; 22019d099b4fSAlexander V. Chernikov size = count * sizeof(ipfw_ta_info) + sizeof(ipfw_obj_lheader); 22029d099b4fSAlexander V. Chernikov 22039d099b4fSAlexander V. Chernikov /* Fill in header regadless of buffer size */ 22049d099b4fSAlexander V. Chernikov olh->count = count; 22059d099b4fSAlexander V. Chernikov olh->objsize = sizeof(ipfw_ta_info); 22069d099b4fSAlexander V. Chernikov 22079d099b4fSAlexander V. Chernikov if (size > olh->size) { 22089d099b4fSAlexander V. Chernikov olh->size = size; 22099d099b4fSAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 22109d099b4fSAlexander V. Chernikov return (ENOMEM); 22119d099b4fSAlexander V. Chernikov } 22129d099b4fSAlexander V. Chernikov olh->size = size; 22139d099b4fSAlexander V. Chernikov 22149d099b4fSAlexander V. Chernikov for (n = 1; n <= count; n++) { 22159d099b4fSAlexander V. Chernikov i = (ipfw_ta_info *)ipfw_get_sopt_space(sd, sizeof(*i)); 22169d099b4fSAlexander V. Chernikov KASSERT(i != 0, ("previously checked buffer is not enough")); 22179d099b4fSAlexander V. Chernikov ta = tcfg->algo[n]; 22189d099b4fSAlexander V. Chernikov strlcpy(i->algoname, ta->name, sizeof(i->algoname)); 22199d099b4fSAlexander V. Chernikov i->type = ta->type; 22209d099b4fSAlexander V. Chernikov i->refcnt = ta->refcnt; 22219d099b4fSAlexander V. Chernikov } 22229d099b4fSAlexander V. Chernikov 22239d099b4fSAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 22249d099b4fSAlexander V. Chernikov 22259d099b4fSAlexander V. Chernikov return (0); 22269d099b4fSAlexander V. Chernikov } 22279d099b4fSAlexander V. Chernikov 22289f7d47b0SAlexander V. Chernikov 2229b074b7bbSAlexander V. Chernikov /* 2230b074b7bbSAlexander V. Chernikov * Tables rewriting code 2231b074b7bbSAlexander V. Chernikov * 2232b074b7bbSAlexander V. Chernikov */ 2233b074b7bbSAlexander V. Chernikov 2234b074b7bbSAlexander V. Chernikov /* 2235b074b7bbSAlexander V. Chernikov * Determine table number and lookup type for @cmd. 2236b074b7bbSAlexander V. Chernikov * Fill @tbl and @type with appropriate values. 2237b074b7bbSAlexander V. Chernikov * Returns 0 for relevant opcodes, 1 otherwise. 2238b074b7bbSAlexander V. Chernikov */ 2239b074b7bbSAlexander V. Chernikov static int 2240b074b7bbSAlexander V. Chernikov classify_table_opcode(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) 2241b074b7bbSAlexander V. Chernikov { 2242b074b7bbSAlexander V. Chernikov ipfw_insn_if *cmdif; 2243b074b7bbSAlexander V. Chernikov int skip; 2244b074b7bbSAlexander V. Chernikov uint16_t v; 2245b074b7bbSAlexander V. Chernikov 2246b074b7bbSAlexander V. Chernikov skip = 1; 2247b074b7bbSAlexander V. Chernikov 2248b074b7bbSAlexander V. Chernikov switch (cmd->opcode) { 2249b074b7bbSAlexander V. Chernikov case O_IP_SRC_LOOKUP: 2250b074b7bbSAlexander V. Chernikov case O_IP_DST_LOOKUP: 2251b074b7bbSAlexander V. Chernikov /* Basic IPv4/IPv6 or u32 lookups */ 2252b074b7bbSAlexander V. Chernikov *puidx = cmd->arg1; 2253b074b7bbSAlexander V. Chernikov /* Assume CIDR by default */ 2254b074b7bbSAlexander V. Chernikov *ptype = IPFW_TABLE_CIDR; 2255b074b7bbSAlexander V. Chernikov skip = 0; 2256b074b7bbSAlexander V. Chernikov 2257b074b7bbSAlexander V. Chernikov if (F_LEN(cmd) > F_INSN_SIZE(ipfw_insn_u32)) { 2258b074b7bbSAlexander V. Chernikov /* 2259b074b7bbSAlexander V. Chernikov * generic lookup. The key must be 2260b074b7bbSAlexander V. Chernikov * in 32bit big-endian format. 2261b074b7bbSAlexander V. Chernikov */ 2262b074b7bbSAlexander V. Chernikov v = ((ipfw_insn_u32 *)cmd)->d[1]; 2263b074b7bbSAlexander V. Chernikov switch (v) { 2264b074b7bbSAlexander V. Chernikov case 0: 2265b074b7bbSAlexander V. Chernikov case 1: 2266b074b7bbSAlexander V. Chernikov /* IPv4 src/dst */ 2267b074b7bbSAlexander V. Chernikov break; 2268b074b7bbSAlexander V. Chernikov case 2: 2269b074b7bbSAlexander V. Chernikov case 3: 2270b074b7bbSAlexander V. Chernikov /* src/dst port */ 2271b23d5de9SAlexander V. Chernikov *ptype = IPFW_TABLE_NUMBER; 2272b074b7bbSAlexander V. Chernikov break; 2273b074b7bbSAlexander V. Chernikov case 4: 2274b074b7bbSAlexander V. Chernikov /* uid/gid */ 2275b23d5de9SAlexander V. Chernikov *ptype = IPFW_TABLE_NUMBER; 2276b23d5de9SAlexander V. Chernikov break; 2277b074b7bbSAlexander V. Chernikov case 5: 2278b074b7bbSAlexander V. Chernikov /* jid */ 2279b23d5de9SAlexander V. Chernikov *ptype = IPFW_TABLE_NUMBER; 2280b23d5de9SAlexander V. Chernikov break; 2281b074b7bbSAlexander V. Chernikov case 6: 2282b074b7bbSAlexander V. Chernikov /* dscp */ 2283b23d5de9SAlexander V. Chernikov *ptype = IPFW_TABLE_NUMBER; 2284b074b7bbSAlexander V. Chernikov break; 2285b074b7bbSAlexander V. Chernikov } 2286b074b7bbSAlexander V. Chernikov } 2287b074b7bbSAlexander V. Chernikov break; 2288b074b7bbSAlexander V. Chernikov case O_XMIT: 2289b074b7bbSAlexander V. Chernikov case O_RECV: 2290b074b7bbSAlexander V. Chernikov case O_VIA: 2291b074b7bbSAlexander V. Chernikov /* Interface table, possibly */ 2292b074b7bbSAlexander V. Chernikov cmdif = (ipfw_insn_if *)cmd; 2293b074b7bbSAlexander V. Chernikov if (cmdif->name[0] != '\1') 2294b074b7bbSAlexander V. Chernikov break; 2295b074b7bbSAlexander V. Chernikov 2296b074b7bbSAlexander V. Chernikov *ptype = IPFW_TABLE_INTERFACE; 22978bd19212SAlexander V. Chernikov *puidx = cmdif->p.glob; 2298b074b7bbSAlexander V. Chernikov skip = 0; 2299b074b7bbSAlexander V. Chernikov break; 2300914bffb6SAlexander V. Chernikov case O_IP_FLOW_LOOKUP: 2301914bffb6SAlexander V. Chernikov *puidx = cmd->arg1; 2302914bffb6SAlexander V. Chernikov *ptype = IPFW_TABLE_FLOW; 2303914bffb6SAlexander V. Chernikov skip = 0; 2304914bffb6SAlexander V. Chernikov break; 2305b074b7bbSAlexander V. Chernikov } 2306b074b7bbSAlexander V. Chernikov 2307b074b7bbSAlexander V. Chernikov return (skip); 2308b074b7bbSAlexander V. Chernikov } 2309b074b7bbSAlexander V. Chernikov 2310b074b7bbSAlexander V. Chernikov /* 2311b074b7bbSAlexander V. Chernikov * Sets new table value for given opcode. 2312b074b7bbSAlexander V. Chernikov * Assume the same opcodes as classify_table_opcode() 2313b074b7bbSAlexander V. Chernikov */ 2314b074b7bbSAlexander V. Chernikov static void 2315b074b7bbSAlexander V. Chernikov update_table_opcode(ipfw_insn *cmd, uint16_t idx) 2316b074b7bbSAlexander V. Chernikov { 2317b074b7bbSAlexander V. Chernikov ipfw_insn_if *cmdif; 2318b074b7bbSAlexander V. Chernikov 2319b074b7bbSAlexander V. Chernikov switch (cmd->opcode) { 2320b074b7bbSAlexander V. Chernikov case O_IP_SRC_LOOKUP: 2321b074b7bbSAlexander V. Chernikov case O_IP_DST_LOOKUP: 2322b074b7bbSAlexander V. Chernikov /* Basic IPv4/IPv6 or u32 lookups */ 2323b074b7bbSAlexander V. Chernikov cmd->arg1 = idx; 2324b074b7bbSAlexander V. Chernikov break; 2325b074b7bbSAlexander V. Chernikov case O_XMIT: 2326b074b7bbSAlexander V. Chernikov case O_RECV: 2327b074b7bbSAlexander V. Chernikov case O_VIA: 2328b074b7bbSAlexander V. Chernikov /* Interface table, possibly */ 2329b074b7bbSAlexander V. Chernikov cmdif = (ipfw_insn_if *)cmd; 23308bd19212SAlexander V. Chernikov cmdif->p.glob = idx; 2331b074b7bbSAlexander V. Chernikov break; 2332914bffb6SAlexander V. Chernikov case O_IP_FLOW_LOOKUP: 2333914bffb6SAlexander V. Chernikov cmd->arg1 = idx; 2334914bffb6SAlexander V. Chernikov break; 2335b074b7bbSAlexander V. Chernikov } 2336b074b7bbSAlexander V. Chernikov } 2337b074b7bbSAlexander V. Chernikov 2338ac35ff17SAlexander V. Chernikov /* 2339ac35ff17SAlexander V. Chernikov * Checks table name for validity. 2340ac35ff17SAlexander V. Chernikov * Enforce basic length checks, the rest 2341ac35ff17SAlexander V. Chernikov * should be done in userland. 2342ac35ff17SAlexander V. Chernikov * 2343ac35ff17SAlexander V. Chernikov * Returns 0 if name is considered valid. 2344ac35ff17SAlexander V. Chernikov */ 2345ac35ff17SAlexander V. Chernikov int 2346ac35ff17SAlexander V. Chernikov ipfw_check_table_name(char *name) 2347ac35ff17SAlexander V. Chernikov { 2348ac35ff17SAlexander V. Chernikov int nsize; 2349ac35ff17SAlexander V. Chernikov ipfw_obj_ntlv *ntlv = NULL; 2350ac35ff17SAlexander V. Chernikov 2351ac35ff17SAlexander V. Chernikov nsize = sizeof(ntlv->name); 2352ac35ff17SAlexander V. Chernikov 2353ac35ff17SAlexander V. Chernikov if (strnlen(name, nsize) == nsize) 2354ac35ff17SAlexander V. Chernikov return (EINVAL); 2355ac35ff17SAlexander V. Chernikov 2356ac35ff17SAlexander V. Chernikov if (name[0] == '\0') 2357ac35ff17SAlexander V. Chernikov return (EINVAL); 2358ac35ff17SAlexander V. Chernikov 2359ac35ff17SAlexander V. Chernikov /* 2360ac35ff17SAlexander V. Chernikov * TODO: do some more complicated checks 2361ac35ff17SAlexander V. Chernikov */ 2362ac35ff17SAlexander V. Chernikov 2363ac35ff17SAlexander V. Chernikov return (0); 2364ac35ff17SAlexander V. Chernikov } 2365ac35ff17SAlexander V. Chernikov 2366ac35ff17SAlexander V. Chernikov /* 2367ac35ff17SAlexander V. Chernikov * Find tablename TLV by @uid. 2368ac35ff17SAlexander V. Chernikov * Check @tlvs for valid data inside. 2369ac35ff17SAlexander V. Chernikov * 2370ac35ff17SAlexander V. Chernikov * Returns pointer to found TLV or NULL. 2371ac35ff17SAlexander V. Chernikov */ 2372ac35ff17SAlexander V. Chernikov static ipfw_obj_ntlv * 2373b074b7bbSAlexander V. Chernikov find_name_tlv(void *tlvs, int len, uint16_t uidx) 2374b074b7bbSAlexander V. Chernikov { 23759f7d47b0SAlexander V. Chernikov ipfw_obj_ntlv *ntlv; 2376b074b7bbSAlexander V. Chernikov uintptr_t pa, pe; 2377b074b7bbSAlexander V. Chernikov int l; 2378b074b7bbSAlexander V. Chernikov 2379b074b7bbSAlexander V. Chernikov pa = (uintptr_t)tlvs; 2380b074b7bbSAlexander V. Chernikov pe = pa + len; 2381b074b7bbSAlexander V. Chernikov l = 0; 2382b074b7bbSAlexander V. Chernikov for (; pa < pe; pa += l) { 23839f7d47b0SAlexander V. Chernikov ntlv = (ipfw_obj_ntlv *)pa; 2384b074b7bbSAlexander V. Chernikov l = ntlv->head.length; 2385ac35ff17SAlexander V. Chernikov 2386ac35ff17SAlexander V. Chernikov if (l != sizeof(*ntlv)) 2387ac35ff17SAlexander V. Chernikov return (NULL); 2388ac35ff17SAlexander V. Chernikov 2389563b5ab1SAlexander V. Chernikov if (ntlv->head.type != IPFW_TLV_TBL_NAME) 2390b074b7bbSAlexander V. Chernikov continue; 2391ac35ff17SAlexander V. Chernikov 2392b074b7bbSAlexander V. Chernikov if (ntlv->idx != uidx) 2393b074b7bbSAlexander V. Chernikov continue; 2394b074b7bbSAlexander V. Chernikov 2395ac35ff17SAlexander V. Chernikov if (ipfw_check_table_name(ntlv->name) != 0) 2396ac35ff17SAlexander V. Chernikov return (NULL); 2397ac35ff17SAlexander V. Chernikov 2398ac35ff17SAlexander V. Chernikov return (ntlv); 2399b074b7bbSAlexander V. Chernikov } 2400b074b7bbSAlexander V. Chernikov 2401b074b7bbSAlexander V. Chernikov return (NULL); 2402b074b7bbSAlexander V. Chernikov } 2403b074b7bbSAlexander V. Chernikov 2404ac35ff17SAlexander V. Chernikov /* 2405ac35ff17SAlexander V. Chernikov * Finds table config based on either legacy index 2406ac35ff17SAlexander V. Chernikov * or name in ntlv. 2407ac35ff17SAlexander V. Chernikov * Note @ti structure contains unchecked data from userland. 2408ac35ff17SAlexander V. Chernikov * 2409ac35ff17SAlexander V. Chernikov * Returns pointer to table_config or NULL. 2410ac35ff17SAlexander V. Chernikov */ 2411b074b7bbSAlexander V. Chernikov static struct table_config * 2412b074b7bbSAlexander V. Chernikov find_table(struct namedobj_instance *ni, struct tid_info *ti) 2413b074b7bbSAlexander V. Chernikov { 2414b074b7bbSAlexander V. Chernikov char *name, bname[16]; 2415b074b7bbSAlexander V. Chernikov struct named_object *no; 2416ac35ff17SAlexander V. Chernikov ipfw_obj_ntlv *ntlv; 2417ac35ff17SAlexander V. Chernikov uint32_t set; 2418b074b7bbSAlexander V. Chernikov 2419b074b7bbSAlexander V. Chernikov if (ti->tlvs != NULL) { 2420ac35ff17SAlexander V. Chernikov ntlv = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx); 2421ac35ff17SAlexander V. Chernikov if (ntlv == NULL) 2422b074b7bbSAlexander V. Chernikov return (NULL); 2423ac35ff17SAlexander V. Chernikov name = ntlv->name; 2424ac35ff17SAlexander V. Chernikov set = ntlv->set; 2425b074b7bbSAlexander V. Chernikov } else { 2426b074b7bbSAlexander V. Chernikov snprintf(bname, sizeof(bname), "%d", ti->uidx); 2427b074b7bbSAlexander V. Chernikov name = bname; 2428ac35ff17SAlexander V. Chernikov set = 0; 2429b074b7bbSAlexander V. Chernikov } 2430b074b7bbSAlexander V. Chernikov 2431ac35ff17SAlexander V. Chernikov no = ipfw_objhash_lookup_name(ni, set, name); 2432b074b7bbSAlexander V. Chernikov 2433b074b7bbSAlexander V. Chernikov return ((struct table_config *)no); 2434b074b7bbSAlexander V. Chernikov } 2435b074b7bbSAlexander V. Chernikov 2436b074b7bbSAlexander V. Chernikov static struct table_config * 243768394ec8SAlexander V. Chernikov alloc_table_config(struct ip_fw_chain *ch, struct tid_info *ti, 2438914bffb6SAlexander V. Chernikov struct table_algo *ta, char *aname, uint8_t tflags, uint8_t vtype) 2439b074b7bbSAlexander V. Chernikov { 2440b074b7bbSAlexander V. Chernikov char *name, bname[16]; 2441b074b7bbSAlexander V. Chernikov struct table_config *tc; 2442b074b7bbSAlexander V. Chernikov int error; 2443ac35ff17SAlexander V. Chernikov ipfw_obj_ntlv *ntlv; 2444ac35ff17SAlexander V. Chernikov uint32_t set; 2445b074b7bbSAlexander V. Chernikov 2446b074b7bbSAlexander V. Chernikov if (ti->tlvs != NULL) { 2447ac35ff17SAlexander V. Chernikov ntlv = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx); 2448ac35ff17SAlexander V. Chernikov if (ntlv == NULL) 2449b074b7bbSAlexander V. Chernikov return (NULL); 2450ac35ff17SAlexander V. Chernikov name = ntlv->name; 2451ac35ff17SAlexander V. Chernikov set = ntlv->set; 2452b074b7bbSAlexander V. Chernikov } else { 2453b074b7bbSAlexander V. Chernikov snprintf(bname, sizeof(bname), "%d", ti->uidx); 2454b074b7bbSAlexander V. Chernikov name = bname; 2455ac35ff17SAlexander V. Chernikov set = 0; 2456b074b7bbSAlexander V. Chernikov } 2457b074b7bbSAlexander V. Chernikov 2458b074b7bbSAlexander V. Chernikov tc = malloc(sizeof(struct table_config), M_IPFW, M_WAITOK | M_ZERO); 2459b074b7bbSAlexander V. Chernikov tc->no.name = tc->tablename; 2460b074b7bbSAlexander V. Chernikov tc->no.type = ti->type; 2461ac35ff17SAlexander V. Chernikov tc->no.set = set; 2462914bffb6SAlexander V. Chernikov tc->tflags = tflags; 24639f7d47b0SAlexander V. Chernikov tc->ta = ta; 2464b074b7bbSAlexander V. Chernikov strlcpy(tc->tablename, name, sizeof(tc->tablename)); 2465ac35ff17SAlexander V. Chernikov /* Set default value type to u32 for compability reasons */ 2466db785d31SAlexander V. Chernikov if (vtype == 0) 2467ac35ff17SAlexander V. Chernikov tc->vtype = IPFW_VTYPE_U32; 2468db785d31SAlexander V. Chernikov else 2469db785d31SAlexander V. Chernikov tc->vtype = vtype; 2470b074b7bbSAlexander V. Chernikov 2471b074b7bbSAlexander V. Chernikov if (ti->tlvs == NULL) { 2472b074b7bbSAlexander V. Chernikov tc->no.compat = 1; 2473b074b7bbSAlexander V. Chernikov tc->no.uidx = ti->uidx; 2474b074b7bbSAlexander V. Chernikov } 2475b074b7bbSAlexander V. Chernikov 2476b074b7bbSAlexander V. Chernikov /* Preallocate data structures for new tables */ 2477914bffb6SAlexander V. Chernikov error = ta->init(ch, &tc->astate, &tc->ti, aname, tflags); 2478b074b7bbSAlexander V. Chernikov if (error != 0) { 2479b074b7bbSAlexander V. Chernikov free(tc, M_IPFW); 2480b074b7bbSAlexander V. Chernikov return (NULL); 2481b074b7bbSAlexander V. Chernikov } 2482b074b7bbSAlexander V. Chernikov 2483b074b7bbSAlexander V. Chernikov return (tc); 2484b074b7bbSAlexander V. Chernikov } 2485b074b7bbSAlexander V. Chernikov 2486b074b7bbSAlexander V. Chernikov static void 2487b074b7bbSAlexander V. Chernikov free_table_config(struct namedobj_instance *ni, struct table_config *tc) 2488b074b7bbSAlexander V. Chernikov { 2489b074b7bbSAlexander V. Chernikov 2490b074b7bbSAlexander V. Chernikov if (tc->linked == 0) 249168394ec8SAlexander V. Chernikov tc->ta->destroy(tc->astate, &tc->ti); 2492b074b7bbSAlexander V. Chernikov 2493b074b7bbSAlexander V. Chernikov free(tc, M_IPFW); 2494b074b7bbSAlexander V. Chernikov } 2495b074b7bbSAlexander V. Chernikov 2496b074b7bbSAlexander V. Chernikov /* 2497b074b7bbSAlexander V. Chernikov * Links @tc to @chain table named instance. 2498b074b7bbSAlexander V. Chernikov * Sets appropriate type/states in @chain table info. 2499b074b7bbSAlexander V. Chernikov */ 2500b074b7bbSAlexander V. Chernikov static void 25019f7d47b0SAlexander V. Chernikov link_table(struct ip_fw_chain *ch, struct table_config *tc) 2502b074b7bbSAlexander V. Chernikov { 2503b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 25049f7d47b0SAlexander V. Chernikov struct table_info *ti; 2505b074b7bbSAlexander V. Chernikov uint16_t kidx; 2506b074b7bbSAlexander V. Chernikov 25079f7d47b0SAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(ch); 25089f7d47b0SAlexander V. Chernikov IPFW_WLOCK_ASSERT(ch); 2509b074b7bbSAlexander V. Chernikov 25109f7d47b0SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 2511b074b7bbSAlexander V. Chernikov kidx = tc->no.kidx; 2512b074b7bbSAlexander V. Chernikov 2513b074b7bbSAlexander V. Chernikov ipfw_objhash_add(ni, &tc->no); 25149f7d47b0SAlexander V. Chernikov 25159f7d47b0SAlexander V. Chernikov ti = KIDX_TO_TI(ch, kidx); 25169f7d47b0SAlexander V. Chernikov *ti = tc->ti; 2517b074b7bbSAlexander V. Chernikov 251868394ec8SAlexander V. Chernikov /* Notify algo on real @ti address */ 251968394ec8SAlexander V. Chernikov if (tc->ta->change_ti != NULL) 252068394ec8SAlexander V. Chernikov tc->ta->change_ti(tc->astate, ti); 252168394ec8SAlexander V. Chernikov 2522b074b7bbSAlexander V. Chernikov tc->linked = 1; 25239d099b4fSAlexander V. Chernikov tc->ta->refcnt++; 2524b074b7bbSAlexander V. Chernikov } 2525b074b7bbSAlexander V. Chernikov 2526b074b7bbSAlexander V. Chernikov /* 2527b074b7bbSAlexander V. Chernikov * Unlinks @tc from @chain table named instance. 2528b074b7bbSAlexander V. Chernikov * Zeroes states in @chain and stores them in @tc. 2529b074b7bbSAlexander V. Chernikov */ 2530b074b7bbSAlexander V. Chernikov static void 25319f7d47b0SAlexander V. Chernikov unlink_table(struct ip_fw_chain *ch, struct table_config *tc) 2532b074b7bbSAlexander V. Chernikov { 2533b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 25349f7d47b0SAlexander V. Chernikov struct table_info *ti; 2535b074b7bbSAlexander V. Chernikov uint16_t kidx; 2536b074b7bbSAlexander V. Chernikov 25379f7d47b0SAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(ch); 25389f7d47b0SAlexander V. Chernikov IPFW_WLOCK_ASSERT(ch); 2539b074b7bbSAlexander V. Chernikov 25409f7d47b0SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 2541b074b7bbSAlexander V. Chernikov kidx = tc->no.kidx; 2542b074b7bbSAlexander V. Chernikov 25439f7d47b0SAlexander V. Chernikov /* Clear state. @ti copy is already saved inside @tc */ 2544b074b7bbSAlexander V. Chernikov ipfw_objhash_del(ni, &tc->no); 25459f7d47b0SAlexander V. Chernikov ti = KIDX_TO_TI(ch, kidx); 25469f7d47b0SAlexander V. Chernikov memset(ti, 0, sizeof(struct table_info)); 2547b074b7bbSAlexander V. Chernikov tc->linked = 0; 25489d099b4fSAlexander V. Chernikov tc->ta->refcnt--; 254968394ec8SAlexander V. Chernikov 255068394ec8SAlexander V. Chernikov /* Notify algo on real @ti address */ 255168394ec8SAlexander V. Chernikov if (tc->ta->change_ti != NULL) 255268394ec8SAlexander V. Chernikov tc->ta->change_ti(tc->astate, NULL); 2553b074b7bbSAlexander V. Chernikov } 2554b074b7bbSAlexander V. Chernikov 2555b074b7bbSAlexander V. Chernikov /* 2556b074b7bbSAlexander V. Chernikov * Finds named object by @uidx number. 2557b074b7bbSAlexander V. Chernikov * Refs found object, allocate new index for non-existing object. 25589490a627SAlexander V. Chernikov * Fills in @oib with userland/kernel indexes. 25599490a627SAlexander V. Chernikov * First free oidx pointer is saved back in @oib. 2560b074b7bbSAlexander V. Chernikov * 2561b074b7bbSAlexander V. Chernikov * Returns 0 on success. 2562b074b7bbSAlexander V. Chernikov */ 2563b074b7bbSAlexander V. Chernikov static int 25649490a627SAlexander V. Chernikov bind_table_rule(struct ip_fw_chain *ch, struct ip_fw *rule, 25659490a627SAlexander V. Chernikov struct rule_check_info *ci, struct obj_idx **oib, struct tid_info *ti) 2566b074b7bbSAlexander V. Chernikov { 2567b074b7bbSAlexander V. Chernikov struct table_config *tc; 25689490a627SAlexander V. Chernikov struct namedobj_instance *ni; 25699490a627SAlexander V. Chernikov struct named_object *no; 25709490a627SAlexander V. Chernikov int error, l, cmdlen; 25719490a627SAlexander V. Chernikov ipfw_insn *cmd; 25729490a627SAlexander V. Chernikov struct obj_idx *pidx, *p; 25739490a627SAlexander V. Chernikov 25749490a627SAlexander V. Chernikov pidx = *oib; 25759490a627SAlexander V. Chernikov l = rule->cmd_len; 25769490a627SAlexander V. Chernikov cmd = rule->cmd; 25779490a627SAlexander V. Chernikov cmdlen = 0; 25789490a627SAlexander V. Chernikov error = 0; 25799490a627SAlexander V. Chernikov 25809490a627SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 25819490a627SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 25829490a627SAlexander V. Chernikov 25839490a627SAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 25849490a627SAlexander V. Chernikov cmdlen = F_LEN(cmd); 25859490a627SAlexander V. Chernikov 25869490a627SAlexander V. Chernikov if (classify_table_opcode(cmd, &ti->uidx, &ti->type) != 0) 25879490a627SAlexander V. Chernikov continue; 2588b074b7bbSAlexander V. Chernikov 2589b074b7bbSAlexander V. Chernikov pidx->uidx = ti->uidx; 2590b074b7bbSAlexander V. Chernikov pidx->type = ti->type; 2591b074b7bbSAlexander V. Chernikov 25929490a627SAlexander V. Chernikov if ((tc = find_table(ni, ti)) != NULL) { 25939490a627SAlexander V. Chernikov if (tc->no.type != ti->type) { 25949490a627SAlexander V. Chernikov /* Incompatible types */ 25959490a627SAlexander V. Chernikov error = EINVAL; 25969490a627SAlexander V. Chernikov break; 25979490a627SAlexander V. Chernikov } 25989f7d47b0SAlexander V. Chernikov 25999490a627SAlexander V. Chernikov /* Reference found table and save kidx */ 26009490a627SAlexander V. Chernikov tc->no.refcnt++; 26019490a627SAlexander V. Chernikov pidx->kidx = tc->no.kidx; 26029490a627SAlexander V. Chernikov pidx++; 26039490a627SAlexander V. Chernikov continue; 26049490a627SAlexander V. Chernikov } 26059490a627SAlexander V. Chernikov 26069490a627SAlexander V. Chernikov /* Table not found. Allocate new index and save for later */ 2607ac35ff17SAlexander V. Chernikov if (ipfw_objhash_alloc_idx(ni, &pidx->kidx) != 0) { 2608ac35ff17SAlexander V. Chernikov printf("Unable to allocate table %s index in set %u." 2609b074b7bbSAlexander V. Chernikov " Consider increasing net.inet.ip.fw.tables_max", 2610ac35ff17SAlexander V. Chernikov "", ti->set); 26119490a627SAlexander V. Chernikov error = EBUSY; 26129490a627SAlexander V. Chernikov break; 2613b074b7bbSAlexander V. Chernikov } 2614b074b7bbSAlexander V. Chernikov 26153a845e10SAlexander V. Chernikov ci->flags |= IPFW_RCF_TABLES; 26169490a627SAlexander V. Chernikov pidx->new = 1; 26179490a627SAlexander V. Chernikov pidx++; 2618b074b7bbSAlexander V. Chernikov } 2619b074b7bbSAlexander V. Chernikov 26209490a627SAlexander V. Chernikov if (error != 0) { 26219490a627SAlexander V. Chernikov /* Unref everything we have already done */ 26229490a627SAlexander V. Chernikov for (p = *oib; p < pidx; p++) { 26239490a627SAlexander V. Chernikov if (p->new != 0) { 2624ac35ff17SAlexander V. Chernikov ipfw_objhash_free_idx(ni, p->kidx); 26259490a627SAlexander V. Chernikov continue; 26269490a627SAlexander V. Chernikov } 2627b074b7bbSAlexander V. Chernikov 26289490a627SAlexander V. Chernikov /* Find & unref by existing idx */ 2629ac35ff17SAlexander V. Chernikov no = ipfw_objhash_lookup_kidx(ni, p->kidx); 26309490a627SAlexander V. Chernikov KASSERT(no != NULL, ("Ref'd table %d disappeared", 26319490a627SAlexander V. Chernikov p->kidx)); 2632b074b7bbSAlexander V. Chernikov 26339490a627SAlexander V. Chernikov no->refcnt--; 26349490a627SAlexander V. Chernikov } 26359490a627SAlexander V. Chernikov } 26369490a627SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 2637b074b7bbSAlexander V. Chernikov 26389490a627SAlexander V. Chernikov *oib = pidx; 26399490a627SAlexander V. Chernikov 26409490a627SAlexander V. Chernikov return (error); 2641b074b7bbSAlexander V. Chernikov } 2642b074b7bbSAlexander V. Chernikov 2643a73d728dSAlexander V. Chernikov struct swap_table_args { 2644a73d728dSAlexander V. Chernikov int set; 2645a73d728dSAlexander V. Chernikov int new_set; 2646a73d728dSAlexander V. Chernikov int mv; 2647a73d728dSAlexander V. Chernikov }; 2648a73d728dSAlexander V. Chernikov 2649a73d728dSAlexander V. Chernikov /* 2650a73d728dSAlexander V. Chernikov * Change set for each matching table. 2651a73d728dSAlexander V. Chernikov * 2652a73d728dSAlexander V. Chernikov * Ensure we dispatch each table once by setting/checking ochange 2653a73d728dSAlexander V. Chernikov * fields. 2654a73d728dSAlexander V. Chernikov */ 2655a73d728dSAlexander V. Chernikov static void 2656a73d728dSAlexander V. Chernikov swap_table_set(struct namedobj_instance *ni, struct named_object *no, 2657a73d728dSAlexander V. Chernikov void *arg) 2658a73d728dSAlexander V. Chernikov { 2659a73d728dSAlexander V. Chernikov struct table_config *tc; 2660a73d728dSAlexander V. Chernikov struct swap_table_args *sta; 2661a73d728dSAlexander V. Chernikov 2662a73d728dSAlexander V. Chernikov tc = (struct table_config *)no; 2663a73d728dSAlexander V. Chernikov sta = (struct swap_table_args *)arg; 2664a73d728dSAlexander V. Chernikov 2665a73d728dSAlexander V. Chernikov if (no->set != sta->set && (no->set != sta->new_set || sta->mv != 0)) 2666a73d728dSAlexander V. Chernikov return; 2667a73d728dSAlexander V. Chernikov 2668a73d728dSAlexander V. Chernikov if (tc->ochanged != 0) 2669a73d728dSAlexander V. Chernikov return; 2670a73d728dSAlexander V. Chernikov 2671a73d728dSAlexander V. Chernikov tc->ochanged = 1; 2672a73d728dSAlexander V. Chernikov ipfw_objhash_del(ni, no); 2673a73d728dSAlexander V. Chernikov if (no->set == sta->set) 2674a73d728dSAlexander V. Chernikov no->set = sta->new_set; 2675a73d728dSAlexander V. Chernikov else 2676a73d728dSAlexander V. Chernikov no->set = sta->set; 2677a73d728dSAlexander V. Chernikov ipfw_objhash_add(ni, no); 2678a73d728dSAlexander V. Chernikov } 2679a73d728dSAlexander V. Chernikov 2680a73d728dSAlexander V. Chernikov /* 2681a73d728dSAlexander V. Chernikov * Cleans up ochange field for all tables. 2682a73d728dSAlexander V. Chernikov */ 2683a73d728dSAlexander V. Chernikov static void 2684a73d728dSAlexander V. Chernikov clean_table_set_data(struct namedobj_instance *ni, struct named_object *no, 2685a73d728dSAlexander V. Chernikov void *arg) 2686a73d728dSAlexander V. Chernikov { 2687a73d728dSAlexander V. Chernikov struct table_config *tc; 2688a73d728dSAlexander V. Chernikov struct swap_table_args *sta; 2689a73d728dSAlexander V. Chernikov 2690a73d728dSAlexander V. Chernikov tc = (struct table_config *)no; 2691a73d728dSAlexander V. Chernikov sta = (struct swap_table_args *)arg; 2692a73d728dSAlexander V. Chernikov 2693a73d728dSAlexander V. Chernikov tc->ochanged = 0; 2694a73d728dSAlexander V. Chernikov } 2695a73d728dSAlexander V. Chernikov 2696a73d728dSAlexander V. Chernikov /* 2697a73d728dSAlexander V. Chernikov * Swaps tables within two sets. 2698a73d728dSAlexander V. Chernikov */ 2699a73d728dSAlexander V. Chernikov void 2700a73d728dSAlexander V. Chernikov ipfw_swap_tables_sets(struct ip_fw_chain *ch, uint32_t set, 2701a73d728dSAlexander V. Chernikov uint32_t new_set, int mv) 2702a73d728dSAlexander V. Chernikov { 2703a73d728dSAlexander V. Chernikov struct swap_table_args sta; 2704a73d728dSAlexander V. Chernikov 2705a73d728dSAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(ch); 2706a73d728dSAlexander V. Chernikov 2707a73d728dSAlexander V. Chernikov sta.set = set; 2708a73d728dSAlexander V. Chernikov sta.new_set = new_set; 2709a73d728dSAlexander V. Chernikov sta.mv = mv; 2710a73d728dSAlexander V. Chernikov 2711a73d728dSAlexander V. Chernikov ipfw_objhash_foreach(CHAIN_TO_NI(ch), swap_table_set, &sta); 2712a73d728dSAlexander V. Chernikov ipfw_objhash_foreach(CHAIN_TO_NI(ch), clean_table_set_data, &sta); 2713a73d728dSAlexander V. Chernikov } 2714a73d728dSAlexander V. Chernikov 2715a73d728dSAlexander V. Chernikov /* 2716a73d728dSAlexander V. Chernikov * Move all tables which are reference by rules in @rr to set @new_set. 2717a73d728dSAlexander V. Chernikov * Makes sure that all relevant tables are referenced ONLLY by given rules. 2718a73d728dSAlexander V. Chernikov * 2719a73d728dSAlexander V. Chernikov * Retuns 0 on success, 2720a73d728dSAlexander V. Chernikov */ 2721a73d728dSAlexander V. Chernikov int 2722a73d728dSAlexander V. Chernikov ipfw_move_tables_sets(struct ip_fw_chain *ch, ipfw_range_tlv *rt, 2723a73d728dSAlexander V. Chernikov uint32_t new_set) 2724a73d728dSAlexander V. Chernikov { 2725a73d728dSAlexander V. Chernikov struct ip_fw *rule; 2726a73d728dSAlexander V. Chernikov struct table_config *tc; 2727a73d728dSAlexander V. Chernikov struct named_object *no; 2728a73d728dSAlexander V. Chernikov struct namedobj_instance *ni; 2729a73d728dSAlexander V. Chernikov int bad, i, l, cmdlen; 2730a73d728dSAlexander V. Chernikov uint16_t kidx; 2731a73d728dSAlexander V. Chernikov uint8_t type; 2732a73d728dSAlexander V. Chernikov ipfw_insn *cmd; 2733a73d728dSAlexander V. Chernikov 2734a73d728dSAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(ch); 2735a73d728dSAlexander V. Chernikov 2736a73d728dSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 2737a73d728dSAlexander V. Chernikov 2738a73d728dSAlexander V. Chernikov /* Stage 1: count number of references by given rules */ 2739a73d728dSAlexander V. Chernikov for (i = 0; i < ch->n_rules - 1; i++) { 2740a73d728dSAlexander V. Chernikov rule = ch->map[i]; 2741a73d728dSAlexander V. Chernikov if (ipfw_match_range(rule, rt) == 0) 2742a73d728dSAlexander V. Chernikov continue; 2743a73d728dSAlexander V. Chernikov 2744a73d728dSAlexander V. Chernikov l = rule->cmd_len; 2745a73d728dSAlexander V. Chernikov cmd = rule->cmd; 2746a73d728dSAlexander V. Chernikov cmdlen = 0; 2747a73d728dSAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 2748a73d728dSAlexander V. Chernikov cmdlen = F_LEN(cmd); 2749a73d728dSAlexander V. Chernikov if (classify_table_opcode(cmd, &kidx, &type) != 0) 2750a73d728dSAlexander V. Chernikov continue; 2751a73d728dSAlexander V. Chernikov no = ipfw_objhash_lookup_kidx(ni, kidx); 2752a73d728dSAlexander V. Chernikov KASSERT(no != NULL, 2753a73d728dSAlexander V. Chernikov ("objhash lookup failed on index %d", kidx)); 2754a73d728dSAlexander V. Chernikov tc = (struct table_config *)no; 2755a73d728dSAlexander V. Chernikov tc->ocount++; 2756a73d728dSAlexander V. Chernikov } 2757a73d728dSAlexander V. Chernikov 2758a73d728dSAlexander V. Chernikov } 2759a73d728dSAlexander V. Chernikov 2760a73d728dSAlexander V. Chernikov /* Stage 2: verify "ownership" */ 2761a73d728dSAlexander V. Chernikov bad = 0; 2762a73d728dSAlexander V. Chernikov for (i = 0; i < ch->n_rules - 1; i++) { 2763a73d728dSAlexander V. Chernikov rule = ch->map[i]; 2764a73d728dSAlexander V. Chernikov if (ipfw_match_range(rule, rt) == 0) 2765a73d728dSAlexander V. Chernikov continue; 2766a73d728dSAlexander V. Chernikov 2767a73d728dSAlexander V. Chernikov l = rule->cmd_len; 2768a73d728dSAlexander V. Chernikov cmd = rule->cmd; 2769a73d728dSAlexander V. Chernikov cmdlen = 0; 2770a73d728dSAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 2771a73d728dSAlexander V. Chernikov cmdlen = F_LEN(cmd); 2772a73d728dSAlexander V. Chernikov if (classify_table_opcode(cmd, &kidx, &type) != 0) 2773a73d728dSAlexander V. Chernikov continue; 2774a73d728dSAlexander V. Chernikov no = ipfw_objhash_lookup_kidx(ni, kidx); 2775a73d728dSAlexander V. Chernikov KASSERT(no != NULL, 2776a73d728dSAlexander V. Chernikov ("objhash lookup failed on index %d", kidx)); 2777a73d728dSAlexander V. Chernikov tc = (struct table_config *)no; 2778a73d728dSAlexander V. Chernikov if (tc->no.refcnt != tc->ocount) { 2779a73d728dSAlexander V. Chernikov 2780a73d728dSAlexander V. Chernikov /* 2781a73d728dSAlexander V. Chernikov * Number of references differ: 2782a73d728dSAlexander V. Chernikov * Other rule(s) are holding reference to given 2783a73d728dSAlexander V. Chernikov * table, so it is not possible to change its set. 2784a73d728dSAlexander V. Chernikov * 2785a73d728dSAlexander V. Chernikov * Note that refcnt may account 2786a73d728dSAlexander V. Chernikov * references to some going-to-be-added rules. 2787a73d728dSAlexander V. Chernikov * Since we don't know their numbers (and event 2788a73d728dSAlexander V. Chernikov * if they will be added) it is perfectly OK 2789a73d728dSAlexander V. Chernikov * to return error here. 2790a73d728dSAlexander V. Chernikov */ 2791a73d728dSAlexander V. Chernikov bad = 1; 2792a73d728dSAlexander V. Chernikov break; 2793a73d728dSAlexander V. Chernikov } 2794a73d728dSAlexander V. Chernikov } 2795a73d728dSAlexander V. Chernikov 2796a73d728dSAlexander V. Chernikov if (bad != 0) 2797a73d728dSAlexander V. Chernikov break; 2798a73d728dSAlexander V. Chernikov } 2799a73d728dSAlexander V. Chernikov 2800a73d728dSAlexander V. Chernikov /* Stage 3: change set or cleanup */ 2801a73d728dSAlexander V. Chernikov for (i = 0; i < ch->n_rules - 1; i++) { 2802a73d728dSAlexander V. Chernikov rule = ch->map[i]; 2803a73d728dSAlexander V. Chernikov if (ipfw_match_range(rule, rt) == 0) 2804a73d728dSAlexander V. Chernikov continue; 2805a73d728dSAlexander V. Chernikov 2806a73d728dSAlexander V. Chernikov l = rule->cmd_len; 2807a73d728dSAlexander V. Chernikov cmd = rule->cmd; 2808a73d728dSAlexander V. Chernikov cmdlen = 0; 2809a73d728dSAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 2810a73d728dSAlexander V. Chernikov cmdlen = F_LEN(cmd); 2811a73d728dSAlexander V. Chernikov if (classify_table_opcode(cmd, &kidx, &type) != 0) 2812a73d728dSAlexander V. Chernikov continue; 2813a73d728dSAlexander V. Chernikov no = ipfw_objhash_lookup_kidx(ni, kidx); 2814a73d728dSAlexander V. Chernikov KASSERT(no != NULL, 2815a73d728dSAlexander V. Chernikov ("objhash lookup failed on index %d", kidx)); 2816a73d728dSAlexander V. Chernikov tc = (struct table_config *)no; 2817a73d728dSAlexander V. Chernikov 2818a73d728dSAlexander V. Chernikov tc->ocount = 0; 2819a73d728dSAlexander V. Chernikov if (bad != 0) 2820a73d728dSAlexander V. Chernikov continue; 2821a73d728dSAlexander V. Chernikov 2822a73d728dSAlexander V. Chernikov /* Actually change set. */ 2823a73d728dSAlexander V. Chernikov ipfw_objhash_del(ni, no); 2824a73d728dSAlexander V. Chernikov no->set = new_set; 2825a73d728dSAlexander V. Chernikov ipfw_objhash_add(ni, no); 2826a73d728dSAlexander V. Chernikov } 2827a73d728dSAlexander V. Chernikov } 2828a73d728dSAlexander V. Chernikov 2829a73d728dSAlexander V. Chernikov return (bad); 2830a73d728dSAlexander V. Chernikov } 2831a73d728dSAlexander V. Chernikov 2832b074b7bbSAlexander V. Chernikov /* 2833b074b7bbSAlexander V. Chernikov * Compatibility function for old ipfw(8) binaries. 2834b074b7bbSAlexander V. Chernikov * Rewrites table kernel indices with userland ones. 2835b074b7bbSAlexander V. Chernikov * Works for \d+ talbes only (e.g. for tables, converted 2836b074b7bbSAlexander V. Chernikov * from old numbered system calls). 2837b074b7bbSAlexander V. Chernikov * 2838b074b7bbSAlexander V. Chernikov * Returns 0 on success. 2839b074b7bbSAlexander V. Chernikov * Raises error on any other tables. 2840b074b7bbSAlexander V. Chernikov */ 2841b074b7bbSAlexander V. Chernikov int 28427e767c79SAlexander V. Chernikov ipfw_rewrite_table_kidx(struct ip_fw_chain *chain, struct ip_fw_rule0 *rule) 2843b074b7bbSAlexander V. Chernikov { 28441832a7b3SAlexander V. Chernikov int cmdlen, error, l; 2845b074b7bbSAlexander V. Chernikov ipfw_insn *cmd; 28461832a7b3SAlexander V. Chernikov uint16_t kidx, uidx; 2847b074b7bbSAlexander V. Chernikov uint8_t type; 2848b074b7bbSAlexander V. Chernikov struct named_object *no; 2849b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 2850b074b7bbSAlexander V. Chernikov 2851b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(chain); 28521832a7b3SAlexander V. Chernikov error = 0; 2853b074b7bbSAlexander V. Chernikov 2854b074b7bbSAlexander V. Chernikov l = rule->cmd_len; 2855b074b7bbSAlexander V. Chernikov cmd = rule->cmd; 2856b074b7bbSAlexander V. Chernikov cmdlen = 0; 2857b074b7bbSAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 2858b074b7bbSAlexander V. Chernikov cmdlen = F_LEN(cmd); 2859b074b7bbSAlexander V. Chernikov 2860b074b7bbSAlexander V. Chernikov if (classify_table_opcode(cmd, &kidx, &type) != 0) 2861b074b7bbSAlexander V. Chernikov continue; 2862b074b7bbSAlexander V. Chernikov 2863ac35ff17SAlexander V. Chernikov if ((no = ipfw_objhash_lookup_kidx(ni, kidx)) == NULL) 2864b074b7bbSAlexander V. Chernikov return (1); 2865b074b7bbSAlexander V. Chernikov 28661832a7b3SAlexander V. Chernikov uidx = no->uidx; 28671832a7b3SAlexander V. Chernikov if (no->compat == 0) { 2868b074b7bbSAlexander V. Chernikov 28691832a7b3SAlexander V. Chernikov /* 28701832a7b3SAlexander V. Chernikov * We are called via legacy opcode. 28711832a7b3SAlexander V. Chernikov * Save error and show table as fake number 28721832a7b3SAlexander V. Chernikov * not to make ipfw(8) hang. 28731832a7b3SAlexander V. Chernikov */ 28741832a7b3SAlexander V. Chernikov uidx = 65535; 28751832a7b3SAlexander V. Chernikov error = 2; 2876b074b7bbSAlexander V. Chernikov } 2877b074b7bbSAlexander V. Chernikov 28781832a7b3SAlexander V. Chernikov update_table_opcode(cmd, uidx); 28791832a7b3SAlexander V. Chernikov } 28801832a7b3SAlexander V. Chernikov 28811832a7b3SAlexander V. Chernikov return (error); 2882b074b7bbSAlexander V. Chernikov } 2883b074b7bbSAlexander V. Chernikov 2884563b5ab1SAlexander V. Chernikov /* 2885563b5ab1SAlexander V. Chernikov * Sets every table kidx in @bmask which is used in rule @rule. 2886563b5ab1SAlexander V. Chernikov * 2887563b5ab1SAlexander V. Chernikov * Returns number of newly-referenced tables. 2888563b5ab1SAlexander V. Chernikov */ 2889563b5ab1SAlexander V. Chernikov int 2890563b5ab1SAlexander V. Chernikov ipfw_mark_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule, 2891563b5ab1SAlexander V. Chernikov uint32_t *bmask) 2892563b5ab1SAlexander V. Chernikov { 2893563b5ab1SAlexander V. Chernikov int cmdlen, l, count; 2894563b5ab1SAlexander V. Chernikov ipfw_insn *cmd; 2895563b5ab1SAlexander V. Chernikov uint16_t kidx; 2896563b5ab1SAlexander V. Chernikov uint8_t type; 2897563b5ab1SAlexander V. Chernikov 2898563b5ab1SAlexander V. Chernikov l = rule->cmd_len; 2899563b5ab1SAlexander V. Chernikov cmd = rule->cmd; 2900563b5ab1SAlexander V. Chernikov cmdlen = 0; 2901563b5ab1SAlexander V. Chernikov count = 0; 2902563b5ab1SAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 2903563b5ab1SAlexander V. Chernikov cmdlen = F_LEN(cmd); 2904563b5ab1SAlexander V. Chernikov 2905563b5ab1SAlexander V. Chernikov if (classify_table_opcode(cmd, &kidx, &type) != 0) 2906563b5ab1SAlexander V. Chernikov continue; 2907563b5ab1SAlexander V. Chernikov 2908563b5ab1SAlexander V. Chernikov if ((bmask[kidx / 32] & (1 << (kidx % 32))) == 0) 2909563b5ab1SAlexander V. Chernikov count++; 2910563b5ab1SAlexander V. Chernikov 2911563b5ab1SAlexander V. Chernikov bmask[kidx / 32] |= 1 << (kidx % 32); 2912563b5ab1SAlexander V. Chernikov } 2913563b5ab1SAlexander V. Chernikov 2914563b5ab1SAlexander V. Chernikov return (count); 2915563b5ab1SAlexander V. Chernikov } 2916563b5ab1SAlexander V. Chernikov 2917563b5ab1SAlexander V. Chernikov 2918b074b7bbSAlexander V. Chernikov 2919b074b7bbSAlexander V. Chernikov /* 2920b074b7bbSAlexander V. Chernikov * Checks is opcode is referencing table of appropriate type. 2921b074b7bbSAlexander V. Chernikov * Adds reference count for found table if true. 2922b074b7bbSAlexander V. Chernikov * Rewrites user-supplied opcode values with kernel ones. 2923b074b7bbSAlexander V. Chernikov * 2924b074b7bbSAlexander V. Chernikov * Returns 0 on success and appropriate error code otherwise. 2925b074b7bbSAlexander V. Chernikov */ 2926b074b7bbSAlexander V. Chernikov int 2927b074b7bbSAlexander V. Chernikov ipfw_rewrite_table_uidx(struct ip_fw_chain *chain, 2928b074b7bbSAlexander V. Chernikov struct rule_check_info *ci) 2929b074b7bbSAlexander V. Chernikov { 2930b074b7bbSAlexander V. Chernikov int cmdlen, error, ftype, l; 2931b074b7bbSAlexander V. Chernikov ipfw_insn *cmd; 2932b074b7bbSAlexander V. Chernikov uint16_t uidx; 2933b074b7bbSAlexander V. Chernikov uint8_t type; 2934b074b7bbSAlexander V. Chernikov struct table_config *tc; 29359f7d47b0SAlexander V. Chernikov struct table_algo *ta; 2936b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 2937b074b7bbSAlexander V. Chernikov struct named_object *no, *no_n, *no_tmp; 29389490a627SAlexander V. Chernikov struct obj_idx *p, *pidx_first, *pidx_last; 2939b074b7bbSAlexander V. Chernikov struct namedobjects_head nh; 2940b074b7bbSAlexander V. Chernikov struct tid_info ti; 2941b074b7bbSAlexander V. Chernikov 2942b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(chain); 2943b074b7bbSAlexander V. Chernikov 29449490a627SAlexander V. Chernikov /* Prepare queue to store configs */ 29459490a627SAlexander V. Chernikov TAILQ_INIT(&nh); 29469490a627SAlexander V. Chernikov 2947b074b7bbSAlexander V. Chernikov /* 2948b074b7bbSAlexander V. Chernikov * Prepare an array for storing opcode indices. 2949b074b7bbSAlexander V. Chernikov * Use stack allocation by default. 2950b074b7bbSAlexander V. Chernikov */ 2951b074b7bbSAlexander V. Chernikov if (ci->table_opcodes <= (sizeof(ci->obuf)/sizeof(ci->obuf[0]))) { 2952b074b7bbSAlexander V. Chernikov /* Stack */ 29539490a627SAlexander V. Chernikov pidx_first = ci->obuf; 2954b074b7bbSAlexander V. Chernikov } else 29559490a627SAlexander V. Chernikov pidx_first = malloc(ci->table_opcodes * sizeof(struct obj_idx), 2956b074b7bbSAlexander V. Chernikov M_IPFW, M_WAITOK | M_ZERO); 2957b074b7bbSAlexander V. Chernikov 29589490a627SAlexander V. Chernikov pidx_last = pidx_first; 2959b074b7bbSAlexander V. Chernikov error = 0; 2960b074b7bbSAlexander V. Chernikov 2961b074b7bbSAlexander V. Chernikov type = 0; 2962b074b7bbSAlexander V. Chernikov ftype = 0; 2963b074b7bbSAlexander V. Chernikov 2964b074b7bbSAlexander V. Chernikov memset(&ti, 0, sizeof(ti)); 29651832a7b3SAlexander V. Chernikov 29661832a7b3SAlexander V. Chernikov /* 29671832a7b3SAlexander V. Chernikov * Use default set for looking up tables (old way) or 29681832a7b3SAlexander V. Chernikov * use set rule is assigned to (new way). 29691832a7b3SAlexander V. Chernikov */ 29701832a7b3SAlexander V. Chernikov ti.set = (V_fw_tables_sets != 0) ? ci->krule->set : 0; 29716c2997ffSAlexander V. Chernikov if (ci->ctlv != NULL) { 29726c2997ffSAlexander V. Chernikov ti.tlvs = (void *)(ci->ctlv + 1); 29736c2997ffSAlexander V. Chernikov ti.tlen = ci->ctlv->head.length - sizeof(ipfw_obj_ctlv); 29746c2997ffSAlexander V. Chernikov } 2975b074b7bbSAlexander V. Chernikov 2976b074b7bbSAlexander V. Chernikov /* 29779490a627SAlexander V. Chernikov * Stage 1: reference existing tables, determine number 29789490a627SAlexander V. Chernikov * of tables we need to allocate and allocate indexes for each. 2979b074b7bbSAlexander V. Chernikov */ 29809490a627SAlexander V. Chernikov error = bind_table_rule(chain, ci->krule, ci, &pidx_last, &ti); 2981b074b7bbSAlexander V. Chernikov 2982b074b7bbSAlexander V. Chernikov if (error != 0) { 29839490a627SAlexander V. Chernikov if (pidx_first != ci->obuf) 29849490a627SAlexander V. Chernikov free(pidx_first, M_IPFW); 2985b074b7bbSAlexander V. Chernikov 2986b074b7bbSAlexander V. Chernikov return (error); 2987b074b7bbSAlexander V. Chernikov } 2988b074b7bbSAlexander V. Chernikov 2989b074b7bbSAlexander V. Chernikov /* 2990b074b7bbSAlexander V. Chernikov * Stage 2: allocate table configs for every non-existent table 2991b074b7bbSAlexander V. Chernikov */ 2992b074b7bbSAlexander V. Chernikov 29933a845e10SAlexander V. Chernikov if ((ci->flags & IPFW_RCF_TABLES) != 0) { 29949490a627SAlexander V. Chernikov for (p = pidx_first; p < pidx_last; p++) { 2995b074b7bbSAlexander V. Chernikov if (p->new == 0) 2996b074b7bbSAlexander V. Chernikov continue; 2997b074b7bbSAlexander V. Chernikov 2998b074b7bbSAlexander V. Chernikov ti.uidx = p->uidx; 2999b074b7bbSAlexander V. Chernikov ti.type = p->type; 30009f7d47b0SAlexander V. Chernikov ti.atype = 0; 3001b074b7bbSAlexander V. Chernikov 30029490a627SAlexander V. Chernikov ta = find_table_algo(CHAIN_TO_TCFG(chain), &ti, NULL); 30039f7d47b0SAlexander V. Chernikov if (ta == NULL) { 30049f7d47b0SAlexander V. Chernikov error = ENOTSUP; 30059f7d47b0SAlexander V. Chernikov goto free; 30069f7d47b0SAlexander V. Chernikov } 3007914bffb6SAlexander V. Chernikov tc = alloc_table_config(chain, &ti, ta, NULL, 0, 300868394ec8SAlexander V. Chernikov IPFW_VTYPE_U32); 3009b074b7bbSAlexander V. Chernikov 3010b074b7bbSAlexander V. Chernikov if (tc == NULL) { 3011b074b7bbSAlexander V. Chernikov error = ENOMEM; 3012b074b7bbSAlexander V. Chernikov goto free; 3013b074b7bbSAlexander V. Chernikov } 3014b074b7bbSAlexander V. Chernikov 3015b074b7bbSAlexander V. Chernikov tc->no.kidx = p->kidx; 3016b074b7bbSAlexander V. Chernikov tc->no.refcnt = 1; 3017b074b7bbSAlexander V. Chernikov 3018b074b7bbSAlexander V. Chernikov /* Add to list */ 3019b074b7bbSAlexander V. Chernikov TAILQ_INSERT_TAIL(&nh, &tc->no, nn_next); 3020b074b7bbSAlexander V. Chernikov } 3021b074b7bbSAlexander V. Chernikov 3022b074b7bbSAlexander V. Chernikov /* 3023b074b7bbSAlexander V. Chernikov * Stage 2.1: Check if we're going to create 2 tables 3024b074b7bbSAlexander V. Chernikov * with the same name, but different table types. 3025b074b7bbSAlexander V. Chernikov */ 3026b074b7bbSAlexander V. Chernikov TAILQ_FOREACH(no, &nh, nn_next) { 3027b074b7bbSAlexander V. Chernikov TAILQ_FOREACH(no_tmp, &nh, nn_next) { 30289490a627SAlexander V. Chernikov if (ipfw_objhash_same_name(ni, no, no_tmp) == 0) 3029b074b7bbSAlexander V. Chernikov continue; 3030b074b7bbSAlexander V. Chernikov if (no->type != no_tmp->type) { 3031b074b7bbSAlexander V. Chernikov error = EINVAL; 3032b074b7bbSAlexander V. Chernikov goto free; 3033b074b7bbSAlexander V. Chernikov } 3034b074b7bbSAlexander V. Chernikov } 3035b074b7bbSAlexander V. Chernikov } 30369f7d47b0SAlexander V. Chernikov } 3037b074b7bbSAlexander V. Chernikov 30389f7d47b0SAlexander V. Chernikov IPFW_UH_WLOCK(chain); 30399f7d47b0SAlexander V. Chernikov 30403a845e10SAlexander V. Chernikov if ((ci->flags & IPFW_RCF_TABLES) != 0) { 3041b074b7bbSAlexander V. Chernikov /* 3042b074b7bbSAlexander V. Chernikov * Stage 3: link & reference new table configs 3043b074b7bbSAlexander V. Chernikov */ 3044b074b7bbSAlexander V. Chernikov 3045b074b7bbSAlexander V. Chernikov 3046b074b7bbSAlexander V. Chernikov /* 3047b074b7bbSAlexander V. Chernikov * Step 3.1: Check if some tables we need to create have been 3048b074b7bbSAlexander V. Chernikov * already created with different table type. 3049b074b7bbSAlexander V. Chernikov */ 3050b074b7bbSAlexander V. Chernikov 3051b074b7bbSAlexander V. Chernikov error = 0; 3052b074b7bbSAlexander V. Chernikov TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) { 3053b074b7bbSAlexander V. Chernikov no_n = ipfw_objhash_lookup_name(ni, no->set, no->name); 3054b074b7bbSAlexander V. Chernikov if (no_n == NULL) 3055b074b7bbSAlexander V. Chernikov continue; 3056b074b7bbSAlexander V. Chernikov 3057b074b7bbSAlexander V. Chernikov if (no_n->type != no->type) { 3058b074b7bbSAlexander V. Chernikov error = EINVAL; 3059b074b7bbSAlexander V. Chernikov break; 3060b074b7bbSAlexander V. Chernikov } 3061b074b7bbSAlexander V. Chernikov 3062b074b7bbSAlexander V. Chernikov } 3063b074b7bbSAlexander V. Chernikov 3064b074b7bbSAlexander V. Chernikov if (error != 0) { 3065b074b7bbSAlexander V. Chernikov /* 3066b074b7bbSAlexander V. Chernikov * Someone has allocated table with different table type. 3067b074b7bbSAlexander V. Chernikov * We have to rollback everything. 3068b074b7bbSAlexander V. Chernikov */ 3069b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 3070b074b7bbSAlexander V. Chernikov goto free; 3071b074b7bbSAlexander V. Chernikov } 3072b074b7bbSAlexander V. Chernikov 3073b074b7bbSAlexander V. Chernikov /* 30749f7d47b0SAlexander V. Chernikov * Attach new tables. 30759f7d47b0SAlexander V. Chernikov * We need to set table pointers for each new table, 3076b074b7bbSAlexander V. Chernikov * so we have to acquire main WLOCK. 3077b074b7bbSAlexander V. Chernikov */ 3078b074b7bbSAlexander V. Chernikov IPFW_WLOCK(chain); 3079b074b7bbSAlexander V. Chernikov TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) { 3080b074b7bbSAlexander V. Chernikov no_n = ipfw_objhash_lookup_name(ni, no->set, no->name); 3081b074b7bbSAlexander V. Chernikov 30829490a627SAlexander V. Chernikov if (no_n == NULL) { 30839490a627SAlexander V. Chernikov /* New table. Attach to runtime hash */ 30849490a627SAlexander V. Chernikov TAILQ_REMOVE(&nh, no, nn_next); 30859490a627SAlexander V. Chernikov link_table(chain, (struct table_config *)no); 3086b074b7bbSAlexander V. Chernikov continue; 3087b074b7bbSAlexander V. Chernikov } 3088b074b7bbSAlexander V. Chernikov 30899490a627SAlexander V. Chernikov /* 30909490a627SAlexander V. Chernikov * Newly-allocated table with the same type. 30919490a627SAlexander V. Chernikov * Reference it and update out @pidx array 30929490a627SAlexander V. Chernikov * rewrite info. 30939490a627SAlexander V. Chernikov */ 30949490a627SAlexander V. Chernikov no_n->refcnt++; 30959490a627SAlexander V. Chernikov /* Keep oib array in sync: update kidx */ 30969490a627SAlexander V. Chernikov for (p = pidx_first; p < pidx_last; p++) { 30979490a627SAlexander V. Chernikov if (p->kidx != no->kidx) 30989490a627SAlexander V. Chernikov continue; 30999490a627SAlexander V. Chernikov /* Update kidx */ 31009490a627SAlexander V. Chernikov p->kidx = no_n->kidx; 31019490a627SAlexander V. Chernikov break; 31029490a627SAlexander V. Chernikov } 3103b074b7bbSAlexander V. Chernikov } 3104b074b7bbSAlexander V. Chernikov IPFW_WUNLOCK(chain); 31059f7d47b0SAlexander V. Chernikov } 3106b074b7bbSAlexander V. Chernikov 3107b074b7bbSAlexander V. Chernikov /* Perform rule rewrite */ 3108b074b7bbSAlexander V. Chernikov l = ci->krule->cmd_len; 3109b074b7bbSAlexander V. Chernikov cmd = ci->krule->cmd; 3110b074b7bbSAlexander V. Chernikov cmdlen = 0; 31119490a627SAlexander V. Chernikov p = pidx_first; 3112b074b7bbSAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 3113b074b7bbSAlexander V. Chernikov cmdlen = F_LEN(cmd); 3114b074b7bbSAlexander V. Chernikov 3115b074b7bbSAlexander V. Chernikov if (classify_table_opcode(cmd, &uidx, &type) != 0) 3116b074b7bbSAlexander V. Chernikov continue; 31179490a627SAlexander V. Chernikov update_table_opcode(cmd, p->kidx); 31189490a627SAlexander V. Chernikov p++; 3119b074b7bbSAlexander V. Chernikov } 3120b074b7bbSAlexander V. Chernikov 3121b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 3122b074b7bbSAlexander V. Chernikov 3123b074b7bbSAlexander V. Chernikov error = 0; 3124b074b7bbSAlexander V. Chernikov 3125b074b7bbSAlexander V. Chernikov /* 3126b074b7bbSAlexander V. Chernikov * Stage 4: free resources 3127b074b7bbSAlexander V. Chernikov */ 3128b074b7bbSAlexander V. Chernikov free: 31299490a627SAlexander V. Chernikov if (!TAILQ_EMPTY(&nh)) { 31309490a627SAlexander V. Chernikov /* Free indexes first */ 31319490a627SAlexander V. Chernikov IPFW_UH_WLOCK(chain); 31329490a627SAlexander V. Chernikov TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) { 3133ac35ff17SAlexander V. Chernikov ipfw_objhash_free_idx(ni, no->kidx); 31349490a627SAlexander V. Chernikov } 31359490a627SAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 31369490a627SAlexander V. Chernikov /* Free configs */ 3137b074b7bbSAlexander V. Chernikov TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) 3138b074b7bbSAlexander V. Chernikov free_table_config(ni, tc); 31399490a627SAlexander V. Chernikov } 3140b074b7bbSAlexander V. Chernikov 31419490a627SAlexander V. Chernikov if (pidx_first != ci->obuf) 31429490a627SAlexander V. Chernikov free(pidx_first, M_IPFW); 3143b074b7bbSAlexander V. Chernikov 3144b074b7bbSAlexander V. Chernikov return (error); 3145b074b7bbSAlexander V. Chernikov } 3146b074b7bbSAlexander V. Chernikov 3147b074b7bbSAlexander V. Chernikov /* 3148b074b7bbSAlexander V. Chernikov * Remove references from every table used in @rule. 3149b074b7bbSAlexander V. Chernikov */ 3150b074b7bbSAlexander V. Chernikov void 3151b074b7bbSAlexander V. Chernikov ipfw_unbind_table_rule(struct ip_fw_chain *chain, struct ip_fw *rule) 3152b074b7bbSAlexander V. Chernikov { 3153b074b7bbSAlexander V. Chernikov int cmdlen, l; 3154b074b7bbSAlexander V. Chernikov ipfw_insn *cmd; 3155b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 3156b074b7bbSAlexander V. Chernikov struct named_object *no; 3157b074b7bbSAlexander V. Chernikov uint16_t kidx; 3158b074b7bbSAlexander V. Chernikov uint8_t type; 3159b074b7bbSAlexander V. Chernikov 3160030b184fSAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(chain); 3161b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(chain); 3162b074b7bbSAlexander V. Chernikov 3163b074b7bbSAlexander V. Chernikov l = rule->cmd_len; 3164b074b7bbSAlexander V. Chernikov cmd = rule->cmd; 3165b074b7bbSAlexander V. Chernikov cmdlen = 0; 3166b074b7bbSAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 3167b074b7bbSAlexander V. Chernikov cmdlen = F_LEN(cmd); 3168b074b7bbSAlexander V. Chernikov 3169b074b7bbSAlexander V. Chernikov if (classify_table_opcode(cmd, &kidx, &type) != 0) 3170b074b7bbSAlexander V. Chernikov continue; 3171b074b7bbSAlexander V. Chernikov 3172ac35ff17SAlexander V. Chernikov no = ipfw_objhash_lookup_kidx(ni, kidx); 3173b074b7bbSAlexander V. Chernikov 3174b074b7bbSAlexander V. Chernikov KASSERT(no != NULL, ("table id %d not found", kidx)); 3175b074b7bbSAlexander V. Chernikov KASSERT(no->type == type, ("wrong type %d (%d) for table id %d", 3176b074b7bbSAlexander V. Chernikov no->type, type, kidx)); 3177b074b7bbSAlexander V. Chernikov KASSERT(no->refcnt > 0, ("refcount for table %d is %d", 3178b074b7bbSAlexander V. Chernikov kidx, no->refcnt)); 3179b074b7bbSAlexander V. Chernikov 3180b074b7bbSAlexander V. Chernikov no->refcnt--; 3181b074b7bbSAlexander V. Chernikov } 3182b074b7bbSAlexander V. Chernikov } 3183b074b7bbSAlexander V. Chernikov 31843b3a8eb9SGleb Smirnoff /* end of file */ 3185