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 */ 79adf3b2b9SAlexander V. Chernikov uint8_t spare0; 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 2134c0c07a5SAlexander V. Chernikov /* Try to exit early on limit hit */ 2143a845e10SAlexander V. Chernikov if ((error = check_table_limit(tc, tei)) != 0 && count == 1) { 2154c0c07a5SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 2164c0c07a5SAlexander V. Chernikov return (EFBIG); 2174c0c07a5SAlexander V. Chernikov } 2184c0c07a5SAlexander V. Chernikov 2199f7d47b0SAlexander V. Chernikov /* Reference and unlock */ 2209f7d47b0SAlexander V. Chernikov tc->no.refcnt++; 2219f7d47b0SAlexander V. Chernikov ta = tc->ta; 2229f7d47b0SAlexander V. Chernikov } 2239f7d47b0SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 2243b3a8eb9SGleb Smirnoff 225ac35ff17SAlexander V. Chernikov if (tc == NULL) { 226db785d31SAlexander V. Chernikov /* Compability mode: create new table for old clients */ 227db785d31SAlexander V. Chernikov if ((tei->flags & TEI_FLAGS_COMPAT) == 0) 228db785d31SAlexander V. Chernikov return (ESRCH); 2293b3a8eb9SGleb Smirnoff 230b6ee846eSAlexander V. Chernikov xi = malloc(sizeof(ipfw_xtable_info), M_TEMP, M_WAITOK|M_ZERO); 231b6ee846eSAlexander V. Chernikov xi->vtype = IPFW_VTYPE_U32; 2324c0c07a5SAlexander V. Chernikov 233b6ee846eSAlexander V. Chernikov error = create_table_internal(ch, ti, NULL, xi); 234b6ee846eSAlexander V. Chernikov free(xi, M_TEMP); 235db785d31SAlexander V. Chernikov 236db785d31SAlexander V. Chernikov if (error != 0) 237db785d31SAlexander V. Chernikov return (error); 238db785d31SAlexander V. Chernikov 239db785d31SAlexander V. Chernikov /* Let's try to find & reference another time */ 240db785d31SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 241db785d31SAlexander V. Chernikov if ((tc = find_table(ni, ti)) == NULL) { 242db785d31SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 243db785d31SAlexander V. Chernikov return (EINVAL); 244db785d31SAlexander V. Chernikov } 245db785d31SAlexander V. Chernikov 246db785d31SAlexander V. Chernikov if (tc->no.type != ti->type) { 247db785d31SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 248db785d31SAlexander V. Chernikov return (EINVAL); 249db785d31SAlexander V. Chernikov } 250db785d31SAlexander V. Chernikov 251db785d31SAlexander V. Chernikov /* Reference and unlock */ 252db785d31SAlexander V. Chernikov tc->no.refcnt++; 253db785d31SAlexander V. Chernikov ta = tc->ta; 254db785d31SAlexander V. Chernikov 255db785d31SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 256db785d31SAlexander V. Chernikov } 257db785d31SAlexander V. Chernikov 2589f7d47b0SAlexander V. Chernikov /* Prepare record (allocate memory) */ 2593a845e10SAlexander V. Chernikov ta_buf_sz = ta->ta_buf_size; 2603a845e10SAlexander V. Chernikov rollback = 0; 2613a845e10SAlexander V. Chernikov if (count == 1) { 2629f7d47b0SAlexander V. Chernikov memset(&ta_buf, 0, sizeof(ta_buf)); 2633a845e10SAlexander V. Chernikov ta_buf_m = ta_buf; 2643a845e10SAlexander V. Chernikov } else { 2653a845e10SAlexander V. Chernikov 2663a845e10SAlexander V. Chernikov /* 2673a845e10SAlexander V. Chernikov * Multiple adds, allocate larger buffer 2683a845e10SAlexander V. Chernikov * sufficient to hold both ADD state 2693a845e10SAlexander V. Chernikov * and DELETE state (this may be needed 2703a845e10SAlexander V. Chernikov * if we need to rollback all changes) 2713a845e10SAlexander V. Chernikov */ 2723a845e10SAlexander V. Chernikov ta_buf_m = malloc(2 * count * ta_buf_sz, M_TEMP, 2733a845e10SAlexander V. Chernikov M_WAITOK | M_ZERO); 2743a845e10SAlexander V. Chernikov } 2753a845e10SAlexander V. Chernikov v = ta_buf_m; 2763a845e10SAlexander V. Chernikov for (i = 0; i < count; i++, v += ta_buf_sz) { 2773a845e10SAlexander V. Chernikov error = ta->prepare_add(ch, &tei[i], v); 2783a845e10SAlexander V. Chernikov 2793a845e10SAlexander V. Chernikov /* 2803a845e10SAlexander V. Chernikov * Some syntax error (incorrect mask, or address, or 2813a845e10SAlexander V. Chernikov * anything). Return error regardless of atomicity 2823a845e10SAlexander V. Chernikov * settings. 2833a845e10SAlexander V. Chernikov */ 284db785d31SAlexander V. Chernikov if (error != 0) 2853a845e10SAlexander V. Chernikov goto cleanup; 2863a845e10SAlexander V. Chernikov } 2873b3a8eb9SGleb Smirnoff 288b074b7bbSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 2893b3a8eb9SGleb Smirnoff 290b6ee846eSAlexander V. Chernikov /* 291b6ee846eSAlexander V. Chernikov * Ensure we are able to add all entries without additional 292b6ee846eSAlexander V. Chernikov * memory allocations. May release/reacquire UH_WLOCK. 293b6ee846eSAlexander V. Chernikov */ 294b6ee846eSAlexander V. Chernikov kidx = tc->no.kidx; 295b6ee846eSAlexander V. Chernikov error = check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), count); 296b6ee846eSAlexander V. Chernikov if (error != 0) { 297b6ee846eSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 2983a845e10SAlexander V. Chernikov goto cleanup; 299b6ee846eSAlexander V. Chernikov } 300b6ee846eSAlexander V. Chernikov 301b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 3023b3a8eb9SGleb Smirnoff 3039f7d47b0SAlexander V. Chernikov /* Drop reference we've used in first search */ 3049f7d47b0SAlexander V. Chernikov tc->no.refcnt--; 3053a845e10SAlexander V. Chernikov /* We've got valid table in @tc. Let's try to add data */ 3069f7d47b0SAlexander V. Chernikov kidx = tc->no.kidx; 3079f7d47b0SAlexander V. Chernikov ta = tc->ta; 3083a845e10SAlexander V. Chernikov numadd = 0; 3093a845e10SAlexander V. Chernikov first_error = 0; 3109f7d47b0SAlexander V. Chernikov 311b074b7bbSAlexander V. Chernikov IPFW_WLOCK(ch); 3123a845e10SAlexander V. Chernikov 3133a845e10SAlexander V. Chernikov v = ta_buf_m; 3143a845e10SAlexander V. Chernikov for (i = 0; i < count; i++, v += ta_buf_sz) { 3153a845e10SAlexander V. Chernikov ptei = &tei[i]; 3163a845e10SAlexander V. Chernikov num = 0; 3173a845e10SAlexander V. Chernikov /* check limit before adding */ 3183a845e10SAlexander V. Chernikov if ((error = check_table_limit(tc, ptei)) == 0) { 3193a845e10SAlexander V. Chernikov error = ta->add(tc->astate, KIDX_TO_TI(ch, kidx), 3203a845e10SAlexander V. Chernikov ptei, v, &num); 3213a845e10SAlexander V. Chernikov /* Set status flag to inform userland */ 3223a845e10SAlexander V. Chernikov if (error == 0 && num != 0) 3233a845e10SAlexander V. Chernikov ptei->flags |= TEI_FLAGS_ADDED; 3243a845e10SAlexander V. Chernikov else if (error == ENOENT) 3253a845e10SAlexander V. Chernikov ptei->flags |= TEI_FLAGS_NOTFOUND; 3263a845e10SAlexander V. Chernikov else if (error == EEXIST) 3273a845e10SAlexander V. Chernikov ptei->flags |= TEI_FLAGS_EXISTS; 3283a845e10SAlexander V. Chernikov else 3293a845e10SAlexander V. Chernikov ptei->flags |= TEI_FLAGS_ERROR; 3303a845e10SAlexander V. Chernikov } 3313a845e10SAlexander V. Chernikov if (error == 0) { 3323a845e10SAlexander V. Chernikov /* Update number of records to ease limit checking */ 3333a845e10SAlexander V. Chernikov tc->count += num; 3343a845e10SAlexander V. Chernikov numadd += num; 3353a845e10SAlexander V. Chernikov continue; 3363a845e10SAlexander V. Chernikov } 3373a845e10SAlexander V. Chernikov 3383a845e10SAlexander V. Chernikov if (first_error == 0) 3393a845e10SAlexander V. Chernikov first_error = error; 3403a845e10SAlexander V. Chernikov 3413a845e10SAlexander V. Chernikov /* 3423a845e10SAlexander V. Chernikov * Some error have happened. Check our atomicity 3433a845e10SAlexander V. Chernikov * settings: continue if atomicity is not required, 3443a845e10SAlexander V. Chernikov * rollback changes otherwise. 3453a845e10SAlexander V. Chernikov */ 3463a845e10SAlexander V. Chernikov if ((flags & IPFW_CTF_ATOMIC) == 0) 3473a845e10SAlexander V. Chernikov continue; 3483a845e10SAlexander V. Chernikov 3493a845e10SAlexander V. Chernikov /* 3503a845e10SAlexander V. Chernikov * We need to rollback changes. 3513a845e10SAlexander V. Chernikov * This is tricky since some entries may have been 3523a845e10SAlexander V. Chernikov * updated, so we need to change their value back 3533a845e10SAlexander V. Chernikov * instead of deletion. 3543a845e10SAlexander V. Chernikov */ 3553a845e10SAlexander V. Chernikov rollback = 1; 3563a845e10SAlexander V. Chernikov v = ta_buf_m; 3573a845e10SAlexander V. Chernikov vv = v + count * ta_buf_sz; 3583a845e10SAlexander V. Chernikov for (j = 0; j < i; j++, v += ta_buf_sz, vv += ta_buf_sz) { 3593a845e10SAlexander V. Chernikov ptei = &tei[j]; 3603a845e10SAlexander V. Chernikov if ((ptei->flags & TEI_FLAGS_UPDATED) != 0) { 3613a845e10SAlexander V. Chernikov 3623a845e10SAlexander V. Chernikov /* 3633a845e10SAlexander V. Chernikov * We have old value stored by previous 3643a845e10SAlexander V. Chernikov * call in @ptei->value. Do add once again 3653a845e10SAlexander V. Chernikov * to restore it. 3663a845e10SAlexander V. Chernikov */ 3673a845e10SAlexander V. Chernikov rerror = ta->add(tc->astate, 3683a845e10SAlexander V. Chernikov KIDX_TO_TI(ch, kidx), ptei, v, &num); 3693a845e10SAlexander V. Chernikov KASSERT(rerror == 0, ("rollback UPDATE fail")); 3703a845e10SAlexander V. Chernikov KASSERT(num == 0, ("rollback UPDATE fail2")); 3713a845e10SAlexander V. Chernikov continue; 3723a845e10SAlexander V. Chernikov } 3733a845e10SAlexander V. Chernikov 3743a845e10SAlexander V. Chernikov rerror = ta->prepare_del(ch, ptei, vv); 3753a845e10SAlexander V. Chernikov KASSERT(rerror == 0, ("pre-rollback INSERT failed")); 3763a845e10SAlexander V. Chernikov rerror = ta->del(tc->astate, KIDX_TO_TI(ch, kidx), ptei, 3773a845e10SAlexander V. Chernikov vv, &num); 3783a845e10SAlexander V. Chernikov KASSERT(rerror == 0, ("rollback INSERT failed")); 3793a845e10SAlexander V. Chernikov tc->count -= num; 3803a845e10SAlexander V. Chernikov } 3813a845e10SAlexander V. Chernikov 3823a845e10SAlexander V. Chernikov break; 3833a845e10SAlexander V. Chernikov } 3843a845e10SAlexander V. Chernikov 3853b3a8eb9SGleb Smirnoff IPFW_WUNLOCK(ch); 3869f7d47b0SAlexander V. Chernikov 387b6ee846eSAlexander V. Chernikov /* Permit post-add algorithm grow/rehash. */ 3883a845e10SAlexander V. Chernikov if (numadd != 0) 3893a845e10SAlexander V. Chernikov check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), 0); 390db785d31SAlexander V. Chernikov 391b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 392b074b7bbSAlexander V. Chernikov 3933a845e10SAlexander V. Chernikov /* Return first error to user, if any */ 3943a845e10SAlexander V. Chernikov error = first_error; 3953a845e10SAlexander V. Chernikov 3963a845e10SAlexander V. Chernikov cleanup: 397ac35ff17SAlexander V. Chernikov /* Run cleaning callback anyway */ 3983a845e10SAlexander V. Chernikov v = ta_buf_m; 3993a845e10SAlexander V. Chernikov for (i = 0; i < count; i++, v += ta_buf_sz) 4003a845e10SAlexander V. Chernikov ta->flush_entry(ch, &tei[i], v); 4013a845e10SAlexander V. Chernikov 4023a845e10SAlexander V. Chernikov /* Clean up "deleted" state in case of rollback */ 4033a845e10SAlexander V. Chernikov if (rollback != 0) { 4043a845e10SAlexander V. Chernikov vv = ta_buf_m + count * ta_buf_sz; 4053a845e10SAlexander V. Chernikov for (i = 0; i < count; i++, vv += ta_buf_sz) 4063a845e10SAlexander V. Chernikov ta->flush_entry(ch, &tei[i], vv); 4073a845e10SAlexander V. Chernikov } 4083a845e10SAlexander V. Chernikov 4093a845e10SAlexander V. Chernikov if (ta_buf_m != ta_buf) 4103a845e10SAlexander V. Chernikov free(ta_buf_m, M_TEMP); 411b074b7bbSAlexander V. Chernikov 4129f7d47b0SAlexander V. Chernikov return (error); 4133b3a8eb9SGleb Smirnoff } 4143b3a8eb9SGleb Smirnoff 4153a845e10SAlexander V. Chernikov /* 4163a845e10SAlexander V. Chernikov * Deletes one or more entries in table @ti. 4173a845e10SAlexander V. Chernikov * 4183a845e10SAlexander V. Chernikov * Returns 0 on success. 4193a845e10SAlexander V. Chernikov */ 4203b3a8eb9SGleb Smirnoff int 4211832a7b3SAlexander V. Chernikov del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti, 4223a845e10SAlexander V. Chernikov struct tentry_info *tei, uint8_t flags, uint32_t count) 4233b3a8eb9SGleb Smirnoff { 424b074b7bbSAlexander V. Chernikov struct table_config *tc; 4259f7d47b0SAlexander V. Chernikov struct table_algo *ta; 426b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 4273a845e10SAlexander V. Chernikov struct tentry_info *ptei; 428b074b7bbSAlexander V. Chernikov uint16_t kidx; 4293a845e10SAlexander V. Chernikov int error, first_error, i; 4303a845e10SAlexander V. Chernikov uint32_t num, numdel; 431b6ee846eSAlexander V. Chernikov char ta_buf[TA_BUF_SZ]; 4323a845e10SAlexander V. Chernikov size_t ta_buf_sz; 4333a845e10SAlexander V. Chernikov caddr_t ta_buf_m, v; 4343b3a8eb9SGleb Smirnoff 4359f7d47b0SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 436b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 437b074b7bbSAlexander V. Chernikov if ((tc = find_table(ni, ti)) == NULL) { 4389f7d47b0SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 4393b3a8eb9SGleb Smirnoff return (ESRCH); 4403b3a8eb9SGleb Smirnoff } 4413b3a8eb9SGleb Smirnoff 442b074b7bbSAlexander V. Chernikov if (tc->no.type != ti->type) { 4439f7d47b0SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 4443b3a8eb9SGleb Smirnoff return (EINVAL); 4453b3a8eb9SGleb Smirnoff } 4469f7d47b0SAlexander V. Chernikov 447db785d31SAlexander V. Chernikov /* 448b6ee846eSAlexander V. Chernikov * Give a chance for algorithm to shrink. 449b6ee846eSAlexander V. Chernikov * May release/reacquire UH_WLOCK. 450db785d31SAlexander V. Chernikov */ 451b6ee846eSAlexander V. Chernikov kidx = tc->no.kidx; 452b6ee846eSAlexander V. Chernikov error = check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), 0); 453db785d31SAlexander V. Chernikov if (error != 0) { 454db785d31SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 455db785d31SAlexander V. Chernikov return (error); 456db785d31SAlexander V. Chernikov } 457db785d31SAlexander V. Chernikov 4583a845e10SAlexander V. Chernikov /* Reference and unlock */ 4593a845e10SAlexander V. Chernikov tc->no.refcnt++; 4603a845e10SAlexander V. Chernikov ta = tc->ta; 4613a845e10SAlexander V. Chernikov 4623a845e10SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 4633a845e10SAlexander V. Chernikov 4643a845e10SAlexander V. Chernikov /* Prepare record (allocate memory) */ 4653a845e10SAlexander V. Chernikov ta_buf_sz = ta->ta_buf_size; 4663a845e10SAlexander V. Chernikov if (count == 1) { 4673a845e10SAlexander V. Chernikov memset(&ta_buf, 0, sizeof(ta_buf)); 4683a845e10SAlexander V. Chernikov ta_buf_m = ta_buf; 4693a845e10SAlexander V. Chernikov } else { 4703a845e10SAlexander V. Chernikov 471db785d31SAlexander V. Chernikov /* 4723a845e10SAlexander V. Chernikov * Multiple deletes, allocate larger buffer 4733a845e10SAlexander V. Chernikov * sufficient to hold delete state. 474db785d31SAlexander V. Chernikov */ 4753a845e10SAlexander V. Chernikov ta_buf_m = malloc(count * ta_buf_sz, M_TEMP, 4763a845e10SAlexander V. Chernikov M_WAITOK | M_ZERO); 4773a845e10SAlexander V. Chernikov } 4783a845e10SAlexander V. Chernikov v = ta_buf_m; 4793a845e10SAlexander V. Chernikov for (i = 0; i < count; i++, v += ta_buf_sz) { 4803a845e10SAlexander V. Chernikov error = ta->prepare_del(ch, &tei[i], v); 4813a845e10SAlexander V. Chernikov 4823a845e10SAlexander V. Chernikov /* 4833a845e10SAlexander V. Chernikov * Some syntax error (incorrect mask, or address, or 4843a845e10SAlexander V. Chernikov * anything). Return error immediately. 4853a845e10SAlexander V. Chernikov */ 4863a845e10SAlexander V. Chernikov if (error != 0) 4873a845e10SAlexander V. Chernikov goto cleanup; 4889f7d47b0SAlexander V. Chernikov } 4899f7d47b0SAlexander V. Chernikov 4903a845e10SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 4913a845e10SAlexander V. Chernikov 4923a845e10SAlexander V. Chernikov /* Drop reference we've used in first search */ 4933a845e10SAlexander V. Chernikov tc->no.refcnt--; 4943a845e10SAlexander V. Chernikov 495b074b7bbSAlexander V. Chernikov kidx = tc->no.kidx; 4963a845e10SAlexander V. Chernikov numdel = 0; 4973a845e10SAlexander V. Chernikov first_error = 0; 498b074b7bbSAlexander V. Chernikov 499b074b7bbSAlexander V. Chernikov IPFW_WLOCK(ch); 5003a845e10SAlexander V. Chernikov v = ta_buf_m; 5013a845e10SAlexander V. Chernikov for (i = 0; i < count; i++, v += ta_buf_sz) { 5023a845e10SAlexander V. Chernikov ptei = &tei[i]; 5033a845e10SAlexander V. Chernikov num = 0; 5043a845e10SAlexander V. Chernikov error = ta->del(tc->astate, KIDX_TO_TI(ch, kidx), ptei, v, 5053a845e10SAlexander V. Chernikov &num); 5063a845e10SAlexander V. Chernikov /* Save state for userland */ 5073a845e10SAlexander V. Chernikov if (error == 0) 5083a845e10SAlexander V. Chernikov ptei->flags |= TEI_FLAGS_DELETED; 5093a845e10SAlexander V. Chernikov else if (error == ENOENT) 5103a845e10SAlexander V. Chernikov ptei->flags |= TEI_FLAGS_NOTFOUND; 5113a845e10SAlexander V. Chernikov else 5123a845e10SAlexander V. Chernikov ptei->flags |= TEI_FLAGS_ERROR; 5133a845e10SAlexander V. Chernikov if (error != 0 && first_error == 0) 5143a845e10SAlexander V. Chernikov first_error = error; 5153a845e10SAlexander V. Chernikov tc->count -= num; 5163a845e10SAlexander V. Chernikov numdel += num; 5173a845e10SAlexander V. Chernikov } 5183b3a8eb9SGleb Smirnoff IPFW_WUNLOCK(ch); 5193b3a8eb9SGleb Smirnoff 5203a845e10SAlexander V. Chernikov if (numdel != 0) { 521b6ee846eSAlexander V. Chernikov /* Run post-del hook to permit shrinking */ 522b6ee846eSAlexander V. Chernikov error = check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), 0); 523b6ee846eSAlexander V. Chernikov } 524b074b7bbSAlexander V. Chernikov 5259f7d47b0SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 5263b3a8eb9SGleb Smirnoff 5273a845e10SAlexander V. Chernikov /* Return first error to user, if any */ 5283a845e10SAlexander V. Chernikov error = first_error; 5293a845e10SAlexander V. Chernikov 5303a845e10SAlexander V. Chernikov cleanup: 5313a845e10SAlexander V. Chernikov /* Run cleaning callback anyway */ 5323a845e10SAlexander V. Chernikov v = ta_buf_m; 5333a845e10SAlexander V. Chernikov for (i = 0; i < count; i++, v += ta_buf_sz) 5343a845e10SAlexander V. Chernikov ta->flush_entry(ch, &tei[i], v); 5353a845e10SAlexander V. Chernikov 5363a845e10SAlexander V. Chernikov if (ta_buf_m != ta_buf) 5373a845e10SAlexander V. Chernikov free(ta_buf_m, M_TEMP); 538ac35ff17SAlexander V. Chernikov 539ac35ff17SAlexander V. Chernikov return (error); 540ac35ff17SAlexander V. Chernikov } 541ac35ff17SAlexander V. Chernikov 542db785d31SAlexander V. Chernikov /* 543b6ee846eSAlexander V. Chernikov * Ensure that table @tc has enough space to add @count entries without 544b6ee846eSAlexander V. Chernikov * need for reallocation. 545db785d31SAlexander V. Chernikov * 546db785d31SAlexander V. Chernikov * Callbacks order: 547b6ee846eSAlexander V. Chernikov * 0) has_space() (UH_WLOCK) - checks if @count items can be added w/o resize. 548b6ee846eSAlexander V. Chernikov * 549db785d31SAlexander V. Chernikov * 1) alloc_modify (no locks, M_WAITOK) - alloc new state based on @pflags. 550db785d31SAlexander V. Chernikov * 2) prepare_modifyt (UH_WLOCK) - copy old data into new storage 551db785d31SAlexander V. Chernikov * 3) modify (UH_WLOCK + WLOCK) - switch pointers 552b6ee846eSAlexander V. Chernikov * 4) flush_modify (UH_WLOCK) - free state, if needed 553b6ee846eSAlexander V. Chernikov * 554b6ee846eSAlexander V. Chernikov * Returns 0 on success. 555db785d31SAlexander V. Chernikov */ 556db785d31SAlexander V. Chernikov static int 557b6ee846eSAlexander V. Chernikov check_table_space(struct ip_fw_chain *ch, struct table_config *tc, 558b6ee846eSAlexander V. Chernikov struct table_info *ti, uint32_t count) 559db785d31SAlexander V. Chernikov { 560b6ee846eSAlexander V. Chernikov struct table_algo *ta; 561b6ee846eSAlexander V. Chernikov uint64_t pflags; 562b6ee846eSAlexander V. Chernikov char ta_buf[TA_BUF_SZ]; 563db785d31SAlexander V. Chernikov int error; 564db785d31SAlexander V. Chernikov 565b6ee846eSAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(ch); 566db785d31SAlexander V. Chernikov 567b6ee846eSAlexander V. Chernikov error = 0; 568b6ee846eSAlexander V. Chernikov ta = tc->ta; 569b6ee846eSAlexander V. Chernikov /* Acquire reference not to loose @tc between locks/unlocks */ 570b6ee846eSAlexander V. Chernikov tc->no.refcnt++; 571db785d31SAlexander V. Chernikov 572db785d31SAlexander V. Chernikov /* 573b6ee846eSAlexander V. Chernikov * TODO: think about avoiding race between large add/large delete 574b6ee846eSAlexander V. Chernikov * operation on algorithm which implements shrinking along with 575b6ee846eSAlexander V. Chernikov * growing. 576db785d31SAlexander V. Chernikov */ 577b6ee846eSAlexander V. Chernikov while (true) { 578b6ee846eSAlexander V. Chernikov pflags = 0; 579b6ee846eSAlexander V. Chernikov if (ta->has_space(tc->astate, ti, count, &pflags) != 0) { 580b6ee846eSAlexander V. Chernikov tc->no.refcnt--; 581b6ee846eSAlexander V. Chernikov return (0); 582b6ee846eSAlexander V. Chernikov } 583db785d31SAlexander V. Chernikov 584b6ee846eSAlexander V. Chernikov /* We have to shrink/grow table */ 585b6ee846eSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 586b6ee846eSAlexander V. Chernikov memset(&ta_buf, 0, sizeof(ta_buf)); 587b6ee846eSAlexander V. Chernikov 588b6ee846eSAlexander V. Chernikov if ((error = ta->prepare_mod(ta_buf, &pflags)) != 0) { 589b6ee846eSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 590b6ee846eSAlexander V. Chernikov break; 591b6ee846eSAlexander V. Chernikov } 592b6ee846eSAlexander V. Chernikov 593b6ee846eSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 594b6ee846eSAlexander V. Chernikov 595b6ee846eSAlexander V. Chernikov /* Check if we still need to alter table */ 596b6ee846eSAlexander V. Chernikov ti = KIDX_TO_TI(ch, tc->no.kidx); 597b6ee846eSAlexander V. Chernikov if (ta->has_space(tc->astate, ti, count, &pflags) != 0) { 598b6ee846eSAlexander V. Chernikov 599b6ee846eSAlexander V. Chernikov /* 600b6ee846eSAlexander V. Chernikov * Other threads has already performed resize. 601b6ee846eSAlexander V. Chernikov * Flush our state and return/ 602b6ee846eSAlexander V. Chernikov */ 603b6ee846eSAlexander V. Chernikov ta->flush_mod(ta_buf); 604b6ee846eSAlexander V. Chernikov break; 605b6ee846eSAlexander V. Chernikov } 606b6ee846eSAlexander V. Chernikov 607b6ee846eSAlexander V. Chernikov error = ta->fill_mod(tc->astate, ti, ta_buf, &pflags); 608b6ee846eSAlexander V. Chernikov if (error == 0) { 609db785d31SAlexander V. Chernikov /* Do actual modification */ 610db785d31SAlexander V. Chernikov IPFW_WLOCK(ch); 61168394ec8SAlexander V. Chernikov ta->modify(tc->astate, ti, ta_buf, pflags); 612db785d31SAlexander V. Chernikov IPFW_WUNLOCK(ch); 613db785d31SAlexander V. Chernikov } 614db785d31SAlexander V. Chernikov 615b6ee846eSAlexander V. Chernikov /* Anyway, flush data and retry */ 616db785d31SAlexander V. Chernikov ta->flush_mod(ta_buf); 617b6ee846eSAlexander V. Chernikov } 618db785d31SAlexander V. Chernikov 619b6ee846eSAlexander V. Chernikov tc->no.refcnt--; 620db785d31SAlexander V. Chernikov return (error); 621db785d31SAlexander V. Chernikov } 622db785d31SAlexander V. Chernikov 6233a845e10SAlexander V. Chernikov /* 6243a845e10SAlexander V. Chernikov * Selects appropriate table operation handler 6253a845e10SAlexander V. Chernikov * depending on opcode version. 6263a845e10SAlexander V. Chernikov */ 627ac35ff17SAlexander V. Chernikov int 628db785d31SAlexander V. Chernikov ipfw_manage_table_ent(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 629ac35ff17SAlexander V. Chernikov struct sockopt_data *sd) 630ac35ff17SAlexander V. Chernikov { 631ac35ff17SAlexander V. Chernikov int error; 632ac35ff17SAlexander V. Chernikov 633ac35ff17SAlexander V. Chernikov switch (op3->version) { 634ac35ff17SAlexander V. Chernikov case 0: 635db785d31SAlexander V. Chernikov error = ipfw_manage_table_ent_v0(ch, op3, sd); 636ac35ff17SAlexander V. Chernikov break; 637ac35ff17SAlexander V. Chernikov case 1: 638db785d31SAlexander V. Chernikov error = ipfw_manage_table_ent_v1(ch, op3, sd); 639ac35ff17SAlexander V. Chernikov break; 640ac35ff17SAlexander V. Chernikov default: 641ac35ff17SAlexander V. Chernikov error = ENOTSUP; 642ac35ff17SAlexander V. Chernikov } 643ac35ff17SAlexander V. Chernikov 644ac35ff17SAlexander V. Chernikov return (error); 645ac35ff17SAlexander V. Chernikov } 646ac35ff17SAlexander V. Chernikov 647ac35ff17SAlexander V. Chernikov /* 648ac35ff17SAlexander V. Chernikov * Adds or deletes record in table. 649ac35ff17SAlexander V. Chernikov * Data layout (v0): 650ac35ff17SAlexander V. Chernikov * Request: [ ip_fw3_opheader ipfw_table_xentry ] 651ac35ff17SAlexander V. Chernikov * 652ac35ff17SAlexander V. Chernikov * Returns 0 on success 653ac35ff17SAlexander V. Chernikov */ 654ac35ff17SAlexander V. Chernikov static int 655db785d31SAlexander V. Chernikov ipfw_manage_table_ent_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 656ac35ff17SAlexander V. Chernikov struct sockopt_data *sd) 657ac35ff17SAlexander V. Chernikov { 658ac35ff17SAlexander V. Chernikov ipfw_table_xentry *xent; 659ac35ff17SAlexander V. Chernikov struct tentry_info tei; 660ac35ff17SAlexander V. Chernikov struct tid_info ti; 661ac35ff17SAlexander V. Chernikov int error, hdrlen, read; 662ac35ff17SAlexander V. Chernikov 663ac35ff17SAlexander V. Chernikov hdrlen = offsetof(ipfw_table_xentry, k); 664ac35ff17SAlexander V. Chernikov 665ac35ff17SAlexander V. Chernikov /* Check minimum header size */ 666ac35ff17SAlexander V. Chernikov if (sd->valsize < (sizeof(*op3) + hdrlen)) 667ac35ff17SAlexander V. Chernikov return (EINVAL); 668ac35ff17SAlexander V. Chernikov 669ac35ff17SAlexander V. Chernikov read = sizeof(ip_fw3_opheader); 670ac35ff17SAlexander V. Chernikov 671ac35ff17SAlexander V. Chernikov /* Check if xentry len field is valid */ 672ac35ff17SAlexander V. Chernikov xent = (ipfw_table_xentry *)(op3 + 1); 673ac35ff17SAlexander V. Chernikov if (xent->len < hdrlen || xent->len + read > sd->valsize) 674ac35ff17SAlexander V. Chernikov return (EINVAL); 675ac35ff17SAlexander V. Chernikov 676ac35ff17SAlexander V. Chernikov memset(&tei, 0, sizeof(tei)); 677ac35ff17SAlexander V. Chernikov tei.paddr = &xent->k; 678ac35ff17SAlexander V. Chernikov tei.masklen = xent->masklen; 679ac35ff17SAlexander V. Chernikov tei.value = xent->value; 680ac35ff17SAlexander V. Chernikov /* Old requests compability */ 681db785d31SAlexander V. Chernikov tei.flags = TEI_FLAGS_COMPAT; 682ac35ff17SAlexander V. Chernikov if (xent->type == IPFW_TABLE_CIDR) { 683ac35ff17SAlexander V. Chernikov if (xent->len - hdrlen == sizeof(in_addr_t)) 684ac35ff17SAlexander V. Chernikov tei.subtype = AF_INET; 685ac35ff17SAlexander V. Chernikov else 686ac35ff17SAlexander V. Chernikov tei.subtype = AF_INET6; 687ac35ff17SAlexander V. Chernikov } 688ac35ff17SAlexander V. Chernikov 689ac35ff17SAlexander V. Chernikov memset(&ti, 0, sizeof(ti)); 690ac35ff17SAlexander V. Chernikov ti.uidx = xent->tbl; 691ac35ff17SAlexander V. Chernikov ti.type = xent->type; 692ac35ff17SAlexander V. Chernikov 693ac35ff17SAlexander V. Chernikov error = (op3->opcode == IP_FW_TABLE_XADD) ? 6943a845e10SAlexander V. Chernikov add_table_entry(ch, &ti, &tei, 0, 1) : 6953a845e10SAlexander V. Chernikov del_table_entry(ch, &ti, &tei, 0, 1); 696ac35ff17SAlexander V. Chernikov 697ac35ff17SAlexander V. Chernikov return (error); 698ac35ff17SAlexander V. Chernikov } 699ac35ff17SAlexander V. Chernikov 700ac35ff17SAlexander V. Chernikov /* 701ac35ff17SAlexander V. Chernikov * Adds or deletes record in table. 702ac35ff17SAlexander V. Chernikov * Data layout (v1)(current): 703db785d31SAlexander V. Chernikov * Request: [ ipfw_obj_header 704db785d31SAlexander V. Chernikov * ipfw_obj_ctlv(IPFW_TLV_TBLENT_LIST) [ ipfw_obj_tentry x N ] 705db785d31SAlexander V. Chernikov * ] 706ac35ff17SAlexander V. Chernikov * 707ac35ff17SAlexander V. Chernikov * Returns 0 on success 708ac35ff17SAlexander V. Chernikov */ 709ac35ff17SAlexander V. Chernikov static int 710db785d31SAlexander V. Chernikov ipfw_manage_table_ent_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 711ac35ff17SAlexander V. Chernikov struct sockopt_data *sd) 712ac35ff17SAlexander V. Chernikov { 7133a845e10SAlexander V. Chernikov ipfw_obj_tentry *tent, *ptent; 714db785d31SAlexander V. Chernikov ipfw_obj_ctlv *ctlv; 715ac35ff17SAlexander V. Chernikov ipfw_obj_header *oh; 7163a845e10SAlexander V. Chernikov struct tentry_info *ptei, tei, *tei_buf; 717ac35ff17SAlexander V. Chernikov struct tid_info ti; 7183a845e10SAlexander V. Chernikov int error, i, kidx, read; 719ac35ff17SAlexander V. Chernikov 720ac35ff17SAlexander V. Chernikov /* Check minimum header size */ 721db785d31SAlexander V. Chernikov if (sd->valsize < (sizeof(*oh) + sizeof(*ctlv))) 722ac35ff17SAlexander V. Chernikov return (EINVAL); 723ac35ff17SAlexander V. Chernikov 724ac35ff17SAlexander V. Chernikov /* Check if passed data is too long */ 725ac35ff17SAlexander V. Chernikov if (sd->valsize != sd->kavail) 726ac35ff17SAlexander V. Chernikov return (EINVAL); 727ac35ff17SAlexander V. Chernikov 728ac35ff17SAlexander V. Chernikov oh = (ipfw_obj_header *)sd->kbuf; 729ac35ff17SAlexander V. Chernikov 730ac35ff17SAlexander V. Chernikov /* Basic length checks for TLVs */ 731ac35ff17SAlexander V. Chernikov if (oh->ntlv.head.length != sizeof(oh->ntlv)) 732ac35ff17SAlexander V. Chernikov return (EINVAL); 733ac35ff17SAlexander V. Chernikov 734ac35ff17SAlexander V. Chernikov read = sizeof(*oh); 735ac35ff17SAlexander V. Chernikov 736db785d31SAlexander V. Chernikov ctlv = (ipfw_obj_ctlv *)(oh + 1); 737db785d31SAlexander V. Chernikov if (ctlv->head.length + read != sd->valsize) 738db785d31SAlexander V. Chernikov return (EINVAL); 739db785d31SAlexander V. Chernikov 740db785d31SAlexander V. Chernikov read += sizeof(*ctlv); 741db785d31SAlexander V. Chernikov tent = (ipfw_obj_tentry *)(ctlv + 1); 7423a845e10SAlexander V. Chernikov if (ctlv->count * sizeof(*tent) + read != sd->valsize) 743ac35ff17SAlexander V. Chernikov return (EINVAL); 744ac35ff17SAlexander V. Chernikov 7453a845e10SAlexander V. Chernikov if (ctlv->count == 0) 7463a845e10SAlexander V. Chernikov return (0); 747ac35ff17SAlexander V. Chernikov 7483a845e10SAlexander V. Chernikov /* 7493a845e10SAlexander V. Chernikov * Mark entire buffer as "read". 7503a845e10SAlexander V. Chernikov * This makes sopt api write it back 7513a845e10SAlexander V. Chernikov * after function return. 7523a845e10SAlexander V. Chernikov */ 7533a845e10SAlexander V. Chernikov ipfw_get_sopt_header(sd, sd->valsize); 7543a845e10SAlexander V. Chernikov 7553a845e10SAlexander V. Chernikov /* Perform basic checks for each entry */ 7563a845e10SAlexander V. Chernikov ptent = tent; 7573a845e10SAlexander V. Chernikov kidx = tent->idx; 7583a845e10SAlexander V. Chernikov for (i = 0; i < ctlv->count; i++, ptent++) { 7593a845e10SAlexander V. Chernikov if (ptent->head.length != sizeof(*ptent)) 7603a845e10SAlexander V. Chernikov return (EINVAL); 7613a845e10SAlexander V. Chernikov if (ptent->idx != kidx) 7623a845e10SAlexander V. Chernikov return (ENOTSUP); 7633a845e10SAlexander V. Chernikov } 7643a845e10SAlexander V. Chernikov 7653a845e10SAlexander V. Chernikov /* Convert data into kernel request objects */ 76681d3153dSAlexander V. Chernikov objheader_to_ti(oh, &ti); 767ac35ff17SAlexander V. Chernikov ti.type = oh->ntlv.type; 7683a845e10SAlexander V. Chernikov ti.uidx = kidx; 7693a845e10SAlexander V. Chernikov 7703a845e10SAlexander V. Chernikov /* Use on-stack buffer for single add/del */ 7713a845e10SAlexander V. Chernikov if (ctlv->count == 1) { 7723a845e10SAlexander V. Chernikov memset(&tei, 0, sizeof(tei)); 7733a845e10SAlexander V. Chernikov tei_buf = &tei; 7743a845e10SAlexander V. Chernikov } else 7753a845e10SAlexander V. Chernikov tei_buf = malloc(ctlv->count * sizeof(tei), M_TEMP, 7763a845e10SAlexander V. Chernikov M_WAITOK | M_ZERO); 7773a845e10SAlexander V. Chernikov 7783a845e10SAlexander V. Chernikov ptei = tei_buf; 7793a845e10SAlexander V. Chernikov ptent = tent; 7803a845e10SAlexander V. Chernikov for (i = 0; i < ctlv->count; i++, ptent++, ptei++) { 7813a845e10SAlexander V. Chernikov ptei->paddr = &ptent->k; 7823a845e10SAlexander V. Chernikov ptei->subtype = ptent->subtype; 7833a845e10SAlexander V. Chernikov ptei->masklen = ptent->masklen; 7843a845e10SAlexander V. Chernikov if (ptent->head.flags & IPFW_TF_UPDATE) 7853a845e10SAlexander V. Chernikov ptei->flags |= TEI_FLAGS_UPDATE; 7863a845e10SAlexander V. Chernikov ptei->value = ptent->value; 7873a845e10SAlexander V. Chernikov } 788ac35ff17SAlexander V. Chernikov 789ac35ff17SAlexander V. Chernikov error = (oh->opheader.opcode == IP_FW_TABLE_XADD) ? 7903a845e10SAlexander V. Chernikov add_table_entry(ch, &ti, tei_buf, ctlv->flags, ctlv->count) : 7913a845e10SAlexander V. Chernikov del_table_entry(ch, &ti, tei_buf, ctlv->flags, ctlv->count); 7923a845e10SAlexander V. Chernikov 7933a845e10SAlexander V. Chernikov /* Translate result back to userland */ 7943a845e10SAlexander V. Chernikov ptei = tei_buf; 7953a845e10SAlexander V. Chernikov ptent = tent; 7963a845e10SAlexander V. Chernikov for (i = 0; i < ctlv->count; i++, ptent++, ptei++) { 7973a845e10SAlexander V. Chernikov if (ptei->flags & TEI_FLAGS_ADDED) 7983a845e10SAlexander V. Chernikov ptent->result = IPFW_TR_ADDED; 7993a845e10SAlexander V. Chernikov else if (ptei->flags & TEI_FLAGS_DELETED) 8003a845e10SAlexander V. Chernikov ptent->result = IPFW_TR_DELETED; 8013a845e10SAlexander V. Chernikov else if (ptei->flags & TEI_FLAGS_UPDATED) 8023a845e10SAlexander V. Chernikov ptent->result = IPFW_TR_UPDATED; 8033a845e10SAlexander V. Chernikov else if (ptei->flags & TEI_FLAGS_LIMIT) 8043a845e10SAlexander V. Chernikov ptent->result = IPFW_TR_LIMIT; 8053a845e10SAlexander V. Chernikov else if (ptei->flags & TEI_FLAGS_ERROR) 8063a845e10SAlexander V. Chernikov ptent->result = IPFW_TR_ERROR; 8073a845e10SAlexander V. Chernikov else if (ptei->flags & TEI_FLAGS_NOTFOUND) 8083a845e10SAlexander V. Chernikov ptent->result = IPFW_TR_NOTFOUND; 8093a845e10SAlexander V. Chernikov else if (ptei->flags & TEI_FLAGS_EXISTS) 8103a845e10SAlexander V. Chernikov ptent->result = IPFW_TR_EXISTS; 8113a845e10SAlexander V. Chernikov } 8123a845e10SAlexander V. Chernikov 8133a845e10SAlexander V. Chernikov if (tei_buf != &tei) 8143a845e10SAlexander V. Chernikov free(tei_buf, M_TEMP); 815ac35ff17SAlexander V. Chernikov 816ac35ff17SAlexander V. Chernikov return (error); 817ac35ff17SAlexander V. Chernikov } 818ac35ff17SAlexander V. Chernikov 81981d3153dSAlexander V. Chernikov /* 82081d3153dSAlexander V. Chernikov * Looks up an entry in given table. 82181d3153dSAlexander V. Chernikov * Data layout (v0)(current): 82281d3153dSAlexander V. Chernikov * Request: [ ipfw_obj_header ipfw_obj_tentry ] 82381d3153dSAlexander V. Chernikov * Reply: [ ipfw_obj_header ipfw_obj_tentry ] 82481d3153dSAlexander V. Chernikov * 82581d3153dSAlexander V. Chernikov * Returns 0 on success 82681d3153dSAlexander V. Chernikov */ 82781d3153dSAlexander V. Chernikov int 82881d3153dSAlexander V. Chernikov ipfw_find_table_entry(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 82981d3153dSAlexander V. Chernikov struct sockopt_data *sd) 83081d3153dSAlexander V. Chernikov { 83181d3153dSAlexander V. Chernikov ipfw_obj_tentry *tent; 83281d3153dSAlexander V. Chernikov ipfw_obj_header *oh; 83381d3153dSAlexander V. Chernikov struct tid_info ti; 83481d3153dSAlexander V. Chernikov struct table_config *tc; 83581d3153dSAlexander V. Chernikov struct table_algo *ta; 83681d3153dSAlexander V. Chernikov struct table_info *kti; 83781d3153dSAlexander V. Chernikov struct namedobj_instance *ni; 838914bffb6SAlexander V. Chernikov int error; 83981d3153dSAlexander V. Chernikov size_t sz; 84081d3153dSAlexander V. Chernikov 84181d3153dSAlexander V. Chernikov /* Check minimum header size */ 84281d3153dSAlexander V. Chernikov sz = sizeof(*oh) + sizeof(*tent); 84381d3153dSAlexander V. Chernikov if (sd->valsize != sz) 84481d3153dSAlexander V. Chernikov return (EINVAL); 84581d3153dSAlexander V. Chernikov 84681d3153dSAlexander V. Chernikov oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 84781d3153dSAlexander V. Chernikov tent = (ipfw_obj_tentry *)(oh + 1); 84881d3153dSAlexander V. Chernikov 84981d3153dSAlexander V. Chernikov /* Basic length checks for TLVs */ 85081d3153dSAlexander V. Chernikov if (oh->ntlv.head.length != sizeof(oh->ntlv)) 85181d3153dSAlexander V. Chernikov return (EINVAL); 85281d3153dSAlexander V. Chernikov 85381d3153dSAlexander V. Chernikov objheader_to_ti(oh, &ti); 85481d3153dSAlexander V. Chernikov ti.type = oh->ntlv.type; 85581d3153dSAlexander V. Chernikov ti.uidx = tent->idx; 85681d3153dSAlexander V. Chernikov 85781d3153dSAlexander V. Chernikov IPFW_UH_RLOCK(ch); 85881d3153dSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 85981d3153dSAlexander V. Chernikov 86081d3153dSAlexander V. Chernikov /* 86181d3153dSAlexander V. Chernikov * Find existing table and check its type . 86281d3153dSAlexander V. Chernikov */ 86381d3153dSAlexander V. Chernikov ta = NULL; 86481d3153dSAlexander V. Chernikov if ((tc = find_table(ni, &ti)) == NULL) { 86581d3153dSAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 86681d3153dSAlexander V. Chernikov return (ESRCH); 86781d3153dSAlexander V. Chernikov } 86881d3153dSAlexander V. Chernikov 86981d3153dSAlexander V. Chernikov /* check table type */ 87081d3153dSAlexander V. Chernikov if (tc->no.type != ti.type) { 87181d3153dSAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 87281d3153dSAlexander V. Chernikov return (EINVAL); 87381d3153dSAlexander V. Chernikov } 87481d3153dSAlexander V. Chernikov 87581d3153dSAlexander V. Chernikov kti = KIDX_TO_TI(ch, tc->no.kidx); 87681d3153dSAlexander V. Chernikov ta = tc->ta; 87781d3153dSAlexander V. Chernikov 878914bffb6SAlexander V. Chernikov if (ta->find_tentry == NULL) 879914bffb6SAlexander V. Chernikov return (ENOTSUP); 880914bffb6SAlexander V. Chernikov 881914bffb6SAlexander V. Chernikov error = ta->find_tentry(tc->astate, kti, tent); 88281d3153dSAlexander V. Chernikov 88381d3153dSAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 88481d3153dSAlexander V. Chernikov 88581d3153dSAlexander V. Chernikov return (error); 88681d3153dSAlexander V. Chernikov } 88781d3153dSAlexander V. Chernikov 88846d52008SAlexander V. Chernikov /* 88946d52008SAlexander V. Chernikov * Flushes all entries or destroys given table. 89046d52008SAlexander V. Chernikov * Data layout (v0)(current): 89146d52008SAlexander V. Chernikov * Request: [ ipfw_obj_header ] 89246d52008SAlexander V. Chernikov * 89346d52008SAlexander V. Chernikov * Returns 0 on success 89446d52008SAlexander V. Chernikov */ 895ac35ff17SAlexander V. Chernikov int 896ac35ff17SAlexander V. Chernikov ipfw_flush_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 897ac35ff17SAlexander V. Chernikov struct sockopt_data *sd) 898ac35ff17SAlexander V. Chernikov { 899ac35ff17SAlexander V. Chernikov int error; 900ac35ff17SAlexander V. Chernikov struct _ipfw_obj_header *oh; 901ac35ff17SAlexander V. Chernikov struct tid_info ti; 902ac35ff17SAlexander V. Chernikov 903ac35ff17SAlexander V. Chernikov if (sd->valsize != sizeof(*oh)) 904ac35ff17SAlexander V. Chernikov return (EINVAL); 905ac35ff17SAlexander V. Chernikov 906ac35ff17SAlexander V. Chernikov oh = (struct _ipfw_obj_header *)op3; 907ac35ff17SAlexander V. Chernikov objheader_to_ti(oh, &ti); 908ac35ff17SAlexander V. Chernikov 9091832a7b3SAlexander V. Chernikov if (op3->opcode == IP_FW_TABLE_XDESTROY) 910ac35ff17SAlexander V. Chernikov error = destroy_table(ch, &ti); 9111832a7b3SAlexander V. Chernikov else if (op3->opcode == IP_FW_TABLE_XFLUSH) 912ac35ff17SAlexander V. Chernikov error = flush_table(ch, &ti); 913ac35ff17SAlexander V. Chernikov else 914ac35ff17SAlexander V. Chernikov return (ENOTSUP); 915ac35ff17SAlexander V. Chernikov 916ac35ff17SAlexander V. Chernikov return (error); 9173b3a8eb9SGleb Smirnoff } 9183b3a8eb9SGleb Smirnoff 9191832a7b3SAlexander V. Chernikov int 920ac35ff17SAlexander V. Chernikov flush_table(struct ip_fw_chain *ch, struct tid_info *ti) 9213b3a8eb9SGleb Smirnoff { 922b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 923b074b7bbSAlexander V. Chernikov struct table_config *tc; 9249f7d47b0SAlexander V. Chernikov struct table_algo *ta; 9259f7d47b0SAlexander V. Chernikov struct table_info ti_old, ti_new, *tablestate; 9269f7d47b0SAlexander V. Chernikov void *astate_old, *astate_new; 927914bffb6SAlexander V. Chernikov char algostate[64], *pstate; 928b074b7bbSAlexander V. Chernikov int error; 929b074b7bbSAlexander V. Chernikov uint16_t kidx; 930914bffb6SAlexander V. Chernikov uint8_t tflags; 9313b3a8eb9SGleb Smirnoff 9323b3a8eb9SGleb Smirnoff /* 9339f7d47b0SAlexander V. Chernikov * Stage 1: save table algoritm. 934b074b7bbSAlexander V. Chernikov * Reference found table to ensure it won't disappear. 9353b3a8eb9SGleb Smirnoff */ 936b074b7bbSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 937b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 938b074b7bbSAlexander V. Chernikov if ((tc = find_table(ni, ti)) == NULL) { 939b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 940b074b7bbSAlexander V. Chernikov return (ESRCH); 941b074b7bbSAlexander V. Chernikov } 9429f7d47b0SAlexander V. Chernikov ta = tc->ta; 943b074b7bbSAlexander V. Chernikov tc->no.refcnt++; 944daabb523SAlexander V. Chernikov /* Save statup algo parameters */ 945daabb523SAlexander V. Chernikov if (ta->print_config != NULL) { 946daabb523SAlexander V. Chernikov ta->print_config(tc->astate, KIDX_TO_TI(ch, tc->no.kidx), 947daabb523SAlexander V. Chernikov algostate, sizeof(algostate)); 948daabb523SAlexander V. Chernikov pstate = algostate; 949daabb523SAlexander V. Chernikov } else 950daabb523SAlexander V. Chernikov pstate = NULL; 951914bffb6SAlexander V. Chernikov tflags = tc->tflags; 952b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 9533b3a8eb9SGleb Smirnoff 954b074b7bbSAlexander V. Chernikov /* 9559f7d47b0SAlexander V. Chernikov * Stage 2: allocate new table instance using same algo. 956b074b7bbSAlexander V. Chernikov */ 9579f7d47b0SAlexander V. Chernikov memset(&ti_new, 0, sizeof(struct table_info)); 958914bffb6SAlexander V. Chernikov if ((error = ta->init(ch, &astate_new, &ti_new, pstate, tflags)) != 0) { 959b074b7bbSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 960b074b7bbSAlexander V. Chernikov tc->no.refcnt--; 961b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 962b074b7bbSAlexander V. Chernikov return (error); 963b074b7bbSAlexander V. Chernikov } 964b074b7bbSAlexander V. Chernikov 965b074b7bbSAlexander V. Chernikov /* 966b074b7bbSAlexander V. Chernikov * Stage 3: swap old state pointers with newly-allocated ones. 967b074b7bbSAlexander V. Chernikov * Decrease refcount. 968b074b7bbSAlexander V. Chernikov */ 969b074b7bbSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 970b074b7bbSAlexander V. Chernikov 971b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 972b074b7bbSAlexander V. Chernikov kidx = tc->no.kidx; 9739f7d47b0SAlexander V. Chernikov tablestate = (struct table_info *)ch->tablestate; 974b074b7bbSAlexander V. Chernikov 9759f7d47b0SAlexander V. Chernikov IPFW_WLOCK(ch); 9769f7d47b0SAlexander V. Chernikov ti_old = tablestate[kidx]; 9779f7d47b0SAlexander V. Chernikov tablestate[kidx] = ti_new; 9789f7d47b0SAlexander V. Chernikov IPFW_WUNLOCK(ch); 979b074b7bbSAlexander V. Chernikov 9809f7d47b0SAlexander V. Chernikov astate_old = tc->astate; 9819f7d47b0SAlexander V. Chernikov tc->astate = astate_new; 9829f7d47b0SAlexander V. Chernikov tc->ti = ti_new; 9839f7d47b0SAlexander V. Chernikov tc->count = 0; 984b074b7bbSAlexander V. Chernikov tc->no.refcnt--; 985b074b7bbSAlexander V. Chernikov 986b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 9873b3a8eb9SGleb Smirnoff 988b074b7bbSAlexander V. Chernikov /* 989b074b7bbSAlexander V. Chernikov * Stage 4: perform real flush. 990b074b7bbSAlexander V. Chernikov */ 9919f7d47b0SAlexander V. Chernikov ta->destroy(astate_old, &ti_old); 9923b3a8eb9SGleb Smirnoff 9933b3a8eb9SGleb Smirnoff return (0); 9943b3a8eb9SGleb Smirnoff } 9953b3a8eb9SGleb Smirnoff 996b074b7bbSAlexander V. Chernikov /* 99746d52008SAlexander V. Chernikov * Swaps two tables. 99846d52008SAlexander V. Chernikov * Data layout (v0)(current): 99946d52008SAlexander V. Chernikov * Request: [ ipfw_obj_header ipfw_obj_ntlv ] 100046d52008SAlexander V. Chernikov * 100146d52008SAlexander V. Chernikov * Returns 0 on success 100246d52008SAlexander V. Chernikov */ 100346d52008SAlexander V. Chernikov int 100446d52008SAlexander V. Chernikov ipfw_swap_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 100546d52008SAlexander V. Chernikov struct sockopt_data *sd) 100646d52008SAlexander V. Chernikov { 100746d52008SAlexander V. Chernikov int error; 100846d52008SAlexander V. Chernikov struct _ipfw_obj_header *oh; 100946d52008SAlexander V. Chernikov struct tid_info ti_a, ti_b; 101046d52008SAlexander V. Chernikov 101146d52008SAlexander V. Chernikov if (sd->valsize != sizeof(*oh) + sizeof(ipfw_obj_ntlv)) 101246d52008SAlexander V. Chernikov return (EINVAL); 101346d52008SAlexander V. Chernikov 101446d52008SAlexander V. Chernikov oh = (struct _ipfw_obj_header *)op3; 101546d52008SAlexander V. Chernikov ntlv_to_ti(&oh->ntlv, &ti_a); 101646d52008SAlexander V. Chernikov ntlv_to_ti((ipfw_obj_ntlv *)(oh + 1), &ti_b); 101746d52008SAlexander V. Chernikov 1018a73d728dSAlexander V. Chernikov error = swap_tables(ch, &ti_a, &ti_b); 101946d52008SAlexander V. Chernikov 102046d52008SAlexander V. Chernikov return (error); 102146d52008SAlexander V. Chernikov } 102246d52008SAlexander V. Chernikov 102346d52008SAlexander V. Chernikov static int 1024a73d728dSAlexander V. Chernikov swap_tables(struct ip_fw_chain *ch, struct tid_info *a, 102546d52008SAlexander V. Chernikov struct tid_info *b) 102646d52008SAlexander V. Chernikov { 102746d52008SAlexander V. Chernikov struct namedobj_instance *ni; 102846d52008SAlexander V. Chernikov struct table_config *tc_a, *tc_b; 102946d52008SAlexander V. Chernikov struct table_algo *ta; 103046d52008SAlexander V. Chernikov struct table_info ti, *tablestate; 103146d52008SAlexander V. Chernikov void *astate; 103246d52008SAlexander V. Chernikov uint32_t count; 103346d52008SAlexander V. Chernikov 103446d52008SAlexander V. Chernikov /* 103546d52008SAlexander V. Chernikov * Stage 1: find both tables and ensure they are of 103646d52008SAlexander V. Chernikov * the same type and algo. 103746d52008SAlexander V. Chernikov */ 103846d52008SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 103946d52008SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 104046d52008SAlexander V. Chernikov if ((tc_a = find_table(ni, a)) == NULL) { 104146d52008SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 104246d52008SAlexander V. Chernikov return (ESRCH); 104346d52008SAlexander V. Chernikov } 104446d52008SAlexander V. Chernikov if ((tc_b = find_table(ni, b)) == NULL) { 104546d52008SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 104646d52008SAlexander V. Chernikov return (ESRCH); 104746d52008SAlexander V. Chernikov } 104846d52008SAlexander V. Chernikov 104946d52008SAlexander V. Chernikov /* It is very easy to swap between the same table */ 105046d52008SAlexander V. Chernikov if (tc_a == tc_b) { 105146d52008SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 105246d52008SAlexander V. Chernikov return (0); 105346d52008SAlexander V. Chernikov } 105446d52008SAlexander V. Chernikov 105546d52008SAlexander V. Chernikov /* Check type and value are the same */ 105646d52008SAlexander V. Chernikov if (tc_a->no.type != tc_b->no.type || tc_a->tflags != tc_b->tflags || 105746d52008SAlexander V. Chernikov tc_a->vtype != tc_b->vtype) { 105846d52008SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 105946d52008SAlexander V. Chernikov return (EINVAL); 106046d52008SAlexander V. Chernikov } 106146d52008SAlexander V. Chernikov 106246d52008SAlexander V. Chernikov /* Check limits before swap */ 106346d52008SAlexander V. Chernikov if ((tc_a->limit != 0 && tc_b->count > tc_a->limit) || 106446d52008SAlexander V. Chernikov (tc_b->limit != 0 && tc_a->count > tc_b->limit)) { 106546d52008SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 106646d52008SAlexander V. Chernikov return (EFBIG); 106746d52008SAlexander V. Chernikov } 106846d52008SAlexander V. Chernikov 106946d52008SAlexander V. Chernikov /* Everything is fine, prepare to swap */ 107046d52008SAlexander V. Chernikov tablestate = (struct table_info *)ch->tablestate; 107146d52008SAlexander V. Chernikov ti = tablestate[tc_a->no.kidx]; 107246d52008SAlexander V. Chernikov ta = tc_a->ta; 107346d52008SAlexander V. Chernikov astate = tc_a->astate; 107446d52008SAlexander V. Chernikov count = tc_a->count; 107546d52008SAlexander V. Chernikov 107646d52008SAlexander V. Chernikov IPFW_WLOCK(ch); 107746d52008SAlexander V. Chernikov /* a <- b */ 107846d52008SAlexander V. Chernikov tablestate[tc_a->no.kidx] = tablestate[tc_b->no.kidx]; 107946d52008SAlexander V. Chernikov tc_a->ta = tc_b->ta; 108046d52008SAlexander V. Chernikov tc_a->astate = tc_b->astate; 108146d52008SAlexander V. Chernikov tc_a->count = tc_b->count; 108246d52008SAlexander V. Chernikov /* b <- a */ 108346d52008SAlexander V. Chernikov tablestate[tc_b->no.kidx] = ti; 108446d52008SAlexander V. Chernikov tc_b->ta = ta; 108546d52008SAlexander V. Chernikov tc_b->astate = astate; 108646d52008SAlexander V. Chernikov tc_b->count = count; 108746d52008SAlexander V. Chernikov IPFW_WUNLOCK(ch); 108846d52008SAlexander V. Chernikov 108946d52008SAlexander V. Chernikov /* Ensure tc.ti copies are in sync */ 109046d52008SAlexander V. Chernikov tc_a->ti = tablestate[tc_a->no.kidx]; 109146d52008SAlexander V. Chernikov tc_b->ti = tablestate[tc_b->no.kidx]; 109246d52008SAlexander V. Chernikov 109346d52008SAlexander V. Chernikov /* Notify both tables on @ti change */ 109446d52008SAlexander V. Chernikov if (tc_a->ta->change_ti != NULL) 109546d52008SAlexander V. Chernikov tc_a->ta->change_ti(tc_a->astate, &tablestate[tc_a->no.kidx]); 109646d52008SAlexander V. Chernikov if (tc_b->ta->change_ti != NULL) 109746d52008SAlexander V. Chernikov tc_b->ta->change_ti(tc_b->astate, &tablestate[tc_b->no.kidx]); 109846d52008SAlexander V. Chernikov 109946d52008SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 110046d52008SAlexander V. Chernikov 110146d52008SAlexander V. Chernikov return (0); 110246d52008SAlexander V. Chernikov } 110346d52008SAlexander V. Chernikov 110446d52008SAlexander V. Chernikov /* 11059f7d47b0SAlexander V. Chernikov * Destroys table specified by @ti. 1106ac35ff17SAlexander V. Chernikov * Data layout (v0)(current): 1107ac35ff17SAlexander V. Chernikov * Request: [ ip_fw3_opheader ] 1108ac35ff17SAlexander V. Chernikov * 1109ac35ff17SAlexander V. Chernikov * Returns 0 on success 1110b074b7bbSAlexander V. Chernikov */ 1111ac35ff17SAlexander V. Chernikov static int 1112ac35ff17SAlexander V. Chernikov destroy_table(struct ip_fw_chain *ch, struct tid_info *ti) 1113b074b7bbSAlexander V. Chernikov { 1114b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 1115b074b7bbSAlexander V. Chernikov struct table_config *tc; 1116b074b7bbSAlexander V. Chernikov 1117b074b7bbSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 1118b074b7bbSAlexander V. Chernikov 1119b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 1120b074b7bbSAlexander V. Chernikov if ((tc = find_table(ni, ti)) == NULL) { 1121b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1122b074b7bbSAlexander V. Chernikov return (ESRCH); 1123b074b7bbSAlexander V. Chernikov } 1124b074b7bbSAlexander V. Chernikov 11259f7d47b0SAlexander V. Chernikov /* Do not permit destroying referenced tables */ 11269f7d47b0SAlexander V. Chernikov if (tc->no.refcnt > 0) { 1127b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1128b074b7bbSAlexander V. Chernikov return (EBUSY); 1129b074b7bbSAlexander V. Chernikov } 1130b074b7bbSAlexander V. Chernikov 1131b074b7bbSAlexander V. Chernikov IPFW_WLOCK(ch); 1132b074b7bbSAlexander V. Chernikov unlink_table(ch, tc); 1133b074b7bbSAlexander V. Chernikov IPFW_WUNLOCK(ch); 1134b074b7bbSAlexander V. Chernikov 1135b074b7bbSAlexander V. Chernikov /* Free obj index */ 1136ac35ff17SAlexander V. Chernikov if (ipfw_objhash_free_idx(ni, tc->no.kidx) != 0) 1137b074b7bbSAlexander V. Chernikov printf("Error unlinking kidx %d from table %s\n", 1138b074b7bbSAlexander V. Chernikov tc->no.kidx, tc->tablename); 1139b074b7bbSAlexander V. Chernikov 1140b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1141b074b7bbSAlexander V. Chernikov 1142b074b7bbSAlexander V. Chernikov free_table_config(ni, tc); 1143b074b7bbSAlexander V. Chernikov 1144b074b7bbSAlexander V. Chernikov return (0); 1145b074b7bbSAlexander V. Chernikov } 1146b074b7bbSAlexander V. Chernikov 1147b074b7bbSAlexander V. Chernikov static void 1148b074b7bbSAlexander V. Chernikov destroy_table_locked(struct namedobj_instance *ni, struct named_object *no, 1149b074b7bbSAlexander V. Chernikov void *arg) 1150b074b7bbSAlexander V. Chernikov { 1151b074b7bbSAlexander V. Chernikov 1152b074b7bbSAlexander V. Chernikov unlink_table((struct ip_fw_chain *)arg, (struct table_config *)no); 1153ac35ff17SAlexander V. Chernikov if (ipfw_objhash_free_idx(ni, no->kidx) != 0) 1154b074b7bbSAlexander V. Chernikov printf("Error unlinking kidx %d from table %s\n", 1155b074b7bbSAlexander V. Chernikov no->kidx, no->name); 1156b074b7bbSAlexander V. Chernikov free_table_config(ni, (struct table_config *)no); 1157b074b7bbSAlexander V. Chernikov } 1158b074b7bbSAlexander V. Chernikov 11593b3a8eb9SGleb Smirnoff void 11603b3a8eb9SGleb Smirnoff ipfw_destroy_tables(struct ip_fw_chain *ch) 11613b3a8eb9SGleb Smirnoff { 11623b3a8eb9SGleb Smirnoff 1163b074b7bbSAlexander V. Chernikov /* Remove all tables from working set */ 1164b074b7bbSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 1165b074b7bbSAlexander V. Chernikov IPFW_WLOCK(ch); 1166b074b7bbSAlexander V. Chernikov ipfw_objhash_foreach(CHAIN_TO_NI(ch), destroy_table_locked, ch); 1167b074b7bbSAlexander V. Chernikov IPFW_WUNLOCK(ch); 1168b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 11693b3a8eb9SGleb Smirnoff 11703b3a8eb9SGleb Smirnoff /* Free pointers itself */ 11719f7d47b0SAlexander V. Chernikov free(ch->tablestate, M_IPFW); 11729f7d47b0SAlexander V. Chernikov 11739f7d47b0SAlexander V. Chernikov ipfw_table_algo_destroy(ch); 1174b074b7bbSAlexander V. Chernikov 1175b074b7bbSAlexander V. Chernikov ipfw_objhash_destroy(CHAIN_TO_NI(ch)); 1176b074b7bbSAlexander V. Chernikov free(CHAIN_TO_TCFG(ch), M_IPFW); 11773b3a8eb9SGleb Smirnoff } 11783b3a8eb9SGleb Smirnoff 11793b3a8eb9SGleb Smirnoff int 11803b3a8eb9SGleb Smirnoff ipfw_init_tables(struct ip_fw_chain *ch) 11813b3a8eb9SGleb Smirnoff { 1182b074b7bbSAlexander V. Chernikov struct tables_config *tcfg; 1183b074b7bbSAlexander V. Chernikov 11843b3a8eb9SGleb Smirnoff /* Allocate pointers */ 11859f7d47b0SAlexander V. Chernikov ch->tablestate = malloc(V_fw_tables_max * sizeof(struct table_info), 11869f7d47b0SAlexander V. Chernikov M_IPFW, M_WAITOK | M_ZERO); 1187b074b7bbSAlexander V. Chernikov 1188b074b7bbSAlexander V. Chernikov tcfg = malloc(sizeof(struct tables_config), M_IPFW, M_WAITOK | M_ZERO); 1189b074b7bbSAlexander V. Chernikov tcfg->namehash = ipfw_objhash_create(V_fw_tables_max); 1190b074b7bbSAlexander V. Chernikov ch->tblcfg = tcfg; 1191b074b7bbSAlexander V. Chernikov 11929f7d47b0SAlexander V. Chernikov ipfw_table_algo_init(ch); 11939f7d47b0SAlexander V. Chernikov 11943b3a8eb9SGleb Smirnoff return (0); 11953b3a8eb9SGleb Smirnoff } 11963b3a8eb9SGleb Smirnoff 11973b3a8eb9SGleb Smirnoff int 11983b3a8eb9SGleb Smirnoff ipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables) 11993b3a8eb9SGleb Smirnoff { 12003b3a8eb9SGleb Smirnoff unsigned int ntables_old, tbl; 1201b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 12029f7d47b0SAlexander V. Chernikov void *new_idx, *old_tablestate, *tablestate; 120368394ec8SAlexander V. Chernikov struct table_info *ti; 120468394ec8SAlexander V. Chernikov struct table_config *tc; 120568394ec8SAlexander V. Chernikov int i, new_blocks; 12063b3a8eb9SGleb Smirnoff 12073b3a8eb9SGleb Smirnoff /* Check new value for validity */ 12083b3a8eb9SGleb Smirnoff if (ntables > IPFW_TABLES_MAX) 12093b3a8eb9SGleb Smirnoff ntables = IPFW_TABLES_MAX; 12103b3a8eb9SGleb Smirnoff 12113b3a8eb9SGleb Smirnoff /* Allocate new pointers */ 12129f7d47b0SAlexander V. Chernikov tablestate = malloc(ntables * sizeof(struct table_info), 12139f7d47b0SAlexander V. Chernikov M_IPFW, M_WAITOK | M_ZERO); 12149f7d47b0SAlexander V. Chernikov 1215b074b7bbSAlexander V. Chernikov ipfw_objhash_bitmap_alloc(ntables, (void *)&new_idx, &new_blocks); 12163b3a8eb9SGleb Smirnoff 12179f7d47b0SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 12183b3a8eb9SGleb Smirnoff 12193b3a8eb9SGleb Smirnoff tbl = (ntables >= V_fw_tables_max) ? V_fw_tables_max : ntables; 1220b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 1221b074b7bbSAlexander V. Chernikov 12229f7d47b0SAlexander V. Chernikov /* Temporary restrict decreasing max_tables */ 12239f7d47b0SAlexander V. Chernikov if (ntables < V_fw_tables_max) { 12249f7d47b0SAlexander V. Chernikov 12259f7d47b0SAlexander V. Chernikov /* 12269f7d47b0SAlexander V. Chernikov * FIXME: Check if we really can shrink 12279f7d47b0SAlexander V. Chernikov */ 12289f7d47b0SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1229b074b7bbSAlexander V. Chernikov return (EINVAL); 1230b074b7bbSAlexander V. Chernikov } 12313b3a8eb9SGleb Smirnoff 12329f7d47b0SAlexander V. Chernikov /* Copy table info/indices */ 12339f7d47b0SAlexander V. Chernikov memcpy(tablestate, ch->tablestate, sizeof(struct table_info) * tbl); 12349f7d47b0SAlexander V. Chernikov ipfw_objhash_bitmap_merge(ni, &new_idx, &new_blocks); 12353b3a8eb9SGleb Smirnoff 12369f7d47b0SAlexander V. Chernikov IPFW_WLOCK(ch); 12379f7d47b0SAlexander V. Chernikov 12389f7d47b0SAlexander V. Chernikov /* Change pointers */ 12399f7d47b0SAlexander V. Chernikov old_tablestate = ch->tablestate; 12409f7d47b0SAlexander V. Chernikov ch->tablestate = tablestate; 12419f7d47b0SAlexander V. Chernikov ipfw_objhash_bitmap_swap(ni, &new_idx, &new_blocks); 12423b3a8eb9SGleb Smirnoff 12433b3a8eb9SGleb Smirnoff ntables_old = V_fw_tables_max; 12443b3a8eb9SGleb Smirnoff V_fw_tables_max = ntables; 12453b3a8eb9SGleb Smirnoff 12463b3a8eb9SGleb Smirnoff IPFW_WUNLOCK(ch); 124768394ec8SAlexander V. Chernikov 124868394ec8SAlexander V. Chernikov /* Notify all consumers that their @ti pointer has changed */ 124968394ec8SAlexander V. Chernikov ti = (struct table_info *)ch->tablestate; 125068394ec8SAlexander V. Chernikov for (i = 0; i < tbl; i++, ti++) { 125168394ec8SAlexander V. Chernikov if (ti->lookup == NULL) 125268394ec8SAlexander V. Chernikov continue; 125368394ec8SAlexander V. Chernikov tc = (struct table_config *)ipfw_objhash_lookup_kidx(ni, i); 125468394ec8SAlexander V. Chernikov if (tc == NULL || tc->ta->change_ti == NULL) 125568394ec8SAlexander V. Chernikov continue; 125668394ec8SAlexander V. Chernikov 125768394ec8SAlexander V. Chernikov tc->ta->change_ti(tc->astate, ti); 125868394ec8SAlexander V. Chernikov } 125968394ec8SAlexander V. Chernikov 12609f7d47b0SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 12613b3a8eb9SGleb Smirnoff 12623b3a8eb9SGleb Smirnoff /* Free old pointers */ 12639f7d47b0SAlexander V. Chernikov free(old_tablestate, M_IPFW); 1264b074b7bbSAlexander V. Chernikov ipfw_objhash_bitmap_free(new_idx, new_blocks); 12653b3a8eb9SGleb Smirnoff 12663b3a8eb9SGleb Smirnoff return (0); 12673b3a8eb9SGleb Smirnoff } 12683b3a8eb9SGleb Smirnoff 1269a73d728dSAlexander V. Chernikov /* 1270a73d728dSAlexander V. Chernikov * Switch between "set 0" and "rule set" table binding, 1271a73d728dSAlexander V. Chernikov * Check all ruleset bindings and permits changing 1272a73d728dSAlexander V. Chernikov * IFF each binding has both rule AND table in default set (set 0). 1273a73d728dSAlexander V. Chernikov * 1274a73d728dSAlexander V. Chernikov * Returns 0 on success. 1275a73d728dSAlexander V. Chernikov */ 1276a73d728dSAlexander V. Chernikov int 1277a73d728dSAlexander V. Chernikov ipfw_switch_tables_namespace(struct ip_fw_chain *ch, unsigned int sets) 1278a73d728dSAlexander V. Chernikov { 1279a73d728dSAlexander V. Chernikov struct namedobj_instance *ni; 1280a73d728dSAlexander V. Chernikov struct named_object *no; 1281a73d728dSAlexander V. Chernikov struct ip_fw *rule; 1282a73d728dSAlexander V. Chernikov ipfw_insn *cmd; 1283a73d728dSAlexander V. Chernikov int cmdlen, i, l; 1284a73d728dSAlexander V. Chernikov uint16_t kidx; 1285a73d728dSAlexander V. Chernikov uint8_t type; 1286a73d728dSAlexander V. Chernikov 1287a73d728dSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 1288a73d728dSAlexander V. Chernikov 1289a73d728dSAlexander V. Chernikov if (V_fw_tables_sets == sets) { 1290a73d728dSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1291a73d728dSAlexander V. Chernikov return (0); 1292a73d728dSAlexander V. Chernikov } 1293a73d728dSAlexander V. Chernikov 1294a73d728dSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 1295a73d728dSAlexander V. Chernikov 1296a73d728dSAlexander V. Chernikov for (i = 0; i < ch->n_rules; i++) { 1297a73d728dSAlexander V. Chernikov rule = ch->map[i]; 1298a73d728dSAlexander V. Chernikov 1299a73d728dSAlexander V. Chernikov l = rule->cmd_len; 1300a73d728dSAlexander V. Chernikov cmd = rule->cmd; 1301a73d728dSAlexander V. Chernikov cmdlen = 0; 1302a73d728dSAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 1303a73d728dSAlexander V. Chernikov cmdlen = F_LEN(cmd); 1304a73d728dSAlexander V. Chernikov 1305a73d728dSAlexander V. Chernikov if (classify_table_opcode(cmd, &kidx, &type) != 0) 1306a73d728dSAlexander V. Chernikov continue; 1307a73d728dSAlexander V. Chernikov 1308a73d728dSAlexander V. Chernikov no = ipfw_objhash_lookup_kidx(ni, kidx); 1309a73d728dSAlexander V. Chernikov 1310a73d728dSAlexander V. Chernikov if (no->set != 0 || rule->set != 0) { 1311a73d728dSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1312a73d728dSAlexander V. Chernikov return (EBUSY); 1313a73d728dSAlexander V. Chernikov } 1314a73d728dSAlexander V. Chernikov 1315a73d728dSAlexander V. Chernikov } 1316a73d728dSAlexander V. Chernikov } 1317a73d728dSAlexander V. Chernikov V_fw_tables_sets = sets; 1318a73d728dSAlexander V. Chernikov 1319a73d728dSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1320a73d728dSAlexander V. Chernikov 1321a73d728dSAlexander V. Chernikov return (0); 1322a73d728dSAlexander V. Chernikov } 1323a73d728dSAlexander V. Chernikov 13243b3a8eb9SGleb Smirnoff int 13253b3a8eb9SGleb Smirnoff ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, 13263b3a8eb9SGleb Smirnoff uint32_t *val) 13273b3a8eb9SGleb Smirnoff { 13289f7d47b0SAlexander V. Chernikov struct table_info *ti; 13293b3a8eb9SGleb Smirnoff 13309f7d47b0SAlexander V. Chernikov ti = &(((struct table_info *)ch->tablestate)[tbl]); 13319f7d47b0SAlexander V. Chernikov 13329f7d47b0SAlexander V. Chernikov return (ti->lookup(ti, &addr, sizeof(in_addr_t), val)); 13333b3a8eb9SGleb Smirnoff } 13349f7d47b0SAlexander V. Chernikov 13359f7d47b0SAlexander V. Chernikov int 13369f7d47b0SAlexander V. Chernikov ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, uint16_t plen, 13379f7d47b0SAlexander V. Chernikov void *paddr, uint32_t *val) 13389f7d47b0SAlexander V. Chernikov { 13399f7d47b0SAlexander V. Chernikov struct table_info *ti; 13409f7d47b0SAlexander V. Chernikov 13419f7d47b0SAlexander V. Chernikov ti = &(((struct table_info *)ch->tablestate)[tbl]); 13429f7d47b0SAlexander V. Chernikov 13439f7d47b0SAlexander V. Chernikov return (ti->lookup(ti, paddr, plen, val)); 13449f7d47b0SAlexander V. Chernikov } 13459f7d47b0SAlexander V. Chernikov 13469f7d47b0SAlexander V. Chernikov /* 13479f7d47b0SAlexander V. Chernikov * Info/List/dump support for tables. 13489f7d47b0SAlexander V. Chernikov * 13499f7d47b0SAlexander V. Chernikov */ 13509f7d47b0SAlexander V. Chernikov 1351f1220db8SAlexander V. Chernikov /* 1352d3a4f924SAlexander V. Chernikov * High-level 'get' cmds sysctl handlers 1353d3a4f924SAlexander V. Chernikov */ 1354d3a4f924SAlexander V. Chernikov 1355d3a4f924SAlexander V. Chernikov /* 1356d3a4f924SAlexander V. Chernikov * Lists all tables currently available in kernel. 1357ac35ff17SAlexander V. Chernikov * Data layout (v0)(current): 1358d3a4f924SAlexander V. Chernikov * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size 1359d3a4f924SAlexander V. Chernikov * Reply: [ ipfw_obj_lheader ipfw_xtable_info x N ] 1360d3a4f924SAlexander V. Chernikov * 1361d3a4f924SAlexander V. Chernikov * Returns 0 on success 1362d3a4f924SAlexander V. Chernikov */ 1363f1220db8SAlexander V. Chernikov int 13642d99a349SAlexander V. Chernikov ipfw_list_tables(struct ip_fw_chain *ch, struct sockopt_data *sd) 1365f1220db8SAlexander V. Chernikov { 1366f1220db8SAlexander V. Chernikov struct _ipfw_obj_lheader *olh; 1367f1220db8SAlexander V. Chernikov int error; 1368f1220db8SAlexander V. Chernikov 13692d99a349SAlexander V. Chernikov olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh)); 13702d99a349SAlexander V. Chernikov if (olh == NULL) 1371d3a4f924SAlexander V. Chernikov return (EINVAL); 137268394ec8SAlexander V. Chernikov if (sd->valsize < olh->size) 137368394ec8SAlexander V. Chernikov return (EINVAL); 1374d3a4f924SAlexander V. Chernikov 1375f1220db8SAlexander V. Chernikov IPFW_UH_RLOCK(ch); 13762d99a349SAlexander V. Chernikov error = export_tables(ch, olh, sd); 1377f1220db8SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 1378f1220db8SAlexander V. Chernikov 1379f1220db8SAlexander V. Chernikov return (error); 1380f1220db8SAlexander V. Chernikov } 1381f1220db8SAlexander V. Chernikov 1382f1220db8SAlexander V. Chernikov /* 13832d99a349SAlexander V. Chernikov * Store table info to buffer provided by @sd. 1384ac35ff17SAlexander V. Chernikov * Data layout (v0)(current): 1385d3a4f924SAlexander V. Chernikov * Request: [ ipfw_obj_header ipfw_xtable_info(empty)] 1386d3a4f924SAlexander V. Chernikov * Reply: [ ipfw_obj_header ipfw_xtable_info ] 1387d3a4f924SAlexander V. Chernikov * 1388d3a4f924SAlexander V. Chernikov * Returns 0 on success. 1389d3a4f924SAlexander V. Chernikov */ 1390d3a4f924SAlexander V. Chernikov int 13912d99a349SAlexander V. Chernikov ipfw_describe_table(struct ip_fw_chain *ch, struct sockopt_data *sd) 1392d3a4f924SAlexander V. Chernikov { 1393d3a4f924SAlexander V. Chernikov struct _ipfw_obj_header *oh; 1394d3a4f924SAlexander V. Chernikov struct table_config *tc; 1395d3a4f924SAlexander V. Chernikov struct tid_info ti; 1396d3a4f924SAlexander V. Chernikov size_t sz; 1397d3a4f924SAlexander V. Chernikov 1398d3a4f924SAlexander V. Chernikov sz = sizeof(*oh) + sizeof(ipfw_xtable_info); 13992d99a349SAlexander V. Chernikov oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 14002d99a349SAlexander V. Chernikov if (oh == NULL) 1401d3a4f924SAlexander V. Chernikov return (EINVAL); 1402d3a4f924SAlexander V. Chernikov 1403d3a4f924SAlexander V. Chernikov objheader_to_ti(oh, &ti); 1404d3a4f924SAlexander V. Chernikov 1405d3a4f924SAlexander V. Chernikov IPFW_UH_RLOCK(ch); 1406d3a4f924SAlexander V. Chernikov if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) { 1407d3a4f924SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 1408d3a4f924SAlexander V. Chernikov return (ESRCH); 1409d3a4f924SAlexander V. Chernikov } 1410d3a4f924SAlexander V. Chernikov 1411ac35ff17SAlexander V. Chernikov export_table_info(ch, tc, (ipfw_xtable_info *)(oh + 1)); 1412d3a4f924SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 1413d3a4f924SAlexander V. Chernikov 14142d99a349SAlexander V. Chernikov return (0); 1415d3a4f924SAlexander V. Chernikov } 1416d3a4f924SAlexander V. Chernikov 1417f1220db8SAlexander V. Chernikov struct dump_args { 1418f1220db8SAlexander V. Chernikov struct table_info *ti; 1419f1220db8SAlexander V. Chernikov struct table_config *tc; 14202d99a349SAlexander V. Chernikov struct sockopt_data *sd; 1421f1220db8SAlexander V. Chernikov uint32_t cnt; 1422f1220db8SAlexander V. Chernikov uint16_t uidx; 142381d3153dSAlexander V. Chernikov int error; 14242d99a349SAlexander V. Chernikov ipfw_table_entry *ent; 14252d99a349SAlexander V. Chernikov uint32_t size; 142681d3153dSAlexander V. Chernikov ipfw_obj_tentry tent; 1427f1220db8SAlexander V. Chernikov }; 1428f1220db8SAlexander V. Chernikov 1429f1220db8SAlexander V. Chernikov int 14302d99a349SAlexander V. Chernikov ipfw_dump_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 14312d99a349SAlexander V. Chernikov struct sockopt_data *sd) 1432f1220db8SAlexander V. Chernikov { 1433d3a4f924SAlexander V. Chernikov int error; 1434d3a4f924SAlexander V. Chernikov 1435d3a4f924SAlexander V. Chernikov switch (op3->version) { 1436d3a4f924SAlexander V. Chernikov case 0: 14372d99a349SAlexander V. Chernikov error = ipfw_dump_table_v0(ch, sd); 1438d3a4f924SAlexander V. Chernikov break; 1439d3a4f924SAlexander V. Chernikov case 1: 14402d99a349SAlexander V. Chernikov error = ipfw_dump_table_v1(ch, sd); 1441d3a4f924SAlexander V. Chernikov break; 1442d3a4f924SAlexander V. Chernikov default: 1443d3a4f924SAlexander V. Chernikov error = ENOTSUP; 1444d3a4f924SAlexander V. Chernikov } 1445d3a4f924SAlexander V. Chernikov 1446d3a4f924SAlexander V. Chernikov return (error); 1447d3a4f924SAlexander V. Chernikov } 1448d3a4f924SAlexander V. Chernikov 1449d3a4f924SAlexander V. Chernikov /* 1450d3a4f924SAlexander V. Chernikov * Dumps all table data 1451ac35ff17SAlexander V. Chernikov * Data layout (v1)(current): 14522d99a349SAlexander V. Chernikov * Request: [ ipfw_obj_header ], size = ipfw_xtable_info.size 145381d3153dSAlexander V. Chernikov * Reply: [ ipfw_obj_header ipfw_xtable_info ipfw_obj_tentry x N ] 1454d3a4f924SAlexander V. Chernikov * 1455d3a4f924SAlexander V. Chernikov * Returns 0 on success 1456d3a4f924SAlexander V. Chernikov */ 1457d3a4f924SAlexander V. Chernikov static int 14582d99a349SAlexander V. Chernikov ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt_data *sd) 1459d3a4f924SAlexander V. Chernikov { 1460f1220db8SAlexander V. Chernikov struct _ipfw_obj_header *oh; 1461f1220db8SAlexander V. Chernikov ipfw_xtable_info *i; 1462f1220db8SAlexander V. Chernikov struct tid_info ti; 1463f1220db8SAlexander V. Chernikov struct table_config *tc; 1464f1220db8SAlexander V. Chernikov struct table_algo *ta; 1465f1220db8SAlexander V. Chernikov struct dump_args da; 1466d3a4f924SAlexander V. Chernikov uint32_t sz; 1467f1220db8SAlexander V. Chernikov 14682d99a349SAlexander V. Chernikov sz = sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info); 14692d99a349SAlexander V. Chernikov oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 14702d99a349SAlexander V. Chernikov if (oh == NULL) 1471d3a4f924SAlexander V. Chernikov return (EINVAL); 1472d3a4f924SAlexander V. Chernikov 14732d99a349SAlexander V. Chernikov i = (ipfw_xtable_info *)(oh + 1); 1474d3a4f924SAlexander V. Chernikov objheader_to_ti(oh, &ti); 1475f1220db8SAlexander V. Chernikov 1476f1220db8SAlexander V. Chernikov IPFW_UH_RLOCK(ch); 1477f1220db8SAlexander V. Chernikov if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) { 1478f1220db8SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 1479f1220db8SAlexander V. Chernikov return (ESRCH); 1480f1220db8SAlexander V. Chernikov } 1481ac35ff17SAlexander V. Chernikov export_table_info(ch, tc, i); 14822d99a349SAlexander V. Chernikov 1483720ee730SAlexander V. Chernikov if (sd->valsize < i->size) { 14842d99a349SAlexander V. Chernikov 14852d99a349SAlexander V. Chernikov /* 14862d99a349SAlexander V. Chernikov * Submitted buffer size is not enough. 14872d99a349SAlexander V. Chernikov * WE've already filled in @i structure with 14882d99a349SAlexander V. Chernikov * relevant table info including size, so we 14892d99a349SAlexander V. Chernikov * can return. Buffer will be flushed automatically. 14902d99a349SAlexander V. Chernikov */ 1491f1220db8SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 14922d99a349SAlexander V. Chernikov return (ENOMEM); 1493f1220db8SAlexander V. Chernikov } 1494f1220db8SAlexander V. Chernikov 1495f1220db8SAlexander V. Chernikov /* 1496f1220db8SAlexander V. Chernikov * Do the actual dump in eXtended format 1497f1220db8SAlexander V. Chernikov */ 1498d3a4f924SAlexander V. Chernikov memset(&da, 0, sizeof(da)); 1499f1220db8SAlexander V. Chernikov da.ti = KIDX_TO_TI(ch, tc->no.kidx); 1500f1220db8SAlexander V. Chernikov da.tc = tc; 15012d99a349SAlexander V. Chernikov da.sd = sd; 1502f1220db8SAlexander V. Chernikov 1503f1220db8SAlexander V. Chernikov ta = tc->ta; 1504f1220db8SAlexander V. Chernikov 150581d3153dSAlexander V. Chernikov ta->foreach(tc->astate, da.ti, dump_table_tentry, &da); 1506f1220db8SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 1507f1220db8SAlexander V. Chernikov 150881d3153dSAlexander V. Chernikov return (da.error); 1509f1220db8SAlexander V. Chernikov } 1510f1220db8SAlexander V. Chernikov 1511d3a4f924SAlexander V. Chernikov /* 1512d3a4f924SAlexander V. Chernikov * Dumps all table data 15132d99a349SAlexander V. Chernikov * Data layout (version 0)(legacy): 1514d3a4f924SAlexander V. Chernikov * Request: [ ipfw_xtable ], size = IP_FW_TABLE_XGETSIZE() 1515d3a4f924SAlexander V. Chernikov * Reply: [ ipfw_xtable ipfw_table_xentry x N ] 1516d3a4f924SAlexander V. Chernikov * 1517d3a4f924SAlexander V. Chernikov * Returns 0 on success 1518d3a4f924SAlexander V. Chernikov */ 1519d3a4f924SAlexander V. Chernikov static int 15202d99a349SAlexander V. Chernikov ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt_data *sd) 1521d3a4f924SAlexander V. Chernikov { 1522d3a4f924SAlexander V. Chernikov ipfw_xtable *xtbl; 1523d3a4f924SAlexander V. Chernikov struct tid_info ti; 1524d3a4f924SAlexander V. Chernikov struct table_config *tc; 1525d3a4f924SAlexander V. Chernikov struct table_algo *ta; 1526d3a4f924SAlexander V. Chernikov struct dump_args da; 1527d3a4f924SAlexander V. Chernikov size_t sz; 1528d3a4f924SAlexander V. Chernikov 15292d99a349SAlexander V. Chernikov xtbl = (ipfw_xtable *)ipfw_get_sopt_header(sd, sizeof(ipfw_xtable)); 15302d99a349SAlexander V. Chernikov if (xtbl == NULL) 1531d3a4f924SAlexander V. Chernikov return (EINVAL); 1532d3a4f924SAlexander V. Chernikov 1533d3a4f924SAlexander V. Chernikov memset(&ti, 0, sizeof(ti)); 1534d3a4f924SAlexander V. Chernikov ti.uidx = xtbl->tbl; 1535d3a4f924SAlexander V. Chernikov 1536d3a4f924SAlexander V. Chernikov IPFW_UH_RLOCK(ch); 1537d3a4f924SAlexander V. Chernikov if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) { 1538d3a4f924SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 1539d3a4f924SAlexander V. Chernikov return (0); 1540d3a4f924SAlexander V. Chernikov } 1541d3a4f924SAlexander V. Chernikov sz = tc->count * sizeof(ipfw_table_xentry) + sizeof(ipfw_xtable); 15422d99a349SAlexander V. Chernikov 15432d99a349SAlexander V. Chernikov xtbl->cnt = tc->count; 15442d99a349SAlexander V. Chernikov xtbl->size = sz; 15452d99a349SAlexander V. Chernikov xtbl->type = tc->no.type; 15462d99a349SAlexander V. Chernikov xtbl->tbl = ti.uidx; 15472d99a349SAlexander V. Chernikov 15482d99a349SAlexander V. Chernikov if (sd->valsize < sz) { 15492d99a349SAlexander V. Chernikov 15502d99a349SAlexander V. Chernikov /* 15512d99a349SAlexander V. Chernikov * Submitted buffer size is not enough. 15522d99a349SAlexander V. Chernikov * WE've already filled in @i structure with 15532d99a349SAlexander V. Chernikov * relevant table info including size, so we 15542d99a349SAlexander V. Chernikov * can return. Buffer will be flushed automatically. 15552d99a349SAlexander V. Chernikov */ 1556d3a4f924SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 15572d99a349SAlexander V. Chernikov return (ENOMEM); 1558d3a4f924SAlexander V. Chernikov } 1559d3a4f924SAlexander V. Chernikov 1560d3a4f924SAlexander V. Chernikov /* Do the actual dump in eXtended format */ 1561d3a4f924SAlexander V. Chernikov memset(&da, 0, sizeof(da)); 1562d3a4f924SAlexander V. Chernikov da.ti = KIDX_TO_TI(ch, tc->no.kidx); 1563d3a4f924SAlexander V. Chernikov da.tc = tc; 15642d99a349SAlexander V. Chernikov da.sd = sd; 15652d99a349SAlexander V. Chernikov 1566d3a4f924SAlexander V. Chernikov ta = tc->ta; 1567d3a4f924SAlexander V. Chernikov 1568d3a4f924SAlexander V. Chernikov ta->foreach(tc->astate, da.ti, dump_table_xentry, &da); 1569d3a4f924SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 1570d3a4f924SAlexander V. Chernikov 15712d99a349SAlexander V. Chernikov return (0); 1572d3a4f924SAlexander V. Chernikov } 1573d3a4f924SAlexander V. Chernikov 1574d3a4f924SAlexander V. Chernikov /* 1575adf3b2b9SAlexander V. Chernikov * Modifies existing table. 1576adf3b2b9SAlexander V. Chernikov * Data layout (v0)(current): 1577adf3b2b9SAlexander V. Chernikov * Request: [ ipfw_obj_header ipfw_xtable_info ] 1578adf3b2b9SAlexander V. Chernikov * 1579adf3b2b9SAlexander V. Chernikov * Returns 0 on success 1580adf3b2b9SAlexander V. Chernikov */ 1581adf3b2b9SAlexander V. Chernikov int 1582adf3b2b9SAlexander V. Chernikov ipfw_modify_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 1583adf3b2b9SAlexander V. Chernikov struct sockopt_data *sd) 1584adf3b2b9SAlexander V. Chernikov { 1585adf3b2b9SAlexander V. Chernikov struct _ipfw_obj_header *oh; 1586adf3b2b9SAlexander V. Chernikov ipfw_xtable_info *i; 1587adf3b2b9SAlexander V. Chernikov char *tname; 1588adf3b2b9SAlexander V. Chernikov struct tid_info ti; 1589adf3b2b9SAlexander V. Chernikov struct namedobj_instance *ni; 1590adf3b2b9SAlexander V. Chernikov struct table_config *tc; 1591adf3b2b9SAlexander V. Chernikov 1592adf3b2b9SAlexander V. Chernikov if (sd->valsize != sizeof(*oh) + sizeof(ipfw_xtable_info)) 1593adf3b2b9SAlexander V. Chernikov return (EINVAL); 1594adf3b2b9SAlexander V. Chernikov 1595adf3b2b9SAlexander V. Chernikov oh = (struct _ipfw_obj_header *)sd->kbuf; 1596adf3b2b9SAlexander V. Chernikov i = (ipfw_xtable_info *)(oh + 1); 1597adf3b2b9SAlexander V. Chernikov 1598adf3b2b9SAlexander V. Chernikov /* 1599adf3b2b9SAlexander V. Chernikov * Verify user-supplied strings. 1600adf3b2b9SAlexander V. Chernikov * Check for null-terminated/zero-length strings/ 1601adf3b2b9SAlexander V. Chernikov */ 1602adf3b2b9SAlexander V. Chernikov tname = oh->ntlv.name; 1603adf3b2b9SAlexander V. Chernikov if (ipfw_check_table_name(tname) != 0) 1604adf3b2b9SAlexander V. Chernikov return (EINVAL); 1605adf3b2b9SAlexander V. Chernikov 1606adf3b2b9SAlexander V. Chernikov objheader_to_ti(oh, &ti); 1607adf3b2b9SAlexander V. Chernikov ti.type = i->type; 1608adf3b2b9SAlexander V. Chernikov 1609adf3b2b9SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 1610adf3b2b9SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 1611adf3b2b9SAlexander V. Chernikov if ((tc = find_table(ni, &ti)) == NULL) { 1612adf3b2b9SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1613adf3b2b9SAlexander V. Chernikov return (ESRCH); 1614adf3b2b9SAlexander V. Chernikov } 1615adf3b2b9SAlexander V. Chernikov if ((i->mflags & IPFW_TMFLAGS_FTYPE) != 0) 1616adf3b2b9SAlexander V. Chernikov tc->vftype = i->vftype; 1617adf3b2b9SAlexander V. Chernikov if ((i->mflags & IPFW_TMFLAGS_LIMIT) != 0) 1618adf3b2b9SAlexander V. Chernikov tc->limit = i->limit; 1619adf3b2b9SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1620adf3b2b9SAlexander V. Chernikov 1621adf3b2b9SAlexander V. Chernikov return (0); 1622adf3b2b9SAlexander V. Chernikov } 1623adf3b2b9SAlexander V. Chernikov 1624adf3b2b9SAlexander V. Chernikov /* 16259490a627SAlexander V. Chernikov * Creates new table. 1626ac35ff17SAlexander V. Chernikov * Data layout (v0)(current): 16279490a627SAlexander V. Chernikov * Request: [ ipfw_obj_header ipfw_xtable_info ] 16289490a627SAlexander V. Chernikov * 16299490a627SAlexander V. Chernikov * Returns 0 on success 16309490a627SAlexander V. Chernikov */ 16319490a627SAlexander V. Chernikov int 1632ac35ff17SAlexander V. Chernikov ipfw_create_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 1633ac35ff17SAlexander V. Chernikov struct sockopt_data *sd) 16349490a627SAlexander V. Chernikov { 16359490a627SAlexander V. Chernikov struct _ipfw_obj_header *oh; 16369490a627SAlexander V. Chernikov ipfw_xtable_info *i; 16379490a627SAlexander V. Chernikov char *tname, *aname; 16389490a627SAlexander V. Chernikov struct tid_info ti; 16399490a627SAlexander V. Chernikov struct namedobj_instance *ni; 16409490a627SAlexander V. Chernikov struct table_config *tc; 16419490a627SAlexander V. Chernikov 1642ac35ff17SAlexander V. Chernikov if (sd->valsize != sizeof(*oh) + sizeof(ipfw_xtable_info)) 16439490a627SAlexander V. Chernikov return (EINVAL); 16449490a627SAlexander V. Chernikov 1645ac35ff17SAlexander V. Chernikov oh = (struct _ipfw_obj_header *)sd->kbuf; 16469490a627SAlexander V. Chernikov i = (ipfw_xtable_info *)(oh + 1); 16479490a627SAlexander V. Chernikov 16489490a627SAlexander V. Chernikov /* 16499490a627SAlexander V. Chernikov * Verify user-supplied strings. 16502d99a349SAlexander V. Chernikov * Check for null-terminated/zero-length strings/ 16519490a627SAlexander V. Chernikov */ 1652ac35ff17SAlexander V. Chernikov tname = oh->ntlv.name; 16539490a627SAlexander V. Chernikov aname = i->algoname; 1654ac35ff17SAlexander V. Chernikov if (ipfw_check_table_name(tname) != 0 || 16559490a627SAlexander V. Chernikov strnlen(aname, sizeof(i->algoname)) == sizeof(i->algoname)) 16569490a627SAlexander V. Chernikov return (EINVAL); 16579490a627SAlexander V. Chernikov 16589490a627SAlexander V. Chernikov if (aname[0] == '\0') { 16599490a627SAlexander V. Chernikov /* Use default algorithm */ 16609490a627SAlexander V. Chernikov aname = NULL; 16619490a627SAlexander V. Chernikov } 16629490a627SAlexander V. Chernikov 16639490a627SAlexander V. Chernikov objheader_to_ti(oh, &ti); 1664ac35ff17SAlexander V. Chernikov ti.type = i->type; 16659490a627SAlexander V. Chernikov 16669490a627SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 16679490a627SAlexander V. Chernikov 16689490a627SAlexander V. Chernikov IPFW_UH_RLOCK(ch); 16699490a627SAlexander V. Chernikov if ((tc = find_table(ni, &ti)) != NULL) { 16709490a627SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 16719490a627SAlexander V. Chernikov return (EEXIST); 16729490a627SAlexander V. Chernikov } 16739490a627SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 16749490a627SAlexander V. Chernikov 16754c0c07a5SAlexander V. Chernikov return (create_table_internal(ch, &ti, aname, i)); 1676db785d31SAlexander V. Chernikov } 1677db785d31SAlexander V. Chernikov 1678db785d31SAlexander V. Chernikov /* 1679db785d31SAlexander V. Chernikov * Creates new table based on @ti and @aname. 1680db785d31SAlexander V. Chernikov * 1681db785d31SAlexander V. Chernikov * Relies on table name checking inside find_name_tlv() 1682db785d31SAlexander V. Chernikov * Assume @aname to be checked and valid. 1683db785d31SAlexander V. Chernikov * 1684db785d31SAlexander V. Chernikov * Returns 0 on success. 1685db785d31SAlexander V. Chernikov */ 1686db785d31SAlexander V. Chernikov static int 1687db785d31SAlexander V. Chernikov create_table_internal(struct ip_fw_chain *ch, struct tid_info *ti, 16884c0c07a5SAlexander V. Chernikov char *aname, ipfw_xtable_info *i) 1689db785d31SAlexander V. Chernikov { 1690db785d31SAlexander V. Chernikov struct namedobj_instance *ni; 1691db785d31SAlexander V. Chernikov struct table_config *tc; 1692db785d31SAlexander V. Chernikov struct table_algo *ta; 1693db785d31SAlexander V. Chernikov uint16_t kidx; 1694db785d31SAlexander V. Chernikov 1695db785d31SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 1696db785d31SAlexander V. Chernikov 1697db785d31SAlexander V. Chernikov ta = find_table_algo(CHAIN_TO_TCFG(ch), ti, aname); 16989490a627SAlexander V. Chernikov if (ta == NULL) 16999490a627SAlexander V. Chernikov return (ENOTSUP); 17009490a627SAlexander V. Chernikov 17014c0c07a5SAlexander V. Chernikov tc = alloc_table_config(ch, ti, ta, aname, i->tflags, i->vtype); 17024c0c07a5SAlexander V. Chernikov if (tc == NULL) 17039490a627SAlexander V. Chernikov return (ENOMEM); 17049490a627SAlexander V. Chernikov 1705adf3b2b9SAlexander V. Chernikov tc->vftype = i->vftype; 17064c0c07a5SAlexander V. Chernikov tc->limit = i->limit; 17074c0c07a5SAlexander V. Chernikov 17089490a627SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 1709db785d31SAlexander V. Chernikov 1710db785d31SAlexander V. Chernikov /* Check if table has been already created */ 1711db785d31SAlexander V. Chernikov if (find_table(ni, ti) != NULL) { 1712db785d31SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1713db785d31SAlexander V. Chernikov free_table_config(ni, tc); 1714db785d31SAlexander V. Chernikov return (EEXIST); 1715db785d31SAlexander V. Chernikov } 1716db785d31SAlexander V. Chernikov 1717ac35ff17SAlexander V. Chernikov if (ipfw_objhash_alloc_idx(ni, &kidx) != 0) { 17189490a627SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1719db785d31SAlexander V. Chernikov printf("Unable to allocate table index." 1720db785d31SAlexander V. Chernikov " Consider increasing net.inet.ip.fw.tables_max"); 17219490a627SAlexander V. Chernikov free_table_config(ni, tc); 17229490a627SAlexander V. Chernikov return (EBUSY); 17239490a627SAlexander V. Chernikov } 17249490a627SAlexander V. Chernikov 17259490a627SAlexander V. Chernikov tc->no.kidx = kidx; 17269490a627SAlexander V. Chernikov 17279490a627SAlexander V. Chernikov IPFW_WLOCK(ch); 17289490a627SAlexander V. Chernikov link_table(ch, tc); 17299490a627SAlexander V. Chernikov IPFW_WUNLOCK(ch); 17309490a627SAlexander V. Chernikov 17319490a627SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 17329490a627SAlexander V. Chernikov 17339490a627SAlexander V. Chernikov return (0); 17349490a627SAlexander V. Chernikov } 17359490a627SAlexander V. Chernikov 173646d52008SAlexander V. Chernikov static void 173746d52008SAlexander V. Chernikov ntlv_to_ti(ipfw_obj_ntlv *ntlv, struct tid_info *ti) 1738d3a4f924SAlexander V. Chernikov { 1739d3a4f924SAlexander V. Chernikov 1740d3a4f924SAlexander V. Chernikov memset(ti, 0, sizeof(struct tid_info)); 174146d52008SAlexander V. Chernikov ti->set = ntlv->set; 174246d52008SAlexander V. Chernikov ti->uidx = ntlv->idx; 174346d52008SAlexander V. Chernikov ti->tlvs = ntlv; 174446d52008SAlexander V. Chernikov ti->tlen = ntlv->head.length; 174546d52008SAlexander V. Chernikov } 174646d52008SAlexander V. Chernikov 174746d52008SAlexander V. Chernikov static void 174846d52008SAlexander V. Chernikov objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti) 174946d52008SAlexander V. Chernikov { 175046d52008SAlexander V. Chernikov 175146d52008SAlexander V. Chernikov ntlv_to_ti(&oh->ntlv, ti); 1752d3a4f924SAlexander V. Chernikov } 1753d3a4f924SAlexander V. Chernikov 1754563b5ab1SAlexander V. Chernikov int 1755563b5ab1SAlexander V. Chernikov ipfw_export_table_ntlv(struct ip_fw_chain *ch, uint16_t kidx, 1756563b5ab1SAlexander V. Chernikov struct sockopt_data *sd) 1757563b5ab1SAlexander V. Chernikov { 1758563b5ab1SAlexander V. Chernikov struct namedobj_instance *ni; 1759563b5ab1SAlexander V. Chernikov struct named_object *no; 1760563b5ab1SAlexander V. Chernikov ipfw_obj_ntlv *ntlv; 1761563b5ab1SAlexander V. Chernikov 1762563b5ab1SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 1763563b5ab1SAlexander V. Chernikov 1764ac35ff17SAlexander V. Chernikov no = ipfw_objhash_lookup_kidx(ni, kidx); 1765563b5ab1SAlexander V. Chernikov KASSERT(no != NULL, ("invalid table kidx passed")); 1766563b5ab1SAlexander V. Chernikov 1767563b5ab1SAlexander V. Chernikov ntlv = (ipfw_obj_ntlv *)ipfw_get_sopt_space(sd, sizeof(*ntlv)); 1768563b5ab1SAlexander V. Chernikov if (ntlv == NULL) 1769563b5ab1SAlexander V. Chernikov return (ENOMEM); 1770563b5ab1SAlexander V. Chernikov 1771563b5ab1SAlexander V. Chernikov ntlv->head.type = IPFW_TLV_TBL_NAME; 1772563b5ab1SAlexander V. Chernikov ntlv->head.length = sizeof(*ntlv); 1773563b5ab1SAlexander V. Chernikov ntlv->idx = no->kidx; 1774563b5ab1SAlexander V. Chernikov strlcpy(ntlv->name, no->name, sizeof(ntlv->name)); 1775563b5ab1SAlexander V. Chernikov 1776563b5ab1SAlexander V. Chernikov return (0); 1777563b5ab1SAlexander V. Chernikov } 1778563b5ab1SAlexander V. Chernikov 17799f7d47b0SAlexander V. Chernikov static void 1780ac35ff17SAlexander V. Chernikov export_table_info(struct ip_fw_chain *ch, struct table_config *tc, 1781ac35ff17SAlexander V. Chernikov ipfw_xtable_info *i) 17829f7d47b0SAlexander V. Chernikov { 1783ac35ff17SAlexander V. Chernikov struct table_info *ti; 17845f379342SAlexander V. Chernikov struct table_algo *ta; 17859f7d47b0SAlexander V. Chernikov 17869f7d47b0SAlexander V. Chernikov i->type = tc->no.type; 1787914bffb6SAlexander V. Chernikov i->tflags = tc->tflags; 1788ac35ff17SAlexander V. Chernikov i->vtype = tc->vtype; 1789adf3b2b9SAlexander V. Chernikov i->vftype = tc->vftype; 17909f7d47b0SAlexander V. Chernikov i->set = tc->no.set; 17919f7d47b0SAlexander V. Chernikov i->kidx = tc->no.kidx; 17929f7d47b0SAlexander V. Chernikov i->refcnt = tc->no.refcnt; 17939f7d47b0SAlexander V. Chernikov i->count = tc->count; 17944c0c07a5SAlexander V. Chernikov i->limit = tc->limit; 179581d3153dSAlexander V. Chernikov i->size = tc->count * sizeof(ipfw_obj_tentry); 1796f1220db8SAlexander V. Chernikov i->size += sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info); 17979f7d47b0SAlexander V. Chernikov strlcpy(i->tablename, tc->tablename, sizeof(i->tablename)); 1798ac35ff17SAlexander V. Chernikov ti = KIDX_TO_TI(ch, tc->no.kidx); 17995f379342SAlexander V. Chernikov ta = tc->ta; 18005f379342SAlexander V. Chernikov if (ta->print_config != NULL) { 18015f379342SAlexander V. Chernikov /* Use algo function to print table config to string */ 18025f379342SAlexander V. Chernikov ta->print_config(tc->astate, ti, i->algoname, 1803ac35ff17SAlexander V. Chernikov sizeof(i->algoname)); 1804ac35ff17SAlexander V. Chernikov } else 18055f379342SAlexander V. Chernikov strlcpy(i->algoname, ta->name, sizeof(i->algoname)); 18065f379342SAlexander V. Chernikov /* Dump algo-specific data, if possible */ 18075f379342SAlexander V. Chernikov if (ta->dump_tinfo != NULL) { 18085f379342SAlexander V. Chernikov ta->dump_tinfo(tc->astate, ti, &i->ta_info); 18095f379342SAlexander V. Chernikov i->ta_info.flags |= IPFW_TATFLAGS_DATA; 18105f379342SAlexander V. Chernikov } 18119f7d47b0SAlexander V. Chernikov } 18129f7d47b0SAlexander V. Chernikov 1813ac35ff17SAlexander V. Chernikov struct dump_table_args { 1814ac35ff17SAlexander V. Chernikov struct ip_fw_chain *ch; 1815ac35ff17SAlexander V. Chernikov struct sockopt_data *sd; 1816ac35ff17SAlexander V. Chernikov }; 1817ac35ff17SAlexander V. Chernikov 18189f7d47b0SAlexander V. Chernikov static void 18199f7d47b0SAlexander V. Chernikov export_table_internal(struct namedobj_instance *ni, struct named_object *no, 18209f7d47b0SAlexander V. Chernikov void *arg) 18213b3a8eb9SGleb Smirnoff { 18229f7d47b0SAlexander V. Chernikov ipfw_xtable_info *i; 1823ac35ff17SAlexander V. Chernikov struct dump_table_args *dta; 18243b3a8eb9SGleb Smirnoff 1825ac35ff17SAlexander V. Chernikov dta = (struct dump_table_args *)arg; 1826ac35ff17SAlexander V. Chernikov 1827ac35ff17SAlexander V. Chernikov i = (ipfw_xtable_info *)ipfw_get_sopt_space(dta->sd, sizeof(*i)); 182868394ec8SAlexander V. Chernikov KASSERT(i != 0, ("previously checked buffer is not enough")); 18299f7d47b0SAlexander V. Chernikov 1830ac35ff17SAlexander V. Chernikov export_table_info(dta->ch, (struct table_config *)no, i); 18319f7d47b0SAlexander V. Chernikov } 18329f7d47b0SAlexander V. Chernikov 1833f1220db8SAlexander V. Chernikov /* 1834f1220db8SAlexander V. Chernikov * Export all tables as ipfw_xtable_info structures to 18352d99a349SAlexander V. Chernikov * storage provided by @sd. 183628ea4fa3SAlexander V. Chernikov * 183728ea4fa3SAlexander V. Chernikov * If supplied buffer is too small, fills in required size 183828ea4fa3SAlexander V. Chernikov * and returns ENOMEM. 1839f1220db8SAlexander V. Chernikov * Returns 0 on success. 1840f1220db8SAlexander V. Chernikov */ 1841f1220db8SAlexander V. Chernikov static int 18422d99a349SAlexander V. Chernikov export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh, 18432d99a349SAlexander V. Chernikov struct sockopt_data *sd) 18449f7d47b0SAlexander V. Chernikov { 18459f7d47b0SAlexander V. Chernikov uint32_t size; 18469f7d47b0SAlexander V. Chernikov uint32_t count; 1847ac35ff17SAlexander V. Chernikov struct dump_table_args dta; 18489f7d47b0SAlexander V. Chernikov 18499f7d47b0SAlexander V. Chernikov count = ipfw_objhash_count(CHAIN_TO_NI(ch)); 18509f7d47b0SAlexander V. Chernikov size = count * sizeof(ipfw_xtable_info) + sizeof(ipfw_obj_lheader); 18512d99a349SAlexander V. Chernikov 18522d99a349SAlexander V. Chernikov /* Fill in header regadless of buffer size */ 1853f1220db8SAlexander V. Chernikov olh->count = count; 1854f1220db8SAlexander V. Chernikov olh->objsize = sizeof(ipfw_xtable_info); 18552d99a349SAlexander V. Chernikov 18562d99a349SAlexander V. Chernikov if (size > olh->size) { 18572d99a349SAlexander V. Chernikov olh->size = size; 18589f7d47b0SAlexander V. Chernikov return (ENOMEM); 1859f1220db8SAlexander V. Chernikov } 186068394ec8SAlexander V. Chernikov 18619f7d47b0SAlexander V. Chernikov olh->size = size; 18622d99a349SAlexander V. Chernikov 1863ac35ff17SAlexander V. Chernikov dta.ch = ch; 1864ac35ff17SAlexander V. Chernikov dta.sd = sd; 1865ac35ff17SAlexander V. Chernikov 1866ac35ff17SAlexander V. Chernikov ipfw_objhash_foreach(CHAIN_TO_NI(ch), export_table_internal, &dta); 18679f7d47b0SAlexander V. Chernikov 18683b3a8eb9SGleb Smirnoff return (0); 18693b3a8eb9SGleb Smirnoff } 18703b3a8eb9SGleb Smirnoff 187181d3153dSAlexander V. Chernikov /* 187281d3153dSAlexander V. Chernikov * Legacy IP_FW_TABLE_GETSIZE handler 187381d3153dSAlexander V. Chernikov */ 18743b3a8eb9SGleb Smirnoff int 1875b074b7bbSAlexander V. Chernikov ipfw_count_table(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt) 18763b3a8eb9SGleb Smirnoff { 1877b074b7bbSAlexander V. Chernikov struct table_config *tc; 18783b3a8eb9SGleb Smirnoff 1879b074b7bbSAlexander V. Chernikov if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL) 1880b074b7bbSAlexander V. Chernikov return (ESRCH); 18819f7d47b0SAlexander V. Chernikov *cnt = tc->count; 18823b3a8eb9SGleb Smirnoff return (0); 18833b3a8eb9SGleb Smirnoff } 18843b3a8eb9SGleb Smirnoff 18859f7d47b0SAlexander V. Chernikov 188681d3153dSAlexander V. Chernikov /* 188781d3153dSAlexander V. Chernikov * Legacy IP_FW_TABLE_XGETSIZE handler 188881d3153dSAlexander V. Chernikov */ 18899f7d47b0SAlexander V. Chernikov int 18909f7d47b0SAlexander V. Chernikov ipfw_count_xtable(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt) 18913b3a8eb9SGleb Smirnoff { 18929f7d47b0SAlexander V. Chernikov struct table_config *tc; 18939f7d47b0SAlexander V. Chernikov 1894d3a4f924SAlexander V. Chernikov if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL) { 1895d3a4f924SAlexander V. Chernikov *cnt = 0; 18969f7d47b0SAlexander V. Chernikov return (0); /* 'table all list' requires success */ 1897d3a4f924SAlexander V. Chernikov } 18989f7d47b0SAlexander V. Chernikov *cnt = tc->count * sizeof(ipfw_table_xentry); 18999f7d47b0SAlexander V. Chernikov if (tc->count > 0) 19009f7d47b0SAlexander V. Chernikov *cnt += sizeof(ipfw_xtable); 19019f7d47b0SAlexander V. Chernikov return (0); 19029f7d47b0SAlexander V. Chernikov } 19039f7d47b0SAlexander V. Chernikov 19049f7d47b0SAlexander V. Chernikov static int 19059f7d47b0SAlexander V. Chernikov dump_table_entry(void *e, void *arg) 19069f7d47b0SAlexander V. Chernikov { 19079f7d47b0SAlexander V. Chernikov struct dump_args *da; 19089f7d47b0SAlexander V. Chernikov struct table_config *tc; 19099f7d47b0SAlexander V. Chernikov struct table_algo *ta; 19103b3a8eb9SGleb Smirnoff ipfw_table_entry *ent; 191181d3153dSAlexander V. Chernikov int error; 19123b3a8eb9SGleb Smirnoff 19139f7d47b0SAlexander V. Chernikov da = (struct dump_args *)arg; 19149f7d47b0SAlexander V. Chernikov 19159f7d47b0SAlexander V. Chernikov tc = da->tc; 19169f7d47b0SAlexander V. Chernikov ta = tc->ta; 19179f7d47b0SAlexander V. Chernikov 19189f7d47b0SAlexander V. Chernikov /* Out of memory, returning */ 1919f1220db8SAlexander V. Chernikov if (da->cnt == da->size) 19203b3a8eb9SGleb Smirnoff return (1); 1921f1220db8SAlexander V. Chernikov ent = da->ent++; 1922f1220db8SAlexander V. Chernikov ent->tbl = da->uidx; 1923f1220db8SAlexander V. Chernikov da->cnt++; 19249f7d47b0SAlexander V. Chernikov 192581d3153dSAlexander V. Chernikov error = ta->dump_tentry(tc->astate, da->ti, e, &da->tent); 192681d3153dSAlexander V. Chernikov if (error != 0) 192781d3153dSAlexander V. Chernikov return (error); 192881d3153dSAlexander V. Chernikov 192981d3153dSAlexander V. Chernikov ent->addr = da->tent.k.addr.s_addr; 193081d3153dSAlexander V. Chernikov ent->masklen = da->tent.masklen; 193181d3153dSAlexander V. Chernikov ent->value = da->tent.value; 193281d3153dSAlexander V. Chernikov 193381d3153dSAlexander V. Chernikov return (0); 19343b3a8eb9SGleb Smirnoff } 19353b3a8eb9SGleb Smirnoff 1936f1220db8SAlexander V. Chernikov /* 1937f1220db8SAlexander V. Chernikov * Dumps table in pre-8.1 legacy format. 1938f1220db8SAlexander V. Chernikov */ 19393b3a8eb9SGleb Smirnoff int 1940f1220db8SAlexander V. Chernikov ipfw_dump_table_legacy(struct ip_fw_chain *ch, struct tid_info *ti, 1941f1220db8SAlexander V. Chernikov ipfw_table *tbl) 19423b3a8eb9SGleb Smirnoff { 1943b074b7bbSAlexander V. Chernikov struct table_config *tc; 19449f7d47b0SAlexander V. Chernikov struct table_algo *ta; 19459f7d47b0SAlexander V. Chernikov struct dump_args da; 19463b3a8eb9SGleb Smirnoff 19473b3a8eb9SGleb Smirnoff tbl->cnt = 0; 19483b3a8eb9SGleb Smirnoff 1949b074b7bbSAlexander V. Chernikov if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL) 1950b074b7bbSAlexander V. Chernikov return (0); /* XXX: We should return ESRCH */ 19519f7d47b0SAlexander V. Chernikov 19529f7d47b0SAlexander V. Chernikov ta = tc->ta; 19539f7d47b0SAlexander V. Chernikov 195481d3153dSAlexander V. Chernikov /* This dump format supports IPv4 only */ 195581d3153dSAlexander V. Chernikov if (tc->no.type != IPFW_TABLE_CIDR) 195681d3153dSAlexander V. Chernikov return (0); 19579f7d47b0SAlexander V. Chernikov 1958d3a4f924SAlexander V. Chernikov memset(&da, 0, sizeof(da)); 19599f7d47b0SAlexander V. Chernikov da.ti = KIDX_TO_TI(ch, tc->no.kidx); 19609f7d47b0SAlexander V. Chernikov da.tc = tc; 1961f1220db8SAlexander V. Chernikov da.ent = &tbl->ent[0]; 1962f1220db8SAlexander V. Chernikov da.size = tbl->size; 19639f7d47b0SAlexander V. Chernikov 19649f7d47b0SAlexander V. Chernikov tbl->cnt = 0; 19659f7d47b0SAlexander V. Chernikov ta->foreach(tc->astate, da.ti, dump_table_entry, &da); 1966f1220db8SAlexander V. Chernikov tbl->cnt = da.cnt; 19679f7d47b0SAlexander V. Chernikov 19683b3a8eb9SGleb Smirnoff return (0); 19693b3a8eb9SGleb Smirnoff } 19703b3a8eb9SGleb Smirnoff 19719490a627SAlexander V. Chernikov /* 197281d3153dSAlexander V. Chernikov * Dumps table entry in eXtended format (v1)(current). 197381d3153dSAlexander V. Chernikov */ 197481d3153dSAlexander V. Chernikov static int 197581d3153dSAlexander V. Chernikov dump_table_tentry(void *e, void *arg) 197681d3153dSAlexander V. Chernikov { 197781d3153dSAlexander V. Chernikov struct dump_args *da; 197881d3153dSAlexander V. Chernikov struct table_config *tc; 197981d3153dSAlexander V. Chernikov struct table_algo *ta; 198081d3153dSAlexander V. Chernikov ipfw_obj_tentry *tent; 198181d3153dSAlexander V. Chernikov 198281d3153dSAlexander V. Chernikov da = (struct dump_args *)arg; 198381d3153dSAlexander V. Chernikov 198481d3153dSAlexander V. Chernikov tc = da->tc; 198581d3153dSAlexander V. Chernikov ta = tc->ta; 198681d3153dSAlexander V. Chernikov 198781d3153dSAlexander V. Chernikov tent = (ipfw_obj_tentry *)ipfw_get_sopt_space(da->sd, sizeof(*tent)); 198881d3153dSAlexander V. Chernikov /* Out of memory, returning */ 198981d3153dSAlexander V. Chernikov if (tent == NULL) { 199081d3153dSAlexander V. Chernikov da->error = ENOMEM; 199181d3153dSAlexander V. Chernikov return (1); 199281d3153dSAlexander V. Chernikov } 199381d3153dSAlexander V. Chernikov tent->head.length = sizeof(ipfw_obj_tentry); 199481d3153dSAlexander V. Chernikov tent->idx = da->uidx; 199581d3153dSAlexander V. Chernikov 199681d3153dSAlexander V. Chernikov return (ta->dump_tentry(tc->astate, da->ti, e, tent)); 199781d3153dSAlexander V. Chernikov } 199881d3153dSAlexander V. Chernikov 199981d3153dSAlexander V. Chernikov /* 200081d3153dSAlexander V. Chernikov * Dumps table entry in eXtended format (v0). 20019490a627SAlexander V. Chernikov */ 20023b3a8eb9SGleb Smirnoff static int 20039f7d47b0SAlexander V. Chernikov dump_table_xentry(void *e, void *arg) 20043b3a8eb9SGleb Smirnoff { 20059f7d47b0SAlexander V. Chernikov struct dump_args *da; 20069f7d47b0SAlexander V. Chernikov struct table_config *tc; 20079f7d47b0SAlexander V. Chernikov struct table_algo *ta; 20083b3a8eb9SGleb Smirnoff ipfw_table_xentry *xent; 200981d3153dSAlexander V. Chernikov ipfw_obj_tentry *tent; 201081d3153dSAlexander V. Chernikov int error; 20113b3a8eb9SGleb Smirnoff 20129f7d47b0SAlexander V. Chernikov da = (struct dump_args *)arg; 20139f7d47b0SAlexander V. Chernikov 20149f7d47b0SAlexander V. Chernikov tc = da->tc; 20159f7d47b0SAlexander V. Chernikov ta = tc->ta; 20169f7d47b0SAlexander V. Chernikov 20172d99a349SAlexander V. Chernikov xent = (ipfw_table_xentry *)ipfw_get_sopt_space(da->sd, sizeof(*xent)); 20183b3a8eb9SGleb Smirnoff /* Out of memory, returning */ 20192d99a349SAlexander V. Chernikov if (xent == NULL) 20203b3a8eb9SGleb Smirnoff return (1); 20213b3a8eb9SGleb Smirnoff xent->len = sizeof(ipfw_table_xentry); 2022f1220db8SAlexander V. Chernikov xent->tbl = da->uidx; 20239f7d47b0SAlexander V. Chernikov 202481d3153dSAlexander V. Chernikov memset(&da->tent, 0, sizeof(da->tent)); 202581d3153dSAlexander V. Chernikov tent = &da->tent; 202681d3153dSAlexander V. Chernikov error = ta->dump_tentry(tc->astate, da->ti, e, tent); 202781d3153dSAlexander V. Chernikov if (error != 0) 202881d3153dSAlexander V. Chernikov return (error); 202981d3153dSAlexander V. Chernikov 203081d3153dSAlexander V. Chernikov /* Convert current format to previous one */ 203181d3153dSAlexander V. Chernikov xent->masklen = tent->masklen; 203281d3153dSAlexander V. Chernikov xent->value = tent->value; 203381d3153dSAlexander V. Chernikov /* Apply some hacks */ 203481d3153dSAlexander V. Chernikov if (tc->no.type == IPFW_TABLE_CIDR && tent->subtype == AF_INET) { 203581d3153dSAlexander V. Chernikov xent->k.addr6.s6_addr32[3] = tent->k.addr.s_addr; 203681d3153dSAlexander V. Chernikov xent->flags = IPFW_TCF_INET; 203781d3153dSAlexander V. Chernikov } else 203881d3153dSAlexander V. Chernikov memcpy(&xent->k, &tent->k, sizeof(xent->k)); 203981d3153dSAlexander V. Chernikov 204081d3153dSAlexander V. Chernikov return (0); 20419f7d47b0SAlexander V. Chernikov } 20429f7d47b0SAlexander V. Chernikov 20439f7d47b0SAlexander V. Chernikov /* 20449f7d47b0SAlexander V. Chernikov * Table algorithms 20459f7d47b0SAlexander V. Chernikov */ 20463b3a8eb9SGleb Smirnoff 20479f7d47b0SAlexander V. Chernikov /* 20489490a627SAlexander V. Chernikov * Finds algoritm by index, table type or supplied name 20499f7d47b0SAlexander V. Chernikov */ 20509f7d47b0SAlexander V. Chernikov static struct table_algo * 20519490a627SAlexander V. Chernikov find_table_algo(struct tables_config *tcfg, struct tid_info *ti, char *name) 20529f7d47b0SAlexander V. Chernikov { 20539490a627SAlexander V. Chernikov int i, l; 20549490a627SAlexander V. Chernikov struct table_algo *ta; 20559f7d47b0SAlexander V. Chernikov 205657a1cf95SAlexander V. Chernikov if (ti->type > IPFW_TABLE_MAXTYPE) 205757a1cf95SAlexander V. Chernikov return (NULL); 205857a1cf95SAlexander V. Chernikov 20599f7d47b0SAlexander V. Chernikov /* Search by index */ 20609f7d47b0SAlexander V. Chernikov if (ti->atype != 0) { 20619f7d47b0SAlexander V. Chernikov if (ti->atype > tcfg->algo_count) 20629f7d47b0SAlexander V. Chernikov return (NULL); 20639f7d47b0SAlexander V. Chernikov return (tcfg->algo[ti->atype]); 20649f7d47b0SAlexander V. Chernikov } 20659f7d47b0SAlexander V. Chernikov 20669490a627SAlexander V. Chernikov /* Search by name if supplied */ 20679490a627SAlexander V. Chernikov if (name != NULL) { 20689490a627SAlexander V. Chernikov /* TODO: better search */ 20699490a627SAlexander V. Chernikov for (i = 1; i <= tcfg->algo_count; i++) { 20709490a627SAlexander V. Chernikov ta = tcfg->algo[i]; 20719490a627SAlexander V. Chernikov 20729490a627SAlexander V. Chernikov /* 20739490a627SAlexander V. Chernikov * One can supply additional algorithm 20749490a627SAlexander V. Chernikov * parameters so we compare only the first word 20759490a627SAlexander V. Chernikov * of supplied name: 20769490a627SAlexander V. Chernikov * 'hash_cidr hsize=32' 20779490a627SAlexander V. Chernikov * '^^^^^^^^^' 20789490a627SAlexander V. Chernikov * 20799490a627SAlexander V. Chernikov */ 20809490a627SAlexander V. Chernikov l = strlen(ta->name); 20819490a627SAlexander V. Chernikov if (strncmp(name, ta->name, l) == 0) { 20829490a627SAlexander V. Chernikov if (name[l] == '\0' || name[l] == ' ') 20839490a627SAlexander V. Chernikov return (ta); 20849490a627SAlexander V. Chernikov } 20859490a627SAlexander V. Chernikov } 20869490a627SAlexander V. Chernikov 20879490a627SAlexander V. Chernikov return (NULL); 20889490a627SAlexander V. Chernikov } 20899490a627SAlexander V. Chernikov 209057a1cf95SAlexander V. Chernikov /* Return default algorithm for given type if set */ 209157a1cf95SAlexander V. Chernikov return (tcfg->def_algo[ti->type]); 20923b3a8eb9SGleb Smirnoff } 20933b3a8eb9SGleb Smirnoff 2094b6ee846eSAlexander V. Chernikov /* 2095b6ee846eSAlexander V. Chernikov * Register new table algo @ta. 2096b6ee846eSAlexander V. Chernikov * Stores algo id iside @idx.<F2> 2097b6ee846eSAlexander V. Chernikov * 2098b6ee846eSAlexander V. Chernikov * Returns 0 on success. 2099b6ee846eSAlexander V. Chernikov */ 21000b565ac0SAlexander V. Chernikov int 21010b565ac0SAlexander V. Chernikov ipfw_add_table_algo(struct ip_fw_chain *ch, struct table_algo *ta, size_t size, 21020b565ac0SAlexander V. Chernikov int *idx) 21033b3a8eb9SGleb Smirnoff { 21049f7d47b0SAlexander V. Chernikov struct tables_config *tcfg; 21050b565ac0SAlexander V. Chernikov struct table_algo *ta_new; 2106b6ee846eSAlexander V. Chernikov size_t sz; 21070b565ac0SAlexander V. Chernikov 21080b565ac0SAlexander V. Chernikov if (size > sizeof(struct table_algo)) 21090b565ac0SAlexander V. Chernikov return (EINVAL); 21100b565ac0SAlexander V. Chernikov 2111b6ee846eSAlexander V. Chernikov /* Check for the required on-stack size for add/del */ 2112b6ee846eSAlexander V. Chernikov sz = roundup2(ta->ta_buf_size, sizeof(void *)); 2113b6ee846eSAlexander V. Chernikov if (sz > TA_BUF_SZ) 2114b6ee846eSAlexander V. Chernikov return (EINVAL); 2115b6ee846eSAlexander V. Chernikov 211657a1cf95SAlexander V. Chernikov KASSERT(ta->type >= IPFW_TABLE_MAXTYPE,("Increase IPFW_TABLE_MAXTYPE")); 211757a1cf95SAlexander V. Chernikov 21180b565ac0SAlexander V. Chernikov ta_new = malloc(sizeof(struct table_algo), M_IPFW, M_WAITOK | M_ZERO); 21190b565ac0SAlexander V. Chernikov memcpy(ta_new, ta, size); 21203b3a8eb9SGleb Smirnoff 21219f7d47b0SAlexander V. Chernikov tcfg = CHAIN_TO_TCFG(ch); 2122b074b7bbSAlexander V. Chernikov 21239f7d47b0SAlexander V. Chernikov KASSERT(tcfg->algo_count < 255, ("Increase algo array size")); 21249f7d47b0SAlexander V. Chernikov 21250b565ac0SAlexander V. Chernikov tcfg->algo[++tcfg->algo_count] = ta_new; 21260b565ac0SAlexander V. Chernikov ta_new->idx = tcfg->algo_count; 21270b565ac0SAlexander V. Chernikov 212857a1cf95SAlexander V. Chernikov /* Set algorithm as default one for given type */ 212957a1cf95SAlexander V. Chernikov if ((ta_new->flags & TA_FLAG_DEFAULT) != 0 && 213057a1cf95SAlexander V. Chernikov tcfg->def_algo[ta_new->type] == NULL) 213157a1cf95SAlexander V. Chernikov tcfg->def_algo[ta_new->type] = ta_new; 213257a1cf95SAlexander V. Chernikov 21330b565ac0SAlexander V. Chernikov *idx = ta_new->idx; 21340b565ac0SAlexander V. Chernikov 21350b565ac0SAlexander V. Chernikov return (0); 21360b565ac0SAlexander V. Chernikov } 21370b565ac0SAlexander V. Chernikov 2138b6ee846eSAlexander V. Chernikov /* 2139b6ee846eSAlexander V. Chernikov * Unregisters table algo using @idx as id. 2140b6ee846eSAlexander V. Chernikov */ 21410b565ac0SAlexander V. Chernikov void 21420b565ac0SAlexander V. Chernikov ipfw_del_table_algo(struct ip_fw_chain *ch, int idx) 21430b565ac0SAlexander V. Chernikov { 21440b565ac0SAlexander V. Chernikov struct tables_config *tcfg; 21450b565ac0SAlexander V. Chernikov struct table_algo *ta; 21460b565ac0SAlexander V. Chernikov 21470b565ac0SAlexander V. Chernikov tcfg = CHAIN_TO_TCFG(ch); 21480b565ac0SAlexander V. Chernikov 2149b6ee846eSAlexander V. Chernikov KASSERT(idx <= tcfg->algo_count, ("algo idx %d out of range 1..%d", 2150b6ee846eSAlexander V. Chernikov idx, tcfg->algo_count)); 21510b565ac0SAlexander V. Chernikov 21520b565ac0SAlexander V. Chernikov ta = tcfg->algo[idx]; 21530b565ac0SAlexander V. Chernikov KASSERT(ta != NULL, ("algo idx %d is NULL", idx)); 215457a1cf95SAlexander V. Chernikov 215557a1cf95SAlexander V. Chernikov if (tcfg->def_algo[ta->type] == ta) 215657a1cf95SAlexander V. Chernikov tcfg->def_algo[ta->type] = NULL; 215757a1cf95SAlexander V. Chernikov 21580b565ac0SAlexander V. Chernikov free(ta, M_IPFW); 21593b3a8eb9SGleb Smirnoff } 21603b3a8eb9SGleb Smirnoff 21619d099b4fSAlexander V. Chernikov /* 21629d099b4fSAlexander V. Chernikov * Lists all table algorithms currently available. 21639d099b4fSAlexander V. Chernikov * Data layout (v0)(current): 21649d099b4fSAlexander V. Chernikov * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size 21659d099b4fSAlexander V. Chernikov * Reply: [ ipfw_obj_lheader ipfw_ta_info x N ] 21669d099b4fSAlexander V. Chernikov * 21679d099b4fSAlexander V. Chernikov * Returns 0 on success 21689d099b4fSAlexander V. Chernikov */ 21699d099b4fSAlexander V. Chernikov int 21709d099b4fSAlexander V. Chernikov ipfw_list_table_algo(struct ip_fw_chain *ch, struct sockopt_data *sd) 21719d099b4fSAlexander V. Chernikov { 21729d099b4fSAlexander V. Chernikov struct _ipfw_obj_lheader *olh; 21739d099b4fSAlexander V. Chernikov struct tables_config *tcfg; 21749d099b4fSAlexander V. Chernikov ipfw_ta_info *i; 21759d099b4fSAlexander V. Chernikov struct table_algo *ta; 21769d099b4fSAlexander V. Chernikov uint32_t count, n, size; 21779d099b4fSAlexander V. Chernikov 21789d099b4fSAlexander V. Chernikov olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh)); 21799d099b4fSAlexander V. Chernikov if (olh == NULL) 21809d099b4fSAlexander V. Chernikov return (EINVAL); 21819d099b4fSAlexander V. Chernikov if (sd->valsize < olh->size) 21829d099b4fSAlexander V. Chernikov return (EINVAL); 21839d099b4fSAlexander V. Chernikov 21849d099b4fSAlexander V. Chernikov IPFW_UH_RLOCK(ch); 21859d099b4fSAlexander V. Chernikov tcfg = CHAIN_TO_TCFG(ch); 21869d099b4fSAlexander V. Chernikov count = tcfg->algo_count; 21879d099b4fSAlexander V. Chernikov size = count * sizeof(ipfw_ta_info) + sizeof(ipfw_obj_lheader); 21889d099b4fSAlexander V. Chernikov 21899d099b4fSAlexander V. Chernikov /* Fill in header regadless of buffer size */ 21909d099b4fSAlexander V. Chernikov olh->count = count; 21919d099b4fSAlexander V. Chernikov olh->objsize = sizeof(ipfw_ta_info); 21929d099b4fSAlexander V. Chernikov 21939d099b4fSAlexander V. Chernikov if (size > olh->size) { 21949d099b4fSAlexander V. Chernikov olh->size = size; 21959d099b4fSAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 21969d099b4fSAlexander V. Chernikov return (ENOMEM); 21979d099b4fSAlexander V. Chernikov } 21989d099b4fSAlexander V. Chernikov olh->size = size; 21999d099b4fSAlexander V. Chernikov 22009d099b4fSAlexander V. Chernikov for (n = 1; n <= count; n++) { 22019d099b4fSAlexander V. Chernikov i = (ipfw_ta_info *)ipfw_get_sopt_space(sd, sizeof(*i)); 22029d099b4fSAlexander V. Chernikov KASSERT(i != 0, ("previously checked buffer is not enough")); 22039d099b4fSAlexander V. Chernikov ta = tcfg->algo[n]; 22049d099b4fSAlexander V. Chernikov strlcpy(i->algoname, ta->name, sizeof(i->algoname)); 22059d099b4fSAlexander V. Chernikov i->type = ta->type; 22069d099b4fSAlexander V. Chernikov i->refcnt = ta->refcnt; 22079d099b4fSAlexander V. Chernikov } 22089d099b4fSAlexander V. Chernikov 22099d099b4fSAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 22109d099b4fSAlexander V. Chernikov 22119d099b4fSAlexander V. Chernikov return (0); 22129d099b4fSAlexander V. Chernikov } 22139d099b4fSAlexander V. Chernikov 22149f7d47b0SAlexander V. Chernikov 2215b074b7bbSAlexander V. Chernikov /* 2216b074b7bbSAlexander V. Chernikov * Tables rewriting code 2217b074b7bbSAlexander V. Chernikov * 2218b074b7bbSAlexander V. Chernikov */ 2219b074b7bbSAlexander V. Chernikov 2220b074b7bbSAlexander V. Chernikov /* 2221b074b7bbSAlexander V. Chernikov * Determine table number and lookup type for @cmd. 2222b074b7bbSAlexander V. Chernikov * Fill @tbl and @type with appropriate values. 2223b074b7bbSAlexander V. Chernikov * Returns 0 for relevant opcodes, 1 otherwise. 2224b074b7bbSAlexander V. Chernikov */ 2225b074b7bbSAlexander V. Chernikov static int 2226b074b7bbSAlexander V. Chernikov classify_table_opcode(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) 2227b074b7bbSAlexander V. Chernikov { 2228b074b7bbSAlexander V. Chernikov ipfw_insn_if *cmdif; 2229b074b7bbSAlexander V. Chernikov int skip; 2230b074b7bbSAlexander V. Chernikov uint16_t v; 2231b074b7bbSAlexander V. Chernikov 2232b074b7bbSAlexander V. Chernikov skip = 1; 2233b074b7bbSAlexander V. Chernikov 2234b074b7bbSAlexander V. Chernikov switch (cmd->opcode) { 2235b074b7bbSAlexander V. Chernikov case O_IP_SRC_LOOKUP: 2236b074b7bbSAlexander V. Chernikov case O_IP_DST_LOOKUP: 2237b074b7bbSAlexander V. Chernikov /* Basic IPv4/IPv6 or u32 lookups */ 2238b074b7bbSAlexander V. Chernikov *puidx = cmd->arg1; 2239b074b7bbSAlexander V. Chernikov /* Assume CIDR by default */ 2240b074b7bbSAlexander V. Chernikov *ptype = IPFW_TABLE_CIDR; 2241b074b7bbSAlexander V. Chernikov skip = 0; 2242b074b7bbSAlexander V. Chernikov 2243b074b7bbSAlexander V. Chernikov if (F_LEN(cmd) > F_INSN_SIZE(ipfw_insn_u32)) { 2244b074b7bbSAlexander V. Chernikov /* 2245b074b7bbSAlexander V. Chernikov * generic lookup. The key must be 2246b074b7bbSAlexander V. Chernikov * in 32bit big-endian format. 2247b074b7bbSAlexander V. Chernikov */ 2248b074b7bbSAlexander V. Chernikov v = ((ipfw_insn_u32 *)cmd)->d[1]; 2249b074b7bbSAlexander V. Chernikov switch (v) { 2250b074b7bbSAlexander V. Chernikov case 0: 2251b074b7bbSAlexander V. Chernikov case 1: 2252b074b7bbSAlexander V. Chernikov /* IPv4 src/dst */ 2253b074b7bbSAlexander V. Chernikov break; 2254b074b7bbSAlexander V. Chernikov case 2: 2255b074b7bbSAlexander V. Chernikov case 3: 2256b074b7bbSAlexander V. Chernikov /* src/dst port */ 2257b23d5de9SAlexander V. Chernikov *ptype = IPFW_TABLE_NUMBER; 2258b074b7bbSAlexander V. Chernikov break; 2259b074b7bbSAlexander V. Chernikov case 4: 2260b074b7bbSAlexander V. Chernikov /* uid/gid */ 2261b23d5de9SAlexander V. Chernikov *ptype = IPFW_TABLE_NUMBER; 2262b23d5de9SAlexander V. Chernikov break; 2263b074b7bbSAlexander V. Chernikov case 5: 2264b074b7bbSAlexander V. Chernikov /* jid */ 2265b23d5de9SAlexander V. Chernikov *ptype = IPFW_TABLE_NUMBER; 2266b23d5de9SAlexander V. Chernikov break; 2267b074b7bbSAlexander V. Chernikov case 6: 2268b074b7bbSAlexander V. Chernikov /* dscp */ 2269b23d5de9SAlexander V. Chernikov *ptype = IPFW_TABLE_NUMBER; 2270b074b7bbSAlexander V. Chernikov break; 2271b074b7bbSAlexander V. Chernikov } 2272b074b7bbSAlexander V. Chernikov } 2273b074b7bbSAlexander V. Chernikov break; 2274b074b7bbSAlexander V. Chernikov case O_XMIT: 2275b074b7bbSAlexander V. Chernikov case O_RECV: 2276b074b7bbSAlexander V. Chernikov case O_VIA: 2277b074b7bbSAlexander V. Chernikov /* Interface table, possibly */ 2278b074b7bbSAlexander V. Chernikov cmdif = (ipfw_insn_if *)cmd; 2279b074b7bbSAlexander V. Chernikov if (cmdif->name[0] != '\1') 2280b074b7bbSAlexander V. Chernikov break; 2281b074b7bbSAlexander V. Chernikov 2282b074b7bbSAlexander V. Chernikov *ptype = IPFW_TABLE_INTERFACE; 22838bd19212SAlexander V. Chernikov *puidx = cmdif->p.glob; 2284b074b7bbSAlexander V. Chernikov skip = 0; 2285b074b7bbSAlexander V. Chernikov break; 2286914bffb6SAlexander V. Chernikov case O_IP_FLOW_LOOKUP: 2287914bffb6SAlexander V. Chernikov *puidx = cmd->arg1; 2288914bffb6SAlexander V. Chernikov *ptype = IPFW_TABLE_FLOW; 2289914bffb6SAlexander V. Chernikov skip = 0; 2290914bffb6SAlexander V. Chernikov break; 2291b074b7bbSAlexander V. Chernikov } 2292b074b7bbSAlexander V. Chernikov 2293b074b7bbSAlexander V. Chernikov return (skip); 2294b074b7bbSAlexander V. Chernikov } 2295b074b7bbSAlexander V. Chernikov 2296b074b7bbSAlexander V. Chernikov /* 2297b074b7bbSAlexander V. Chernikov * Sets new table value for given opcode. 2298b074b7bbSAlexander V. Chernikov * Assume the same opcodes as classify_table_opcode() 2299b074b7bbSAlexander V. Chernikov */ 2300b074b7bbSAlexander V. Chernikov static void 2301b074b7bbSAlexander V. Chernikov update_table_opcode(ipfw_insn *cmd, uint16_t idx) 2302b074b7bbSAlexander V. Chernikov { 2303b074b7bbSAlexander V. Chernikov ipfw_insn_if *cmdif; 2304b074b7bbSAlexander V. Chernikov 2305b074b7bbSAlexander V. Chernikov switch (cmd->opcode) { 2306b074b7bbSAlexander V. Chernikov case O_IP_SRC_LOOKUP: 2307b074b7bbSAlexander V. Chernikov case O_IP_DST_LOOKUP: 2308b074b7bbSAlexander V. Chernikov /* Basic IPv4/IPv6 or u32 lookups */ 2309b074b7bbSAlexander V. Chernikov cmd->arg1 = idx; 2310b074b7bbSAlexander V. Chernikov break; 2311b074b7bbSAlexander V. Chernikov case O_XMIT: 2312b074b7bbSAlexander V. Chernikov case O_RECV: 2313b074b7bbSAlexander V. Chernikov case O_VIA: 2314b074b7bbSAlexander V. Chernikov /* Interface table, possibly */ 2315b074b7bbSAlexander V. Chernikov cmdif = (ipfw_insn_if *)cmd; 23168bd19212SAlexander V. Chernikov cmdif->p.glob = idx; 2317b074b7bbSAlexander V. Chernikov break; 2318914bffb6SAlexander V. Chernikov case O_IP_FLOW_LOOKUP: 2319914bffb6SAlexander V. Chernikov cmd->arg1 = idx; 2320914bffb6SAlexander V. Chernikov break; 2321b074b7bbSAlexander V. Chernikov } 2322b074b7bbSAlexander V. Chernikov } 2323b074b7bbSAlexander V. Chernikov 2324ac35ff17SAlexander V. Chernikov /* 2325ac35ff17SAlexander V. Chernikov * Checks table name for validity. 2326ac35ff17SAlexander V. Chernikov * Enforce basic length checks, the rest 2327ac35ff17SAlexander V. Chernikov * should be done in userland. 2328ac35ff17SAlexander V. Chernikov * 2329ac35ff17SAlexander V. Chernikov * Returns 0 if name is considered valid. 2330ac35ff17SAlexander V. Chernikov */ 2331ac35ff17SAlexander V. Chernikov int 2332ac35ff17SAlexander V. Chernikov ipfw_check_table_name(char *name) 2333ac35ff17SAlexander V. Chernikov { 2334ac35ff17SAlexander V. Chernikov int nsize; 2335ac35ff17SAlexander V. Chernikov ipfw_obj_ntlv *ntlv = NULL; 2336ac35ff17SAlexander V. Chernikov 2337ac35ff17SAlexander V. Chernikov nsize = sizeof(ntlv->name); 2338ac35ff17SAlexander V. Chernikov 2339ac35ff17SAlexander V. Chernikov if (strnlen(name, nsize) == nsize) 2340ac35ff17SAlexander V. Chernikov return (EINVAL); 2341ac35ff17SAlexander V. Chernikov 2342ac35ff17SAlexander V. Chernikov if (name[0] == '\0') 2343ac35ff17SAlexander V. Chernikov return (EINVAL); 2344ac35ff17SAlexander V. Chernikov 2345ac35ff17SAlexander V. Chernikov /* 2346ac35ff17SAlexander V. Chernikov * TODO: do some more complicated checks 2347ac35ff17SAlexander V. Chernikov */ 2348ac35ff17SAlexander V. Chernikov 2349ac35ff17SAlexander V. Chernikov return (0); 2350ac35ff17SAlexander V. Chernikov } 2351ac35ff17SAlexander V. Chernikov 2352ac35ff17SAlexander V. Chernikov /* 2353ac35ff17SAlexander V. Chernikov * Find tablename TLV by @uid. 2354ac35ff17SAlexander V. Chernikov * Check @tlvs for valid data inside. 2355ac35ff17SAlexander V. Chernikov * 2356ac35ff17SAlexander V. Chernikov * Returns pointer to found TLV or NULL. 2357ac35ff17SAlexander V. Chernikov */ 2358ac35ff17SAlexander V. Chernikov static ipfw_obj_ntlv * 2359b074b7bbSAlexander V. Chernikov find_name_tlv(void *tlvs, int len, uint16_t uidx) 2360b074b7bbSAlexander V. Chernikov { 23619f7d47b0SAlexander V. Chernikov ipfw_obj_ntlv *ntlv; 2362b074b7bbSAlexander V. Chernikov uintptr_t pa, pe; 2363b074b7bbSAlexander V. Chernikov int l; 2364b074b7bbSAlexander V. Chernikov 2365b074b7bbSAlexander V. Chernikov pa = (uintptr_t)tlvs; 2366b074b7bbSAlexander V. Chernikov pe = pa + len; 2367b074b7bbSAlexander V. Chernikov l = 0; 2368b074b7bbSAlexander V. Chernikov for (; pa < pe; pa += l) { 23699f7d47b0SAlexander V. Chernikov ntlv = (ipfw_obj_ntlv *)pa; 2370b074b7bbSAlexander V. Chernikov l = ntlv->head.length; 2371ac35ff17SAlexander V. Chernikov 2372ac35ff17SAlexander V. Chernikov if (l != sizeof(*ntlv)) 2373ac35ff17SAlexander V. Chernikov return (NULL); 2374ac35ff17SAlexander V. Chernikov 2375563b5ab1SAlexander V. Chernikov if (ntlv->head.type != IPFW_TLV_TBL_NAME) 2376b074b7bbSAlexander V. Chernikov continue; 2377ac35ff17SAlexander V. Chernikov 2378b074b7bbSAlexander V. Chernikov if (ntlv->idx != uidx) 2379b074b7bbSAlexander V. Chernikov continue; 2380b074b7bbSAlexander V. Chernikov 2381ac35ff17SAlexander V. Chernikov if (ipfw_check_table_name(ntlv->name) != 0) 2382ac35ff17SAlexander V. Chernikov return (NULL); 2383ac35ff17SAlexander V. Chernikov 2384ac35ff17SAlexander V. Chernikov return (ntlv); 2385b074b7bbSAlexander V. Chernikov } 2386b074b7bbSAlexander V. Chernikov 2387b074b7bbSAlexander V. Chernikov return (NULL); 2388b074b7bbSAlexander V. Chernikov } 2389b074b7bbSAlexander V. Chernikov 2390ac35ff17SAlexander V. Chernikov /* 2391ac35ff17SAlexander V. Chernikov * Finds table config based on either legacy index 2392ac35ff17SAlexander V. Chernikov * or name in ntlv. 2393ac35ff17SAlexander V. Chernikov * Note @ti structure contains unchecked data from userland. 2394ac35ff17SAlexander V. Chernikov * 2395ac35ff17SAlexander V. Chernikov * Returns pointer to table_config or NULL. 2396ac35ff17SAlexander V. Chernikov */ 2397b074b7bbSAlexander V. Chernikov static struct table_config * 2398b074b7bbSAlexander V. Chernikov find_table(struct namedobj_instance *ni, struct tid_info *ti) 2399b074b7bbSAlexander V. Chernikov { 2400b074b7bbSAlexander V. Chernikov char *name, bname[16]; 2401b074b7bbSAlexander V. Chernikov struct named_object *no; 2402ac35ff17SAlexander V. Chernikov ipfw_obj_ntlv *ntlv; 2403ac35ff17SAlexander V. Chernikov uint32_t set; 2404b074b7bbSAlexander V. Chernikov 2405b074b7bbSAlexander V. Chernikov if (ti->tlvs != NULL) { 2406ac35ff17SAlexander V. Chernikov ntlv = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx); 2407ac35ff17SAlexander V. Chernikov if (ntlv == NULL) 2408b074b7bbSAlexander V. Chernikov return (NULL); 2409ac35ff17SAlexander V. Chernikov name = ntlv->name; 2410ac35ff17SAlexander V. Chernikov set = ntlv->set; 2411b074b7bbSAlexander V. Chernikov } else { 2412b074b7bbSAlexander V. Chernikov snprintf(bname, sizeof(bname), "%d", ti->uidx); 2413b074b7bbSAlexander V. Chernikov name = bname; 2414ac35ff17SAlexander V. Chernikov set = 0; 2415b074b7bbSAlexander V. Chernikov } 2416b074b7bbSAlexander V. Chernikov 2417ac35ff17SAlexander V. Chernikov no = ipfw_objhash_lookup_name(ni, set, name); 2418b074b7bbSAlexander V. Chernikov 2419b074b7bbSAlexander V. Chernikov return ((struct table_config *)no); 2420b074b7bbSAlexander V. Chernikov } 2421b074b7bbSAlexander V. Chernikov 2422b074b7bbSAlexander V. Chernikov static struct table_config * 242368394ec8SAlexander V. Chernikov alloc_table_config(struct ip_fw_chain *ch, struct tid_info *ti, 2424914bffb6SAlexander V. Chernikov struct table_algo *ta, char *aname, uint8_t tflags, uint8_t vtype) 2425b074b7bbSAlexander V. Chernikov { 2426b074b7bbSAlexander V. Chernikov char *name, bname[16]; 2427b074b7bbSAlexander V. Chernikov struct table_config *tc; 2428b074b7bbSAlexander V. Chernikov int error; 2429ac35ff17SAlexander V. Chernikov ipfw_obj_ntlv *ntlv; 2430ac35ff17SAlexander V. Chernikov uint32_t set; 2431b074b7bbSAlexander V. Chernikov 2432b074b7bbSAlexander V. Chernikov if (ti->tlvs != NULL) { 2433ac35ff17SAlexander V. Chernikov ntlv = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx); 2434ac35ff17SAlexander V. Chernikov if (ntlv == NULL) 2435b074b7bbSAlexander V. Chernikov return (NULL); 2436ac35ff17SAlexander V. Chernikov name = ntlv->name; 2437ac35ff17SAlexander V. Chernikov set = ntlv->set; 2438b074b7bbSAlexander V. Chernikov } else { 2439b074b7bbSAlexander V. Chernikov snprintf(bname, sizeof(bname), "%d", ti->uidx); 2440b074b7bbSAlexander V. Chernikov name = bname; 2441ac35ff17SAlexander V. Chernikov set = 0; 2442b074b7bbSAlexander V. Chernikov } 2443b074b7bbSAlexander V. Chernikov 2444b074b7bbSAlexander V. Chernikov tc = malloc(sizeof(struct table_config), M_IPFW, M_WAITOK | M_ZERO); 2445b074b7bbSAlexander V. Chernikov tc->no.name = tc->tablename; 2446b074b7bbSAlexander V. Chernikov tc->no.type = ti->type; 2447ac35ff17SAlexander V. Chernikov tc->no.set = set; 2448914bffb6SAlexander V. Chernikov tc->tflags = tflags; 24499f7d47b0SAlexander V. Chernikov tc->ta = ta; 2450b074b7bbSAlexander V. Chernikov strlcpy(tc->tablename, name, sizeof(tc->tablename)); 2451ac35ff17SAlexander V. Chernikov /* Set default value type to u32 for compability reasons */ 2452db785d31SAlexander V. Chernikov if (vtype == 0) 2453ac35ff17SAlexander V. Chernikov tc->vtype = IPFW_VTYPE_U32; 2454db785d31SAlexander V. Chernikov else 2455db785d31SAlexander V. Chernikov tc->vtype = vtype; 2456b074b7bbSAlexander V. Chernikov 2457b074b7bbSAlexander V. Chernikov if (ti->tlvs == NULL) { 2458b074b7bbSAlexander V. Chernikov tc->no.compat = 1; 2459b074b7bbSAlexander V. Chernikov tc->no.uidx = ti->uidx; 2460b074b7bbSAlexander V. Chernikov } 2461b074b7bbSAlexander V. Chernikov 2462b074b7bbSAlexander V. Chernikov /* Preallocate data structures for new tables */ 2463914bffb6SAlexander V. Chernikov error = ta->init(ch, &tc->astate, &tc->ti, aname, tflags); 2464b074b7bbSAlexander V. Chernikov if (error != 0) { 2465b074b7bbSAlexander V. Chernikov free(tc, M_IPFW); 2466b074b7bbSAlexander V. Chernikov return (NULL); 2467b074b7bbSAlexander V. Chernikov } 2468b074b7bbSAlexander V. Chernikov 2469b074b7bbSAlexander V. Chernikov return (tc); 2470b074b7bbSAlexander V. Chernikov } 2471b074b7bbSAlexander V. Chernikov 2472b074b7bbSAlexander V. Chernikov static void 2473b074b7bbSAlexander V. Chernikov free_table_config(struct namedobj_instance *ni, struct table_config *tc) 2474b074b7bbSAlexander V. Chernikov { 2475b074b7bbSAlexander V. Chernikov 2476b074b7bbSAlexander V. Chernikov if (tc->linked == 0) 247768394ec8SAlexander V. Chernikov tc->ta->destroy(tc->astate, &tc->ti); 2478b074b7bbSAlexander V. Chernikov 2479b074b7bbSAlexander V. Chernikov free(tc, M_IPFW); 2480b074b7bbSAlexander V. Chernikov } 2481b074b7bbSAlexander V. Chernikov 2482b074b7bbSAlexander V. Chernikov /* 2483b074b7bbSAlexander V. Chernikov * Links @tc to @chain table named instance. 2484b074b7bbSAlexander V. Chernikov * Sets appropriate type/states in @chain table info. 2485b074b7bbSAlexander V. Chernikov */ 2486b074b7bbSAlexander V. Chernikov static void 24879f7d47b0SAlexander V. Chernikov link_table(struct ip_fw_chain *ch, struct table_config *tc) 2488b074b7bbSAlexander V. Chernikov { 2489b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 24909f7d47b0SAlexander V. Chernikov struct table_info *ti; 2491b074b7bbSAlexander V. Chernikov uint16_t kidx; 2492b074b7bbSAlexander V. Chernikov 24939f7d47b0SAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(ch); 24949f7d47b0SAlexander V. Chernikov IPFW_WLOCK_ASSERT(ch); 2495b074b7bbSAlexander V. Chernikov 24969f7d47b0SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 2497b074b7bbSAlexander V. Chernikov kidx = tc->no.kidx; 2498b074b7bbSAlexander V. Chernikov 2499b074b7bbSAlexander V. Chernikov ipfw_objhash_add(ni, &tc->no); 25009f7d47b0SAlexander V. Chernikov 25019f7d47b0SAlexander V. Chernikov ti = KIDX_TO_TI(ch, kidx); 25029f7d47b0SAlexander V. Chernikov *ti = tc->ti; 2503b074b7bbSAlexander V. Chernikov 250468394ec8SAlexander V. Chernikov /* Notify algo on real @ti address */ 250568394ec8SAlexander V. Chernikov if (tc->ta->change_ti != NULL) 250668394ec8SAlexander V. Chernikov tc->ta->change_ti(tc->astate, ti); 250768394ec8SAlexander V. Chernikov 2508b074b7bbSAlexander V. Chernikov tc->linked = 1; 25099d099b4fSAlexander V. Chernikov tc->ta->refcnt++; 2510b074b7bbSAlexander V. Chernikov } 2511b074b7bbSAlexander V. Chernikov 2512b074b7bbSAlexander V. Chernikov /* 2513b074b7bbSAlexander V. Chernikov * Unlinks @tc from @chain table named instance. 2514b074b7bbSAlexander V. Chernikov * Zeroes states in @chain and stores them in @tc. 2515b074b7bbSAlexander V. Chernikov */ 2516b074b7bbSAlexander V. Chernikov static void 25179f7d47b0SAlexander V. Chernikov unlink_table(struct ip_fw_chain *ch, struct table_config *tc) 2518b074b7bbSAlexander V. Chernikov { 2519b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 25209f7d47b0SAlexander V. Chernikov struct table_info *ti; 2521b074b7bbSAlexander V. Chernikov uint16_t kidx; 2522b074b7bbSAlexander V. Chernikov 25239f7d47b0SAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(ch); 25249f7d47b0SAlexander V. Chernikov IPFW_WLOCK_ASSERT(ch); 2525b074b7bbSAlexander V. Chernikov 25269f7d47b0SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 2527b074b7bbSAlexander V. Chernikov kidx = tc->no.kidx; 2528b074b7bbSAlexander V. Chernikov 25299f7d47b0SAlexander V. Chernikov /* Clear state. @ti copy is already saved inside @tc */ 2530b074b7bbSAlexander V. Chernikov ipfw_objhash_del(ni, &tc->no); 25319f7d47b0SAlexander V. Chernikov ti = KIDX_TO_TI(ch, kidx); 25329f7d47b0SAlexander V. Chernikov memset(ti, 0, sizeof(struct table_info)); 2533b074b7bbSAlexander V. Chernikov tc->linked = 0; 25349d099b4fSAlexander V. Chernikov tc->ta->refcnt--; 253568394ec8SAlexander V. Chernikov 253668394ec8SAlexander V. Chernikov /* Notify algo on real @ti address */ 253768394ec8SAlexander V. Chernikov if (tc->ta->change_ti != NULL) 253868394ec8SAlexander V. Chernikov tc->ta->change_ti(tc->astate, NULL); 2539b074b7bbSAlexander V. Chernikov } 2540b074b7bbSAlexander V. Chernikov 2541b074b7bbSAlexander V. Chernikov /* 2542b074b7bbSAlexander V. Chernikov * Finds named object by @uidx number. 2543b074b7bbSAlexander V. Chernikov * Refs found object, allocate new index for non-existing object. 25449490a627SAlexander V. Chernikov * Fills in @oib with userland/kernel indexes. 25459490a627SAlexander V. Chernikov * First free oidx pointer is saved back in @oib. 2546b074b7bbSAlexander V. Chernikov * 2547b074b7bbSAlexander V. Chernikov * Returns 0 on success. 2548b074b7bbSAlexander V. Chernikov */ 2549b074b7bbSAlexander V. Chernikov static int 25509490a627SAlexander V. Chernikov bind_table_rule(struct ip_fw_chain *ch, struct ip_fw *rule, 25519490a627SAlexander V. Chernikov struct rule_check_info *ci, struct obj_idx **oib, struct tid_info *ti) 2552b074b7bbSAlexander V. Chernikov { 2553b074b7bbSAlexander V. Chernikov struct table_config *tc; 25549490a627SAlexander V. Chernikov struct namedobj_instance *ni; 25559490a627SAlexander V. Chernikov struct named_object *no; 25569490a627SAlexander V. Chernikov int error, l, cmdlen; 25579490a627SAlexander V. Chernikov ipfw_insn *cmd; 25589490a627SAlexander V. Chernikov struct obj_idx *pidx, *p; 25599490a627SAlexander V. Chernikov 25609490a627SAlexander V. Chernikov pidx = *oib; 25619490a627SAlexander V. Chernikov l = rule->cmd_len; 25629490a627SAlexander V. Chernikov cmd = rule->cmd; 25639490a627SAlexander V. Chernikov cmdlen = 0; 25649490a627SAlexander V. Chernikov error = 0; 25659490a627SAlexander V. Chernikov 25669490a627SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 25679490a627SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 25689490a627SAlexander V. Chernikov 25699490a627SAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 25709490a627SAlexander V. Chernikov cmdlen = F_LEN(cmd); 25719490a627SAlexander V. Chernikov 25729490a627SAlexander V. Chernikov if (classify_table_opcode(cmd, &ti->uidx, &ti->type) != 0) 25739490a627SAlexander V. Chernikov continue; 2574b074b7bbSAlexander V. Chernikov 2575b074b7bbSAlexander V. Chernikov pidx->uidx = ti->uidx; 2576b074b7bbSAlexander V. Chernikov pidx->type = ti->type; 2577b074b7bbSAlexander V. Chernikov 25789490a627SAlexander V. Chernikov if ((tc = find_table(ni, ti)) != NULL) { 25799490a627SAlexander V. Chernikov if (tc->no.type != ti->type) { 25809490a627SAlexander V. Chernikov /* Incompatible types */ 25819490a627SAlexander V. Chernikov error = EINVAL; 25829490a627SAlexander V. Chernikov break; 25839490a627SAlexander V. Chernikov } 25849f7d47b0SAlexander V. Chernikov 25859490a627SAlexander V. Chernikov /* Reference found table and save kidx */ 25869490a627SAlexander V. Chernikov tc->no.refcnt++; 25879490a627SAlexander V. Chernikov pidx->kidx = tc->no.kidx; 25889490a627SAlexander V. Chernikov pidx++; 25899490a627SAlexander V. Chernikov continue; 25909490a627SAlexander V. Chernikov } 25919490a627SAlexander V. Chernikov 25929490a627SAlexander V. Chernikov /* Table not found. Allocate new index and save for later */ 2593ac35ff17SAlexander V. Chernikov if (ipfw_objhash_alloc_idx(ni, &pidx->kidx) != 0) { 2594ac35ff17SAlexander V. Chernikov printf("Unable to allocate table %s index in set %u." 2595b074b7bbSAlexander V. Chernikov " Consider increasing net.inet.ip.fw.tables_max", 2596ac35ff17SAlexander V. Chernikov "", ti->set); 25979490a627SAlexander V. Chernikov error = EBUSY; 25989490a627SAlexander V. Chernikov break; 2599b074b7bbSAlexander V. Chernikov } 2600b074b7bbSAlexander V. Chernikov 26013a845e10SAlexander V. Chernikov ci->flags |= IPFW_RCF_TABLES; 26029490a627SAlexander V. Chernikov pidx->new = 1; 26039490a627SAlexander V. Chernikov pidx++; 2604b074b7bbSAlexander V. Chernikov } 2605b074b7bbSAlexander V. Chernikov 26069490a627SAlexander V. Chernikov if (error != 0) { 26079490a627SAlexander V. Chernikov /* Unref everything we have already done */ 26089490a627SAlexander V. Chernikov for (p = *oib; p < pidx; p++) { 26099490a627SAlexander V. Chernikov if (p->new != 0) { 2610ac35ff17SAlexander V. Chernikov ipfw_objhash_free_idx(ni, p->kidx); 26119490a627SAlexander V. Chernikov continue; 26129490a627SAlexander V. Chernikov } 2613b074b7bbSAlexander V. Chernikov 26149490a627SAlexander V. Chernikov /* Find & unref by existing idx */ 2615ac35ff17SAlexander V. Chernikov no = ipfw_objhash_lookup_kidx(ni, p->kidx); 26169490a627SAlexander V. Chernikov KASSERT(no != NULL, ("Ref'd table %d disappeared", 26179490a627SAlexander V. Chernikov p->kidx)); 2618b074b7bbSAlexander V. Chernikov 26199490a627SAlexander V. Chernikov no->refcnt--; 26209490a627SAlexander V. Chernikov } 26219490a627SAlexander V. Chernikov } 26229490a627SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 2623b074b7bbSAlexander V. Chernikov 26249490a627SAlexander V. Chernikov *oib = pidx; 26259490a627SAlexander V. Chernikov 26269490a627SAlexander V. Chernikov return (error); 2627b074b7bbSAlexander V. Chernikov } 2628b074b7bbSAlexander V. Chernikov 2629a73d728dSAlexander V. Chernikov struct swap_table_args { 2630a73d728dSAlexander V. Chernikov int set; 2631a73d728dSAlexander V. Chernikov int new_set; 2632a73d728dSAlexander V. Chernikov int mv; 2633a73d728dSAlexander V. Chernikov }; 2634a73d728dSAlexander V. Chernikov 2635a73d728dSAlexander V. Chernikov /* 2636a73d728dSAlexander V. Chernikov * Change set for each matching table. 2637a73d728dSAlexander V. Chernikov * 2638a73d728dSAlexander V. Chernikov * Ensure we dispatch each table once by setting/checking ochange 2639a73d728dSAlexander V. Chernikov * fields. 2640a73d728dSAlexander V. Chernikov */ 2641a73d728dSAlexander V. Chernikov static void 2642a73d728dSAlexander V. Chernikov swap_table_set(struct namedobj_instance *ni, struct named_object *no, 2643a73d728dSAlexander V. Chernikov void *arg) 2644a73d728dSAlexander V. Chernikov { 2645a73d728dSAlexander V. Chernikov struct table_config *tc; 2646a73d728dSAlexander V. Chernikov struct swap_table_args *sta; 2647a73d728dSAlexander V. Chernikov 2648a73d728dSAlexander V. Chernikov tc = (struct table_config *)no; 2649a73d728dSAlexander V. Chernikov sta = (struct swap_table_args *)arg; 2650a73d728dSAlexander V. Chernikov 2651a73d728dSAlexander V. Chernikov if (no->set != sta->set && (no->set != sta->new_set || sta->mv != 0)) 2652a73d728dSAlexander V. Chernikov return; 2653a73d728dSAlexander V. Chernikov 2654a73d728dSAlexander V. Chernikov if (tc->ochanged != 0) 2655a73d728dSAlexander V. Chernikov return; 2656a73d728dSAlexander V. Chernikov 2657a73d728dSAlexander V. Chernikov tc->ochanged = 1; 2658a73d728dSAlexander V. Chernikov ipfw_objhash_del(ni, no); 2659a73d728dSAlexander V. Chernikov if (no->set == sta->set) 2660a73d728dSAlexander V. Chernikov no->set = sta->new_set; 2661a73d728dSAlexander V. Chernikov else 2662a73d728dSAlexander V. Chernikov no->set = sta->set; 2663a73d728dSAlexander V. Chernikov ipfw_objhash_add(ni, no); 2664a73d728dSAlexander V. Chernikov } 2665a73d728dSAlexander V. Chernikov 2666a73d728dSAlexander V. Chernikov /* 2667a73d728dSAlexander V. Chernikov * Cleans up ochange field for all tables. 2668a73d728dSAlexander V. Chernikov */ 2669a73d728dSAlexander V. Chernikov static void 2670a73d728dSAlexander V. Chernikov clean_table_set_data(struct namedobj_instance *ni, struct named_object *no, 2671a73d728dSAlexander V. Chernikov void *arg) 2672a73d728dSAlexander V. Chernikov { 2673a73d728dSAlexander V. Chernikov struct table_config *tc; 2674a73d728dSAlexander V. Chernikov struct swap_table_args *sta; 2675a73d728dSAlexander V. Chernikov 2676a73d728dSAlexander V. Chernikov tc = (struct table_config *)no; 2677a73d728dSAlexander V. Chernikov sta = (struct swap_table_args *)arg; 2678a73d728dSAlexander V. Chernikov 2679a73d728dSAlexander V. Chernikov tc->ochanged = 0; 2680a73d728dSAlexander V. Chernikov } 2681a73d728dSAlexander V. Chernikov 2682a73d728dSAlexander V. Chernikov /* 2683a73d728dSAlexander V. Chernikov * Swaps tables within two sets. 2684a73d728dSAlexander V. Chernikov */ 2685a73d728dSAlexander V. Chernikov void 2686a73d728dSAlexander V. Chernikov ipfw_swap_tables_sets(struct ip_fw_chain *ch, uint32_t set, 2687a73d728dSAlexander V. Chernikov uint32_t new_set, int mv) 2688a73d728dSAlexander V. Chernikov { 2689a73d728dSAlexander V. Chernikov struct swap_table_args sta; 2690a73d728dSAlexander V. Chernikov 2691a73d728dSAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(ch); 2692a73d728dSAlexander V. Chernikov 2693a73d728dSAlexander V. Chernikov sta.set = set; 2694a73d728dSAlexander V. Chernikov sta.new_set = new_set; 2695a73d728dSAlexander V. Chernikov sta.mv = mv; 2696a73d728dSAlexander V. Chernikov 2697a73d728dSAlexander V. Chernikov ipfw_objhash_foreach(CHAIN_TO_NI(ch), swap_table_set, &sta); 2698a73d728dSAlexander V. Chernikov ipfw_objhash_foreach(CHAIN_TO_NI(ch), clean_table_set_data, &sta); 2699a73d728dSAlexander V. Chernikov } 2700a73d728dSAlexander V. Chernikov 2701a73d728dSAlexander V. Chernikov /* 2702a73d728dSAlexander V. Chernikov * Move all tables which are reference by rules in @rr to set @new_set. 2703a73d728dSAlexander V. Chernikov * Makes sure that all relevant tables are referenced ONLLY by given rules. 2704a73d728dSAlexander V. Chernikov * 2705a73d728dSAlexander V. Chernikov * Retuns 0 on success, 2706a73d728dSAlexander V. Chernikov */ 2707a73d728dSAlexander V. Chernikov int 2708a73d728dSAlexander V. Chernikov ipfw_move_tables_sets(struct ip_fw_chain *ch, ipfw_range_tlv *rt, 2709a73d728dSAlexander V. Chernikov uint32_t new_set) 2710a73d728dSAlexander V. Chernikov { 2711a73d728dSAlexander V. Chernikov struct ip_fw *rule; 2712a73d728dSAlexander V. Chernikov struct table_config *tc; 2713a73d728dSAlexander V. Chernikov struct named_object *no; 2714a73d728dSAlexander V. Chernikov struct namedobj_instance *ni; 2715a73d728dSAlexander V. Chernikov int bad, i, l, cmdlen; 2716a73d728dSAlexander V. Chernikov uint16_t kidx; 2717a73d728dSAlexander V. Chernikov uint8_t type; 2718a73d728dSAlexander V. Chernikov ipfw_insn *cmd; 2719a73d728dSAlexander V. Chernikov 2720a73d728dSAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(ch); 2721a73d728dSAlexander V. Chernikov 2722a73d728dSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 2723a73d728dSAlexander V. Chernikov 2724a73d728dSAlexander V. Chernikov /* Stage 1: count number of references by given rules */ 2725a73d728dSAlexander V. Chernikov for (i = 0; i < ch->n_rules - 1; i++) { 2726a73d728dSAlexander V. Chernikov rule = ch->map[i]; 2727a73d728dSAlexander V. Chernikov if (ipfw_match_range(rule, rt) == 0) 2728a73d728dSAlexander V. Chernikov continue; 2729a73d728dSAlexander V. Chernikov 2730a73d728dSAlexander V. Chernikov l = rule->cmd_len; 2731a73d728dSAlexander V. Chernikov cmd = rule->cmd; 2732a73d728dSAlexander V. Chernikov cmdlen = 0; 2733a73d728dSAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 2734a73d728dSAlexander V. Chernikov cmdlen = F_LEN(cmd); 2735a73d728dSAlexander V. Chernikov if (classify_table_opcode(cmd, &kidx, &type) != 0) 2736a73d728dSAlexander V. Chernikov continue; 2737a73d728dSAlexander V. Chernikov no = ipfw_objhash_lookup_kidx(ni, kidx); 2738a73d728dSAlexander V. Chernikov KASSERT(no != NULL, 2739a73d728dSAlexander V. Chernikov ("objhash lookup failed on index %d", kidx)); 2740a73d728dSAlexander V. Chernikov tc = (struct table_config *)no; 2741a73d728dSAlexander V. Chernikov tc->ocount++; 2742a73d728dSAlexander V. Chernikov } 2743a73d728dSAlexander V. Chernikov 2744a73d728dSAlexander V. Chernikov } 2745a73d728dSAlexander V. Chernikov 2746a73d728dSAlexander V. Chernikov /* Stage 2: verify "ownership" */ 2747a73d728dSAlexander V. Chernikov bad = 0; 2748a73d728dSAlexander V. Chernikov for (i = 0; i < ch->n_rules - 1; i++) { 2749a73d728dSAlexander V. Chernikov rule = ch->map[i]; 2750a73d728dSAlexander V. Chernikov if (ipfw_match_range(rule, rt) == 0) 2751a73d728dSAlexander V. Chernikov continue; 2752a73d728dSAlexander V. Chernikov 2753a73d728dSAlexander V. Chernikov l = rule->cmd_len; 2754a73d728dSAlexander V. Chernikov cmd = rule->cmd; 2755a73d728dSAlexander V. Chernikov cmdlen = 0; 2756a73d728dSAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 2757a73d728dSAlexander V. Chernikov cmdlen = F_LEN(cmd); 2758a73d728dSAlexander V. Chernikov if (classify_table_opcode(cmd, &kidx, &type) != 0) 2759a73d728dSAlexander V. Chernikov continue; 2760a73d728dSAlexander V. Chernikov no = ipfw_objhash_lookup_kidx(ni, kidx); 2761a73d728dSAlexander V. Chernikov KASSERT(no != NULL, 2762a73d728dSAlexander V. Chernikov ("objhash lookup failed on index %d", kidx)); 2763a73d728dSAlexander V. Chernikov tc = (struct table_config *)no; 2764a73d728dSAlexander V. Chernikov if (tc->no.refcnt != tc->ocount) { 2765a73d728dSAlexander V. Chernikov 2766a73d728dSAlexander V. Chernikov /* 2767a73d728dSAlexander V. Chernikov * Number of references differ: 2768a73d728dSAlexander V. Chernikov * Other rule(s) are holding reference to given 2769a73d728dSAlexander V. Chernikov * table, so it is not possible to change its set. 2770a73d728dSAlexander V. Chernikov * 2771a73d728dSAlexander V. Chernikov * Note that refcnt may account 2772a73d728dSAlexander V. Chernikov * references to some going-to-be-added rules. 2773a73d728dSAlexander V. Chernikov * Since we don't know their numbers (and event 2774a73d728dSAlexander V. Chernikov * if they will be added) it is perfectly OK 2775a73d728dSAlexander V. Chernikov * to return error here. 2776a73d728dSAlexander V. Chernikov */ 2777a73d728dSAlexander V. Chernikov bad = 1; 2778a73d728dSAlexander V. Chernikov break; 2779a73d728dSAlexander V. Chernikov } 2780a73d728dSAlexander V. Chernikov } 2781a73d728dSAlexander V. Chernikov 2782a73d728dSAlexander V. Chernikov if (bad != 0) 2783a73d728dSAlexander V. Chernikov break; 2784a73d728dSAlexander V. Chernikov } 2785a73d728dSAlexander V. Chernikov 2786a73d728dSAlexander V. Chernikov /* Stage 3: change set or cleanup */ 2787a73d728dSAlexander V. Chernikov for (i = 0; i < ch->n_rules - 1; i++) { 2788a73d728dSAlexander V. Chernikov rule = ch->map[i]; 2789a73d728dSAlexander V. Chernikov if (ipfw_match_range(rule, rt) == 0) 2790a73d728dSAlexander V. Chernikov continue; 2791a73d728dSAlexander V. Chernikov 2792a73d728dSAlexander V. Chernikov l = rule->cmd_len; 2793a73d728dSAlexander V. Chernikov cmd = rule->cmd; 2794a73d728dSAlexander V. Chernikov cmdlen = 0; 2795a73d728dSAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 2796a73d728dSAlexander V. Chernikov cmdlen = F_LEN(cmd); 2797a73d728dSAlexander V. Chernikov if (classify_table_opcode(cmd, &kidx, &type) != 0) 2798a73d728dSAlexander V. Chernikov continue; 2799a73d728dSAlexander V. Chernikov no = ipfw_objhash_lookup_kidx(ni, kidx); 2800a73d728dSAlexander V. Chernikov KASSERT(no != NULL, 2801a73d728dSAlexander V. Chernikov ("objhash lookup failed on index %d", kidx)); 2802a73d728dSAlexander V. Chernikov tc = (struct table_config *)no; 2803a73d728dSAlexander V. Chernikov 2804a73d728dSAlexander V. Chernikov tc->ocount = 0; 2805a73d728dSAlexander V. Chernikov if (bad != 0) 2806a73d728dSAlexander V. Chernikov continue; 2807a73d728dSAlexander V. Chernikov 2808a73d728dSAlexander V. Chernikov /* Actually change set. */ 2809a73d728dSAlexander V. Chernikov ipfw_objhash_del(ni, no); 2810a73d728dSAlexander V. Chernikov no->set = new_set; 2811a73d728dSAlexander V. Chernikov ipfw_objhash_add(ni, no); 2812a73d728dSAlexander V. Chernikov } 2813a73d728dSAlexander V. Chernikov } 2814a73d728dSAlexander V. Chernikov 2815a73d728dSAlexander V. Chernikov return (bad); 2816a73d728dSAlexander V. Chernikov } 2817a73d728dSAlexander V. Chernikov 2818b074b7bbSAlexander V. Chernikov /* 2819b074b7bbSAlexander V. Chernikov * Compatibility function for old ipfw(8) binaries. 2820b074b7bbSAlexander V. Chernikov * Rewrites table kernel indices with userland ones. 2821b074b7bbSAlexander V. Chernikov * Works for \d+ talbes only (e.g. for tables, converted 2822b074b7bbSAlexander V. Chernikov * from old numbered system calls). 2823b074b7bbSAlexander V. Chernikov * 2824b074b7bbSAlexander V. Chernikov * Returns 0 on success. 2825b074b7bbSAlexander V. Chernikov * Raises error on any other tables. 2826b074b7bbSAlexander V. Chernikov */ 2827b074b7bbSAlexander V. Chernikov int 28287e767c79SAlexander V. Chernikov ipfw_rewrite_table_kidx(struct ip_fw_chain *chain, struct ip_fw_rule0 *rule) 2829b074b7bbSAlexander V. Chernikov { 28301832a7b3SAlexander V. Chernikov int cmdlen, error, l; 2831b074b7bbSAlexander V. Chernikov ipfw_insn *cmd; 28321832a7b3SAlexander V. Chernikov uint16_t kidx, uidx; 2833b074b7bbSAlexander V. Chernikov uint8_t type; 2834b074b7bbSAlexander V. Chernikov struct named_object *no; 2835b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 2836b074b7bbSAlexander V. Chernikov 2837b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(chain); 28381832a7b3SAlexander V. Chernikov error = 0; 2839b074b7bbSAlexander V. Chernikov 2840b074b7bbSAlexander V. Chernikov l = rule->cmd_len; 2841b074b7bbSAlexander V. Chernikov cmd = rule->cmd; 2842b074b7bbSAlexander V. Chernikov cmdlen = 0; 2843b074b7bbSAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 2844b074b7bbSAlexander V. Chernikov cmdlen = F_LEN(cmd); 2845b074b7bbSAlexander V. Chernikov 2846b074b7bbSAlexander V. Chernikov if (classify_table_opcode(cmd, &kidx, &type) != 0) 2847b074b7bbSAlexander V. Chernikov continue; 2848b074b7bbSAlexander V. Chernikov 2849ac35ff17SAlexander V. Chernikov if ((no = ipfw_objhash_lookup_kidx(ni, kidx)) == NULL) 2850b074b7bbSAlexander V. Chernikov return (1); 2851b074b7bbSAlexander V. Chernikov 28521832a7b3SAlexander V. Chernikov uidx = no->uidx; 28531832a7b3SAlexander V. Chernikov if (no->compat == 0) { 2854b074b7bbSAlexander V. Chernikov 28551832a7b3SAlexander V. Chernikov /* 28561832a7b3SAlexander V. Chernikov * We are called via legacy opcode. 28571832a7b3SAlexander V. Chernikov * Save error and show table as fake number 28581832a7b3SAlexander V. Chernikov * not to make ipfw(8) hang. 28591832a7b3SAlexander V. Chernikov */ 28601832a7b3SAlexander V. Chernikov uidx = 65535; 28611832a7b3SAlexander V. Chernikov error = 2; 2862b074b7bbSAlexander V. Chernikov } 2863b074b7bbSAlexander V. Chernikov 28641832a7b3SAlexander V. Chernikov update_table_opcode(cmd, uidx); 28651832a7b3SAlexander V. Chernikov } 28661832a7b3SAlexander V. Chernikov 28671832a7b3SAlexander V. Chernikov return (error); 2868b074b7bbSAlexander V. Chernikov } 2869b074b7bbSAlexander V. Chernikov 2870563b5ab1SAlexander V. Chernikov /* 2871563b5ab1SAlexander V. Chernikov * Sets every table kidx in @bmask which is used in rule @rule. 2872563b5ab1SAlexander V. Chernikov * 2873563b5ab1SAlexander V. Chernikov * Returns number of newly-referenced tables. 2874563b5ab1SAlexander V. Chernikov */ 2875563b5ab1SAlexander V. Chernikov int 2876563b5ab1SAlexander V. Chernikov ipfw_mark_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule, 2877563b5ab1SAlexander V. Chernikov uint32_t *bmask) 2878563b5ab1SAlexander V. Chernikov { 2879563b5ab1SAlexander V. Chernikov int cmdlen, l, count; 2880563b5ab1SAlexander V. Chernikov ipfw_insn *cmd; 2881563b5ab1SAlexander V. Chernikov uint16_t kidx; 2882563b5ab1SAlexander V. Chernikov uint8_t type; 2883563b5ab1SAlexander V. Chernikov 2884563b5ab1SAlexander V. Chernikov l = rule->cmd_len; 2885563b5ab1SAlexander V. Chernikov cmd = rule->cmd; 2886563b5ab1SAlexander V. Chernikov cmdlen = 0; 2887563b5ab1SAlexander V. Chernikov count = 0; 2888563b5ab1SAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 2889563b5ab1SAlexander V. Chernikov cmdlen = F_LEN(cmd); 2890563b5ab1SAlexander V. Chernikov 2891563b5ab1SAlexander V. Chernikov if (classify_table_opcode(cmd, &kidx, &type) != 0) 2892563b5ab1SAlexander V. Chernikov continue; 2893563b5ab1SAlexander V. Chernikov 2894563b5ab1SAlexander V. Chernikov if ((bmask[kidx / 32] & (1 << (kidx % 32))) == 0) 2895563b5ab1SAlexander V. Chernikov count++; 2896563b5ab1SAlexander V. Chernikov 2897563b5ab1SAlexander V. Chernikov bmask[kidx / 32] |= 1 << (kidx % 32); 2898563b5ab1SAlexander V. Chernikov } 2899563b5ab1SAlexander V. Chernikov 2900563b5ab1SAlexander V. Chernikov return (count); 2901563b5ab1SAlexander V. Chernikov } 2902563b5ab1SAlexander V. Chernikov 2903563b5ab1SAlexander V. Chernikov 2904b074b7bbSAlexander V. Chernikov 2905b074b7bbSAlexander V. Chernikov /* 2906b074b7bbSAlexander V. Chernikov * Checks is opcode is referencing table of appropriate type. 2907b074b7bbSAlexander V. Chernikov * Adds reference count for found table if true. 2908b074b7bbSAlexander V. Chernikov * Rewrites user-supplied opcode values with kernel ones. 2909b074b7bbSAlexander V. Chernikov * 2910b074b7bbSAlexander V. Chernikov * Returns 0 on success and appropriate error code otherwise. 2911b074b7bbSAlexander V. Chernikov */ 2912b074b7bbSAlexander V. Chernikov int 2913b074b7bbSAlexander V. Chernikov ipfw_rewrite_table_uidx(struct ip_fw_chain *chain, 2914b074b7bbSAlexander V. Chernikov struct rule_check_info *ci) 2915b074b7bbSAlexander V. Chernikov { 2916b074b7bbSAlexander V. Chernikov int cmdlen, error, ftype, l; 2917b074b7bbSAlexander V. Chernikov ipfw_insn *cmd; 2918b074b7bbSAlexander V. Chernikov uint16_t uidx; 2919b074b7bbSAlexander V. Chernikov uint8_t type; 2920b074b7bbSAlexander V. Chernikov struct table_config *tc; 29219f7d47b0SAlexander V. Chernikov struct table_algo *ta; 2922b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 2923b074b7bbSAlexander V. Chernikov struct named_object *no, *no_n, *no_tmp; 29249490a627SAlexander V. Chernikov struct obj_idx *p, *pidx_first, *pidx_last; 2925b074b7bbSAlexander V. Chernikov struct namedobjects_head nh; 2926b074b7bbSAlexander V. Chernikov struct tid_info ti; 2927b074b7bbSAlexander V. Chernikov 2928b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(chain); 2929b074b7bbSAlexander V. Chernikov 29309490a627SAlexander V. Chernikov /* Prepare queue to store configs */ 29319490a627SAlexander V. Chernikov TAILQ_INIT(&nh); 29329490a627SAlexander V. Chernikov 2933b074b7bbSAlexander V. Chernikov /* 2934b074b7bbSAlexander V. Chernikov * Prepare an array for storing opcode indices. 2935b074b7bbSAlexander V. Chernikov * Use stack allocation by default. 2936b074b7bbSAlexander V. Chernikov */ 2937b074b7bbSAlexander V. Chernikov if (ci->table_opcodes <= (sizeof(ci->obuf)/sizeof(ci->obuf[0]))) { 2938b074b7bbSAlexander V. Chernikov /* Stack */ 29399490a627SAlexander V. Chernikov pidx_first = ci->obuf; 2940b074b7bbSAlexander V. Chernikov } else 29419490a627SAlexander V. Chernikov pidx_first = malloc(ci->table_opcodes * sizeof(struct obj_idx), 2942b074b7bbSAlexander V. Chernikov M_IPFW, M_WAITOK | M_ZERO); 2943b074b7bbSAlexander V. Chernikov 29449490a627SAlexander V. Chernikov pidx_last = pidx_first; 2945b074b7bbSAlexander V. Chernikov error = 0; 2946b074b7bbSAlexander V. Chernikov 2947b074b7bbSAlexander V. Chernikov type = 0; 2948b074b7bbSAlexander V. Chernikov ftype = 0; 2949b074b7bbSAlexander V. Chernikov 2950b074b7bbSAlexander V. Chernikov memset(&ti, 0, sizeof(ti)); 29511832a7b3SAlexander V. Chernikov 29521832a7b3SAlexander V. Chernikov /* 29531832a7b3SAlexander V. Chernikov * Use default set for looking up tables (old way) or 29541832a7b3SAlexander V. Chernikov * use set rule is assigned to (new way). 29551832a7b3SAlexander V. Chernikov */ 29561832a7b3SAlexander V. Chernikov ti.set = (V_fw_tables_sets != 0) ? ci->krule->set : 0; 29576c2997ffSAlexander V. Chernikov if (ci->ctlv != NULL) { 29586c2997ffSAlexander V. Chernikov ti.tlvs = (void *)(ci->ctlv + 1); 29596c2997ffSAlexander V. Chernikov ti.tlen = ci->ctlv->head.length - sizeof(ipfw_obj_ctlv); 29606c2997ffSAlexander V. Chernikov } 2961b074b7bbSAlexander V. Chernikov 2962b074b7bbSAlexander V. Chernikov /* 29639490a627SAlexander V. Chernikov * Stage 1: reference existing tables, determine number 29649490a627SAlexander V. Chernikov * of tables we need to allocate and allocate indexes for each. 2965b074b7bbSAlexander V. Chernikov */ 29669490a627SAlexander V. Chernikov error = bind_table_rule(chain, ci->krule, ci, &pidx_last, &ti); 2967b074b7bbSAlexander V. Chernikov 2968b074b7bbSAlexander V. Chernikov if (error != 0) { 29699490a627SAlexander V. Chernikov if (pidx_first != ci->obuf) 29709490a627SAlexander V. Chernikov free(pidx_first, M_IPFW); 2971b074b7bbSAlexander V. Chernikov 2972b074b7bbSAlexander V. Chernikov return (error); 2973b074b7bbSAlexander V. Chernikov } 2974b074b7bbSAlexander V. Chernikov 2975b074b7bbSAlexander V. Chernikov /* 2976b074b7bbSAlexander V. Chernikov * Stage 2: allocate table configs for every non-existent table 2977b074b7bbSAlexander V. Chernikov */ 2978b074b7bbSAlexander V. Chernikov 29793a845e10SAlexander V. Chernikov if ((ci->flags & IPFW_RCF_TABLES) != 0) { 29809490a627SAlexander V. Chernikov for (p = pidx_first; p < pidx_last; p++) { 2981b074b7bbSAlexander V. Chernikov if (p->new == 0) 2982b074b7bbSAlexander V. Chernikov continue; 2983b074b7bbSAlexander V. Chernikov 2984b074b7bbSAlexander V. Chernikov ti.uidx = p->uidx; 2985b074b7bbSAlexander V. Chernikov ti.type = p->type; 29869f7d47b0SAlexander V. Chernikov ti.atype = 0; 2987b074b7bbSAlexander V. Chernikov 29889490a627SAlexander V. Chernikov ta = find_table_algo(CHAIN_TO_TCFG(chain), &ti, NULL); 29899f7d47b0SAlexander V. Chernikov if (ta == NULL) { 29909f7d47b0SAlexander V. Chernikov error = ENOTSUP; 29919f7d47b0SAlexander V. Chernikov goto free; 29929f7d47b0SAlexander V. Chernikov } 2993914bffb6SAlexander V. Chernikov tc = alloc_table_config(chain, &ti, ta, NULL, 0, 299468394ec8SAlexander V. Chernikov IPFW_VTYPE_U32); 2995b074b7bbSAlexander V. Chernikov 2996b074b7bbSAlexander V. Chernikov if (tc == NULL) { 2997b074b7bbSAlexander V. Chernikov error = ENOMEM; 2998b074b7bbSAlexander V. Chernikov goto free; 2999b074b7bbSAlexander V. Chernikov } 3000b074b7bbSAlexander V. Chernikov 3001b074b7bbSAlexander V. Chernikov tc->no.kidx = p->kidx; 3002b074b7bbSAlexander V. Chernikov tc->no.refcnt = 1; 3003b074b7bbSAlexander V. Chernikov 3004b074b7bbSAlexander V. Chernikov /* Add to list */ 3005b074b7bbSAlexander V. Chernikov TAILQ_INSERT_TAIL(&nh, &tc->no, nn_next); 3006b074b7bbSAlexander V. Chernikov } 3007b074b7bbSAlexander V. Chernikov 3008b074b7bbSAlexander V. Chernikov /* 3009b074b7bbSAlexander V. Chernikov * Stage 2.1: Check if we're going to create 2 tables 3010b074b7bbSAlexander V. Chernikov * with the same name, but different table types. 3011b074b7bbSAlexander V. Chernikov */ 3012b074b7bbSAlexander V. Chernikov TAILQ_FOREACH(no, &nh, nn_next) { 3013b074b7bbSAlexander V. Chernikov TAILQ_FOREACH(no_tmp, &nh, nn_next) { 30149490a627SAlexander V. Chernikov if (ipfw_objhash_same_name(ni, no, no_tmp) == 0) 3015b074b7bbSAlexander V. Chernikov continue; 3016b074b7bbSAlexander V. Chernikov if (no->type != no_tmp->type) { 3017b074b7bbSAlexander V. Chernikov error = EINVAL; 3018b074b7bbSAlexander V. Chernikov goto free; 3019b074b7bbSAlexander V. Chernikov } 3020b074b7bbSAlexander V. Chernikov } 3021b074b7bbSAlexander V. Chernikov } 30229f7d47b0SAlexander V. Chernikov } 3023b074b7bbSAlexander V. Chernikov 30249f7d47b0SAlexander V. Chernikov IPFW_UH_WLOCK(chain); 30259f7d47b0SAlexander V. Chernikov 30263a845e10SAlexander V. Chernikov if ((ci->flags & IPFW_RCF_TABLES) != 0) { 3027b074b7bbSAlexander V. Chernikov /* 3028b074b7bbSAlexander V. Chernikov * Stage 3: link & reference new table configs 3029b074b7bbSAlexander V. Chernikov */ 3030b074b7bbSAlexander V. Chernikov 3031b074b7bbSAlexander V. Chernikov 3032b074b7bbSAlexander V. Chernikov /* 3033b074b7bbSAlexander V. Chernikov * Step 3.1: Check if some tables we need to create have been 3034b074b7bbSAlexander V. Chernikov * already created with different table type. 3035b074b7bbSAlexander V. Chernikov */ 3036b074b7bbSAlexander V. Chernikov 3037b074b7bbSAlexander V. Chernikov error = 0; 3038b074b7bbSAlexander V. Chernikov TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) { 3039b074b7bbSAlexander V. Chernikov no_n = ipfw_objhash_lookup_name(ni, no->set, no->name); 3040b074b7bbSAlexander V. Chernikov if (no_n == NULL) 3041b074b7bbSAlexander V. Chernikov continue; 3042b074b7bbSAlexander V. Chernikov 3043b074b7bbSAlexander V. Chernikov if (no_n->type != no->type) { 3044b074b7bbSAlexander V. Chernikov error = EINVAL; 3045b074b7bbSAlexander V. Chernikov break; 3046b074b7bbSAlexander V. Chernikov } 3047b074b7bbSAlexander V. Chernikov 3048b074b7bbSAlexander V. Chernikov } 3049b074b7bbSAlexander V. Chernikov 3050b074b7bbSAlexander V. Chernikov if (error != 0) { 3051b074b7bbSAlexander V. Chernikov /* 3052b074b7bbSAlexander V. Chernikov * Someone has allocated table with different table type. 3053b074b7bbSAlexander V. Chernikov * We have to rollback everything. 3054b074b7bbSAlexander V. Chernikov */ 3055b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 3056b074b7bbSAlexander V. Chernikov goto free; 3057b074b7bbSAlexander V. Chernikov } 3058b074b7bbSAlexander V. Chernikov 3059b074b7bbSAlexander V. Chernikov /* 30609f7d47b0SAlexander V. Chernikov * Attach new tables. 30619f7d47b0SAlexander V. Chernikov * We need to set table pointers for each new table, 3062b074b7bbSAlexander V. Chernikov * so we have to acquire main WLOCK. 3063b074b7bbSAlexander V. Chernikov */ 3064b074b7bbSAlexander V. Chernikov IPFW_WLOCK(chain); 3065b074b7bbSAlexander V. Chernikov TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) { 3066b074b7bbSAlexander V. Chernikov no_n = ipfw_objhash_lookup_name(ni, no->set, no->name); 3067b074b7bbSAlexander V. Chernikov 30689490a627SAlexander V. Chernikov if (no_n == NULL) { 30699490a627SAlexander V. Chernikov /* New table. Attach to runtime hash */ 30709490a627SAlexander V. Chernikov TAILQ_REMOVE(&nh, no, nn_next); 30719490a627SAlexander V. Chernikov link_table(chain, (struct table_config *)no); 3072b074b7bbSAlexander V. Chernikov continue; 3073b074b7bbSAlexander V. Chernikov } 3074b074b7bbSAlexander V. Chernikov 30759490a627SAlexander V. Chernikov /* 30769490a627SAlexander V. Chernikov * Newly-allocated table with the same type. 30779490a627SAlexander V. Chernikov * Reference it and update out @pidx array 30789490a627SAlexander V. Chernikov * rewrite info. 30799490a627SAlexander V. Chernikov */ 30809490a627SAlexander V. Chernikov no_n->refcnt++; 30819490a627SAlexander V. Chernikov /* Keep oib array in sync: update kidx */ 30829490a627SAlexander V. Chernikov for (p = pidx_first; p < pidx_last; p++) { 30839490a627SAlexander V. Chernikov if (p->kidx != no->kidx) 30849490a627SAlexander V. Chernikov continue; 30859490a627SAlexander V. Chernikov /* Update kidx */ 30869490a627SAlexander V. Chernikov p->kidx = no_n->kidx; 30879490a627SAlexander V. Chernikov break; 30889490a627SAlexander V. Chernikov } 3089b074b7bbSAlexander V. Chernikov } 3090b074b7bbSAlexander V. Chernikov IPFW_WUNLOCK(chain); 30919f7d47b0SAlexander V. Chernikov } 3092b074b7bbSAlexander V. Chernikov 3093b074b7bbSAlexander V. Chernikov /* Perform rule rewrite */ 3094b074b7bbSAlexander V. Chernikov l = ci->krule->cmd_len; 3095b074b7bbSAlexander V. Chernikov cmd = ci->krule->cmd; 3096b074b7bbSAlexander V. Chernikov cmdlen = 0; 30979490a627SAlexander V. Chernikov p = pidx_first; 3098b074b7bbSAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 3099b074b7bbSAlexander V. Chernikov cmdlen = F_LEN(cmd); 3100b074b7bbSAlexander V. Chernikov 3101b074b7bbSAlexander V. Chernikov if (classify_table_opcode(cmd, &uidx, &type) != 0) 3102b074b7bbSAlexander V. Chernikov continue; 31039490a627SAlexander V. Chernikov update_table_opcode(cmd, p->kidx); 31049490a627SAlexander V. Chernikov p++; 3105b074b7bbSAlexander V. Chernikov } 3106b074b7bbSAlexander V. Chernikov 3107b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 3108b074b7bbSAlexander V. Chernikov 3109b074b7bbSAlexander V. Chernikov error = 0; 3110b074b7bbSAlexander V. Chernikov 3111b074b7bbSAlexander V. Chernikov /* 3112b074b7bbSAlexander V. Chernikov * Stage 4: free resources 3113b074b7bbSAlexander V. Chernikov */ 3114b074b7bbSAlexander V. Chernikov free: 31159490a627SAlexander V. Chernikov if (!TAILQ_EMPTY(&nh)) { 31169490a627SAlexander V. Chernikov /* Free indexes first */ 31179490a627SAlexander V. Chernikov IPFW_UH_WLOCK(chain); 31189490a627SAlexander V. Chernikov TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) { 3119ac35ff17SAlexander V. Chernikov ipfw_objhash_free_idx(ni, no->kidx); 31209490a627SAlexander V. Chernikov } 31219490a627SAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 31229490a627SAlexander V. Chernikov /* Free configs */ 3123b074b7bbSAlexander V. Chernikov TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) 3124b074b7bbSAlexander V. Chernikov free_table_config(ni, tc); 31259490a627SAlexander V. Chernikov } 3126b074b7bbSAlexander V. Chernikov 31279490a627SAlexander V. Chernikov if (pidx_first != ci->obuf) 31289490a627SAlexander V. Chernikov free(pidx_first, M_IPFW); 3129b074b7bbSAlexander V. Chernikov 3130b074b7bbSAlexander V. Chernikov return (error); 3131b074b7bbSAlexander V. Chernikov } 3132b074b7bbSAlexander V. Chernikov 3133b074b7bbSAlexander V. Chernikov /* 3134b074b7bbSAlexander V. Chernikov * Remove references from every table used in @rule. 3135b074b7bbSAlexander V. Chernikov */ 3136b074b7bbSAlexander V. Chernikov void 3137b074b7bbSAlexander V. Chernikov ipfw_unbind_table_rule(struct ip_fw_chain *chain, struct ip_fw *rule) 3138b074b7bbSAlexander V. Chernikov { 3139b074b7bbSAlexander V. Chernikov int cmdlen, l; 3140b074b7bbSAlexander V. Chernikov ipfw_insn *cmd; 3141b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 3142b074b7bbSAlexander V. Chernikov struct named_object *no; 3143b074b7bbSAlexander V. Chernikov uint16_t kidx; 3144b074b7bbSAlexander V. Chernikov uint8_t type; 3145b074b7bbSAlexander V. Chernikov 3146030b184fSAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(chain); 3147b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(chain); 3148b074b7bbSAlexander V. Chernikov 3149b074b7bbSAlexander V. Chernikov l = rule->cmd_len; 3150b074b7bbSAlexander V. Chernikov cmd = rule->cmd; 3151b074b7bbSAlexander V. Chernikov cmdlen = 0; 3152b074b7bbSAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 3153b074b7bbSAlexander V. Chernikov cmdlen = F_LEN(cmd); 3154b074b7bbSAlexander V. Chernikov 3155b074b7bbSAlexander V. Chernikov if (classify_table_opcode(cmd, &kidx, &type) != 0) 3156b074b7bbSAlexander V. Chernikov continue; 3157b074b7bbSAlexander V. Chernikov 3158ac35ff17SAlexander V. Chernikov no = ipfw_objhash_lookup_kidx(ni, kidx); 3159b074b7bbSAlexander V. Chernikov 3160b074b7bbSAlexander V. Chernikov KASSERT(no != NULL, ("table id %d not found", kidx)); 3161b074b7bbSAlexander V. Chernikov KASSERT(no->type == type, ("wrong type %d (%d) for table id %d", 3162b074b7bbSAlexander V. Chernikov no->type, type, kidx)); 3163b074b7bbSAlexander V. Chernikov KASSERT(no->refcnt > 0, ("refcount for table %d is %d", 3164b074b7bbSAlexander V. Chernikov kidx, no->refcnt)); 3165b074b7bbSAlexander V. Chernikov 3166b074b7bbSAlexander V. Chernikov no->refcnt--; 3167b074b7bbSAlexander V. Chernikov } 3168b074b7bbSAlexander V. Chernikov } 3169b074b7bbSAlexander V. Chernikov 31703b3a8eb9SGleb Smirnoff /* end of file */ 3171