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 /* 303b3a8eb9SGleb Smirnoff * Lookup table support for ipfw 313b3a8eb9SGleb Smirnoff * 323b3a8eb9SGleb Smirnoff * Lookup tables are implemented (at the moment) using the radix 333b3a8eb9SGleb Smirnoff * tree used for routing tables. Tables store key-value entries, where 343b3a8eb9SGleb Smirnoff * keys are network prefixes (addr/masklen), and values are integers. 353b3a8eb9SGleb Smirnoff * As a degenerate case we can interpret keys as 32-bit integers 363b3a8eb9SGleb Smirnoff * (with a /32 mask). 373b3a8eb9SGleb Smirnoff * 383b3a8eb9SGleb Smirnoff * The table is protected by the IPFW lock even for manipulation coming 393b3a8eb9SGleb Smirnoff * from userland, because operations are typically fast. 403b3a8eb9SGleb Smirnoff */ 413b3a8eb9SGleb Smirnoff 423b3a8eb9SGleb Smirnoff #include "opt_ipfw.h" 433b3a8eb9SGleb Smirnoff 443b3a8eb9SGleb Smirnoff #include <sys/param.h> 453b3a8eb9SGleb Smirnoff #include <sys/systm.h> 463b3a8eb9SGleb Smirnoff #include <sys/malloc.h> 473b3a8eb9SGleb Smirnoff #include <sys/kernel.h> 483b3a8eb9SGleb Smirnoff #include <sys/lock.h> 493b3a8eb9SGleb Smirnoff #include <sys/rwlock.h> 503b3a8eb9SGleb Smirnoff #include <sys/socket.h> 51f1220db8SAlexander V. Chernikov #include <sys/socketvar.h> 523b3a8eb9SGleb Smirnoff #include <sys/queue.h> 533b3a8eb9SGleb Smirnoff #include <net/if.h> /* ip_fw.h requires IFNAMSIZ */ 543b3a8eb9SGleb Smirnoff #include <net/route.h> 553b3a8eb9SGleb Smirnoff #include <net/vnet.h> 563b3a8eb9SGleb Smirnoff 573b3a8eb9SGleb Smirnoff #include <netinet/in.h> 583b3a8eb9SGleb Smirnoff #include <netinet/ip_var.h> /* struct ipfw_rule_ref */ 593b3a8eb9SGleb Smirnoff #include <netinet/ip_fw.h> 603b3a8eb9SGleb Smirnoff 613b3a8eb9SGleb Smirnoff #include <netpfil/ipfw/ip_fw_private.h> 62ea761a5dSAlexander V. Chernikov #include <netpfil/ipfw/ip_fw_table.h> 633b3a8eb9SGleb Smirnoff 643b3a8eb9SGleb Smirnoff 653b3a8eb9SGleb Smirnoff /* 66b074b7bbSAlexander V. Chernikov * Table has the following `type` concepts: 67b074b7bbSAlexander V. Chernikov * 689f7d47b0SAlexander V. Chernikov * `no.type` represents lookup key type (cidr, ifp, uid, etc..) 699f7d47b0SAlexander V. Chernikov * `ta->atype` represents exact lookup algorithm. 70b074b7bbSAlexander V. Chernikov * For example, we can use more efficient search schemes if we plan 71b074b7bbSAlexander V. Chernikov * to use some specific table for storing host-routes only. 729490a627SAlexander V. Chernikov * `ftype` (at the moment )is pure userland field helping to properly 739490a627SAlexander V. Chernikov * format value data e.g. "value is IPv4 nexthop" or "value is DSCP" 749490a627SAlexander V. Chernikov * or "value is port". 75b074b7bbSAlexander V. Chernikov * 76b074b7bbSAlexander V. Chernikov */ 77b074b7bbSAlexander V. Chernikov struct table_config { 78b074b7bbSAlexander V. Chernikov struct named_object no; 79b074b7bbSAlexander V. Chernikov uint8_t ftype; /* format table type */ 80b074b7bbSAlexander V. Chernikov uint8_t linked; /* 1 if already linked */ 819f7d47b0SAlexander V. Chernikov uint16_t spare0; 82b074b7bbSAlexander V. Chernikov uint32_t count; /* Number of records */ 83b074b7bbSAlexander V. Chernikov char tablename[64]; /* table name */ 849f7d47b0SAlexander V. Chernikov struct table_algo *ta; /* Callbacks for given algo */ 859f7d47b0SAlexander V. Chernikov void *astate; /* algorithm state */ 869f7d47b0SAlexander V. Chernikov struct table_info ti; /* data to put to table_info */ 87b074b7bbSAlexander V. Chernikov }; 88b074b7bbSAlexander V. Chernikov #define TABLE_SET(set) ((V_fw_tables_sets != 0) ? set : 0) 89b074b7bbSAlexander V. Chernikov 90b074b7bbSAlexander V. Chernikov struct tables_config { 91b074b7bbSAlexander V. Chernikov struct namedobj_instance *namehash; 929f7d47b0SAlexander V. Chernikov int algo_count; 939f7d47b0SAlexander V. Chernikov struct table_algo *algo[256]; 94b074b7bbSAlexander V. Chernikov }; 95b074b7bbSAlexander V. Chernikov 96b074b7bbSAlexander V. Chernikov static struct table_config *find_table(struct namedobj_instance *ni, 97b074b7bbSAlexander V. Chernikov struct tid_info *ti); 98b074b7bbSAlexander V. Chernikov static struct table_config *alloc_table_config(struct namedobj_instance *ni, 999490a627SAlexander V. Chernikov struct tid_info *ti, struct table_algo *ta, char *adata); 100b074b7bbSAlexander V. Chernikov static void free_table_config(struct namedobj_instance *ni, 101b074b7bbSAlexander V. Chernikov struct table_config *tc); 102b074b7bbSAlexander V. Chernikov static void link_table(struct ip_fw_chain *chain, struct table_config *tc); 103b074b7bbSAlexander V. Chernikov static void unlink_table(struct ip_fw_chain *chain, struct table_config *tc); 104b074b7bbSAlexander V. Chernikov static void free_table_state(void **state, void **xstate, uint8_t type); 105f1220db8SAlexander V. Chernikov static int export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh); 106f1220db8SAlexander V. Chernikov static void export_table_info(struct table_config *tc, ipfw_xtable_info *i); 107f1220db8SAlexander V. Chernikov static int dump_table_xentry(void *e, void *arg); 108b074b7bbSAlexander V. Chernikov 109d3a4f924SAlexander V. Chernikov static int check_buffer(size_t items, size_t item_size, size_t header, 110d3a4f924SAlexander V. Chernikov size_t bufsize); 111d3a4f924SAlexander V. Chernikov 112d3a4f924SAlexander V. Chernikov static int ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt *sopt, 113d3a4f924SAlexander V. Chernikov ip_fw3_opheader *op3, size_t valsize); 114d3a4f924SAlexander V. Chernikov static int ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt *sopt, 115d3a4f924SAlexander V. Chernikov ip_fw3_opheader *op3, size_t valsize); 116d3a4f924SAlexander V. Chernikov 1179f7d47b0SAlexander V. Chernikov static struct table_algo *find_table_algo(struct tables_config *tableconf, 1189490a627SAlexander V. Chernikov struct tid_info *ti, char *name); 119b074b7bbSAlexander V. Chernikov 120b074b7bbSAlexander V. Chernikov #define CHAIN_TO_TCFG(chain) ((struct tables_config *)(chain)->tblcfg) 121b074b7bbSAlexander V. Chernikov #define CHAIN_TO_NI(chain) (CHAIN_TO_TCFG(chain)->namehash) 1229f7d47b0SAlexander V. Chernikov #define KIDX_TO_TI(ch, k) (&(((struct table_info *)(ch)->tablestate)[k])) 123b074b7bbSAlexander V. Chernikov 124b074b7bbSAlexander V. Chernikov 1253b3a8eb9SGleb Smirnoff 1263b3a8eb9SGleb Smirnoff int 127b074b7bbSAlexander V. Chernikov ipfw_add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti, 128b074b7bbSAlexander V. Chernikov struct tentry_info *tei) 1293b3a8eb9SGleb Smirnoff { 130b074b7bbSAlexander V. Chernikov struct table_config *tc, *tc_new; 1319f7d47b0SAlexander V. Chernikov struct table_algo *ta; 132b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 133b074b7bbSAlexander V. Chernikov uint16_t kidx; 1349f7d47b0SAlexander V. Chernikov int error; 1359f7d47b0SAlexander V. Chernikov char ta_buf[128]; 1363b3a8eb9SGleb Smirnoff 1379f7d47b0SAlexander V. Chernikov #if 0 138b074b7bbSAlexander V. Chernikov if (ti->uidx >= V_fw_tables_max) 1393b3a8eb9SGleb Smirnoff return (EINVAL); 1403b3a8eb9SGleb Smirnoff #endif 1419f7d47b0SAlexander V. Chernikov 1429f7d47b0SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 1439f7d47b0SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 1449f7d47b0SAlexander V. Chernikov 1459f7d47b0SAlexander V. Chernikov /* 1469f7d47b0SAlexander V. Chernikov * Find and reference existing table. 1479f7d47b0SAlexander V. Chernikov */ 1489f7d47b0SAlexander V. Chernikov ta = NULL; 1499f7d47b0SAlexander V. Chernikov if ((tc = find_table(ni, ti)) != NULL) { 1509f7d47b0SAlexander V. Chernikov /* check table type */ 1519f7d47b0SAlexander V. Chernikov if (tc->no.type != ti->type) { 1529f7d47b0SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1533b3a8eb9SGleb Smirnoff return (EINVAL); 1543b3a8eb9SGleb Smirnoff } 1553b3a8eb9SGleb Smirnoff 1569f7d47b0SAlexander V. Chernikov /* Reference and unlock */ 1579f7d47b0SAlexander V. Chernikov tc->no.refcnt++; 1589f7d47b0SAlexander V. Chernikov ta = tc->ta; 1599f7d47b0SAlexander V. Chernikov } 1609f7d47b0SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1613b3a8eb9SGleb Smirnoff 1629f7d47b0SAlexander V. Chernikov tc_new = NULL; 1639f7d47b0SAlexander V. Chernikov if (ta == NULL) { 1649f7d47b0SAlexander V. Chernikov /* Table not found. We have to create new one */ 1659490a627SAlexander V. Chernikov if ((ta = find_table_algo(CHAIN_TO_TCFG(ch), ti, NULL)) == NULL) 1669f7d47b0SAlexander V. Chernikov return (ENOTSUP); 1673b3a8eb9SGleb Smirnoff 1689490a627SAlexander V. Chernikov tc_new = alloc_table_config(ni, ti, ta, NULL); 1699f7d47b0SAlexander V. Chernikov if (tc_new == NULL) 1709f7d47b0SAlexander V. Chernikov return (ENOMEM); 1719f7d47b0SAlexander V. Chernikov } 1723b3a8eb9SGleb Smirnoff 1739f7d47b0SAlexander V. Chernikov /* Prepare record (allocate memory) */ 1749f7d47b0SAlexander V. Chernikov memset(&ta_buf, 0, sizeof(ta_buf)); 1759f7d47b0SAlexander V. Chernikov error = ta->prepare_add(tei, &ta_buf); 1769f7d47b0SAlexander V. Chernikov if (error != 0) { 1779f7d47b0SAlexander V. Chernikov if (tc_new != NULL) 1789f7d47b0SAlexander V. Chernikov free_table_config(ni, tc_new); 1799f7d47b0SAlexander V. Chernikov return (error); 1803b3a8eb9SGleb Smirnoff } 1813b3a8eb9SGleb Smirnoff 182b074b7bbSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 1833b3a8eb9SGleb Smirnoff 184b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 1853b3a8eb9SGleb Smirnoff 1869f7d47b0SAlexander V. Chernikov if (tc == NULL) { 1879f7d47b0SAlexander V. Chernikov /* Check if another table was allocated by other thread */ 188b074b7bbSAlexander V. Chernikov if ((tc = find_table(ni, ti)) != NULL) { 1899f7d47b0SAlexander V. Chernikov 1909f7d47b0SAlexander V. Chernikov /* 1919f7d47b0SAlexander V. Chernikov * Check if algoritm is the same since we've 1929f7d47b0SAlexander V. Chernikov * already allocated state using @ta algoritm 1939f7d47b0SAlexander V. Chernikov * callbacks. 1949f7d47b0SAlexander V. Chernikov */ 1959f7d47b0SAlexander V. Chernikov if (tc->ta != ta) { 196b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 197b074b7bbSAlexander V. Chernikov free_table_config(ni, tc); 1983b3a8eb9SGleb Smirnoff return (EINVAL); 1993b3a8eb9SGleb Smirnoff } 2003b3a8eb9SGleb Smirnoff } else { 2013b3a8eb9SGleb Smirnoff /* 2029f7d47b0SAlexander V. Chernikov * We're first to create this table. 203b074b7bbSAlexander V. Chernikov * Set tc_new to zero not to free it afterwards. 2043b3a8eb9SGleb Smirnoff */ 205b074b7bbSAlexander V. Chernikov tc = tc_new; 206b074b7bbSAlexander V. Chernikov tc_new = NULL; 207b074b7bbSAlexander V. Chernikov 208b074b7bbSAlexander V. Chernikov /* Allocate table index. */ 209b074b7bbSAlexander V. Chernikov if (ipfw_objhash_alloc_idx(ni, ti->set, &kidx) != 0) { 210b074b7bbSAlexander V. Chernikov /* Index full. */ 211b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 212b074b7bbSAlexander V. Chernikov printf("Unable to allocate index for table %s." 213b074b7bbSAlexander V. Chernikov " Consider increasing " 214b074b7bbSAlexander V. Chernikov "net.inet.ip.fw.tables_max", 215b074b7bbSAlexander V. Chernikov tc->no.name); 216b074b7bbSAlexander V. Chernikov free_table_config(ni, tc); 217b074b7bbSAlexander V. Chernikov return (EBUSY); 2183b3a8eb9SGleb Smirnoff } 219b074b7bbSAlexander V. Chernikov /* Save kidx */ 220b074b7bbSAlexander V. Chernikov tc->no.kidx = kidx; 221b074b7bbSAlexander V. Chernikov } 222b074b7bbSAlexander V. Chernikov } else { 2239f7d47b0SAlexander V. Chernikov /* Drop reference we've used in first search */ 2249f7d47b0SAlexander V. Chernikov tc->no.refcnt--; 225b074b7bbSAlexander V. Chernikov } 226b074b7bbSAlexander V. Chernikov 227b074b7bbSAlexander V. Chernikov /* We've got valid table in @tc. Let's add data */ 2289f7d47b0SAlexander V. Chernikov kidx = tc->no.kidx; 2299f7d47b0SAlexander V. Chernikov ta = tc->ta; 2309f7d47b0SAlexander V. Chernikov 231b074b7bbSAlexander V. Chernikov IPFW_WLOCK(ch); 232b074b7bbSAlexander V. Chernikov 233b074b7bbSAlexander V. Chernikov if (tc->linked == 0) { 234b074b7bbSAlexander V. Chernikov link_table(ch, tc); 235b074b7bbSAlexander V. Chernikov } 236b074b7bbSAlexander V. Chernikov 2379f7d47b0SAlexander V. Chernikov error = ta->add(tc->astate, KIDX_TO_TI(ch, kidx), tei, &ta_buf); 2383b3a8eb9SGleb Smirnoff 2393b3a8eb9SGleb Smirnoff IPFW_WUNLOCK(ch); 2409f7d47b0SAlexander V. Chernikov 2419f7d47b0SAlexander V. Chernikov if (error == 0) 2429f7d47b0SAlexander V. Chernikov tc->count++; 2439f7d47b0SAlexander V. Chernikov 244b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 245b074b7bbSAlexander V. Chernikov 246b074b7bbSAlexander V. Chernikov if (tc_new != NULL) 247b074b7bbSAlexander V. Chernikov free_table_config(ni, tc); 2483b3a8eb9SGleb Smirnoff 2499f7d47b0SAlexander V. Chernikov if (error != 0) 2509f7d47b0SAlexander V. Chernikov ta->flush_entry(tei, &ta_buf); 251b074b7bbSAlexander V. Chernikov 2529f7d47b0SAlexander V. Chernikov return (error); 2533b3a8eb9SGleb Smirnoff } 2543b3a8eb9SGleb Smirnoff 2553b3a8eb9SGleb Smirnoff int 256b074b7bbSAlexander V. Chernikov ipfw_del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti, 257b074b7bbSAlexander V. Chernikov struct tentry_info *tei) 2583b3a8eb9SGleb Smirnoff { 259b074b7bbSAlexander V. Chernikov struct table_config *tc; 2609f7d47b0SAlexander V. Chernikov struct table_algo *ta; 261b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 262b074b7bbSAlexander V. Chernikov uint16_t kidx; 2639f7d47b0SAlexander V. Chernikov int error; 2649f7d47b0SAlexander V. Chernikov char ta_buf[128]; 2653b3a8eb9SGleb Smirnoff 2669f7d47b0SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 267b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 268b074b7bbSAlexander V. Chernikov if ((tc = find_table(ni, ti)) == NULL) { 2699f7d47b0SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 2703b3a8eb9SGleb Smirnoff return (ESRCH); 2713b3a8eb9SGleb Smirnoff } 2723b3a8eb9SGleb Smirnoff 273b074b7bbSAlexander V. Chernikov if (tc->no.type != ti->type) { 2749f7d47b0SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 2753b3a8eb9SGleb Smirnoff return (EINVAL); 2763b3a8eb9SGleb Smirnoff } 2779f7d47b0SAlexander V. Chernikov 2789f7d47b0SAlexander V. Chernikov ta = tc->ta; 2799f7d47b0SAlexander V. Chernikov 2809f7d47b0SAlexander V. Chernikov memset(&ta_buf, 0, sizeof(ta_buf)); 2819f7d47b0SAlexander V. Chernikov if ((error = ta->prepare_del(tei, &ta_buf)) != 0) { 2829f7d47b0SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 2839f7d47b0SAlexander V. Chernikov return (error); 2849f7d47b0SAlexander V. Chernikov } 2859f7d47b0SAlexander V. Chernikov 286b074b7bbSAlexander V. Chernikov kidx = tc->no.kidx; 287b074b7bbSAlexander V. Chernikov 288b074b7bbSAlexander V. Chernikov IPFW_WLOCK(ch); 2899f7d47b0SAlexander V. Chernikov error = ta->del(tc->astate, KIDX_TO_TI(ch, kidx), tei, &ta_buf); 2903b3a8eb9SGleb Smirnoff IPFW_WUNLOCK(ch); 2913b3a8eb9SGleb Smirnoff 2929f7d47b0SAlexander V. Chernikov if (error == 0) 2939f7d47b0SAlexander V. Chernikov tc->count--; 294b074b7bbSAlexander V. Chernikov 2959f7d47b0SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 2963b3a8eb9SGleb Smirnoff 2979f7d47b0SAlexander V. Chernikov if (error != 0) 2989f7d47b0SAlexander V. Chernikov return (error); 2993b3a8eb9SGleb Smirnoff 3009f7d47b0SAlexander V. Chernikov ta->flush_entry(tei, &ta_buf); 3013b3a8eb9SGleb Smirnoff return (0); 3023b3a8eb9SGleb Smirnoff } 3033b3a8eb9SGleb Smirnoff 304b074b7bbSAlexander V. Chernikov /* 3059f7d47b0SAlexander V. Chernikov * Flushes all entries in given table. 306b074b7bbSAlexander V. Chernikov */ 3073b3a8eb9SGleb Smirnoff int 308b074b7bbSAlexander V. Chernikov ipfw_flush_table(struct ip_fw_chain *ch, struct tid_info *ti) 3093b3a8eb9SGleb Smirnoff { 310b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 311b074b7bbSAlexander V. Chernikov struct table_config *tc; 3129f7d47b0SAlexander V. Chernikov struct table_algo *ta; 3139f7d47b0SAlexander V. Chernikov struct table_info ti_old, ti_new, *tablestate; 3149f7d47b0SAlexander V. Chernikov void *astate_old, *astate_new; 315b074b7bbSAlexander V. Chernikov int error; 316b074b7bbSAlexander V. Chernikov uint16_t kidx; 3173b3a8eb9SGleb Smirnoff 3183b3a8eb9SGleb Smirnoff /* 3199f7d47b0SAlexander V. Chernikov * Stage 1: save table algoritm. 320b074b7bbSAlexander V. Chernikov * Reference found table to ensure it won't disappear. 3213b3a8eb9SGleb Smirnoff */ 322b074b7bbSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 323b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 324b074b7bbSAlexander V. Chernikov if ((tc = find_table(ni, ti)) == NULL) { 325b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 326b074b7bbSAlexander V. Chernikov return (ESRCH); 327b074b7bbSAlexander V. Chernikov } 3289f7d47b0SAlexander V. Chernikov ta = tc->ta; 329b074b7bbSAlexander V. Chernikov tc->no.refcnt++; 330b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 3313b3a8eb9SGleb Smirnoff 332b074b7bbSAlexander V. Chernikov /* 3339f7d47b0SAlexander V. Chernikov * Stage 2: allocate new table instance using same algo. 3349490a627SAlexander V. Chernikov * TODO: pass startup parametes somehow. 335b074b7bbSAlexander V. Chernikov */ 3369f7d47b0SAlexander V. Chernikov memset(&ti_new, 0, sizeof(struct table_info)); 3379490a627SAlexander V. Chernikov if ((error = ta->init(&astate_new, &ti_new, NULL)) != 0) { 338b074b7bbSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 339b074b7bbSAlexander V. Chernikov tc->no.refcnt--; 340b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 341b074b7bbSAlexander V. Chernikov return (error); 342b074b7bbSAlexander V. Chernikov } 343b074b7bbSAlexander V. Chernikov 344b074b7bbSAlexander V. Chernikov /* 345b074b7bbSAlexander V. Chernikov * Stage 3: swap old state pointers with newly-allocated ones. 346b074b7bbSAlexander V. Chernikov * Decrease refcount. 347b074b7bbSAlexander V. Chernikov */ 348b074b7bbSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 349b074b7bbSAlexander V. Chernikov 350b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 351b074b7bbSAlexander V. Chernikov kidx = tc->no.kidx; 3529f7d47b0SAlexander V. Chernikov tablestate = (struct table_info *)ch->tablestate; 353b074b7bbSAlexander V. Chernikov 3549f7d47b0SAlexander V. Chernikov IPFW_WLOCK(ch); 3559f7d47b0SAlexander V. Chernikov ti_old = tablestate[kidx]; 3569f7d47b0SAlexander V. Chernikov tablestate[kidx] = ti_new; 3579f7d47b0SAlexander V. Chernikov IPFW_WUNLOCK(ch); 358b074b7bbSAlexander V. Chernikov 3599f7d47b0SAlexander V. Chernikov astate_old = tc->astate; 3609f7d47b0SAlexander V. Chernikov tc->astate = astate_new; 3619f7d47b0SAlexander V. Chernikov tc->ti = ti_new; 3629f7d47b0SAlexander V. Chernikov tc->count = 0; 363b074b7bbSAlexander V. Chernikov tc->no.refcnt--; 364b074b7bbSAlexander V. Chernikov 365b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 3663b3a8eb9SGleb Smirnoff 367b074b7bbSAlexander V. Chernikov /* 368b074b7bbSAlexander V. Chernikov * Stage 4: perform real flush. 369b074b7bbSAlexander V. Chernikov */ 3709f7d47b0SAlexander V. Chernikov ta->destroy(astate_old, &ti_old); 3713b3a8eb9SGleb Smirnoff 3723b3a8eb9SGleb Smirnoff return (0); 3733b3a8eb9SGleb Smirnoff } 3743b3a8eb9SGleb Smirnoff 375b074b7bbSAlexander V. Chernikov /* 3769f7d47b0SAlexander V. Chernikov * Destroys table specified by @ti. 377b074b7bbSAlexander V. Chernikov */ 378b074b7bbSAlexander V. Chernikov int 3799f7d47b0SAlexander V. Chernikov ipfw_destroy_table(struct ip_fw_chain *ch, struct tid_info *ti) 380b074b7bbSAlexander V. Chernikov { 381b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 382b074b7bbSAlexander V. Chernikov struct table_config *tc; 383b074b7bbSAlexander V. Chernikov 384b074b7bbSAlexander V. Chernikov ti->set = TABLE_SET(ti->set); 385b074b7bbSAlexander V. Chernikov 386b074b7bbSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 387b074b7bbSAlexander V. Chernikov 388b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 389b074b7bbSAlexander V. Chernikov if ((tc = find_table(ni, ti)) == NULL) { 390b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 391b074b7bbSAlexander V. Chernikov return (ESRCH); 392b074b7bbSAlexander V. Chernikov } 393b074b7bbSAlexander V. Chernikov 3949f7d47b0SAlexander V. Chernikov /* Do not permit destroying referenced tables */ 3959f7d47b0SAlexander V. Chernikov if (tc->no.refcnt > 0) { 396b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 397b074b7bbSAlexander V. Chernikov return (EBUSY); 398b074b7bbSAlexander V. Chernikov } 399b074b7bbSAlexander V. Chernikov 400b074b7bbSAlexander V. Chernikov IPFW_WLOCK(ch); 401b074b7bbSAlexander V. Chernikov unlink_table(ch, tc); 402b074b7bbSAlexander V. Chernikov IPFW_WUNLOCK(ch); 403b074b7bbSAlexander V. Chernikov 404b074b7bbSAlexander V. Chernikov /* Free obj index */ 405b074b7bbSAlexander V. Chernikov if (ipfw_objhash_free_idx(ni, tc->no.set, tc->no.kidx) != 0) 406b074b7bbSAlexander V. Chernikov printf("Error unlinking kidx %d from table %s\n", 407b074b7bbSAlexander V. Chernikov tc->no.kidx, tc->tablename); 408b074b7bbSAlexander V. Chernikov 409b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 410b074b7bbSAlexander V. Chernikov 411b074b7bbSAlexander V. Chernikov free_table_config(ni, tc); 412b074b7bbSAlexander V. Chernikov 413b074b7bbSAlexander V. Chernikov return (0); 414b074b7bbSAlexander V. Chernikov } 415b074b7bbSAlexander V. Chernikov 416b074b7bbSAlexander V. Chernikov static void 417b074b7bbSAlexander V. Chernikov destroy_table_locked(struct namedobj_instance *ni, struct named_object *no, 418b074b7bbSAlexander V. Chernikov void *arg) 419b074b7bbSAlexander V. Chernikov { 420b074b7bbSAlexander V. Chernikov 421b074b7bbSAlexander V. Chernikov unlink_table((struct ip_fw_chain *)arg, (struct table_config *)no); 422b074b7bbSAlexander V. Chernikov if (ipfw_objhash_free_idx(ni, no->set, no->kidx) != 0) 423b074b7bbSAlexander V. Chernikov printf("Error unlinking kidx %d from table %s\n", 424b074b7bbSAlexander V. Chernikov no->kidx, no->name); 425b074b7bbSAlexander V. Chernikov free_table_config(ni, (struct table_config *)no); 426b074b7bbSAlexander V. Chernikov } 427b074b7bbSAlexander V. Chernikov 4283b3a8eb9SGleb Smirnoff void 4293b3a8eb9SGleb Smirnoff ipfw_destroy_tables(struct ip_fw_chain *ch) 4303b3a8eb9SGleb Smirnoff { 4313b3a8eb9SGleb Smirnoff 432b074b7bbSAlexander V. Chernikov /* Remove all tables from working set */ 433b074b7bbSAlexander V. Chernikov IPFW_UH_WLOCK(ch); 434b074b7bbSAlexander V. Chernikov IPFW_WLOCK(ch); 435b074b7bbSAlexander V. Chernikov ipfw_objhash_foreach(CHAIN_TO_NI(ch), destroy_table_locked, ch); 436b074b7bbSAlexander V. Chernikov IPFW_WUNLOCK(ch); 437b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 4383b3a8eb9SGleb Smirnoff 4393b3a8eb9SGleb Smirnoff /* Free pointers itself */ 4409f7d47b0SAlexander V. Chernikov free(ch->tablestate, M_IPFW); 4419f7d47b0SAlexander V. Chernikov 4429f7d47b0SAlexander V. Chernikov ipfw_table_algo_destroy(ch); 443b074b7bbSAlexander V. Chernikov 444b074b7bbSAlexander V. Chernikov ipfw_objhash_destroy(CHAIN_TO_NI(ch)); 445b074b7bbSAlexander V. Chernikov free(CHAIN_TO_TCFG(ch), M_IPFW); 4463b3a8eb9SGleb Smirnoff } 4473b3a8eb9SGleb Smirnoff 4483b3a8eb9SGleb Smirnoff int 4493b3a8eb9SGleb Smirnoff ipfw_init_tables(struct ip_fw_chain *ch) 4503b3a8eb9SGleb Smirnoff { 451b074b7bbSAlexander V. Chernikov struct tables_config *tcfg; 452b074b7bbSAlexander V. Chernikov 4533b3a8eb9SGleb Smirnoff /* Allocate pointers */ 4549f7d47b0SAlexander V. Chernikov ch->tablestate = malloc(V_fw_tables_max * sizeof(struct table_info), 4559f7d47b0SAlexander V. Chernikov M_IPFW, M_WAITOK | M_ZERO); 456b074b7bbSAlexander V. Chernikov 457b074b7bbSAlexander V. Chernikov tcfg = malloc(sizeof(struct tables_config), M_IPFW, M_WAITOK | M_ZERO); 458b074b7bbSAlexander V. Chernikov tcfg->namehash = ipfw_objhash_create(V_fw_tables_max); 459b074b7bbSAlexander V. Chernikov ch->tblcfg = tcfg; 460b074b7bbSAlexander V. Chernikov 4619f7d47b0SAlexander V. Chernikov ipfw_table_algo_init(ch); 4629f7d47b0SAlexander V. Chernikov 4633b3a8eb9SGleb Smirnoff return (0); 4643b3a8eb9SGleb Smirnoff } 4653b3a8eb9SGleb Smirnoff 4663b3a8eb9SGleb Smirnoff int 4673b3a8eb9SGleb Smirnoff ipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables) 4683b3a8eb9SGleb Smirnoff { 4693b3a8eb9SGleb Smirnoff unsigned int ntables_old, tbl; 470b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 4719f7d47b0SAlexander V. Chernikov void *new_idx, *old_tablestate, *tablestate; 472b074b7bbSAlexander V. Chernikov int new_blocks; 4733b3a8eb9SGleb Smirnoff 4743b3a8eb9SGleb Smirnoff /* Check new value for validity */ 4753b3a8eb9SGleb Smirnoff if (ntables > IPFW_TABLES_MAX) 4763b3a8eb9SGleb Smirnoff ntables = IPFW_TABLES_MAX; 4773b3a8eb9SGleb Smirnoff 4783b3a8eb9SGleb Smirnoff /* Allocate new pointers */ 4799f7d47b0SAlexander V. Chernikov tablestate = malloc(ntables * sizeof(struct table_info), 4809f7d47b0SAlexander V. Chernikov M_IPFW, M_WAITOK | M_ZERO); 4819f7d47b0SAlexander V. Chernikov 482b074b7bbSAlexander V. Chernikov ipfw_objhash_bitmap_alloc(ntables, (void *)&new_idx, &new_blocks); 4833b3a8eb9SGleb Smirnoff 4849f7d47b0SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 4853b3a8eb9SGleb Smirnoff 4863b3a8eb9SGleb Smirnoff tbl = (ntables >= V_fw_tables_max) ? V_fw_tables_max : ntables; 487b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 488b074b7bbSAlexander V. Chernikov 4899f7d47b0SAlexander V. Chernikov /* Temporary restrict decreasing max_tables */ 4909f7d47b0SAlexander V. Chernikov if (ntables < V_fw_tables_max) { 4919f7d47b0SAlexander V. Chernikov 4929f7d47b0SAlexander V. Chernikov /* 4939f7d47b0SAlexander V. Chernikov * FIXME: Check if we really can shrink 4949f7d47b0SAlexander V. Chernikov */ 4959f7d47b0SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 496b074b7bbSAlexander V. Chernikov return (EINVAL); 497b074b7bbSAlexander V. Chernikov } 4983b3a8eb9SGleb Smirnoff 4999f7d47b0SAlexander V. Chernikov /* Copy table info/indices */ 5009f7d47b0SAlexander V. Chernikov memcpy(tablestate, ch->tablestate, sizeof(struct table_info) * tbl); 5019f7d47b0SAlexander V. Chernikov ipfw_objhash_bitmap_merge(ni, &new_idx, &new_blocks); 5023b3a8eb9SGleb Smirnoff 5039f7d47b0SAlexander V. Chernikov IPFW_WLOCK(ch); 5049f7d47b0SAlexander V. Chernikov 5059f7d47b0SAlexander V. Chernikov /* Change pointers */ 5069f7d47b0SAlexander V. Chernikov old_tablestate = ch->tablestate; 5079f7d47b0SAlexander V. Chernikov ch->tablestate = tablestate; 5089f7d47b0SAlexander V. Chernikov ipfw_objhash_bitmap_swap(ni, &new_idx, &new_blocks); 5093b3a8eb9SGleb Smirnoff 5103b3a8eb9SGleb Smirnoff ntables_old = V_fw_tables_max; 5113b3a8eb9SGleb Smirnoff V_fw_tables_max = ntables; 5123b3a8eb9SGleb Smirnoff 5133b3a8eb9SGleb Smirnoff IPFW_WUNLOCK(ch); 5149f7d47b0SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 5153b3a8eb9SGleb Smirnoff 5163b3a8eb9SGleb Smirnoff /* Free old pointers */ 5179f7d47b0SAlexander V. Chernikov free(old_tablestate, M_IPFW); 518b074b7bbSAlexander V. Chernikov ipfw_objhash_bitmap_free(new_idx, new_blocks); 5193b3a8eb9SGleb Smirnoff 5203b3a8eb9SGleb Smirnoff return (0); 5213b3a8eb9SGleb Smirnoff } 5223b3a8eb9SGleb Smirnoff 5233b3a8eb9SGleb Smirnoff int 5243b3a8eb9SGleb Smirnoff ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, 5253b3a8eb9SGleb Smirnoff uint32_t *val) 5263b3a8eb9SGleb Smirnoff { 5279f7d47b0SAlexander V. Chernikov struct table_info *ti; 5283b3a8eb9SGleb Smirnoff 5299f7d47b0SAlexander V. Chernikov ti = &(((struct table_info *)ch->tablestate)[tbl]); 5309f7d47b0SAlexander V. Chernikov 5319f7d47b0SAlexander V. Chernikov return (ti->lookup(ti, &addr, sizeof(in_addr_t), val)); 5323b3a8eb9SGleb Smirnoff } 5339f7d47b0SAlexander V. Chernikov 5349f7d47b0SAlexander V. Chernikov int 5359f7d47b0SAlexander V. Chernikov ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, uint16_t plen, 5369f7d47b0SAlexander V. Chernikov void *paddr, uint32_t *val) 5379f7d47b0SAlexander V. Chernikov { 5389f7d47b0SAlexander V. Chernikov struct table_info *ti; 5399f7d47b0SAlexander V. Chernikov 5409f7d47b0SAlexander V. Chernikov ti = &(((struct table_info *)ch->tablestate)[tbl]); 5419f7d47b0SAlexander V. Chernikov 5429f7d47b0SAlexander V. Chernikov return (ti->lookup(ti, paddr, plen, val)); 5439f7d47b0SAlexander V. Chernikov } 5449f7d47b0SAlexander V. Chernikov 5459f7d47b0SAlexander V. Chernikov /* 5469f7d47b0SAlexander V. Chernikov * Info/List/dump support for tables. 5479f7d47b0SAlexander V. Chernikov * 5489f7d47b0SAlexander V. Chernikov */ 5499f7d47b0SAlexander V. Chernikov 550f1220db8SAlexander V. Chernikov /* 551d3a4f924SAlexander V. Chernikov * High-level 'get' cmds sysctl handlers 552d3a4f924SAlexander V. Chernikov */ 553d3a4f924SAlexander V. Chernikov 554d3a4f924SAlexander V. Chernikov /* 555d3a4f924SAlexander V. Chernikov * Get buffer size needed to list info for all tables. 556d3a4f924SAlexander V. Chernikov * Data layout: 557d3a4f924SAlexander V. Chernikov * Request: [ empty ], size = sizeof(ipfw_obj_lheader) 558d3a4f924SAlexander V. Chernikov * Reply: [ ipfw_obj_lheader ] 559d3a4f924SAlexander V. Chernikov * 560d3a4f924SAlexander V. Chernikov * Returns 0 on success 561f1220db8SAlexander V. Chernikov */ 562f1220db8SAlexander V. Chernikov int 563f1220db8SAlexander V. Chernikov ipfw_listsize_tables(struct ip_fw_chain *ch, struct sockopt *sopt, 564f1220db8SAlexander V. Chernikov ip_fw3_opheader *op3, size_t valsize) 565f1220db8SAlexander V. Chernikov { 566f1220db8SAlexander V. Chernikov struct _ipfw_obj_lheader *olh; 567f1220db8SAlexander V. Chernikov 568d3a4f924SAlexander V. Chernikov if (sopt->sopt_valsize < sizeof(*olh)) 569d3a4f924SAlexander V. Chernikov return (EINVAL); 570d3a4f924SAlexander V. Chernikov 571f1220db8SAlexander V. Chernikov olh = (struct _ipfw_obj_lheader *)op3; 572f1220db8SAlexander V. Chernikov 573f1220db8SAlexander V. Chernikov olh->size = sizeof(*olh); /* Make export_table store needed size */ 574f1220db8SAlexander V. Chernikov 575f1220db8SAlexander V. Chernikov IPFW_UH_RLOCK(ch); 576f1220db8SAlexander V. Chernikov export_tables(ch, olh); 577f1220db8SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 578f1220db8SAlexander V. Chernikov 579f1220db8SAlexander V. Chernikov sopt->sopt_valsize = sizeof(*olh); 580f1220db8SAlexander V. Chernikov return (sooptcopyout(sopt, olh, sopt->sopt_valsize)); 581f1220db8SAlexander V. Chernikov } 582f1220db8SAlexander V. Chernikov 583d3a4f924SAlexander V. Chernikov /* 584d3a4f924SAlexander V. Chernikov * Lists all tables currently available in kernel. 585d3a4f924SAlexander V. Chernikov * Data layout: 586d3a4f924SAlexander V. Chernikov * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size 587d3a4f924SAlexander V. Chernikov * Reply: [ ipfw_obj_lheader ipfw_xtable_info x N ] 588d3a4f924SAlexander V. Chernikov * 589d3a4f924SAlexander V. Chernikov * Returns 0 on success 590d3a4f924SAlexander V. Chernikov */ 591f1220db8SAlexander V. Chernikov int 592f1220db8SAlexander V. Chernikov ipfw_list_tables(struct ip_fw_chain *ch, struct sockopt *sopt, 593f1220db8SAlexander V. Chernikov ip_fw3_opheader *op3, size_t valsize) 594f1220db8SAlexander V. Chernikov { 595f1220db8SAlexander V. Chernikov struct _ipfw_obj_lheader *olh; 596d3a4f924SAlexander V. Chernikov uint32_t sz; 597f1220db8SAlexander V. Chernikov int error; 598f1220db8SAlexander V. Chernikov 599d3a4f924SAlexander V. Chernikov if (sopt->sopt_valsize < sizeof(*olh)) 600d3a4f924SAlexander V. Chernikov return (EINVAL); 601d3a4f924SAlexander V. Chernikov 602f1220db8SAlexander V. Chernikov olh = (struct _ipfw_obj_lheader *)op3; 603f1220db8SAlexander V. Chernikov 604f1220db8SAlexander V. Chernikov if (valsize != olh->size) 605f1220db8SAlexander V. Chernikov return (EINVAL); 606f1220db8SAlexander V. Chernikov 607f1220db8SAlexander V. Chernikov /* 608f1220db8SAlexander V. Chernikov * Check if array size is "reasonable": 609f1220db8SAlexander V. Chernikov * Permit valsize between current size and 610f1220db8SAlexander V. Chernikov * 2x current size + 1 611f1220db8SAlexander V. Chernikov */ 612f1220db8SAlexander V. Chernikov IPFW_UH_RLOCK(ch); 613f1220db8SAlexander V. Chernikov sz = ipfw_objhash_count(CHAIN_TO_NI(ch)); 614f1220db8SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 615f1220db8SAlexander V. Chernikov 616d3a4f924SAlexander V. Chernikov if (check_buffer(sz, sizeof(ipfw_xtable_info), 617d3a4f924SAlexander V. Chernikov sizeof(*olh), valsize) != 0) 618f1220db8SAlexander V. Chernikov return (EINVAL); 619f1220db8SAlexander V. Chernikov 620f1220db8SAlexander V. Chernikov olh = malloc(valsize, M_TEMP, M_ZERO | M_WAITOK); 621f1220db8SAlexander V. Chernikov /* Copy header to new storage */ 622f1220db8SAlexander V. Chernikov memcpy(olh, op3, sizeof(*olh)); 623f1220db8SAlexander V. Chernikov 624f1220db8SAlexander V. Chernikov IPFW_UH_RLOCK(ch); 625f1220db8SAlexander V. Chernikov error = export_tables(ch, olh); 626f1220db8SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 627f1220db8SAlexander V. Chernikov 628f1220db8SAlexander V. Chernikov if (error != 0) { 629f1220db8SAlexander V. Chernikov free(olh, M_TEMP); 630f1220db8SAlexander V. Chernikov return (error); 631f1220db8SAlexander V. Chernikov } 632f1220db8SAlexander V. Chernikov 633f1220db8SAlexander V. Chernikov /* 634f1220db8SAlexander V. Chernikov * Since we call sooptcopyin() with small buffer, 635f1220db8SAlexander V. Chernikov * sopt_valsize is decreased to reflect supplied 636f1220db8SAlexander V. Chernikov * buffer size. Set it back to original value. 637f1220db8SAlexander V. Chernikov */ 638f1220db8SAlexander V. Chernikov sopt->sopt_valsize = valsize; 639f1220db8SAlexander V. Chernikov error = sooptcopyout(sopt, olh, olh->size); 640f1220db8SAlexander V. Chernikov free(olh, M_TEMP); 641f1220db8SAlexander V. Chernikov 642f1220db8SAlexander V. Chernikov return (0); 643f1220db8SAlexander V. Chernikov } 644f1220db8SAlexander V. Chernikov 645d3a4f924SAlexander V. Chernikov /* 646d3a4f924SAlexander V. Chernikov * Store table info to buffer provided by @op3. 647d3a4f924SAlexander V. Chernikov * Data layout: 648d3a4f924SAlexander V. Chernikov * Request: [ ipfw_obj_header ipfw_xtable_info(empty)] 649d3a4f924SAlexander V. Chernikov * Reply: [ ipfw_obj_header ipfw_xtable_info ] 650d3a4f924SAlexander V. Chernikov * 651d3a4f924SAlexander V. Chernikov * Returns 0 on success. 652d3a4f924SAlexander V. Chernikov */ 653d3a4f924SAlexander V. Chernikov int 654d3a4f924SAlexander V. Chernikov ipfw_describe_table(struct ip_fw_chain *ch, struct sockopt *sopt, 655d3a4f924SAlexander V. Chernikov ip_fw3_opheader *op3, size_t valsize) 656d3a4f924SAlexander V. Chernikov { 657d3a4f924SAlexander V. Chernikov struct _ipfw_obj_header *oh; 658d3a4f924SAlexander V. Chernikov struct table_config *tc; 659d3a4f924SAlexander V. Chernikov struct tid_info ti; 660d3a4f924SAlexander V. Chernikov size_t sz; 661d3a4f924SAlexander V. Chernikov int error; 662d3a4f924SAlexander V. Chernikov 663d3a4f924SAlexander V. Chernikov sz = sizeof(*oh) + sizeof(ipfw_xtable_info); 664d3a4f924SAlexander V. Chernikov if (sopt->sopt_valsize < sz) 665d3a4f924SAlexander V. Chernikov return (EINVAL); 666d3a4f924SAlexander V. Chernikov 667d3a4f924SAlexander V. Chernikov oh = (struct _ipfw_obj_header *)op3; 668d3a4f924SAlexander V. Chernikov 669d3a4f924SAlexander V. Chernikov objheader_to_ti(oh, &ti); 670d3a4f924SAlexander V. Chernikov 671d3a4f924SAlexander V. Chernikov IPFW_UH_RLOCK(ch); 672d3a4f924SAlexander V. Chernikov if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) { 673d3a4f924SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 674d3a4f924SAlexander V. Chernikov return (ESRCH); 675d3a4f924SAlexander V. Chernikov } 676d3a4f924SAlexander V. Chernikov 677d3a4f924SAlexander V. Chernikov export_table_info(tc, (ipfw_xtable_info *)(oh + 1)); 678d3a4f924SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 679d3a4f924SAlexander V. Chernikov 680d3a4f924SAlexander V. Chernikov error = sooptcopyout(sopt, oh, sz); 681d3a4f924SAlexander V. Chernikov 682d3a4f924SAlexander V. Chernikov return (error); 683d3a4f924SAlexander V. Chernikov } 684d3a4f924SAlexander V. Chernikov 685f1220db8SAlexander V. Chernikov struct dump_args { 686f1220db8SAlexander V. Chernikov struct table_info *ti; 687f1220db8SAlexander V. Chernikov struct table_config *tc; 688f1220db8SAlexander V. Chernikov ipfw_table_entry *ent; 689f1220db8SAlexander V. Chernikov ipfw_table_xentry *xent; 690f1220db8SAlexander V. Chernikov uint32_t cnt; 691f1220db8SAlexander V. Chernikov uint32_t size; 692f1220db8SAlexander V. Chernikov uint16_t uidx; 693f1220db8SAlexander V. Chernikov }; 694f1220db8SAlexander V. Chernikov 695f1220db8SAlexander V. Chernikov int 696f1220db8SAlexander V. Chernikov ipfw_dump_table(struct ip_fw_chain *ch, struct sockopt *sopt, 697f1220db8SAlexander V. Chernikov ip_fw3_opheader *op3, size_t valsize) 698f1220db8SAlexander V. Chernikov { 699d3a4f924SAlexander V. Chernikov int error; 700d3a4f924SAlexander V. Chernikov 701d3a4f924SAlexander V. Chernikov switch (op3->version) { 702d3a4f924SAlexander V. Chernikov case 0: 703d3a4f924SAlexander V. Chernikov error = ipfw_dump_table_v0(ch, sopt, op3, valsize); 704d3a4f924SAlexander V. Chernikov break; 705d3a4f924SAlexander V. Chernikov case 1: 706d3a4f924SAlexander V. Chernikov error = ipfw_dump_table_v1(ch, sopt, op3, valsize); 707d3a4f924SAlexander V. Chernikov break; 708d3a4f924SAlexander V. Chernikov default: 709d3a4f924SAlexander V. Chernikov error = ENOTSUP; 710d3a4f924SAlexander V. Chernikov } 711d3a4f924SAlexander V. Chernikov 712d3a4f924SAlexander V. Chernikov return (error); 713d3a4f924SAlexander V. Chernikov } 714d3a4f924SAlexander V. Chernikov 715d3a4f924SAlexander V. Chernikov /* 716d3a4f924SAlexander V. Chernikov * Dumps all table data 717d3a4f924SAlexander V. Chernikov * Data layout (version 1): 718d3a4f924SAlexander V. Chernikov * Request: [ ipfw_obj_lheader ], size = ipfw_xtable_info.size 719d3a4f924SAlexander V. Chernikov * Reply: [ ipfw_obj_lheader ipfw_xtable_info ipfw_table_xentry x N ] 720d3a4f924SAlexander V. Chernikov * 721d3a4f924SAlexander V. Chernikov * Returns 0 on success 722d3a4f924SAlexander V. Chernikov */ 723d3a4f924SAlexander V. Chernikov static int 724d3a4f924SAlexander V. Chernikov ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt *sopt, 725d3a4f924SAlexander V. Chernikov ip_fw3_opheader *op3, size_t valsize) 726d3a4f924SAlexander V. Chernikov { 727f1220db8SAlexander V. Chernikov struct _ipfw_obj_header *oh; 728f1220db8SAlexander V. Chernikov ipfw_xtable_info *i; 729f1220db8SAlexander V. Chernikov struct tid_info ti; 730f1220db8SAlexander V. Chernikov struct table_config *tc; 731f1220db8SAlexander V. Chernikov struct table_algo *ta; 732f1220db8SAlexander V. Chernikov struct dump_args da; 733d3a4f924SAlexander V. Chernikov uint32_t sz; 734f1220db8SAlexander V. Chernikov int error; 735f1220db8SAlexander V. Chernikov 736d3a4f924SAlexander V. Chernikov if (sopt->sopt_valsize < sizeof(*oh)) 737d3a4f924SAlexander V. Chernikov return (EINVAL); 738d3a4f924SAlexander V. Chernikov 739f1220db8SAlexander V. Chernikov oh = (struct _ipfw_obj_header *)op3; 740f1220db8SAlexander V. Chernikov 741d3a4f924SAlexander V. Chernikov objheader_to_ti(oh, &ti); 742f1220db8SAlexander V. Chernikov 743f1220db8SAlexander V. Chernikov IPFW_UH_RLOCK(ch); 744f1220db8SAlexander V. Chernikov if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) { 745f1220db8SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 746f1220db8SAlexander V. Chernikov return (ESRCH); 747f1220db8SAlexander V. Chernikov } 748f1220db8SAlexander V. Chernikov sz = tc->count; 749f1220db8SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 750f1220db8SAlexander V. Chernikov 751d3a4f924SAlexander V. Chernikov if (check_buffer(sz, sizeof(ipfw_table_xentry), 752d3a4f924SAlexander V. Chernikov sizeof(ipfw_xtable_info) + sizeof(*oh), valsize) != 0) 753f1220db8SAlexander V. Chernikov return (EINVAL); 754f1220db8SAlexander V. Chernikov 755f1220db8SAlexander V. Chernikov oh = malloc(valsize, M_TEMP, M_ZERO | M_WAITOK); 756f1220db8SAlexander V. Chernikov i = (ipfw_xtable_info *)(oh + 1); 757f1220db8SAlexander V. Chernikov /* Copy header to new storage */ 758f1220db8SAlexander V. Chernikov memcpy(oh, op3, sizeof(*oh)); 759f1220db8SAlexander V. Chernikov 760f1220db8SAlexander V. Chernikov IPFW_UH_RLOCK(ch); 761f1220db8SAlexander V. Chernikov /* Find table and export info */ 762f1220db8SAlexander V. Chernikov if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) { 763f1220db8SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 764f1220db8SAlexander V. Chernikov free(oh, M_TEMP); 765f1220db8SAlexander V. Chernikov return (ESRCH); 766f1220db8SAlexander V. Chernikov } 767f1220db8SAlexander V. Chernikov 768f1220db8SAlexander V. Chernikov export_table_info(tc, i); 769f1220db8SAlexander V. Chernikov if (i->size > valsize) { 770f1220db8SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 771f1220db8SAlexander V. Chernikov /* XXX: Should we pass size structure back ? */ 772f1220db8SAlexander V. Chernikov free(oh, M_TEMP); 773f1220db8SAlexander V. Chernikov return (EINVAL); 774f1220db8SAlexander V. Chernikov } 775f1220db8SAlexander V. Chernikov 776f1220db8SAlexander V. Chernikov /* 777f1220db8SAlexander V. Chernikov * Do the actual dump in eXtended format 778f1220db8SAlexander V. Chernikov */ 779d3a4f924SAlexander V. Chernikov memset(&da, 0, sizeof(da)); 780f1220db8SAlexander V. Chernikov da.ti = KIDX_TO_TI(ch, tc->no.kidx); 781f1220db8SAlexander V. Chernikov da.tc = tc; 782f1220db8SAlexander V. Chernikov da.xent = (ipfw_table_xentry *)(i + 1); 783f1220db8SAlexander V. Chernikov da.size = (valsize - sizeof(*oh) - sizeof(ipfw_xtable_info)) / 784f1220db8SAlexander V. Chernikov sizeof(ipfw_table_xentry); 785f1220db8SAlexander V. Chernikov 786f1220db8SAlexander V. Chernikov ta = tc->ta; 787f1220db8SAlexander V. Chernikov 788f1220db8SAlexander V. Chernikov ta->foreach(tc->astate, da.ti, dump_table_xentry, &da); 789f1220db8SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 790f1220db8SAlexander V. Chernikov 791f1220db8SAlexander V. Chernikov /* 792f1220db8SAlexander V. Chernikov * Since we call sooptcopyin() with small buffer, 793f1220db8SAlexander V. Chernikov * sopt_valsize is decreased to reflect supplied 794f1220db8SAlexander V. Chernikov * buffer size. Set it back to original value. 795f1220db8SAlexander V. Chernikov */ 796f1220db8SAlexander V. Chernikov sopt->sopt_valsize = valsize; 797f1220db8SAlexander V. Chernikov error = sooptcopyout(sopt, oh, i->size); 798f1220db8SAlexander V. Chernikov free(oh, M_TEMP); 799f1220db8SAlexander V. Chernikov 800f1220db8SAlexander V. Chernikov return (0); 801f1220db8SAlexander V. Chernikov } 802f1220db8SAlexander V. Chernikov 803d3a4f924SAlexander V. Chernikov /* 804d3a4f924SAlexander V. Chernikov * Dumps all table data 805d3a4f924SAlexander V. Chernikov * Data layout (version 0): 806d3a4f924SAlexander V. Chernikov * Request: [ ipfw_xtable ], size = IP_FW_TABLE_XGETSIZE() 807d3a4f924SAlexander V. Chernikov * Reply: [ ipfw_xtable ipfw_table_xentry x N ] 808d3a4f924SAlexander V. Chernikov * 809d3a4f924SAlexander V. Chernikov * Returns 0 on success 810d3a4f924SAlexander V. Chernikov */ 811d3a4f924SAlexander V. Chernikov static int 812d3a4f924SAlexander V. Chernikov ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt *sopt, 813d3a4f924SAlexander V. Chernikov ip_fw3_opheader *op3, size_t valsize) 814d3a4f924SAlexander V. Chernikov { 815d3a4f924SAlexander V. Chernikov ipfw_xtable *xtbl; 816d3a4f924SAlexander V. Chernikov struct tid_info ti; 817d3a4f924SAlexander V. Chernikov struct table_config *tc; 818d3a4f924SAlexander V. Chernikov struct table_algo *ta; 819d3a4f924SAlexander V. Chernikov struct dump_args da; 820d3a4f924SAlexander V. Chernikov int error; 821d3a4f924SAlexander V. Chernikov size_t sz; 822d3a4f924SAlexander V. Chernikov 823d3a4f924SAlexander V. Chernikov if (valsize < sizeof(ipfw_xtable)) 824d3a4f924SAlexander V. Chernikov return (EINVAL); 825d3a4f924SAlexander V. Chernikov 826d3a4f924SAlexander V. Chernikov xtbl = (ipfw_xtable *)op3; 827d3a4f924SAlexander V. Chernikov 828d3a4f924SAlexander V. Chernikov memset(&ti, 0, sizeof(ti)); 829d3a4f924SAlexander V. Chernikov ti.set = 0; /* XXX: No way to specify set */ 830d3a4f924SAlexander V. Chernikov ti.uidx = xtbl->tbl; 831d3a4f924SAlexander V. Chernikov 832d3a4f924SAlexander V. Chernikov IPFW_UH_RLOCK(ch); 833d3a4f924SAlexander V. Chernikov if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) { 834d3a4f924SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 835d3a4f924SAlexander V. Chernikov return (0); 836d3a4f924SAlexander V. Chernikov } 837d3a4f924SAlexander V. Chernikov sz = tc->count; 838d3a4f924SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 839d3a4f924SAlexander V. Chernikov 840d3a4f924SAlexander V. Chernikov if (check_buffer(sz, sizeof(ipfw_table_xentry), 841d3a4f924SAlexander V. Chernikov sizeof(ipfw_xtable) - sizeof(ipfw_table_xentry), valsize) != 0) 842d3a4f924SAlexander V. Chernikov return (EINVAL); 843d3a4f924SAlexander V. Chernikov 844d3a4f924SAlexander V. Chernikov xtbl = malloc(valsize, M_TEMP, M_ZERO | M_WAITOK); 845d3a4f924SAlexander V. Chernikov memcpy(xtbl, op3, sizeof(ipfw_xtable)); 846d3a4f924SAlexander V. Chernikov 847d3a4f924SAlexander V. Chernikov IPFW_UH_RLOCK(ch); 848d3a4f924SAlexander V. Chernikov if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) { 849d3a4f924SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 850d3a4f924SAlexander V. Chernikov free(xtbl, M_TEMP); 851d3a4f924SAlexander V. Chernikov return (0); 852d3a4f924SAlexander V. Chernikov } 853d3a4f924SAlexander V. Chernikov 854d3a4f924SAlexander V. Chernikov /* Check size another time */ 855d3a4f924SAlexander V. Chernikov sz = tc->count * sizeof(ipfw_table_xentry) + sizeof(ipfw_xtable); 856d3a4f924SAlexander V. Chernikov if (sz > valsize) { 857d3a4f924SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 858d3a4f924SAlexander V. Chernikov free(xtbl, M_TEMP); 859d3a4f924SAlexander V. Chernikov return (EINVAL); 860d3a4f924SAlexander V. Chernikov } 861d3a4f924SAlexander V. Chernikov 862d3a4f924SAlexander V. Chernikov /* Do the actual dump in eXtended format */ 863d3a4f924SAlexander V. Chernikov memset(&da, 0, sizeof(da)); 864d3a4f924SAlexander V. Chernikov da.ti = KIDX_TO_TI(ch, tc->no.kidx); 865d3a4f924SAlexander V. Chernikov da.tc = tc; 866d3a4f924SAlexander V. Chernikov da.xent = &xtbl->xent[0]; 867d3a4f924SAlexander V. Chernikov da.size = tc->count; 868d3a4f924SAlexander V. Chernikov xtbl->type = tc->no.type; 869d3a4f924SAlexander V. Chernikov xtbl->tbl = ti.uidx; 870d3a4f924SAlexander V. Chernikov ta = tc->ta; 871d3a4f924SAlexander V. Chernikov 872d3a4f924SAlexander V. Chernikov ta->foreach(tc->astate, da.ti, dump_table_xentry, &da); 873d3a4f924SAlexander V. Chernikov xtbl->cnt = da.cnt; 874d3a4f924SAlexander V. Chernikov xtbl->size = sz; 875d3a4f924SAlexander V. Chernikov 876d3a4f924SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 877d3a4f924SAlexander V. Chernikov 878d3a4f924SAlexander V. Chernikov /* 879d3a4f924SAlexander V. Chernikov * Since we call sooptcopyin() with small buffer, sopt_valsize is 880d3a4f924SAlexander V. Chernikov * decreased to reflect supplied buffer size. Set it back to original value 881d3a4f924SAlexander V. Chernikov */ 882d3a4f924SAlexander V. Chernikov sopt->sopt_valsize = valsize; 883d3a4f924SAlexander V. Chernikov error = sooptcopyout(sopt, xtbl, sz); 884d3a4f924SAlexander V. Chernikov free(xtbl, M_TEMP); 885d3a4f924SAlexander V. Chernikov 886d3a4f924SAlexander V. Chernikov return (error); 887d3a4f924SAlexander V. Chernikov } 888d3a4f924SAlexander V. Chernikov 889d3a4f924SAlexander V. Chernikov /* 8909490a627SAlexander V. Chernikov * High-level setsockopt cmds 8919490a627SAlexander V. Chernikov */ 8929490a627SAlexander V. Chernikov int 8939490a627SAlexander V. Chernikov ipfw_modify_table(struct ip_fw_chain *ch, struct sockopt *sopt, 8949490a627SAlexander V. Chernikov ip_fw3_opheader *op3) 8959490a627SAlexander V. Chernikov { 8969490a627SAlexander V. Chernikov 8979490a627SAlexander V. Chernikov return (ENOTSUP); 8989490a627SAlexander V. Chernikov } 8999490a627SAlexander V. Chernikov 9009490a627SAlexander V. Chernikov /* 9019490a627SAlexander V. Chernikov * Creates new table. 9029490a627SAlexander V. Chernikov * Data layout: 9039490a627SAlexander V. Chernikov * Request: [ ipfw_obj_header ipfw_xtable_info ] 9049490a627SAlexander V. Chernikov * 9059490a627SAlexander V. Chernikov * Returns 0 on success 9069490a627SAlexander V. Chernikov */ 9079490a627SAlexander V. Chernikov int 9089490a627SAlexander V. Chernikov ipfw_create_table(struct ip_fw_chain *ch, struct sockopt *sopt, 9099490a627SAlexander V. Chernikov ip_fw3_opheader *op3) 9109490a627SAlexander V. Chernikov { 9119490a627SAlexander V. Chernikov struct _ipfw_obj_header *oh; 9129490a627SAlexander V. Chernikov ipfw_xtable_info *i; 9139490a627SAlexander V. Chernikov char *tname, *aname; 9149490a627SAlexander V. Chernikov struct tid_info ti; 9159490a627SAlexander V. Chernikov struct namedobj_instance *ni; 9169490a627SAlexander V. Chernikov struct table_config *tc; 9179490a627SAlexander V. Chernikov struct table_algo *ta; 9189490a627SAlexander V. Chernikov uint16_t kidx; 9199490a627SAlexander V. Chernikov 9209490a627SAlexander V. Chernikov if (sopt->sopt_valsize < sizeof(*oh) + sizeof(ipfw_xtable_info)) 9219490a627SAlexander V. Chernikov return (EINVAL); 9229490a627SAlexander V. Chernikov 9239490a627SAlexander V. Chernikov oh = (struct _ipfw_obj_header *)op3; 9249490a627SAlexander V. Chernikov i = (ipfw_xtable_info *)(oh + 1); 9259490a627SAlexander V. Chernikov 9269490a627SAlexander V. Chernikov /* 9279490a627SAlexander V. Chernikov * Verify user-supplied strings. 9289490a627SAlexander V. Chernikov * Check for null-terminated/zero-lenght strings/ 9299490a627SAlexander V. Chernikov */ 9309490a627SAlexander V. Chernikov tname = i->tablename; 9319490a627SAlexander V. Chernikov aname = i->algoname; 9329490a627SAlexander V. Chernikov if (strnlen(tname, sizeof(i->tablename)) == sizeof(i->tablename) || 9339490a627SAlexander V. Chernikov tname[0] == '\0' || 9349490a627SAlexander V. Chernikov strnlen(aname, sizeof(i->algoname)) == sizeof(i->algoname)) 9359490a627SAlexander V. Chernikov return (EINVAL); 9369490a627SAlexander V. Chernikov 9379490a627SAlexander V. Chernikov if (aname[0] == '\0') { 9389490a627SAlexander V. Chernikov /* Use default algorithm */ 9399490a627SAlexander V. Chernikov aname = NULL; 9409490a627SAlexander V. Chernikov } 9419490a627SAlexander V. Chernikov 9429490a627SAlexander V. Chernikov objheader_to_ti(oh, &ti); 9439490a627SAlexander V. Chernikov 9449490a627SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 9459490a627SAlexander V. Chernikov 9469490a627SAlexander V. Chernikov IPFW_UH_RLOCK(ch); 9479490a627SAlexander V. Chernikov if ((tc = find_table(ni, &ti)) != NULL) { 9489490a627SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 9499490a627SAlexander V. Chernikov return (EEXIST); 9509490a627SAlexander V. Chernikov } 9519490a627SAlexander V. Chernikov ta = find_table_algo(CHAIN_TO_TCFG(ch), &ti, aname); 9529490a627SAlexander V. Chernikov IPFW_UH_RUNLOCK(ch); 9539490a627SAlexander V. Chernikov 9549490a627SAlexander V. Chernikov if (ta == NULL) 9559490a627SAlexander V. Chernikov return (ENOTSUP); 9569490a627SAlexander V. Chernikov 9579490a627SAlexander V. Chernikov if ((tc = alloc_table_config(ni, &ti, ta, aname)) == NULL) 9589490a627SAlexander V. Chernikov return (ENOMEM); 9599490a627SAlexander V. Chernikov 9609490a627SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 9619490a627SAlexander V. Chernikov if (ipfw_objhash_alloc_idx(ni, ti.set, &kidx) != 0) { 9629490a627SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 9639490a627SAlexander V. Chernikov printf("Unable to allocate table index for table %s in set %u." 9649490a627SAlexander V. Chernikov " Consider increasing net.inet.ip.fw.tables_max", 9659490a627SAlexander V. Chernikov tname, ti.set); 9669490a627SAlexander V. Chernikov free_table_config(ni, tc); 9679490a627SAlexander V. Chernikov return (EBUSY); 9689490a627SAlexander V. Chernikov } 9699490a627SAlexander V. Chernikov 9709490a627SAlexander V. Chernikov tc->no.kidx = kidx; 9719490a627SAlexander V. Chernikov 9729490a627SAlexander V. Chernikov IPFW_WLOCK(ch); 9739490a627SAlexander V. Chernikov link_table(ch, tc); 9749490a627SAlexander V. Chernikov IPFW_WUNLOCK(ch); 9759490a627SAlexander V. Chernikov 9769490a627SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 9779490a627SAlexander V. Chernikov 9789490a627SAlexander V. Chernikov return (0); 9799490a627SAlexander V. Chernikov } 9809490a627SAlexander V. Chernikov 9819490a627SAlexander V. Chernikov 9829490a627SAlexander V. Chernikov /* 983d3a4f924SAlexander V. Chernikov * Checks if supplied buffer size is "reasonable". 984d3a4f924SAlexander V. Chernikov * Permit valsize between current needed size and 985d3a4f924SAlexander V. Chernikov * 2x needed size + 1 986d3a4f924SAlexander V. Chernikov */ 987d3a4f924SAlexander V. Chernikov static int 988d3a4f924SAlexander V. Chernikov check_buffer(size_t items, size_t item_size, size_t header, size_t bufsize) 989d3a4f924SAlexander V. Chernikov { 990d3a4f924SAlexander V. Chernikov size_t sz_min, sz_max; 991d3a4f924SAlexander V. Chernikov 992d3a4f924SAlexander V. Chernikov sz_min = items * item_size + header; 993d3a4f924SAlexander V. Chernikov sz_max = (2 * items + 1) * item_size + header; 994d3a4f924SAlexander V. Chernikov 995d3a4f924SAlexander V. Chernikov if (bufsize < sz_min || bufsize > sz_max) 996d3a4f924SAlexander V. Chernikov return (EINVAL); 997d3a4f924SAlexander V. Chernikov 998d3a4f924SAlexander V. Chernikov return (0); 999d3a4f924SAlexander V. Chernikov } 1000d3a4f924SAlexander V. Chernikov 1001d3a4f924SAlexander V. Chernikov void 1002d3a4f924SAlexander V. Chernikov objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti) 1003d3a4f924SAlexander V. Chernikov { 1004d3a4f924SAlexander V. Chernikov 1005d3a4f924SAlexander V. Chernikov memset(ti, 0, sizeof(struct tid_info)); 1006d3a4f924SAlexander V. Chernikov ti->set = oh->set; 1007d3a4f924SAlexander V. Chernikov ti->uidx = oh->idx; 1008d3a4f924SAlexander V. Chernikov ti->tlvs = &oh->ntlv; 1009d3a4f924SAlexander V. Chernikov ti->tlen = oh->ntlv.head.length; 1010d3a4f924SAlexander V. Chernikov } 1011d3a4f924SAlexander V. Chernikov 10129f7d47b0SAlexander V. Chernikov static void 10139f7d47b0SAlexander V. Chernikov export_table_info(struct table_config *tc, ipfw_xtable_info *i) 10149f7d47b0SAlexander V. Chernikov { 10159f7d47b0SAlexander V. Chernikov 10169f7d47b0SAlexander V. Chernikov i->type = tc->no.type; 10179f7d47b0SAlexander V. Chernikov i->ftype = tc->ftype; 10189f7d47b0SAlexander V. Chernikov i->atype = tc->ta->idx; 10199f7d47b0SAlexander V. Chernikov i->set = tc->no.set; 10209f7d47b0SAlexander V. Chernikov i->kidx = tc->no.kidx; 10219f7d47b0SAlexander V. Chernikov i->refcnt = tc->no.refcnt; 10229f7d47b0SAlexander V. Chernikov i->count = tc->count; 10239f7d47b0SAlexander V. Chernikov i->size = tc->count * sizeof(ipfw_table_xentry); 1024f1220db8SAlexander V. Chernikov i->size += sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info); 10259f7d47b0SAlexander V. Chernikov strlcpy(i->tablename, tc->tablename, sizeof(i->tablename)); 10269f7d47b0SAlexander V. Chernikov } 10279f7d47b0SAlexander V. Chernikov 10289f7d47b0SAlexander V. Chernikov static void 10299f7d47b0SAlexander V. Chernikov export_table_internal(struct namedobj_instance *ni, struct named_object *no, 10309f7d47b0SAlexander V. Chernikov void *arg) 10313b3a8eb9SGleb Smirnoff { 10329f7d47b0SAlexander V. Chernikov ipfw_obj_lheader *olh; 10339f7d47b0SAlexander V. Chernikov ipfw_xtable_info *i; 10343b3a8eb9SGleb Smirnoff 10359f7d47b0SAlexander V. Chernikov olh = (ipfw_obj_lheader *)arg; 10369f7d47b0SAlexander V. Chernikov i = (ipfw_xtable_info *)(caddr_t)(olh + 1) + olh->count; 10379f7d47b0SAlexander V. Chernikov olh->count++; 10389f7d47b0SAlexander V. Chernikov 10399f7d47b0SAlexander V. Chernikov export_table_info((struct table_config *)no, i); 10409f7d47b0SAlexander V. Chernikov } 10419f7d47b0SAlexander V. Chernikov 1042f1220db8SAlexander V. Chernikov /* 1043f1220db8SAlexander V. Chernikov * Export all tables as ipfw_xtable_info structures to 1044f1220db8SAlexander V. Chernikov * storage provided by @olh. 1045f1220db8SAlexander V. Chernikov * Returns 0 on success. 1046f1220db8SAlexander V. Chernikov */ 1047f1220db8SAlexander V. Chernikov static int 1048f1220db8SAlexander V. Chernikov export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh) 10499f7d47b0SAlexander V. Chernikov { 10509f7d47b0SAlexander V. Chernikov uint32_t size; 10519f7d47b0SAlexander V. Chernikov uint32_t count; 10529f7d47b0SAlexander V. Chernikov 10539f7d47b0SAlexander V. Chernikov count = ipfw_objhash_count(CHAIN_TO_NI(ch)); 10549f7d47b0SAlexander V. Chernikov size = count * sizeof(ipfw_xtable_info) + sizeof(ipfw_obj_lheader); 1055f1220db8SAlexander V. Chernikov if (size > olh->size) { 1056f1220db8SAlexander V. Chernikov /* Store new values anyway */ 1057f1220db8SAlexander V. Chernikov olh->count = count; 1058f1220db8SAlexander V. Chernikov olh->size = size; 1059f1220db8SAlexander V. Chernikov olh->objsize = sizeof(ipfw_xtable_info); 10609f7d47b0SAlexander V. Chernikov return (ENOMEM); 1061f1220db8SAlexander V. Chernikov } 10629f7d47b0SAlexander V. Chernikov 10639f7d47b0SAlexander V. Chernikov olh->count = 0; 10649f7d47b0SAlexander V. Chernikov ipfw_objhash_foreach(CHAIN_TO_NI(ch), export_table_internal, olh); 10659f7d47b0SAlexander V. Chernikov 10669f7d47b0SAlexander V. Chernikov olh->count = count; 10679f7d47b0SAlexander V. Chernikov olh->size = size; 10689f7d47b0SAlexander V. Chernikov olh->objsize = sizeof(ipfw_xtable_info); 10699f7d47b0SAlexander V. Chernikov 10703b3a8eb9SGleb Smirnoff return (0); 10713b3a8eb9SGleb Smirnoff } 10723b3a8eb9SGleb Smirnoff 10733b3a8eb9SGleb Smirnoff int 1074b074b7bbSAlexander V. Chernikov ipfw_count_table(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt) 10753b3a8eb9SGleb Smirnoff { 1076b074b7bbSAlexander V. Chernikov struct table_config *tc; 10773b3a8eb9SGleb Smirnoff 1078b074b7bbSAlexander V. Chernikov if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL) 1079b074b7bbSAlexander V. Chernikov return (ESRCH); 10809f7d47b0SAlexander V. Chernikov *cnt = tc->count; 10813b3a8eb9SGleb Smirnoff return (0); 10823b3a8eb9SGleb Smirnoff } 10833b3a8eb9SGleb Smirnoff 10849f7d47b0SAlexander V. Chernikov 10859f7d47b0SAlexander V. Chernikov int 10869f7d47b0SAlexander V. Chernikov ipfw_count_xtable(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt) 10873b3a8eb9SGleb Smirnoff { 10889f7d47b0SAlexander V. Chernikov struct table_config *tc; 10899f7d47b0SAlexander V. Chernikov 1090d3a4f924SAlexander V. Chernikov if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL) { 1091d3a4f924SAlexander V. Chernikov *cnt = 0; 10929f7d47b0SAlexander V. Chernikov return (0); /* 'table all list' requires success */ 1093d3a4f924SAlexander V. Chernikov } 10949f7d47b0SAlexander V. Chernikov *cnt = tc->count * sizeof(ipfw_table_xentry); 10959f7d47b0SAlexander V. Chernikov if (tc->count > 0) 10969f7d47b0SAlexander V. Chernikov *cnt += sizeof(ipfw_xtable); 10979f7d47b0SAlexander V. Chernikov return (0); 10989f7d47b0SAlexander V. Chernikov } 10999f7d47b0SAlexander V. Chernikov 11009f7d47b0SAlexander V. Chernikov static int 11019f7d47b0SAlexander V. Chernikov dump_table_entry(void *e, void *arg) 11029f7d47b0SAlexander V. Chernikov { 11039f7d47b0SAlexander V. Chernikov struct dump_args *da; 11049f7d47b0SAlexander V. Chernikov struct table_config *tc; 11059f7d47b0SAlexander V. Chernikov struct table_algo *ta; 11063b3a8eb9SGleb Smirnoff ipfw_table_entry *ent; 11073b3a8eb9SGleb Smirnoff 11089f7d47b0SAlexander V. Chernikov da = (struct dump_args *)arg; 11099f7d47b0SAlexander V. Chernikov 11109f7d47b0SAlexander V. Chernikov tc = da->tc; 11119f7d47b0SAlexander V. Chernikov ta = tc->ta; 11129f7d47b0SAlexander V. Chernikov 11139f7d47b0SAlexander V. Chernikov /* Out of memory, returning */ 1114f1220db8SAlexander V. Chernikov if (da->cnt == da->size) 11153b3a8eb9SGleb Smirnoff return (1); 1116f1220db8SAlexander V. Chernikov ent = da->ent++; 1117f1220db8SAlexander V. Chernikov ent->tbl = da->uidx; 1118f1220db8SAlexander V. Chernikov da->cnt++; 11199f7d47b0SAlexander V. Chernikov 11209f7d47b0SAlexander V. Chernikov return (ta->dump_entry(tc->astate, da->ti, e, ent)); 11213b3a8eb9SGleb Smirnoff } 11223b3a8eb9SGleb Smirnoff 1123f1220db8SAlexander V. Chernikov /* 1124f1220db8SAlexander V. Chernikov * Dumps table in pre-8.1 legacy format. 1125f1220db8SAlexander V. Chernikov */ 11263b3a8eb9SGleb Smirnoff int 1127f1220db8SAlexander V. Chernikov ipfw_dump_table_legacy(struct ip_fw_chain *ch, struct tid_info *ti, 1128f1220db8SAlexander V. Chernikov ipfw_table *tbl) 11293b3a8eb9SGleb Smirnoff { 1130b074b7bbSAlexander V. Chernikov struct table_config *tc; 11319f7d47b0SAlexander V. Chernikov struct table_algo *ta; 11329f7d47b0SAlexander V. Chernikov struct dump_args da; 11333b3a8eb9SGleb Smirnoff 11343b3a8eb9SGleb Smirnoff tbl->cnt = 0; 11353b3a8eb9SGleb Smirnoff 1136b074b7bbSAlexander V. Chernikov if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL) 1137b074b7bbSAlexander V. Chernikov return (0); /* XXX: We should return ESRCH */ 11389f7d47b0SAlexander V. Chernikov 11399f7d47b0SAlexander V. Chernikov ta = tc->ta; 11409f7d47b0SAlexander V. Chernikov 11419f7d47b0SAlexander V. Chernikov if (ta->dump_entry == NULL) 11429f7d47b0SAlexander V. Chernikov return (0); /* Legacy dump support is not necessary */ 11439f7d47b0SAlexander V. Chernikov 1144d3a4f924SAlexander V. Chernikov memset(&da, 0, sizeof(da)); 11459f7d47b0SAlexander V. Chernikov da.ti = KIDX_TO_TI(ch, tc->no.kidx); 11469f7d47b0SAlexander V. Chernikov da.tc = tc; 1147f1220db8SAlexander V. Chernikov da.ent = &tbl->ent[0]; 1148f1220db8SAlexander V. Chernikov da.size = tbl->size; 11499f7d47b0SAlexander V. Chernikov 11509f7d47b0SAlexander V. Chernikov tbl->cnt = 0; 11519f7d47b0SAlexander V. Chernikov ta->foreach(tc->astate, da.ti, dump_table_entry, &da); 1152f1220db8SAlexander V. Chernikov tbl->cnt = da.cnt; 11539f7d47b0SAlexander V. Chernikov 11543b3a8eb9SGleb Smirnoff return (0); 11553b3a8eb9SGleb Smirnoff } 11563b3a8eb9SGleb Smirnoff 11579490a627SAlexander V. Chernikov /* 11589490a627SAlexander V. Chernikov * Dumps table entry in eXtended format (current). 11599490a627SAlexander V. Chernikov */ 11603b3a8eb9SGleb Smirnoff static int 11619f7d47b0SAlexander V. Chernikov dump_table_xentry(void *e, void *arg) 11623b3a8eb9SGleb Smirnoff { 11639f7d47b0SAlexander V. Chernikov struct dump_args *da; 11649f7d47b0SAlexander V. Chernikov struct table_config *tc; 11659f7d47b0SAlexander V. Chernikov struct table_algo *ta; 11663b3a8eb9SGleb Smirnoff ipfw_table_xentry *xent; 11673b3a8eb9SGleb Smirnoff 11689f7d47b0SAlexander V. Chernikov da = (struct dump_args *)arg; 11699f7d47b0SAlexander V. Chernikov 11709f7d47b0SAlexander V. Chernikov tc = da->tc; 11719f7d47b0SAlexander V. Chernikov ta = tc->ta; 11729f7d47b0SAlexander V. Chernikov 11733b3a8eb9SGleb Smirnoff /* Out of memory, returning */ 1174f1220db8SAlexander V. Chernikov if (da->cnt == da->size) 11753b3a8eb9SGleb Smirnoff return (1); 1176f1220db8SAlexander V. Chernikov xent = da->xent++; 11773b3a8eb9SGleb Smirnoff xent->len = sizeof(ipfw_table_xentry); 1178f1220db8SAlexander V. Chernikov xent->tbl = da->uidx; 1179f1220db8SAlexander V. Chernikov da->cnt++; 11809f7d47b0SAlexander V. Chernikov 11819f7d47b0SAlexander V. Chernikov return (ta->dump_xentry(tc->astate, da->ti, e, xent)); 11829f7d47b0SAlexander V. Chernikov } 11839f7d47b0SAlexander V. Chernikov 11849f7d47b0SAlexander V. Chernikov /* 11859f7d47b0SAlexander V. Chernikov * Table algorithms 11869f7d47b0SAlexander V. Chernikov */ 11873b3a8eb9SGleb Smirnoff 11889f7d47b0SAlexander V. Chernikov /* 11899490a627SAlexander V. Chernikov * Finds algoritm by index, table type or supplied name 11909f7d47b0SAlexander V. Chernikov */ 11919f7d47b0SAlexander V. Chernikov static struct table_algo * 11929490a627SAlexander V. Chernikov find_table_algo(struct tables_config *tcfg, struct tid_info *ti, char *name) 11939f7d47b0SAlexander V. Chernikov { 11949490a627SAlexander V. Chernikov int i, l; 11959490a627SAlexander V. Chernikov struct table_algo *ta; 11969f7d47b0SAlexander V. Chernikov 11979f7d47b0SAlexander V. Chernikov /* Search by index */ 11989f7d47b0SAlexander V. Chernikov if (ti->atype != 0) { 11999f7d47b0SAlexander V. Chernikov if (ti->atype > tcfg->algo_count) 12009f7d47b0SAlexander V. Chernikov return (NULL); 12019f7d47b0SAlexander V. Chernikov return (tcfg->algo[ti->atype]); 12029f7d47b0SAlexander V. Chernikov } 12039f7d47b0SAlexander V. Chernikov 12049490a627SAlexander V. Chernikov /* Search by name if supplied */ 12059490a627SAlexander V. Chernikov if (name != NULL) { 12069490a627SAlexander V. Chernikov /* TODO: better search */ 12079490a627SAlexander V. Chernikov for (i = 1; i <= tcfg->algo_count; i++) { 12089490a627SAlexander V. Chernikov ta = tcfg->algo[i]; 12099490a627SAlexander V. Chernikov 12109490a627SAlexander V. Chernikov /* 12119490a627SAlexander V. Chernikov * One can supply additional algorithm 12129490a627SAlexander V. Chernikov * parameters so we compare only the first word 12139490a627SAlexander V. Chernikov * of supplied name: 12149490a627SAlexander V. Chernikov * 'hash_cidr hsize=32' 12159490a627SAlexander V. Chernikov * '^^^^^^^^^' 12169490a627SAlexander V. Chernikov * 12179490a627SAlexander V. Chernikov */ 12189490a627SAlexander V. Chernikov l = strlen(ta->name); 12199490a627SAlexander V. Chernikov if (strncmp(name, ta->name, l) == 0) { 12209490a627SAlexander V. Chernikov if (name[l] == '\0' || name[l] == ' ') 12219490a627SAlexander V. Chernikov return (ta); 12229490a627SAlexander V. Chernikov } 12239490a627SAlexander V. Chernikov } 12249490a627SAlexander V. Chernikov 12259490a627SAlexander V. Chernikov return (NULL); 12269490a627SAlexander V. Chernikov } 12279490a627SAlexander V. Chernikov 12289f7d47b0SAlexander V. Chernikov /* Search by type */ 12299f7d47b0SAlexander V. Chernikov switch (ti->type) { 12303b3a8eb9SGleb Smirnoff case IPFW_TABLE_CIDR: 12319f7d47b0SAlexander V. Chernikov return (&radix_cidr); 12323b3a8eb9SGleb Smirnoff case IPFW_TABLE_INTERFACE: 12339f7d47b0SAlexander V. Chernikov return (&radix_iface); 12343b3a8eb9SGleb Smirnoff } 12353b3a8eb9SGleb Smirnoff 12369f7d47b0SAlexander V. Chernikov return (NULL); 12373b3a8eb9SGleb Smirnoff } 12383b3a8eb9SGleb Smirnoff 12399f7d47b0SAlexander V. Chernikov void 12409f7d47b0SAlexander V. Chernikov ipfw_add_table_algo(struct ip_fw_chain *ch, struct table_algo *ta) 12413b3a8eb9SGleb Smirnoff { 12429f7d47b0SAlexander V. Chernikov struct tables_config *tcfg; 12433b3a8eb9SGleb Smirnoff 12449f7d47b0SAlexander V. Chernikov tcfg = CHAIN_TO_TCFG(ch); 1245b074b7bbSAlexander V. Chernikov 12469f7d47b0SAlexander V. Chernikov KASSERT(tcfg->algo_count < 255, ("Increase algo array size")); 12479f7d47b0SAlexander V. Chernikov 12489f7d47b0SAlexander V. Chernikov tcfg->algo[++tcfg->algo_count] = ta; 12499f7d47b0SAlexander V. Chernikov ta->idx = tcfg->algo_count; 12503b3a8eb9SGleb Smirnoff } 12513b3a8eb9SGleb Smirnoff 12529f7d47b0SAlexander V. Chernikov 1253b074b7bbSAlexander V. Chernikov /* 1254b074b7bbSAlexander V. Chernikov * Tables rewriting code 1255b074b7bbSAlexander V. Chernikov * 1256b074b7bbSAlexander V. Chernikov */ 1257b074b7bbSAlexander V. Chernikov 1258b074b7bbSAlexander V. Chernikov /* 1259b074b7bbSAlexander V. Chernikov * Determine table number and lookup type for @cmd. 1260b074b7bbSAlexander V. Chernikov * Fill @tbl and @type with appropriate values. 1261b074b7bbSAlexander V. Chernikov * Returns 0 for relevant opcodes, 1 otherwise. 1262b074b7bbSAlexander V. Chernikov */ 1263b074b7bbSAlexander V. Chernikov static int 1264b074b7bbSAlexander V. Chernikov classify_table_opcode(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) 1265b074b7bbSAlexander V. Chernikov { 1266b074b7bbSAlexander V. Chernikov ipfw_insn_if *cmdif; 1267b074b7bbSAlexander V. Chernikov int skip; 1268b074b7bbSAlexander V. Chernikov uint16_t v; 1269b074b7bbSAlexander V. Chernikov 1270b074b7bbSAlexander V. Chernikov skip = 1; 1271b074b7bbSAlexander V. Chernikov 1272b074b7bbSAlexander V. Chernikov switch (cmd->opcode) { 1273b074b7bbSAlexander V. Chernikov case O_IP_SRC_LOOKUP: 1274b074b7bbSAlexander V. Chernikov case O_IP_DST_LOOKUP: 1275b074b7bbSAlexander V. Chernikov /* Basic IPv4/IPv6 or u32 lookups */ 1276b074b7bbSAlexander V. Chernikov *puidx = cmd->arg1; 1277b074b7bbSAlexander V. Chernikov /* Assume CIDR by default */ 1278b074b7bbSAlexander V. Chernikov *ptype = IPFW_TABLE_CIDR; 1279b074b7bbSAlexander V. Chernikov skip = 0; 1280b074b7bbSAlexander V. Chernikov 1281b074b7bbSAlexander V. Chernikov if (F_LEN(cmd) > F_INSN_SIZE(ipfw_insn_u32)) { 1282b074b7bbSAlexander V. Chernikov /* 1283b074b7bbSAlexander V. Chernikov * generic lookup. The key must be 1284b074b7bbSAlexander V. Chernikov * in 32bit big-endian format. 1285b074b7bbSAlexander V. Chernikov */ 1286b074b7bbSAlexander V. Chernikov v = ((ipfw_insn_u32 *)cmd)->d[1]; 1287b074b7bbSAlexander V. Chernikov switch (v) { 1288b074b7bbSAlexander V. Chernikov case 0: 1289b074b7bbSAlexander V. Chernikov case 1: 1290b074b7bbSAlexander V. Chernikov /* IPv4 src/dst */ 1291b074b7bbSAlexander V. Chernikov break; 1292b074b7bbSAlexander V. Chernikov case 2: 1293b074b7bbSAlexander V. Chernikov case 3: 1294b074b7bbSAlexander V. Chernikov /* src/dst port */ 1295b074b7bbSAlexander V. Chernikov //type = IPFW_TABLE_U16; 1296b074b7bbSAlexander V. Chernikov break; 1297b074b7bbSAlexander V. Chernikov case 4: 1298b074b7bbSAlexander V. Chernikov /* uid/gid */ 1299b074b7bbSAlexander V. Chernikov //type = IPFW_TABLE_U32; 1300b074b7bbSAlexander V. Chernikov case 5: 1301b074b7bbSAlexander V. Chernikov //type = IPFW_TABLE_U32; 1302b074b7bbSAlexander V. Chernikov /* jid */ 1303b074b7bbSAlexander V. Chernikov case 6: 1304b074b7bbSAlexander V. Chernikov //type = IPFW_TABLE_U16; 1305b074b7bbSAlexander V. Chernikov /* dscp */ 1306b074b7bbSAlexander V. Chernikov break; 1307b074b7bbSAlexander V. Chernikov } 1308b074b7bbSAlexander V. Chernikov } 1309b074b7bbSAlexander V. Chernikov break; 1310b074b7bbSAlexander V. Chernikov case O_XMIT: 1311b074b7bbSAlexander V. Chernikov case O_RECV: 1312b074b7bbSAlexander V. Chernikov case O_VIA: 1313b074b7bbSAlexander V. Chernikov /* Interface table, possibly */ 1314b074b7bbSAlexander V. Chernikov cmdif = (ipfw_insn_if *)cmd; 1315b074b7bbSAlexander V. Chernikov if (cmdif->name[0] != '\1') 1316b074b7bbSAlexander V. Chernikov break; 1317b074b7bbSAlexander V. Chernikov 1318b074b7bbSAlexander V. Chernikov *ptype = IPFW_TABLE_INTERFACE; 1319b074b7bbSAlexander V. Chernikov *puidx = cmdif->p.glob; 1320b074b7bbSAlexander V. Chernikov skip = 0; 1321b074b7bbSAlexander V. Chernikov break; 1322b074b7bbSAlexander V. Chernikov } 1323b074b7bbSAlexander V. Chernikov 1324b074b7bbSAlexander V. Chernikov return (skip); 1325b074b7bbSAlexander V. Chernikov } 1326b074b7bbSAlexander V. Chernikov 1327b074b7bbSAlexander V. Chernikov /* 1328b074b7bbSAlexander V. Chernikov * Sets new table value for given opcode. 1329b074b7bbSAlexander V. Chernikov * Assume the same opcodes as classify_table_opcode() 1330b074b7bbSAlexander V. Chernikov */ 1331b074b7bbSAlexander V. Chernikov static void 1332b074b7bbSAlexander V. Chernikov update_table_opcode(ipfw_insn *cmd, uint16_t idx) 1333b074b7bbSAlexander V. Chernikov { 1334b074b7bbSAlexander V. Chernikov ipfw_insn_if *cmdif; 1335b074b7bbSAlexander V. Chernikov 1336b074b7bbSAlexander V. Chernikov switch (cmd->opcode) { 1337b074b7bbSAlexander V. Chernikov case O_IP_SRC_LOOKUP: 1338b074b7bbSAlexander V. Chernikov case O_IP_DST_LOOKUP: 1339b074b7bbSAlexander V. Chernikov /* Basic IPv4/IPv6 or u32 lookups */ 1340b074b7bbSAlexander V. Chernikov cmd->arg1 = idx; 1341b074b7bbSAlexander V. Chernikov break; 1342b074b7bbSAlexander V. Chernikov case O_XMIT: 1343b074b7bbSAlexander V. Chernikov case O_RECV: 1344b074b7bbSAlexander V. Chernikov case O_VIA: 1345b074b7bbSAlexander V. Chernikov /* Interface table, possibly */ 1346b074b7bbSAlexander V. Chernikov cmdif = (ipfw_insn_if *)cmd; 1347b074b7bbSAlexander V. Chernikov cmdif->p.glob = idx; 1348b074b7bbSAlexander V. Chernikov break; 1349b074b7bbSAlexander V. Chernikov } 1350b074b7bbSAlexander V. Chernikov } 1351b074b7bbSAlexander V. Chernikov 1352b074b7bbSAlexander V. Chernikov static char * 1353b074b7bbSAlexander V. Chernikov find_name_tlv(void *tlvs, int len, uint16_t uidx) 1354b074b7bbSAlexander V. Chernikov { 13559f7d47b0SAlexander V. Chernikov ipfw_obj_ntlv *ntlv; 1356b074b7bbSAlexander V. Chernikov uintptr_t pa, pe; 1357b074b7bbSAlexander V. Chernikov int l; 1358b074b7bbSAlexander V. Chernikov 1359b074b7bbSAlexander V. Chernikov pa = (uintptr_t)tlvs; 1360b074b7bbSAlexander V. Chernikov pe = pa + len; 1361b074b7bbSAlexander V. Chernikov l = 0; 1362b074b7bbSAlexander V. Chernikov for (; pa < pe; pa += l) { 13639f7d47b0SAlexander V. Chernikov ntlv = (ipfw_obj_ntlv *)pa; 1364b074b7bbSAlexander V. Chernikov l = ntlv->head.length; 1365b074b7bbSAlexander V. Chernikov if (ntlv->head.type != IPFW_TLV_NAME) 1366b074b7bbSAlexander V. Chernikov continue; 1367b074b7bbSAlexander V. Chernikov if (ntlv->idx != uidx) 1368b074b7bbSAlexander V. Chernikov continue; 1369b074b7bbSAlexander V. Chernikov 1370b074b7bbSAlexander V. Chernikov return (ntlv->name); 1371b074b7bbSAlexander V. Chernikov } 1372b074b7bbSAlexander V. Chernikov 1373b074b7bbSAlexander V. Chernikov return (NULL); 1374b074b7bbSAlexander V. Chernikov } 1375b074b7bbSAlexander V. Chernikov 1376b074b7bbSAlexander V. Chernikov static struct table_config * 1377b074b7bbSAlexander V. Chernikov find_table(struct namedobj_instance *ni, struct tid_info *ti) 1378b074b7bbSAlexander V. Chernikov { 1379b074b7bbSAlexander V. Chernikov char *name, bname[16]; 1380b074b7bbSAlexander V. Chernikov struct named_object *no; 1381b074b7bbSAlexander V. Chernikov 1382b074b7bbSAlexander V. Chernikov if (ti->tlvs != NULL) { 1383b074b7bbSAlexander V. Chernikov name = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx); 1384b074b7bbSAlexander V. Chernikov if (name == NULL) 1385b074b7bbSAlexander V. Chernikov return (NULL); 1386b074b7bbSAlexander V. Chernikov } else { 1387b074b7bbSAlexander V. Chernikov snprintf(bname, sizeof(bname), "%d", ti->uidx); 1388b074b7bbSAlexander V. Chernikov name = bname; 1389b074b7bbSAlexander V. Chernikov } 1390b074b7bbSAlexander V. Chernikov 1391b074b7bbSAlexander V. Chernikov no = ipfw_objhash_lookup_name(ni, ti->set, name); 1392b074b7bbSAlexander V. Chernikov 1393b074b7bbSAlexander V. Chernikov return ((struct table_config *)no); 1394b074b7bbSAlexander V. Chernikov } 1395b074b7bbSAlexander V. Chernikov 1396b074b7bbSAlexander V. Chernikov static struct table_config * 13979f7d47b0SAlexander V. Chernikov alloc_table_config(struct namedobj_instance *ni, struct tid_info *ti, 13989490a627SAlexander V. Chernikov struct table_algo *ta, char *aname) 1399b074b7bbSAlexander V. Chernikov { 1400b074b7bbSAlexander V. Chernikov char *name, bname[16]; 1401b074b7bbSAlexander V. Chernikov struct table_config *tc; 1402b074b7bbSAlexander V. Chernikov int error; 1403b074b7bbSAlexander V. Chernikov 1404b074b7bbSAlexander V. Chernikov if (ti->tlvs != NULL) { 1405b074b7bbSAlexander V. Chernikov name = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx); 1406b074b7bbSAlexander V. Chernikov if (name == NULL) 1407b074b7bbSAlexander V. Chernikov return (NULL); 1408b074b7bbSAlexander V. Chernikov } else { 1409b074b7bbSAlexander V. Chernikov snprintf(bname, sizeof(bname), "%d", ti->uidx); 1410b074b7bbSAlexander V. Chernikov name = bname; 1411b074b7bbSAlexander V. Chernikov } 1412b074b7bbSAlexander V. Chernikov 1413b074b7bbSAlexander V. Chernikov tc = malloc(sizeof(struct table_config), M_IPFW, M_WAITOK | M_ZERO); 1414b074b7bbSAlexander V. Chernikov tc->no.name = tc->tablename; 1415b074b7bbSAlexander V. Chernikov tc->no.type = ti->type; 1416b074b7bbSAlexander V. Chernikov tc->no.set = ti->set; 14179f7d47b0SAlexander V. Chernikov tc->ta = ta; 1418b074b7bbSAlexander V. Chernikov strlcpy(tc->tablename, name, sizeof(tc->tablename)); 1419b074b7bbSAlexander V. Chernikov 1420b074b7bbSAlexander V. Chernikov if (ti->tlvs == NULL) { 1421b074b7bbSAlexander V. Chernikov tc->no.compat = 1; 1422b074b7bbSAlexander V. Chernikov tc->no.uidx = ti->uidx; 1423b074b7bbSAlexander V. Chernikov } 1424b074b7bbSAlexander V. Chernikov 1425b074b7bbSAlexander V. Chernikov /* Preallocate data structures for new tables */ 14269490a627SAlexander V. Chernikov error = ta->init(&tc->astate, &tc->ti, aname); 1427b074b7bbSAlexander V. Chernikov if (error != 0) { 1428b074b7bbSAlexander V. Chernikov free(tc, M_IPFW); 1429b074b7bbSAlexander V. Chernikov return (NULL); 1430b074b7bbSAlexander V. Chernikov } 1431b074b7bbSAlexander V. Chernikov 1432b074b7bbSAlexander V. Chernikov return (tc); 1433b074b7bbSAlexander V. Chernikov } 1434b074b7bbSAlexander V. Chernikov 1435b074b7bbSAlexander V. Chernikov static void 1436b074b7bbSAlexander V. Chernikov free_table_config(struct namedobj_instance *ni, struct table_config *tc) 1437b074b7bbSAlexander V. Chernikov { 1438b074b7bbSAlexander V. Chernikov 1439b074b7bbSAlexander V. Chernikov if (tc->linked == 0) 14409f7d47b0SAlexander V. Chernikov tc->ta->destroy(&tc->astate, &tc->ti); 1441b074b7bbSAlexander V. Chernikov 1442b074b7bbSAlexander V. Chernikov free(tc, M_IPFW); 1443b074b7bbSAlexander V. Chernikov } 1444b074b7bbSAlexander V. Chernikov 1445b074b7bbSAlexander V. Chernikov /* 1446b074b7bbSAlexander V. Chernikov * Links @tc to @chain table named instance. 1447b074b7bbSAlexander V. Chernikov * Sets appropriate type/states in @chain table info. 1448b074b7bbSAlexander V. Chernikov */ 1449b074b7bbSAlexander V. Chernikov static void 14509f7d47b0SAlexander V. Chernikov link_table(struct ip_fw_chain *ch, struct table_config *tc) 1451b074b7bbSAlexander V. Chernikov { 1452b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 14539f7d47b0SAlexander V. Chernikov struct table_info *ti; 1454b074b7bbSAlexander V. Chernikov uint16_t kidx; 1455b074b7bbSAlexander V. Chernikov 14569f7d47b0SAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(ch); 14579f7d47b0SAlexander V. Chernikov IPFW_WLOCK_ASSERT(ch); 1458b074b7bbSAlexander V. Chernikov 14599f7d47b0SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 1460b074b7bbSAlexander V. Chernikov kidx = tc->no.kidx; 1461b074b7bbSAlexander V. Chernikov 1462b074b7bbSAlexander V. Chernikov ipfw_objhash_add(ni, &tc->no); 14639f7d47b0SAlexander V. Chernikov 14649f7d47b0SAlexander V. Chernikov ti = KIDX_TO_TI(ch, kidx); 14659f7d47b0SAlexander V. Chernikov *ti = tc->ti; 1466b074b7bbSAlexander V. Chernikov 1467b074b7bbSAlexander V. Chernikov tc->linked = 1; 1468b074b7bbSAlexander V. Chernikov } 1469b074b7bbSAlexander V. Chernikov 1470b074b7bbSAlexander V. Chernikov /* 1471b074b7bbSAlexander V. Chernikov * Unlinks @tc from @chain table named instance. 1472b074b7bbSAlexander V. Chernikov * Zeroes states in @chain and stores them in @tc. 1473b074b7bbSAlexander V. Chernikov */ 1474b074b7bbSAlexander V. Chernikov static void 14759f7d47b0SAlexander V. Chernikov unlink_table(struct ip_fw_chain *ch, struct table_config *tc) 1476b074b7bbSAlexander V. Chernikov { 1477b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 14789f7d47b0SAlexander V. Chernikov struct table_info *ti; 1479b074b7bbSAlexander V. Chernikov uint16_t kidx; 1480b074b7bbSAlexander V. Chernikov 14819f7d47b0SAlexander V. Chernikov IPFW_UH_WLOCK_ASSERT(ch); 14829f7d47b0SAlexander V. Chernikov IPFW_WLOCK_ASSERT(ch); 1483b074b7bbSAlexander V. Chernikov 14849f7d47b0SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 1485b074b7bbSAlexander V. Chernikov kidx = tc->no.kidx; 1486b074b7bbSAlexander V. Chernikov 14879f7d47b0SAlexander V. Chernikov /* Clear state. @ti copy is already saved inside @tc */ 1488b074b7bbSAlexander V. Chernikov ipfw_objhash_del(ni, &tc->no); 14899f7d47b0SAlexander V. Chernikov ti = KIDX_TO_TI(ch, kidx); 14909f7d47b0SAlexander V. Chernikov memset(ti, 0, sizeof(struct table_info)); 1491b074b7bbSAlexander V. Chernikov tc->linked = 0; 1492b074b7bbSAlexander V. Chernikov } 1493b074b7bbSAlexander V. Chernikov 1494b074b7bbSAlexander V. Chernikov /* 1495b074b7bbSAlexander V. Chernikov * Finds named object by @uidx number. 1496b074b7bbSAlexander V. Chernikov * Refs found object, allocate new index for non-existing object. 14979490a627SAlexander V. Chernikov * Fills in @oib with userland/kernel indexes. 14989490a627SAlexander V. Chernikov * First free oidx pointer is saved back in @oib. 1499b074b7bbSAlexander V. Chernikov * 1500b074b7bbSAlexander V. Chernikov * Returns 0 on success. 1501b074b7bbSAlexander V. Chernikov */ 1502b074b7bbSAlexander V. Chernikov static int 15039490a627SAlexander V. Chernikov bind_table_rule(struct ip_fw_chain *ch, struct ip_fw *rule, 15049490a627SAlexander V. Chernikov struct rule_check_info *ci, struct obj_idx **oib, struct tid_info *ti) 1505b074b7bbSAlexander V. Chernikov { 1506b074b7bbSAlexander V. Chernikov struct table_config *tc; 15079490a627SAlexander V. Chernikov struct namedobj_instance *ni; 15089490a627SAlexander V. Chernikov struct named_object *no; 15099490a627SAlexander V. Chernikov int error, l, cmdlen; 15109490a627SAlexander V. Chernikov ipfw_insn *cmd; 15119490a627SAlexander V. Chernikov struct obj_idx *pidx, *p; 15129490a627SAlexander V. Chernikov 15139490a627SAlexander V. Chernikov pidx = *oib; 15149490a627SAlexander V. Chernikov l = rule->cmd_len; 15159490a627SAlexander V. Chernikov cmd = rule->cmd; 15169490a627SAlexander V. Chernikov cmdlen = 0; 15179490a627SAlexander V. Chernikov error = 0; 15189490a627SAlexander V. Chernikov 15199490a627SAlexander V. Chernikov IPFW_UH_WLOCK(ch); 15209490a627SAlexander V. Chernikov ni = CHAIN_TO_NI(ch); 15219490a627SAlexander V. Chernikov 15229490a627SAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 15239490a627SAlexander V. Chernikov cmdlen = F_LEN(cmd); 15249490a627SAlexander V. Chernikov 15259490a627SAlexander V. Chernikov if (classify_table_opcode(cmd, &ti->uidx, &ti->type) != 0) 15269490a627SAlexander V. Chernikov continue; 1527b074b7bbSAlexander V. Chernikov 1528b074b7bbSAlexander V. Chernikov pidx->uidx = ti->uidx; 1529b074b7bbSAlexander V. Chernikov pidx->type = ti->type; 1530b074b7bbSAlexander V. Chernikov 15319490a627SAlexander V. Chernikov if ((tc = find_table(ni, ti)) != NULL) { 15329490a627SAlexander V. Chernikov if (tc->no.type != ti->type) { 15339490a627SAlexander V. Chernikov /* Incompatible types */ 15349490a627SAlexander V. Chernikov error = EINVAL; 15359490a627SAlexander V. Chernikov break; 15369490a627SAlexander V. Chernikov } 15379f7d47b0SAlexander V. Chernikov 15389490a627SAlexander V. Chernikov /* Reference found table and save kidx */ 15399490a627SAlexander V. Chernikov tc->no.refcnt++; 15409490a627SAlexander V. Chernikov pidx->kidx = tc->no.kidx; 15419490a627SAlexander V. Chernikov pidx++; 15429490a627SAlexander V. Chernikov continue; 15439490a627SAlexander V. Chernikov } 15449490a627SAlexander V. Chernikov 15459490a627SAlexander V. Chernikov /* Table not found. Allocate new index and save for later */ 1546b074b7bbSAlexander V. Chernikov if (ipfw_objhash_alloc_idx(ni, ti->set, &pidx->kidx) != 0) { 1547b074b7bbSAlexander V. Chernikov printf("Unable to allocate table index in set %u." 1548b074b7bbSAlexander V. Chernikov " Consider increasing net.inet.ip.fw.tables_max", 1549b074b7bbSAlexander V. Chernikov ti->set); 15509490a627SAlexander V. Chernikov error = EBUSY; 15519490a627SAlexander V. Chernikov break; 1552b074b7bbSAlexander V. Chernikov } 1553b074b7bbSAlexander V. Chernikov 1554b074b7bbSAlexander V. Chernikov ci->new_tables++; 15559490a627SAlexander V. Chernikov pidx->new = 1; 15569490a627SAlexander V. Chernikov pidx++; 1557b074b7bbSAlexander V. Chernikov } 1558b074b7bbSAlexander V. Chernikov 15599490a627SAlexander V. Chernikov if (error != 0) { 15609490a627SAlexander V. Chernikov /* Unref everything we have already done */ 15619490a627SAlexander V. Chernikov for (p = *oib; p < pidx; p++) { 15629490a627SAlexander V. Chernikov if (p->new != 0) { 15639490a627SAlexander V. Chernikov ipfw_objhash_free_idx(ni, ci->tableset,p->kidx); 15649490a627SAlexander V. Chernikov continue; 15659490a627SAlexander V. Chernikov } 1566b074b7bbSAlexander V. Chernikov 15679490a627SAlexander V. Chernikov /* Find & unref by existing idx */ 15689490a627SAlexander V. Chernikov no = ipfw_objhash_lookup_idx(ni, ci->tableset, p->kidx); 15699490a627SAlexander V. Chernikov KASSERT(no != NULL, ("Ref'd table %d disappeared", 15709490a627SAlexander V. Chernikov p->kidx)); 1571b074b7bbSAlexander V. Chernikov 15729490a627SAlexander V. Chernikov no->refcnt--; 15739490a627SAlexander V. Chernikov } 15749490a627SAlexander V. Chernikov } 15759490a627SAlexander V. Chernikov IPFW_UH_WUNLOCK(ch); 1576b074b7bbSAlexander V. Chernikov 15779490a627SAlexander V. Chernikov *oib = pidx; 15789490a627SAlexander V. Chernikov 15799490a627SAlexander V. Chernikov return (error); 1580b074b7bbSAlexander V. Chernikov } 1581b074b7bbSAlexander V. Chernikov 1582b074b7bbSAlexander V. Chernikov /* 1583b074b7bbSAlexander V. Chernikov * Compatibility function for old ipfw(8) binaries. 1584b074b7bbSAlexander V. Chernikov * Rewrites table kernel indices with userland ones. 1585b074b7bbSAlexander V. Chernikov * Works for \d+ talbes only (e.g. for tables, converted 1586b074b7bbSAlexander V. Chernikov * from old numbered system calls). 1587b074b7bbSAlexander V. Chernikov * 1588b074b7bbSAlexander V. Chernikov * Returns 0 on success. 1589b074b7bbSAlexander V. Chernikov * Raises error on any other tables. 1590b074b7bbSAlexander V. Chernikov */ 1591b074b7bbSAlexander V. Chernikov int 1592b074b7bbSAlexander V. Chernikov ipfw_rewrite_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule) 1593b074b7bbSAlexander V. Chernikov { 1594b074b7bbSAlexander V. Chernikov int cmdlen, l; 1595b074b7bbSAlexander V. Chernikov ipfw_insn *cmd; 1596b074b7bbSAlexander V. Chernikov uint32_t set; 1597b074b7bbSAlexander V. Chernikov uint16_t kidx; 1598b074b7bbSAlexander V. Chernikov uint8_t type; 1599b074b7bbSAlexander V. Chernikov struct named_object *no; 1600b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 1601b074b7bbSAlexander V. Chernikov 1602b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(chain); 1603b074b7bbSAlexander V. Chernikov 1604b074b7bbSAlexander V. Chernikov set = TABLE_SET(rule->set); 1605b074b7bbSAlexander V. Chernikov 1606b074b7bbSAlexander V. Chernikov l = rule->cmd_len; 1607b074b7bbSAlexander V. Chernikov cmd = rule->cmd; 1608b074b7bbSAlexander V. Chernikov cmdlen = 0; 1609b074b7bbSAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 1610b074b7bbSAlexander V. Chernikov cmdlen = F_LEN(cmd); 1611b074b7bbSAlexander V. Chernikov 1612b074b7bbSAlexander V. Chernikov if (classify_table_opcode(cmd, &kidx, &type) != 0) 1613b074b7bbSAlexander V. Chernikov continue; 1614b074b7bbSAlexander V. Chernikov 1615b074b7bbSAlexander V. Chernikov if ((no = ipfw_objhash_lookup_idx(ni, set, kidx)) == NULL) 1616b074b7bbSAlexander V. Chernikov return (1); 1617b074b7bbSAlexander V. Chernikov 1618b074b7bbSAlexander V. Chernikov if (no->compat == 0) 1619b074b7bbSAlexander V. Chernikov return (2); 1620b074b7bbSAlexander V. Chernikov 1621b074b7bbSAlexander V. Chernikov update_table_opcode(cmd, no->uidx); 1622b074b7bbSAlexander V. Chernikov } 1623b074b7bbSAlexander V. Chernikov 1624b074b7bbSAlexander V. Chernikov return (0); 1625b074b7bbSAlexander V. Chernikov } 1626b074b7bbSAlexander V. Chernikov 1627b074b7bbSAlexander V. Chernikov 1628b074b7bbSAlexander V. Chernikov /* 1629b074b7bbSAlexander V. Chernikov * Checks is opcode is referencing table of appropriate type. 1630b074b7bbSAlexander V. Chernikov * Adds reference count for found table if true. 1631b074b7bbSAlexander V. Chernikov * Rewrites user-supplied opcode values with kernel ones. 1632b074b7bbSAlexander V. Chernikov * 1633b074b7bbSAlexander V. Chernikov * Returns 0 on success and appropriate error code otherwise. 1634b074b7bbSAlexander V. Chernikov */ 1635b074b7bbSAlexander V. Chernikov int 1636b074b7bbSAlexander V. Chernikov ipfw_rewrite_table_uidx(struct ip_fw_chain *chain, 1637b074b7bbSAlexander V. Chernikov struct rule_check_info *ci) 1638b074b7bbSAlexander V. Chernikov { 1639b074b7bbSAlexander V. Chernikov int cmdlen, error, ftype, l; 1640b074b7bbSAlexander V. Chernikov ipfw_insn *cmd; 1641b074b7bbSAlexander V. Chernikov uint16_t uidx; 1642b074b7bbSAlexander V. Chernikov uint8_t type; 1643b074b7bbSAlexander V. Chernikov struct table_config *tc; 16449f7d47b0SAlexander V. Chernikov struct table_algo *ta; 1645b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 1646b074b7bbSAlexander V. Chernikov struct named_object *no, *no_n, *no_tmp; 16479490a627SAlexander V. Chernikov struct obj_idx *p, *pidx_first, *pidx_last; 1648b074b7bbSAlexander V. Chernikov struct namedobjects_head nh; 1649b074b7bbSAlexander V. Chernikov struct tid_info ti; 1650b074b7bbSAlexander V. Chernikov 1651b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(chain); 1652b074b7bbSAlexander V. Chernikov 16539490a627SAlexander V. Chernikov /* Prepare queue to store configs */ 16549490a627SAlexander V. Chernikov TAILQ_INIT(&nh); 16559490a627SAlexander V. Chernikov 1656b074b7bbSAlexander V. Chernikov /* 1657b074b7bbSAlexander V. Chernikov * Prepare an array for storing opcode indices. 1658b074b7bbSAlexander V. Chernikov * Use stack allocation by default. 1659b074b7bbSAlexander V. Chernikov */ 1660b074b7bbSAlexander V. Chernikov if (ci->table_opcodes <= (sizeof(ci->obuf)/sizeof(ci->obuf[0]))) { 1661b074b7bbSAlexander V. Chernikov /* Stack */ 16629490a627SAlexander V. Chernikov pidx_first = ci->obuf; 1663b074b7bbSAlexander V. Chernikov } else 16649490a627SAlexander V. Chernikov pidx_first = malloc(ci->table_opcodes * sizeof(struct obj_idx), 1665b074b7bbSAlexander V. Chernikov M_IPFW, M_WAITOK | M_ZERO); 1666b074b7bbSAlexander V. Chernikov 16679490a627SAlexander V. Chernikov pidx_last = pidx_first; 1668b074b7bbSAlexander V. Chernikov error = 0; 1669b074b7bbSAlexander V. Chernikov 1670b074b7bbSAlexander V. Chernikov type = 0; 1671b074b7bbSAlexander V. Chernikov ftype = 0; 1672b074b7bbSAlexander V. Chernikov 1673b074b7bbSAlexander V. Chernikov ci->tableset = TABLE_SET(ci->krule->set); 1674b074b7bbSAlexander V. Chernikov 1675b074b7bbSAlexander V. Chernikov memset(&ti, 0, sizeof(ti)); 1676b074b7bbSAlexander V. Chernikov ti.set = ci->tableset; 1677b074b7bbSAlexander V. Chernikov ti.tlvs = ci->tlvs; 1678b074b7bbSAlexander V. Chernikov ti.tlen = ci->tlen; 1679b074b7bbSAlexander V. Chernikov 1680b074b7bbSAlexander V. Chernikov /* 16819490a627SAlexander V. Chernikov * Stage 1: reference existing tables, determine number 16829490a627SAlexander V. Chernikov * of tables we need to allocate and allocate indexes for each. 1683b074b7bbSAlexander V. Chernikov */ 16849490a627SAlexander V. Chernikov error = bind_table_rule(chain, ci->krule, ci, &pidx_last, &ti); 1685b074b7bbSAlexander V. Chernikov 1686b074b7bbSAlexander V. Chernikov if (error != 0) { 16879490a627SAlexander V. Chernikov if (pidx_first != ci->obuf) 16889490a627SAlexander V. Chernikov free(pidx_first, M_IPFW); 1689b074b7bbSAlexander V. Chernikov 1690b074b7bbSAlexander V. Chernikov return (error); 1691b074b7bbSAlexander V. Chernikov } 1692b074b7bbSAlexander V. Chernikov 1693b074b7bbSAlexander V. Chernikov /* 1694b074b7bbSAlexander V. Chernikov * Stage 2: allocate table configs for every non-existent table 1695b074b7bbSAlexander V. Chernikov */ 1696b074b7bbSAlexander V. Chernikov 16979f7d47b0SAlexander V. Chernikov if (ci->new_tables > 0) { 16989490a627SAlexander V. Chernikov for (p = pidx_first; p < pidx_last; p++) { 1699b074b7bbSAlexander V. Chernikov if (p->new == 0) 1700b074b7bbSAlexander V. Chernikov continue; 1701b074b7bbSAlexander V. Chernikov 1702b074b7bbSAlexander V. Chernikov /* TODO: get name from TLV */ 1703b074b7bbSAlexander V. Chernikov ti.uidx = p->uidx; 1704b074b7bbSAlexander V. Chernikov ti.type = p->type; 17059f7d47b0SAlexander V. Chernikov ti.atype = 0; 1706b074b7bbSAlexander V. Chernikov 17079490a627SAlexander V. Chernikov ta = find_table_algo(CHAIN_TO_TCFG(chain), &ti, NULL); 17089f7d47b0SAlexander V. Chernikov if (ta == NULL) { 17099f7d47b0SAlexander V. Chernikov error = ENOTSUP; 17109f7d47b0SAlexander V. Chernikov goto free; 17119f7d47b0SAlexander V. Chernikov } 17129490a627SAlexander V. Chernikov tc = alloc_table_config(ni, &ti, ta, NULL); 1713b074b7bbSAlexander V. Chernikov 1714b074b7bbSAlexander V. Chernikov if (tc == NULL) { 1715b074b7bbSAlexander V. Chernikov error = ENOMEM; 1716b074b7bbSAlexander V. Chernikov goto free; 1717b074b7bbSAlexander V. Chernikov } 1718b074b7bbSAlexander V. Chernikov 1719b074b7bbSAlexander V. Chernikov tc->no.kidx = p->kidx; 1720b074b7bbSAlexander V. Chernikov tc->no.refcnt = 1; 1721b074b7bbSAlexander V. Chernikov 1722b074b7bbSAlexander V. Chernikov /* Add to list */ 1723b074b7bbSAlexander V. Chernikov TAILQ_INSERT_TAIL(&nh, &tc->no, nn_next); 1724b074b7bbSAlexander V. Chernikov } 1725b074b7bbSAlexander V. Chernikov 1726b074b7bbSAlexander V. Chernikov /* 1727b074b7bbSAlexander V. Chernikov * Stage 2.1: Check if we're going to create 2 tables 1728b074b7bbSAlexander V. Chernikov * with the same name, but different table types. 1729b074b7bbSAlexander V. Chernikov */ 1730b074b7bbSAlexander V. Chernikov TAILQ_FOREACH(no, &nh, nn_next) { 1731b074b7bbSAlexander V. Chernikov TAILQ_FOREACH(no_tmp, &nh, nn_next) { 17329490a627SAlexander V. Chernikov if (ipfw_objhash_same_name(ni, no, no_tmp) == 0) 1733b074b7bbSAlexander V. Chernikov continue; 1734b074b7bbSAlexander V. Chernikov if (no->type != no_tmp->type) { 1735b074b7bbSAlexander V. Chernikov error = EINVAL; 1736b074b7bbSAlexander V. Chernikov goto free; 1737b074b7bbSAlexander V. Chernikov } 1738b074b7bbSAlexander V. Chernikov } 1739b074b7bbSAlexander V. Chernikov } 17409f7d47b0SAlexander V. Chernikov } 1741b074b7bbSAlexander V. Chernikov 17429f7d47b0SAlexander V. Chernikov IPFW_UH_WLOCK(chain); 17439f7d47b0SAlexander V. Chernikov 17449f7d47b0SAlexander V. Chernikov if (ci->new_tables > 0) { 1745b074b7bbSAlexander V. Chernikov /* 1746b074b7bbSAlexander V. Chernikov * Stage 3: link & reference new table configs 1747b074b7bbSAlexander V. Chernikov */ 1748b074b7bbSAlexander V. Chernikov 1749b074b7bbSAlexander V. Chernikov 1750b074b7bbSAlexander V. Chernikov /* 1751b074b7bbSAlexander V. Chernikov * Step 3.1: Check if some tables we need to create have been 1752b074b7bbSAlexander V. Chernikov * already created with different table type. 1753b074b7bbSAlexander V. Chernikov */ 1754b074b7bbSAlexander V. Chernikov 1755b074b7bbSAlexander V. Chernikov error = 0; 1756b074b7bbSAlexander V. Chernikov TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) { 1757b074b7bbSAlexander V. Chernikov no_n = ipfw_objhash_lookup_name(ni, no->set, no->name); 1758b074b7bbSAlexander V. Chernikov if (no_n == NULL) 1759b074b7bbSAlexander V. Chernikov continue; 1760b074b7bbSAlexander V. Chernikov 1761b074b7bbSAlexander V. Chernikov if (no_n->type != no->type) { 1762b074b7bbSAlexander V. Chernikov error = EINVAL; 1763b074b7bbSAlexander V. Chernikov break; 1764b074b7bbSAlexander V. Chernikov } 1765b074b7bbSAlexander V. Chernikov 1766b074b7bbSAlexander V. Chernikov } 1767b074b7bbSAlexander V. Chernikov 1768b074b7bbSAlexander V. Chernikov if (error != 0) { 1769b074b7bbSAlexander V. Chernikov /* 1770b074b7bbSAlexander V. Chernikov * Someone has allocated table with different table type. 1771b074b7bbSAlexander V. Chernikov * We have to rollback everything. 1772b074b7bbSAlexander V. Chernikov */ 1773b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 1774b074b7bbSAlexander V. Chernikov goto free; 1775b074b7bbSAlexander V. Chernikov } 1776b074b7bbSAlexander V. Chernikov 1777b074b7bbSAlexander V. Chernikov 1778b074b7bbSAlexander V. Chernikov /* 17799f7d47b0SAlexander V. Chernikov * Attach new tables. 17809f7d47b0SAlexander V. Chernikov * We need to set table pointers for each new table, 1781b074b7bbSAlexander V. Chernikov * so we have to acquire main WLOCK. 1782b074b7bbSAlexander V. Chernikov */ 1783b074b7bbSAlexander V. Chernikov IPFW_WLOCK(chain); 1784b074b7bbSAlexander V. Chernikov TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) { 1785b074b7bbSAlexander V. Chernikov no_n = ipfw_objhash_lookup_name(ni, no->set, no->name); 1786b074b7bbSAlexander V. Chernikov 17879490a627SAlexander V. Chernikov if (no_n == NULL) { 17889490a627SAlexander V. Chernikov /* New table. Attach to runtime hash */ 17899490a627SAlexander V. Chernikov TAILQ_REMOVE(&nh, no, nn_next); 17909490a627SAlexander V. Chernikov link_table(chain, (struct table_config *)no); 1791b074b7bbSAlexander V. Chernikov continue; 1792b074b7bbSAlexander V. Chernikov } 1793b074b7bbSAlexander V. Chernikov 17949490a627SAlexander V. Chernikov /* 17959490a627SAlexander V. Chernikov * Newly-allocated table with the same type. 17969490a627SAlexander V. Chernikov * Reference it and update out @pidx array 17979490a627SAlexander V. Chernikov * rewrite info. 17989490a627SAlexander V. Chernikov */ 17999490a627SAlexander V. Chernikov no_n->refcnt++; 18009490a627SAlexander V. Chernikov /* Keep oib array in sync: update kidx */ 18019490a627SAlexander V. Chernikov for (p = pidx_first; p < pidx_last; p++) { 18029490a627SAlexander V. Chernikov if (p->kidx != no->kidx) 18039490a627SAlexander V. Chernikov continue; 18049490a627SAlexander V. Chernikov /* Update kidx */ 18059490a627SAlexander V. Chernikov p->kidx = no_n->kidx; 18069490a627SAlexander V. Chernikov break; 18079490a627SAlexander V. Chernikov } 1808b074b7bbSAlexander V. Chernikov } 1809b074b7bbSAlexander V. Chernikov IPFW_WUNLOCK(chain); 18109f7d47b0SAlexander V. Chernikov } 1811b074b7bbSAlexander V. Chernikov 1812b074b7bbSAlexander V. Chernikov /* Perform rule rewrite */ 1813b074b7bbSAlexander V. Chernikov l = ci->krule->cmd_len; 1814b074b7bbSAlexander V. Chernikov cmd = ci->krule->cmd; 1815b074b7bbSAlexander V. Chernikov cmdlen = 0; 18169490a627SAlexander V. Chernikov p = pidx_first; 1817b074b7bbSAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 1818b074b7bbSAlexander V. Chernikov cmdlen = F_LEN(cmd); 1819b074b7bbSAlexander V. Chernikov 1820b074b7bbSAlexander V. Chernikov if (classify_table_opcode(cmd, &uidx, &type) != 0) 1821b074b7bbSAlexander V. Chernikov continue; 18229490a627SAlexander V. Chernikov update_table_opcode(cmd, p->kidx); 18239490a627SAlexander V. Chernikov p++; 1824b074b7bbSAlexander V. Chernikov } 1825b074b7bbSAlexander V. Chernikov 1826b074b7bbSAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 1827b074b7bbSAlexander V. Chernikov 1828b074b7bbSAlexander V. Chernikov error = 0; 1829b074b7bbSAlexander V. Chernikov 1830b074b7bbSAlexander V. Chernikov /* 1831b074b7bbSAlexander V. Chernikov * Stage 4: free resources 1832b074b7bbSAlexander V. Chernikov */ 1833b074b7bbSAlexander V. Chernikov free: 18349490a627SAlexander V. Chernikov if (!TAILQ_EMPTY(&nh)) { 18359490a627SAlexander V. Chernikov /* Free indexes first */ 18369490a627SAlexander V. Chernikov IPFW_UH_WLOCK(chain); 18379490a627SAlexander V. Chernikov TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) { 18389490a627SAlexander V. Chernikov ipfw_objhash_free_idx(ni, ci->tableset, no->kidx); 18399490a627SAlexander V. Chernikov } 18409490a627SAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 18419490a627SAlexander V. Chernikov /* Free configs */ 1842b074b7bbSAlexander V. Chernikov TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) 1843b074b7bbSAlexander V. Chernikov free_table_config(ni, tc); 18449490a627SAlexander V. Chernikov } 1845b074b7bbSAlexander V. Chernikov 18469490a627SAlexander V. Chernikov if (pidx_first != ci->obuf) 18479490a627SAlexander V. Chernikov free(pidx_first, M_IPFW); 1848b074b7bbSAlexander V. Chernikov 1849b074b7bbSAlexander V. Chernikov return (error); 1850b074b7bbSAlexander V. Chernikov } 1851b074b7bbSAlexander V. Chernikov 1852b074b7bbSAlexander V. Chernikov /* 1853b074b7bbSAlexander V. Chernikov * Remove references from every table used in @rule. 1854b074b7bbSAlexander V. Chernikov */ 1855b074b7bbSAlexander V. Chernikov void 1856b074b7bbSAlexander V. Chernikov ipfw_unbind_table_rule(struct ip_fw_chain *chain, struct ip_fw *rule) 1857b074b7bbSAlexander V. Chernikov { 1858b074b7bbSAlexander V. Chernikov int cmdlen, l; 1859b074b7bbSAlexander V. Chernikov ipfw_insn *cmd; 1860b074b7bbSAlexander V. Chernikov struct namedobj_instance *ni; 1861b074b7bbSAlexander V. Chernikov struct named_object *no; 1862b074b7bbSAlexander V. Chernikov uint32_t set; 1863b074b7bbSAlexander V. Chernikov uint16_t kidx; 1864b074b7bbSAlexander V. Chernikov uint8_t type; 1865b074b7bbSAlexander V. Chernikov 1866b074b7bbSAlexander V. Chernikov ni = CHAIN_TO_NI(chain); 1867b074b7bbSAlexander V. Chernikov 1868b074b7bbSAlexander V. Chernikov set = TABLE_SET(rule->set); 1869b074b7bbSAlexander V. Chernikov 1870b074b7bbSAlexander V. Chernikov l = rule->cmd_len; 1871b074b7bbSAlexander V. Chernikov cmd = rule->cmd; 1872b074b7bbSAlexander V. Chernikov cmdlen = 0; 1873b074b7bbSAlexander V. Chernikov for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 1874b074b7bbSAlexander V. Chernikov cmdlen = F_LEN(cmd); 1875b074b7bbSAlexander V. Chernikov 1876b074b7bbSAlexander V. Chernikov if (classify_table_opcode(cmd, &kidx, &type) != 0) 1877b074b7bbSAlexander V. Chernikov continue; 1878b074b7bbSAlexander V. Chernikov 1879b074b7bbSAlexander V. Chernikov no = ipfw_objhash_lookup_idx(ni, set, kidx); 1880b074b7bbSAlexander V. Chernikov 1881b074b7bbSAlexander V. Chernikov KASSERT(no != NULL, ("table id %d not found", kidx)); 1882b074b7bbSAlexander V. Chernikov KASSERT(no->type == type, ("wrong type %d (%d) for table id %d", 1883b074b7bbSAlexander V. Chernikov no->type, type, kidx)); 1884b074b7bbSAlexander V. Chernikov KASSERT(no->refcnt > 0, ("refcount for table %d is %d", 1885b074b7bbSAlexander V. Chernikov kidx, no->refcnt)); 1886b074b7bbSAlexander V. Chernikov 1887b074b7bbSAlexander V. Chernikov no->refcnt--; 1888b074b7bbSAlexander V. Chernikov } 1889b074b7bbSAlexander V. Chernikov } 1890b074b7bbSAlexander V. Chernikov 1891b074b7bbSAlexander V. Chernikov 1892b074b7bbSAlexander V. Chernikov /* 1893b074b7bbSAlexander V. Chernikov * Removes table bindings for every rule in rule chain @head. 1894b074b7bbSAlexander V. Chernikov */ 1895b074b7bbSAlexander V. Chernikov void 1896b074b7bbSAlexander V. Chernikov ipfw_unbind_table_list(struct ip_fw_chain *chain, struct ip_fw *head) 1897b074b7bbSAlexander V. Chernikov { 1898b074b7bbSAlexander V. Chernikov struct ip_fw *rule; 1899b074b7bbSAlexander V. Chernikov 1900b074b7bbSAlexander V. Chernikov while ((rule = head) != NULL) { 1901b074b7bbSAlexander V. Chernikov head = head->x_next; 1902b074b7bbSAlexander V. Chernikov ipfw_unbind_table_rule(chain, rule); 1903b074b7bbSAlexander V. Chernikov } 1904b074b7bbSAlexander V. Chernikov } 1905b074b7bbSAlexander V. Chernikov 1906b074b7bbSAlexander V. Chernikov 19073b3a8eb9SGleb Smirnoff /* end of file */ 1908