1200590Sluigi/*- 2200673Sru * Copyright (c) 2004 Ruslan Ermilov and Vsevolod Lobko. 3272840Smelifaro * Copyright (c) 2014 Yandex LLC 4272840Smelifaro * Copyright (c) 2014 Alexander V. Chernikov 5200590Sluigi * 6200590Sluigi * Redistribution and use in source and binary forms, with or without 7200590Sluigi * modification, are permitted provided that the following conditions 8200590Sluigi * are met: 9200590Sluigi * 1. Redistributions of source code must retain the above copyright 10200590Sluigi * notice, this list of conditions and the following disclaimer. 11200590Sluigi * 2. Redistributions in binary form must reproduce the above copyright 12200590Sluigi * notice, this list of conditions and the following disclaimer in the 13200590Sluigi * documentation and/or other materials provided with the distribution. 14200590Sluigi * 15200590Sluigi * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16200590Sluigi * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17200590Sluigi * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18200590Sluigi * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19200590Sluigi * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20200590Sluigi * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21200590Sluigi * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22200590Sluigi * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23200590Sluigi * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24200590Sluigi * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25200590Sluigi * SUCH DAMAGE. 26200590Sluigi */ 27200590Sluigi 28200590Sluigi#include <sys/cdefs.h> 29200590Sluigi__FBSDID("$FreeBSD: stable/11/sys/netpfil/ipfw/ip_fw_table.c 338082 2018-08-20 01:38:48Z loos $"); 30200590Sluigi 31200590Sluigi/* 32272840Smelifaro * Lookup table support for ipfw. 33200601Sluigi * 34272840Smelifaro * This file contains handlers for all generic tables' operations: 35272840Smelifaro * add/del/flush entries, list/dump tables etc.. 36200838Sluigi * 37272840Smelifaro * Table data modification is protected by both UH and runtime lock 38272840Smelifaro * while reading configuration/data is protected by UH lock. 39272840Smelifaro * 40272840Smelifaro * Lookup algorithms for all table types are located in ip_fw_table_algo.c 41200590Sluigi */ 42200590Sluigi 43225518Sjhb#include "opt_ipfw.h" 44200590Sluigi 45200590Sluigi#include <sys/param.h> 46200590Sluigi#include <sys/systm.h> 47200590Sluigi#include <sys/malloc.h> 48200590Sluigi#include <sys/kernel.h> 49200590Sluigi#include <sys/lock.h> 50200590Sluigi#include <sys/rwlock.h> 51272840Smelifaro#include <sys/rmlock.h> 52200590Sluigi#include <sys/socket.h> 53272840Smelifaro#include <sys/socketvar.h> 54240494Sglebius#include <sys/queue.h> 55200590Sluigi#include <net/if.h> /* ip_fw.h requires IFNAMSIZ */ 56317042Sae#include <net/pfil.h> 57200590Sluigi 58200590Sluigi#include <netinet/in.h> 59201732Sluigi#include <netinet/ip_var.h> /* struct ipfw_rule_ref */ 60200590Sluigi#include <netinet/ip_fw.h> 61200590Sluigi 62240494Sglebius#include <netpfil/ipfw/ip_fw_private.h> 63272840Smelifaro#include <netpfil/ipfw/ip_fw_table.h> 64240494Sglebius 65272840Smelifaro /* 66272840Smelifaro * Table has the following `type` concepts: 67272840Smelifaro * 68272840Smelifaro * `no.type` represents lookup key type (addr, ifp, uid, etc..) 69272840Smelifaro * vmask represents bitmask of table values which are present at the moment. 70272840Smelifaro * Special IPFW_VTYPE_LEGACY ( (uint32_t)-1 ) represents old 71272840Smelifaro * single-value-for-all approach. 72272840Smelifaro */ 73272840Smelifarostruct table_config { 74272840Smelifaro struct named_object no; 75272840Smelifaro uint8_t tflags; /* type flags */ 76272840Smelifaro uint8_t locked; /* 1 if locked from changes */ 77272840Smelifaro uint8_t linked; /* 1 if already linked */ 78272840Smelifaro uint8_t ochanged; /* used by set swapping */ 79272840Smelifaro uint8_t vshared; /* 1 if using shared value array */ 80272840Smelifaro uint8_t spare[3]; 81272840Smelifaro uint32_t count; /* Number of records */ 82272840Smelifaro uint32_t limit; /* Max number of records */ 83272840Smelifaro uint32_t vmask; /* bitmask with supported values */ 84272840Smelifaro uint32_t ocount; /* used by set swapping */ 85272840Smelifaro uint64_t gencnt; /* generation count */ 86272840Smelifaro char tablename[64]; /* table name */ 87272840Smelifaro struct table_algo *ta; /* Callbacks for given algo */ 88272840Smelifaro void *astate; /* algorithm state */ 89272840Smelifaro struct table_info ti_copy; /* data to put to table_info */ 90272840Smelifaro struct namedobj_instance *vi; 91272840Smelifaro}; 92200590Sluigi 93282070Smelifarostatic int find_table_err(struct namedobj_instance *ni, struct tid_info *ti, 94282070Smelifaro struct table_config **tc); 95272840Smelifarostatic struct table_config *find_table(struct namedobj_instance *ni, 96272840Smelifaro struct tid_info *ti); 97272840Smelifarostatic struct table_config *alloc_table_config(struct ip_fw_chain *ch, 98272840Smelifaro struct tid_info *ti, struct table_algo *ta, char *adata, uint8_t tflags); 99272840Smelifarostatic void free_table_config(struct namedobj_instance *ni, 100272840Smelifaro struct table_config *tc); 101272840Smelifarostatic int create_table_internal(struct ip_fw_chain *ch, struct tid_info *ti, 102272840Smelifaro char *aname, ipfw_xtable_info *i, uint16_t *pkidx, int ref); 103272840Smelifarostatic void link_table(struct ip_fw_chain *ch, struct table_config *tc); 104272840Smelifarostatic void unlink_table(struct ip_fw_chain *ch, struct table_config *tc); 105272840Smelifarostatic int find_ref_table(struct ip_fw_chain *ch, struct tid_info *ti, 106272840Smelifaro struct tentry_info *tei, uint32_t count, int op, struct table_config **ptc); 107272840Smelifaro#define OP_ADD 1 108272840Smelifaro#define OP_DEL 0 109272840Smelifarostatic int export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh, 110272840Smelifaro struct sockopt_data *sd); 111272840Smelifarostatic void export_table_info(struct ip_fw_chain *ch, struct table_config *tc, 112272840Smelifaro ipfw_xtable_info *i); 113272840Smelifarostatic int dump_table_tentry(void *e, void *arg); 114272840Smelifarostatic int dump_table_xentry(void *e, void *arg); 115200590Sluigi 116272840Smelifarostatic int swap_tables(struct ip_fw_chain *ch, struct tid_info *a, 117272840Smelifaro struct tid_info *b); 118200590Sluigi 119290332Saestatic int check_table_name(const char *name); 120272840Smelifarostatic int check_table_space(struct ip_fw_chain *ch, struct tableop_state *ts, 121272840Smelifaro struct table_config *tc, struct table_info *ti, uint32_t count); 122272840Smelifarostatic int destroy_table(struct ip_fw_chain *ch, struct tid_info *ti); 123232865Smelifaro 124272840Smelifarostatic struct table_algo *find_table_algo(struct tables_config *tableconf, 125272840Smelifaro struct tid_info *ti, char *name); 126232865Smelifaro 127272840Smelifarostatic void objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti); 128272840Smelifarostatic void ntlv_to_ti(struct _ipfw_obj_ntlv *ntlv, struct tid_info *ti); 129272840Smelifaro 130272840Smelifaro#define CHAIN_TO_NI(chain) (CHAIN_TO_TCFG(chain)->namehash) 131272840Smelifaro#define KIDX_TO_TI(ch, k) (&(((struct table_info *)(ch)->tablestate)[k])) 132272840Smelifaro 133272840Smelifaro#define TA_BUF_SZ 128 /* On-stack buffer for add/delete state */ 134272840Smelifaro 135272840Smelifarovoid 136272840Smelifarorollback_toperation_state(struct ip_fw_chain *ch, void *object) 137272840Smelifaro{ 138272840Smelifaro struct tables_config *tcfg; 139272840Smelifaro struct op_state *os; 140272840Smelifaro 141272840Smelifaro tcfg = CHAIN_TO_TCFG(ch); 142272840Smelifaro TAILQ_FOREACH(os, &tcfg->state_list, next) 143272840Smelifaro os->func(object, os); 144272840Smelifaro} 145272840Smelifaro 146272840Smelifarovoid 147272840Smelifaroadd_toperation_state(struct ip_fw_chain *ch, struct tableop_state *ts) 148272840Smelifaro{ 149272840Smelifaro struct tables_config *tcfg; 150272840Smelifaro 151272840Smelifaro tcfg = CHAIN_TO_TCFG(ch); 152272840Smelifaro TAILQ_INSERT_HEAD(&tcfg->state_list, &ts->opstate, next); 153272840Smelifaro} 154272840Smelifaro 155272840Smelifarovoid 156272840Smelifarodel_toperation_state(struct ip_fw_chain *ch, struct tableop_state *ts) 157272840Smelifaro{ 158272840Smelifaro struct tables_config *tcfg; 159272840Smelifaro 160272840Smelifaro tcfg = CHAIN_TO_TCFG(ch); 161272840Smelifaro TAILQ_REMOVE(&tcfg->state_list, &ts->opstate, next); 162272840Smelifaro} 163272840Smelifaro 164272840Smelifarovoid 165272840Smelifarotc_ref(struct table_config *tc) 166272840Smelifaro{ 167272840Smelifaro 168272840Smelifaro tc->no.refcnt++; 169272840Smelifaro} 170272840Smelifaro 171272840Smelifarovoid 172272840Smelifarotc_unref(struct table_config *tc) 173272840Smelifaro{ 174272840Smelifaro 175272840Smelifaro tc->no.refcnt--; 176272840Smelifaro} 177272840Smelifaro 178272840Smelifarostatic struct table_value * 179272840Smelifaroget_table_value(struct ip_fw_chain *ch, struct table_config *tc, uint32_t kidx) 180272840Smelifaro{ 181272840Smelifaro struct table_value *pval; 182272840Smelifaro 183272840Smelifaro pval = (struct table_value *)ch->valuestate; 184272840Smelifaro 185272840Smelifaro return (&pval[kidx]); 186272840Smelifaro} 187272840Smelifaro 188272840Smelifaro 189201120Sluigi/* 190272840Smelifaro * Checks if we're able to insert/update entry @tei into table 191272840Smelifaro * w.r.t @tc limits. 192272840Smelifaro * May alter @tei to indicate insertion error / insert 193272840Smelifaro * options. 194272840Smelifaro * 195272840Smelifaro * Returns 0 if operation can be performed/ 196201120Sluigi */ 197272840Smelifarostatic int 198272840Smelifarocheck_table_limit(struct table_config *tc, struct tentry_info *tei) 199272840Smelifaro{ 200272840Smelifaro 201272840Smelifaro if (tc->limit == 0 || tc->count < tc->limit) 202272840Smelifaro return (0); 203272840Smelifaro 204272840Smelifaro if ((tei->flags & TEI_FLAGS_UPDATE) == 0) { 205272840Smelifaro /* Notify userland on error cause */ 206272840Smelifaro tei->flags |= TEI_FLAGS_LIMIT; 207272840Smelifaro return (EFBIG); 208272840Smelifaro } 209272840Smelifaro 210272840Smelifaro /* 211272840Smelifaro * We have UPDATE flag set. 212272840Smelifaro * Permit updating record (if found), 213272840Smelifaro * but restrict adding new one since we've 214272840Smelifaro * already hit the limit. 215272840Smelifaro */ 216272840Smelifaro tei->flags |= TEI_FLAGS_DONTADD; 217272840Smelifaro 218272840Smelifaro return (0); 219272840Smelifaro} 220272840Smelifaro 221232865Smelifaro/* 222272840Smelifaro * Convert algorithm callback return code into 223272840Smelifaro * one of pre-defined states known by userland. 224232865Smelifaro */ 225272840Smelifarostatic void 226272840Smelifarostore_tei_result(struct tentry_info *tei, int op, int error, uint32_t num) 227272840Smelifaro{ 228272840Smelifaro int flag; 229201120Sluigi 230272840Smelifaro flag = 0; 231232865Smelifaro 232272840Smelifaro switch (error) { 233272840Smelifaro case 0: 234272840Smelifaro if (op == OP_ADD && num != 0) 235272840Smelifaro flag = TEI_FLAGS_ADDED; 236272840Smelifaro if (op == OP_DEL) 237272840Smelifaro flag = TEI_FLAGS_DELETED; 238272840Smelifaro break; 239272840Smelifaro case ENOENT: 240272840Smelifaro flag = TEI_FLAGS_NOTFOUND; 241272840Smelifaro break; 242272840Smelifaro case EEXIST: 243272840Smelifaro flag = TEI_FLAGS_EXISTS; 244272840Smelifaro break; 245272840Smelifaro default: 246272840Smelifaro flag = TEI_FLAGS_ERROR; 247272840Smelifaro } 248232865Smelifaro 249272840Smelifaro tei->flags |= flag; 250272840Smelifaro} 251272840Smelifaro 252272840Smelifaro/* 253272840Smelifaro * Creates and references table with default parameters. 254272840Smelifaro * Saves table config, algo and allocated kidx info @ptc, @pta and 255272840Smelifaro * @pkidx if non-zero. 256272840Smelifaro * Used for table auto-creation to support old binaries. 257272840Smelifaro * 258272840Smelifaro * Returns 0 on success. 259272840Smelifaro */ 260272840Smelifarostatic int 261272840Smelifarocreate_table_compat(struct ip_fw_chain *ch, struct tid_info *ti, 262272840Smelifaro uint16_t *pkidx) 263232865Smelifaro{ 264272840Smelifaro ipfw_xtable_info xi; 265272840Smelifaro int error; 266232865Smelifaro 267272840Smelifaro memset(&xi, 0, sizeof(xi)); 268272840Smelifaro /* Set default value mask for legacy clients */ 269272840Smelifaro xi.vmask = IPFW_VTYPE_LEGACY; 270272840Smelifaro 271272840Smelifaro error = create_table_internal(ch, ti, NULL, &xi, pkidx, 1); 272272840Smelifaro if (error != 0) 273272840Smelifaro return (error); 274272840Smelifaro 275272840Smelifaro return (0); 276232865Smelifaro} 277232865Smelifaro 278272840Smelifaro/* 279272840Smelifaro * Find and reference existing table optionally 280272840Smelifaro * creating new one. 281272840Smelifaro * 282272840Smelifaro * Saves found table config into @ptc. 283272840Smelifaro * Note function may drop/acquire UH_WLOCK. 284272840Smelifaro * Returns 0 if table was found/created and referenced 285272840Smelifaro * or non-zero return code. 286272840Smelifaro */ 287272840Smelifarostatic int 288272840Smelifarofind_ref_table(struct ip_fw_chain *ch, struct tid_info *ti, 289272840Smelifaro struct tentry_info *tei, uint32_t count, int op, 290272840Smelifaro struct table_config **ptc) 291200590Sluigi{ 292272840Smelifaro struct namedobj_instance *ni; 293272840Smelifaro struct table_config *tc; 294272840Smelifaro uint16_t kidx; 295272840Smelifaro int error; 296200590Sluigi 297272840Smelifaro IPFW_UH_WLOCK_ASSERT(ch); 298232865Smelifaro 299272840Smelifaro ni = CHAIN_TO_NI(ch); 300272840Smelifaro tc = NULL; 301272840Smelifaro if ((tc = find_table(ni, ti)) != NULL) { 302272840Smelifaro /* check table type */ 303282070Smelifaro if (tc->no.subtype != ti->type) 304232865Smelifaro return (EINVAL); 305272840Smelifaro 306272840Smelifaro if (tc->locked != 0) 307272840Smelifaro return (EACCES); 308272840Smelifaro 309272840Smelifaro /* Try to exit early on limit hit */ 310272840Smelifaro if (op == OP_ADD && count == 1 && 311272840Smelifaro check_table_limit(tc, tei) != 0) 312272840Smelifaro return (EFBIG); 313272840Smelifaro 314272840Smelifaro /* Reference and return */ 315272840Smelifaro tc->no.refcnt++; 316272840Smelifaro *ptc = tc; 317272840Smelifaro return (0); 318272840Smelifaro } 319272840Smelifaro 320272840Smelifaro if (op == OP_DEL) 321272840Smelifaro return (ESRCH); 322272840Smelifaro 323298995Spfg /* Compatibility mode: create new table for old clients */ 324272840Smelifaro if ((tei->flags & TEI_FLAGS_COMPAT) == 0) 325272840Smelifaro return (ESRCH); 326272840Smelifaro 327272840Smelifaro IPFW_UH_WUNLOCK(ch); 328272840Smelifaro error = create_table_compat(ch, ti, &kidx); 329272840Smelifaro IPFW_UH_WLOCK(ch); 330272840Smelifaro 331272840Smelifaro if (error != 0) 332272840Smelifaro return (error); 333272840Smelifaro 334272840Smelifaro tc = (struct table_config *)ipfw_objhash_lookup_kidx(ni, kidx); 335272840Smelifaro KASSERT(tc != NULL, ("create_table_compat returned bad idx %d", kidx)); 336272840Smelifaro 337272840Smelifaro /* OK, now we've got referenced table. */ 338272840Smelifaro *ptc = tc; 339272840Smelifaro return (0); 340272840Smelifaro} 341272840Smelifaro 342272840Smelifaro/* 343272840Smelifaro * Rolls back already @added to @tc entries using state array @ta_buf_m. 344272840Smelifaro * Assume the following layout: 345272840Smelifaro * 1) ADD state (ta_buf_m[0] ... t_buf_m[added - 1]) for handling update cases 346272840Smelifaro * 2) DEL state (ta_buf_m[count[ ... t_buf_m[count + added - 1]) 347272840Smelifaro * for storing deleted state 348272840Smelifaro */ 349272840Smelifarostatic void 350272840Smelifarorollback_added_entries(struct ip_fw_chain *ch, struct table_config *tc, 351272840Smelifaro struct table_info *tinfo, struct tentry_info *tei, caddr_t ta_buf_m, 352272840Smelifaro uint32_t count, uint32_t added) 353272840Smelifaro{ 354272840Smelifaro struct table_algo *ta; 355272840Smelifaro struct tentry_info *ptei; 356272840Smelifaro caddr_t v, vv; 357272840Smelifaro size_t ta_buf_sz; 358272840Smelifaro int error, i; 359272840Smelifaro uint32_t num; 360272840Smelifaro 361272840Smelifaro IPFW_UH_WLOCK_ASSERT(ch); 362272840Smelifaro 363272840Smelifaro ta = tc->ta; 364272840Smelifaro ta_buf_sz = ta->ta_buf_size; 365272840Smelifaro v = ta_buf_m; 366272840Smelifaro vv = v + count * ta_buf_sz; 367272840Smelifaro for (i = 0; i < added; i++, v += ta_buf_sz, vv += ta_buf_sz) { 368272840Smelifaro ptei = &tei[i]; 369272840Smelifaro if ((ptei->flags & TEI_FLAGS_UPDATED) != 0) { 370272840Smelifaro 371272840Smelifaro /* 372272840Smelifaro * We have old value stored by previous 373272840Smelifaro * call in @ptei->value. Do add once again 374272840Smelifaro * to restore it. 375272840Smelifaro */ 376272840Smelifaro error = ta->add(tc->astate, tinfo, ptei, v, &num); 377272840Smelifaro KASSERT(error == 0, ("rollback UPDATE fail")); 378272840Smelifaro KASSERT(num == 0, ("rollback UPDATE fail2")); 379272840Smelifaro continue; 380232865Smelifaro } 381272840Smelifaro 382272840Smelifaro error = ta->prepare_del(ch, ptei, vv); 383272840Smelifaro KASSERT(error == 0, ("pre-rollback INSERT failed")); 384272840Smelifaro error = ta->del(tc->astate, tinfo, ptei, vv, &num); 385272840Smelifaro KASSERT(error == 0, ("rollback INSERT failed")); 386272840Smelifaro tc->count -= num; 387272840Smelifaro } 388272840Smelifaro} 389272840Smelifaro 390272840Smelifaro/* 391272840Smelifaro * Prepares add/del state for all @count entries in @tei. 392272840Smelifaro * Uses either stack buffer (@ta_buf) or allocates a new one. 393272840Smelifaro * Stores pointer to allocated buffer back to @ta_buf. 394272840Smelifaro * 395272840Smelifaro * Returns 0 on success. 396272840Smelifaro */ 397272840Smelifarostatic int 398272840Smelifaroprepare_batch_buffer(struct ip_fw_chain *ch, struct table_algo *ta, 399272840Smelifaro struct tentry_info *tei, uint32_t count, int op, caddr_t *ta_buf) 400272840Smelifaro{ 401272840Smelifaro caddr_t ta_buf_m, v; 402272840Smelifaro size_t ta_buf_sz, sz; 403272840Smelifaro struct tentry_info *ptei; 404272840Smelifaro int error, i; 405272840Smelifaro 406272840Smelifaro error = 0; 407272840Smelifaro ta_buf_sz = ta->ta_buf_size; 408272840Smelifaro if (count == 1) { 409331151Seadler /* Single add/delete, use on-stack buffer */ 410272840Smelifaro memset(*ta_buf, 0, TA_BUF_SZ); 411272840Smelifaro ta_buf_m = *ta_buf; 412272840Smelifaro } else { 413272840Smelifaro 414272840Smelifaro /* 415272840Smelifaro * Multiple adds/deletes, allocate larger buffer 416272840Smelifaro * 417272840Smelifaro * Note we need 2xcount buffer for add case: 418272840Smelifaro * we have hold both ADD state 419272840Smelifaro * and DELETE state (this may be needed 420272840Smelifaro * if we need to rollback all changes) 421272840Smelifaro */ 422272840Smelifaro sz = count * ta_buf_sz; 423272840Smelifaro ta_buf_m = malloc((op == OP_ADD) ? sz * 2 : sz, M_TEMP, 424272840Smelifaro M_WAITOK | M_ZERO); 425272840Smelifaro } 426272840Smelifaro 427272840Smelifaro v = ta_buf_m; 428272840Smelifaro for (i = 0; i < count; i++, v += ta_buf_sz) { 429272840Smelifaro ptei = &tei[i]; 430272840Smelifaro error = (op == OP_ADD) ? 431272840Smelifaro ta->prepare_add(ch, ptei, v) : ta->prepare_del(ch, ptei, v); 432272840Smelifaro 433272840Smelifaro /* 434272840Smelifaro * Some syntax error (incorrect mask, or address, or 435272840Smelifaro * anything). Return error regardless of atomicity 436272840Smelifaro * settings. 437272840Smelifaro */ 438272840Smelifaro if (error != 0) 439272840Smelifaro break; 440272840Smelifaro } 441272840Smelifaro 442272840Smelifaro *ta_buf = ta_buf_m; 443272840Smelifaro return (error); 444272840Smelifaro} 445272840Smelifaro 446272840Smelifaro/* 447272840Smelifaro * Flushes allocated state for each @count entries in @tei. 448272840Smelifaro * Frees @ta_buf_m if differs from stack buffer @ta_buf. 449272840Smelifaro */ 450272840Smelifarostatic void 451272840Smelifaroflush_batch_buffer(struct ip_fw_chain *ch, struct table_algo *ta, 452272840Smelifaro struct tentry_info *tei, uint32_t count, int rollback, 453272840Smelifaro caddr_t ta_buf_m, caddr_t ta_buf) 454272840Smelifaro{ 455272840Smelifaro caddr_t v; 456272840Smelifaro struct tentry_info *ptei; 457272840Smelifaro size_t ta_buf_sz; 458272840Smelifaro int i; 459272840Smelifaro 460272840Smelifaro ta_buf_sz = ta->ta_buf_size; 461272840Smelifaro 462272840Smelifaro /* Run cleaning callback anyway */ 463272840Smelifaro v = ta_buf_m; 464272840Smelifaro for (i = 0; i < count; i++, v += ta_buf_sz) { 465272840Smelifaro ptei = &tei[i]; 466272840Smelifaro ta->flush_entry(ch, ptei, v); 467272840Smelifaro if (ptei->ptv != NULL) { 468272840Smelifaro free(ptei->ptv, M_IPFW); 469272840Smelifaro ptei->ptv = NULL; 470272840Smelifaro } 471272840Smelifaro } 472272840Smelifaro 473272840Smelifaro /* Clean up "deleted" state in case of rollback */ 474272840Smelifaro if (rollback != 0) { 475272840Smelifaro v = ta_buf_m + count * ta_buf_sz; 476272840Smelifaro for (i = 0; i < count; i++, v += ta_buf_sz) 477272840Smelifaro ta->flush_entry(ch, &tei[i], v); 478272840Smelifaro } 479272840Smelifaro 480272840Smelifaro if (ta_buf_m != ta_buf) 481272840Smelifaro free(ta_buf_m, M_TEMP); 482272840Smelifaro} 483272840Smelifaro 484272840Smelifaro 485272840Smelifarostatic void 486272840Smelifarorollback_add_entry(void *object, struct op_state *_state) 487272840Smelifaro{ 488272840Smelifaro struct ip_fw_chain *ch; 489272840Smelifaro struct tableop_state *ts; 490272840Smelifaro 491272840Smelifaro ts = (struct tableop_state *)_state; 492272840Smelifaro 493272840Smelifaro if (ts->tc != object && ts->ch != object) 494272840Smelifaro return; 495272840Smelifaro 496272840Smelifaro ch = ts->ch; 497272840Smelifaro 498272840Smelifaro IPFW_UH_WLOCK_ASSERT(ch); 499272840Smelifaro 500272840Smelifaro /* Call specifid unlockers */ 501272840Smelifaro rollback_table_values(ts); 502272840Smelifaro 503272840Smelifaro /* Indicate we've called */ 504272840Smelifaro ts->modified = 1; 505272840Smelifaro} 506272840Smelifaro 507272840Smelifaro/* 508272840Smelifaro * Adds/updates one or more entries in table @ti. 509272840Smelifaro * 510272840Smelifaro * Function may drop/reacquire UH wlock multiple times due to 511272840Smelifaro * items alloc, algorithm callbacks (check_space), value linkage 512272840Smelifaro * (new values, value storage realloc), etc.. 513272840Smelifaro * Other processes like other adds (which may involve storage resize), 514272840Smelifaro * table swaps (which changes table data and may change algo type), 515272840Smelifaro * table modify (which may change value mask) may be executed 516272840Smelifaro * simultaneously so we need to deal with it. 517272840Smelifaro * 518272840Smelifaro * The following approach was implemented: 519272840Smelifaro * we have per-chain linked list, protected with UH lock. 520272840Smelifaro * add_table_entry prepares special on-stack structure wthich is passed 521272840Smelifaro * to its descendants. Users add this structure to this list before unlock. 522272840Smelifaro * After performing needed operations and acquiring UH lock back, each user 523272840Smelifaro * checks if structure has changed. If true, it rolls local state back and 524272840Smelifaro * returns without error to the caller. 525272840Smelifaro * add_table_entry() on its own checks if structure has changed and restarts 526272840Smelifaro * its operation from the beginning (goto restart). 527272840Smelifaro * 528272840Smelifaro * Functions which are modifying fields of interest (currently 529272840Smelifaro * resize_shared_value_storage() and swap_tables() ) 530272840Smelifaro * traverses given list while holding UH lock immediately before 531272840Smelifaro * performing their operations calling function provided be list entry 532272840Smelifaro * ( currently rollback_add_entry ) which performs rollback for all necessary 533272840Smelifaro * state and sets appropriate values in structure indicating rollback 534272840Smelifaro * has happened. 535272840Smelifaro * 536272840Smelifaro * Algo interaction: 537272840Smelifaro * Function references @ti first to ensure table won't 538272840Smelifaro * disappear or change its type. 539272840Smelifaro * After that, prepare_add callback is called for each @tei entry. 540272840Smelifaro * Next, we try to add each entry under UH+WHLOCK 541272840Smelifaro * using add() callback. 542272840Smelifaro * Finally, we free all state by calling flush_entry callback 543272840Smelifaro * for each @tei. 544272840Smelifaro * 545272840Smelifaro * Returns 0 on success. 546272840Smelifaro */ 547272840Smelifaroint 548272840Smelifaroadd_table_entry(struct ip_fw_chain *ch, struct tid_info *ti, 549272840Smelifaro struct tentry_info *tei, uint8_t flags, uint32_t count) 550272840Smelifaro{ 551272840Smelifaro struct table_config *tc; 552272840Smelifaro struct table_algo *ta; 553272840Smelifaro uint16_t kidx; 554272840Smelifaro int error, first_error, i, rollback; 555272840Smelifaro uint32_t num, numadd; 556272840Smelifaro struct tentry_info *ptei; 557272840Smelifaro struct tableop_state ts; 558272840Smelifaro char ta_buf[TA_BUF_SZ]; 559272840Smelifaro caddr_t ta_buf_m, v; 560272840Smelifaro 561272840Smelifaro memset(&ts, 0, sizeof(ts)); 562272840Smelifaro ta = NULL; 563272840Smelifaro IPFW_UH_WLOCK(ch); 564272840Smelifaro 565272840Smelifaro /* 566272840Smelifaro * Find and reference existing table. 567272840Smelifaro */ 568272840Smelifarorestart: 569272840Smelifaro if (ts.modified != 0) { 570272840Smelifaro IPFW_UH_WUNLOCK(ch); 571272840Smelifaro flush_batch_buffer(ch, ta, tei, count, rollback, 572272840Smelifaro ta_buf_m, ta_buf); 573272840Smelifaro memset(&ts, 0, sizeof(ts)); 574272840Smelifaro ta = NULL; 575272840Smelifaro IPFW_UH_WLOCK(ch); 576272840Smelifaro } 577272840Smelifaro 578272840Smelifaro error = find_ref_table(ch, ti, tei, count, OP_ADD, &tc); 579272840Smelifaro if (error != 0) { 580272840Smelifaro IPFW_UH_WUNLOCK(ch); 581272840Smelifaro return (error); 582272840Smelifaro } 583272840Smelifaro ta = tc->ta; 584272840Smelifaro 585272840Smelifaro /* Fill in tablestate */ 586272840Smelifaro ts.ch = ch; 587272840Smelifaro ts.opstate.func = rollback_add_entry; 588272840Smelifaro ts.tc = tc; 589272840Smelifaro ts.vshared = tc->vshared; 590272840Smelifaro ts.vmask = tc->vmask; 591272840Smelifaro ts.ta = ta; 592272840Smelifaro ts.tei = tei; 593272840Smelifaro ts.count = count; 594272840Smelifaro rollback = 0; 595272840Smelifaro add_toperation_state(ch, &ts); 596272840Smelifaro IPFW_UH_WUNLOCK(ch); 597272840Smelifaro 598272840Smelifaro /* Allocate memory and prepare record(s) */ 599272840Smelifaro /* Pass stack buffer by default */ 600272840Smelifaro ta_buf_m = ta_buf; 601272840Smelifaro error = prepare_batch_buffer(ch, ta, tei, count, OP_ADD, &ta_buf_m); 602272840Smelifaro 603272840Smelifaro IPFW_UH_WLOCK(ch); 604282521Smelifaro del_toperation_state(ch, &ts); 605272840Smelifaro /* Drop reference we've used in first search */ 606272840Smelifaro tc->no.refcnt--; 607272840Smelifaro 608282521Smelifaro /* Check prepare_batch_buffer() error */ 609282521Smelifaro if (error != 0) 610282521Smelifaro goto cleanup; 611282521Smelifaro 612272840Smelifaro /* 613272840Smelifaro * Check if table swap has happened. 614272840Smelifaro * (so table algo might be changed). 615272840Smelifaro * Restart operation to achieve consistent behavior. 616272840Smelifaro */ 617272840Smelifaro if (ts.modified != 0) 618272840Smelifaro goto restart; 619272840Smelifaro 620272840Smelifaro /* 621272840Smelifaro * Link all values values to shared/per-table value array. 622272840Smelifaro * 623272840Smelifaro * May release/reacquire UH_WLOCK. 624272840Smelifaro */ 625272840Smelifaro error = ipfw_link_table_values(ch, &ts); 626272840Smelifaro if (error != 0) 627272840Smelifaro goto cleanup; 628272840Smelifaro if (ts.modified != 0) 629272840Smelifaro goto restart; 630272840Smelifaro 631272840Smelifaro /* 632272840Smelifaro * Ensure we are able to add all entries without additional 633272840Smelifaro * memory allocations. May release/reacquire UH_WLOCK. 634272840Smelifaro */ 635272840Smelifaro kidx = tc->no.kidx; 636272840Smelifaro error = check_table_space(ch, &ts, tc, KIDX_TO_TI(ch, kidx), count); 637272840Smelifaro if (error != 0) 638272840Smelifaro goto cleanup; 639272840Smelifaro if (ts.modified != 0) 640272840Smelifaro goto restart; 641272840Smelifaro 642272840Smelifaro /* We've got valid table in @tc. Let's try to add data */ 643272840Smelifaro kidx = tc->no.kidx; 644272840Smelifaro ta = tc->ta; 645272840Smelifaro numadd = 0; 646272840Smelifaro first_error = 0; 647272840Smelifaro 648272840Smelifaro IPFW_WLOCK(ch); 649272840Smelifaro 650272840Smelifaro v = ta_buf_m; 651272840Smelifaro for (i = 0; i < count; i++, v += ta->ta_buf_size) { 652272840Smelifaro ptei = &tei[i]; 653272840Smelifaro num = 0; 654272840Smelifaro /* check limit before adding */ 655272840Smelifaro if ((error = check_table_limit(tc, ptei)) == 0) { 656272840Smelifaro error = ta->add(tc->astate, KIDX_TO_TI(ch, kidx), 657272840Smelifaro ptei, v, &num); 658272840Smelifaro /* Set status flag to inform userland */ 659272840Smelifaro store_tei_result(ptei, OP_ADD, error, num); 660272840Smelifaro } 661272840Smelifaro if (error == 0) { 662272840Smelifaro /* Update number of records to ease limit checking */ 663272840Smelifaro tc->count += num; 664272840Smelifaro numadd += num; 665272840Smelifaro continue; 666272840Smelifaro } 667272840Smelifaro 668272840Smelifaro if (first_error == 0) 669272840Smelifaro first_error = error; 670272840Smelifaro 671272840Smelifaro /* 672272840Smelifaro * Some error have happened. Check our atomicity 673272840Smelifaro * settings: continue if atomicity is not required, 674272840Smelifaro * rollback changes otherwise. 675272840Smelifaro */ 676272840Smelifaro if ((flags & IPFW_CTF_ATOMIC) == 0) 677272840Smelifaro continue; 678272840Smelifaro 679272840Smelifaro rollback_added_entries(ch, tc, KIDX_TO_TI(ch, kidx), 680272840Smelifaro tei, ta_buf_m, count, i); 681272840Smelifaro 682272840Smelifaro rollback = 1; 683232865Smelifaro break; 684272840Smelifaro } 685272840Smelifaro 686272840Smelifaro IPFW_WUNLOCK(ch); 687272840Smelifaro 688272840Smelifaro ipfw_garbage_table_values(ch, tc, tei, count, rollback); 689272840Smelifaro 690272840Smelifaro /* Permit post-add algorithm grow/rehash. */ 691272840Smelifaro if (numadd != 0) 692272840Smelifaro check_table_space(ch, NULL, tc, KIDX_TO_TI(ch, kidx), 0); 693272840Smelifaro 694272840Smelifaro /* Return first error to user, if any */ 695272840Smelifaro error = first_error; 696272840Smelifaro 697272840Smelifarocleanup: 698272840Smelifaro IPFW_UH_WUNLOCK(ch); 699272840Smelifaro 700272840Smelifaro flush_batch_buffer(ch, ta, tei, count, rollback, ta_buf_m, ta_buf); 701232865Smelifaro 702272840Smelifaro return (error); 703272840Smelifaro} 704232865Smelifaro 705272840Smelifaro/* 706272840Smelifaro * Deletes one or more entries in table @ti. 707272840Smelifaro * 708272840Smelifaro * Returns 0 on success. 709272840Smelifaro */ 710272840Smelifaroint 711272840Smelifarodel_table_entry(struct ip_fw_chain *ch, struct tid_info *ti, 712272840Smelifaro struct tentry_info *tei, uint8_t flags, uint32_t count) 713272840Smelifaro{ 714272840Smelifaro struct table_config *tc; 715272840Smelifaro struct table_algo *ta; 716272840Smelifaro struct tentry_info *ptei; 717272840Smelifaro uint16_t kidx; 718272840Smelifaro int error, first_error, i; 719272840Smelifaro uint32_t num, numdel; 720272840Smelifaro char ta_buf[TA_BUF_SZ]; 721272840Smelifaro caddr_t ta_buf_m, v; 722232865Smelifaro 723272840Smelifaro /* 724272840Smelifaro * Find and reference existing table. 725272840Smelifaro */ 726272840Smelifaro IPFW_UH_WLOCK(ch); 727272840Smelifaro error = find_ref_table(ch, ti, tei, count, OP_DEL, &tc); 728272840Smelifaro if (error != 0) { 729272840Smelifaro IPFW_UH_WUNLOCK(ch); 730272840Smelifaro return (error); 731272840Smelifaro } 732272840Smelifaro ta = tc->ta; 733272840Smelifaro IPFW_UH_WUNLOCK(ch); 734232865Smelifaro 735272840Smelifaro /* Allocate memory and prepare record(s) */ 736272840Smelifaro /* Pass stack buffer by default */ 737272840Smelifaro ta_buf_m = ta_buf; 738272840Smelifaro error = prepare_batch_buffer(ch, ta, tei, count, OP_DEL, &ta_buf_m); 739272840Smelifaro if (error != 0) 740272840Smelifaro goto cleanup; 741272840Smelifaro 742272840Smelifaro IPFW_UH_WLOCK(ch); 743272840Smelifaro 744272840Smelifaro /* Drop reference we've used in first search */ 745272840Smelifaro tc->no.refcnt--; 746272840Smelifaro 747272840Smelifaro /* 748272840Smelifaro * Check if table algo is still the same. 749272840Smelifaro * (changed ta may be the result of table swap). 750272840Smelifaro */ 751272840Smelifaro if (ta != tc->ta) { 752272840Smelifaro IPFW_UH_WUNLOCK(ch); 753272840Smelifaro error = EINVAL; 754272840Smelifaro goto cleanup; 755232865Smelifaro } 756232865Smelifaro 757272840Smelifaro kidx = tc->no.kidx; 758272840Smelifaro numdel = 0; 759272840Smelifaro first_error = 0; 760272840Smelifaro 761200590Sluigi IPFW_WLOCK(ch); 762272840Smelifaro v = ta_buf_m; 763272840Smelifaro for (i = 0; i < count; i++, v += ta->ta_buf_size) { 764272840Smelifaro ptei = &tei[i]; 765272840Smelifaro num = 0; 766272840Smelifaro error = ta->del(tc->astate, KIDX_TO_TI(ch, kidx), ptei, v, 767272840Smelifaro &num); 768272840Smelifaro /* Save state for userland */ 769272840Smelifaro store_tei_result(ptei, OP_DEL, error, num); 770272840Smelifaro if (error != 0 && first_error == 0) 771272840Smelifaro first_error = error; 772272840Smelifaro tc->count -= num; 773272840Smelifaro numdel += num; 774272840Smelifaro } 775272840Smelifaro IPFW_WUNLOCK(ch); 776232865Smelifaro 777272840Smelifaro /* Unlink non-used values */ 778272840Smelifaro ipfw_garbage_table_values(ch, tc, tei, count, 0); 779272840Smelifaro 780272840Smelifaro if (numdel != 0) { 781272840Smelifaro /* Run post-del hook to permit shrinking */ 782272840Smelifaro check_table_space(ch, NULL, tc, KIDX_TO_TI(ch, kidx), 0); 783232865Smelifaro } 784232865Smelifaro 785272840Smelifaro IPFW_UH_WUNLOCK(ch); 786272840Smelifaro 787272840Smelifaro /* Return first error to user, if any */ 788272840Smelifaro error = first_error; 789272840Smelifaro 790272840Smelifarocleanup: 791272840Smelifaro flush_batch_buffer(ch, ta, tei, count, 0, ta_buf_m, ta_buf); 792272840Smelifaro 793272840Smelifaro return (error); 794272840Smelifaro} 795272840Smelifaro 796272840Smelifaro/* 797272840Smelifaro * Ensure that table @tc has enough space to add @count entries without 798272840Smelifaro * need for reallocation. 799272840Smelifaro * 800272840Smelifaro * Callbacks order: 801272840Smelifaro * 0) need_modify() (UH_WLOCK) - checks if @count items can be added w/o resize. 802272840Smelifaro * 803272840Smelifaro * 1) alloc_modify (no locks, M_WAITOK) - alloc new state based on @pflags. 804272840Smelifaro * 2) prepare_modifyt (UH_WLOCK) - copy old data into new storage 805272840Smelifaro * 3) modify (UH_WLOCK + WLOCK) - switch pointers 806272840Smelifaro * 4) flush_modify (UH_WLOCK) - free state, if needed 807272840Smelifaro * 808272840Smelifaro * Returns 0 on success. 809272840Smelifaro */ 810272840Smelifarostatic int 811272840Smelifarocheck_table_space(struct ip_fw_chain *ch, struct tableop_state *ts, 812272840Smelifaro struct table_config *tc, struct table_info *ti, uint32_t count) 813272840Smelifaro{ 814272840Smelifaro struct table_algo *ta; 815272840Smelifaro uint64_t pflags; 816272840Smelifaro char ta_buf[TA_BUF_SZ]; 817272840Smelifaro int error; 818272840Smelifaro 819272840Smelifaro IPFW_UH_WLOCK_ASSERT(ch); 820272840Smelifaro 821272840Smelifaro error = 0; 822272840Smelifaro ta = tc->ta; 823272840Smelifaro if (ta->need_modify == NULL) 824272840Smelifaro return (0); 825272840Smelifaro 826272840Smelifaro /* Acquire reference not to loose @tc between locks/unlocks */ 827272840Smelifaro tc->no.refcnt++; 828272840Smelifaro 829272840Smelifaro /* 830272840Smelifaro * TODO: think about avoiding race between large add/large delete 831272840Smelifaro * operation on algorithm which implements shrinking along with 832272840Smelifaro * growing. 833272840Smelifaro */ 834272840Smelifaro while (true) { 835272840Smelifaro pflags = 0; 836272840Smelifaro if (ta->need_modify(tc->astate, ti, count, &pflags) == 0) { 837272840Smelifaro error = 0; 838272840Smelifaro break; 839232865Smelifaro } 840232865Smelifaro 841272840Smelifaro /* We have to shrink/grow table */ 842272840Smelifaro if (ts != NULL) 843272840Smelifaro add_toperation_state(ch, ts); 844272840Smelifaro IPFW_UH_WUNLOCK(ch); 845272840Smelifaro 846272840Smelifaro memset(&ta_buf, 0, sizeof(ta_buf)); 847272840Smelifaro error = ta->prepare_mod(ta_buf, &pflags); 848272840Smelifaro 849272840Smelifaro IPFW_UH_WLOCK(ch); 850272840Smelifaro if (ts != NULL) 851272840Smelifaro del_toperation_state(ch, ts); 852272840Smelifaro 853272840Smelifaro if (error != 0) 854272840Smelifaro break; 855272840Smelifaro 856272840Smelifaro if (ts != NULL && ts->modified != 0) { 857272840Smelifaro 858272840Smelifaro /* 859272840Smelifaro * Swap operation has happened 860272840Smelifaro * so we're currently operating on other 861272840Smelifaro * table data. Stop doing this. 862232865Smelifaro */ 863272840Smelifaro ta->flush_mod(ta_buf); 864272840Smelifaro break; 865232865Smelifaro } 866272840Smelifaro 867272840Smelifaro /* Check if we still need to alter table */ 868272840Smelifaro ti = KIDX_TO_TI(ch, tc->no.kidx); 869272840Smelifaro if (ta->need_modify(tc->astate, ti, count, &pflags) == 0) { 870272840Smelifaro IPFW_UH_WUNLOCK(ch); 871272840Smelifaro 872272840Smelifaro /* 873272840Smelifaro * Other thread has already performed resize. 874272840Smelifaro * Flush our state and return. 875272840Smelifaro */ 876272840Smelifaro ta->flush_mod(ta_buf); 877272840Smelifaro break; 878272840Smelifaro } 879272840Smelifaro 880272840Smelifaro error = ta->fill_mod(tc->astate, ti, ta_buf, &pflags); 881272840Smelifaro if (error == 0) { 882272840Smelifaro /* Do actual modification */ 883272840Smelifaro IPFW_WLOCK(ch); 884272840Smelifaro ta->modify(tc->astate, ti, ta_buf, pflags); 885272840Smelifaro IPFW_WUNLOCK(ch); 886272840Smelifaro } 887272840Smelifaro 888272840Smelifaro /* Anyway, flush data and retry */ 889272840Smelifaro ta->flush_mod(ta_buf); 890232865Smelifaro } 891232865Smelifaro 892272840Smelifaro tc->no.refcnt--; 893272840Smelifaro return (error); 894272840Smelifaro} 895232865Smelifaro 896272840Smelifaro/* 897272840Smelifaro * Adds or deletes record in table. 898272840Smelifaro * Data layout (v0): 899272840Smelifaro * Request: [ ip_fw3_opheader ipfw_table_xentry ] 900272840Smelifaro * 901272840Smelifaro * Returns 0 on success 902272840Smelifaro */ 903272840Smelifarostatic int 904272840Smelifaromanage_table_ent_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 905272840Smelifaro struct sockopt_data *sd) 906272840Smelifaro{ 907272840Smelifaro ipfw_table_xentry *xent; 908272840Smelifaro struct tentry_info tei; 909272840Smelifaro struct tid_info ti; 910272840Smelifaro struct table_value v; 911272840Smelifaro int error, hdrlen, read; 912272840Smelifaro 913272840Smelifaro hdrlen = offsetof(ipfw_table_xentry, k); 914272840Smelifaro 915272840Smelifaro /* Check minimum header size */ 916272840Smelifaro if (sd->valsize < (sizeof(*op3) + hdrlen)) 917272840Smelifaro return (EINVAL); 918272840Smelifaro 919272840Smelifaro read = sizeof(ip_fw3_opheader); 920272840Smelifaro 921272840Smelifaro /* Check if xentry len field is valid */ 922272840Smelifaro xent = (ipfw_table_xentry *)(op3 + 1); 923272840Smelifaro if (xent->len < hdrlen || xent->len + read > sd->valsize) 924272840Smelifaro return (EINVAL); 925272840Smelifaro 926272840Smelifaro memset(&tei, 0, sizeof(tei)); 927272840Smelifaro tei.paddr = &xent->k; 928272840Smelifaro tei.masklen = xent->masklen; 929272840Smelifaro ipfw_import_table_value_legacy(xent->value, &v); 930272840Smelifaro tei.pvalue = &v; 931298995Spfg /* Old requests compatibility */ 932272840Smelifaro tei.flags = TEI_FLAGS_COMPAT; 933272840Smelifaro if (xent->type == IPFW_TABLE_ADDR) { 934272840Smelifaro if (xent->len - hdrlen == sizeof(in_addr_t)) 935272840Smelifaro tei.subtype = AF_INET; 936272840Smelifaro else 937272840Smelifaro tei.subtype = AF_INET6; 938200590Sluigi } 939272840Smelifaro 940272840Smelifaro memset(&ti, 0, sizeof(ti)); 941272840Smelifaro ti.uidx = xent->tbl; 942272840Smelifaro ti.type = xent->type; 943272840Smelifaro 944272840Smelifaro error = (op3->opcode == IP_FW_TABLE_XADD) ? 945272840Smelifaro add_table_entry(ch, &ti, &tei, 0, 1) : 946272840Smelifaro del_table_entry(ch, &ti, &tei, 0, 1); 947272840Smelifaro 948272840Smelifaro return (error); 949200590Sluigi} 950200590Sluigi 951272840Smelifaro/* 952272840Smelifaro * Adds or deletes record in table. 953272840Smelifaro * Data layout (v1)(current): 954272840Smelifaro * Request: [ ipfw_obj_header 955272840Smelifaro * ipfw_obj_ctlv(IPFW_TLV_TBLENT_LIST) [ ipfw_obj_tentry x N ] 956272840Smelifaro * ] 957272840Smelifaro * 958272840Smelifaro * Returns 0 on success 959272840Smelifaro */ 960272840Smelifarostatic int 961272840Smelifaromanage_table_ent_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 962272840Smelifaro struct sockopt_data *sd) 963200590Sluigi{ 964272840Smelifaro ipfw_obj_tentry *tent, *ptent; 965272840Smelifaro ipfw_obj_ctlv *ctlv; 966272840Smelifaro ipfw_obj_header *oh; 967272840Smelifaro struct tentry_info *ptei, tei, *tei_buf; 968272840Smelifaro struct tid_info ti; 969272840Smelifaro int error, i, kidx, read; 970200590Sluigi 971272840Smelifaro /* Check minimum header size */ 972272840Smelifaro if (sd->valsize < (sizeof(*oh) + sizeof(*ctlv))) 973200590Sluigi return (EINVAL); 974232865Smelifaro 975272840Smelifaro /* Check if passed data is too long */ 976272840Smelifaro if (sd->valsize != sd->kavail) 977272840Smelifaro return (EINVAL); 978232865Smelifaro 979272840Smelifaro oh = (ipfw_obj_header *)sd->kbuf; 980272840Smelifaro 981272840Smelifaro /* Basic length checks for TLVs */ 982272840Smelifaro if (oh->ntlv.head.length != sizeof(oh->ntlv)) 983272840Smelifaro return (EINVAL); 984272840Smelifaro 985272840Smelifaro read = sizeof(*oh); 986272840Smelifaro 987272840Smelifaro ctlv = (ipfw_obj_ctlv *)(oh + 1); 988272840Smelifaro if (ctlv->head.length + read != sd->valsize) 989272840Smelifaro return (EINVAL); 990272840Smelifaro 991272840Smelifaro read += sizeof(*ctlv); 992272840Smelifaro tent = (ipfw_obj_tentry *)(ctlv + 1); 993272840Smelifaro if (ctlv->count * sizeof(*tent) + read != sd->valsize) 994272840Smelifaro return (EINVAL); 995272840Smelifaro 996272840Smelifaro if (ctlv->count == 0) 997272840Smelifaro return (0); 998272840Smelifaro 999272840Smelifaro /* 1000272840Smelifaro * Mark entire buffer as "read". 1001272840Smelifaro * This instructs sopt api write it back 1002272840Smelifaro * after function return. 1003272840Smelifaro */ 1004272840Smelifaro ipfw_get_sopt_header(sd, sd->valsize); 1005272840Smelifaro 1006272840Smelifaro /* Perform basic checks for each entry */ 1007272840Smelifaro ptent = tent; 1008272840Smelifaro kidx = tent->idx; 1009272840Smelifaro for (i = 0; i < ctlv->count; i++, ptent++) { 1010272840Smelifaro if (ptent->head.length != sizeof(*ptent)) 1011232865Smelifaro return (EINVAL); 1012272840Smelifaro if (ptent->idx != kidx) 1013272840Smelifaro return (ENOTSUP); 1014272840Smelifaro } 1015232865Smelifaro 1016272840Smelifaro /* Convert data into kernel request objects */ 1017272840Smelifaro objheader_to_ti(oh, &ti); 1018272840Smelifaro ti.type = oh->ntlv.type; 1019272840Smelifaro ti.uidx = kidx; 1020232865Smelifaro 1021272840Smelifaro /* Use on-stack buffer for single add/del */ 1022272840Smelifaro if (ctlv->count == 1) { 1023272840Smelifaro memset(&tei, 0, sizeof(tei)); 1024272840Smelifaro tei_buf = &tei; 1025272840Smelifaro } else 1026272840Smelifaro tei_buf = malloc(ctlv->count * sizeof(tei), M_TEMP, 1027272840Smelifaro M_WAITOK | M_ZERO); 1028238265Smelifaro 1029272840Smelifaro ptei = tei_buf; 1030272840Smelifaro ptent = tent; 1031272840Smelifaro for (i = 0; i < ctlv->count; i++, ptent++, ptei++) { 1032272840Smelifaro ptei->paddr = &ptent->k; 1033272840Smelifaro ptei->subtype = ptent->subtype; 1034272840Smelifaro ptei->masklen = ptent->masklen; 1035272840Smelifaro if (ptent->head.flags & IPFW_TF_UPDATE) 1036272840Smelifaro ptei->flags |= TEI_FLAGS_UPDATE; 1037232865Smelifaro 1038272840Smelifaro ipfw_import_table_value_v1(&ptent->v.value); 1039272840Smelifaro ptei->pvalue = (struct table_value *)&ptent->v.value; 1040272840Smelifaro } 1041232865Smelifaro 1042272840Smelifaro error = (oh->opheader.opcode == IP_FW_TABLE_XADD) ? 1043272840Smelifaro add_table_entry(ch, &ti, tei_buf, ctlv->flags, ctlv->count) : 1044272840Smelifaro del_table_entry(ch, &ti, tei_buf, ctlv->flags, ctlv->count); 1045272840Smelifaro 1046272840Smelifaro /* Translate result back to userland */ 1047272840Smelifaro ptei = tei_buf; 1048272840Smelifaro ptent = tent; 1049272840Smelifaro for (i = 0; i < ctlv->count; i++, ptent++, ptei++) { 1050272840Smelifaro if (ptei->flags & TEI_FLAGS_ADDED) 1051272840Smelifaro ptent->result = IPFW_TR_ADDED; 1052272840Smelifaro else if (ptei->flags & TEI_FLAGS_DELETED) 1053272840Smelifaro ptent->result = IPFW_TR_DELETED; 1054272840Smelifaro else if (ptei->flags & TEI_FLAGS_UPDATED) 1055272840Smelifaro ptent->result = IPFW_TR_UPDATED; 1056272840Smelifaro else if (ptei->flags & TEI_FLAGS_LIMIT) 1057272840Smelifaro ptent->result = IPFW_TR_LIMIT; 1058272840Smelifaro else if (ptei->flags & TEI_FLAGS_ERROR) 1059272840Smelifaro ptent->result = IPFW_TR_ERROR; 1060272840Smelifaro else if (ptei->flags & TEI_FLAGS_NOTFOUND) 1061272840Smelifaro ptent->result = IPFW_TR_NOTFOUND; 1062272840Smelifaro else if (ptei->flags & TEI_FLAGS_EXISTS) 1063272840Smelifaro ptent->result = IPFW_TR_EXISTS; 1064272840Smelifaro ipfw_export_table_value_v1(ptei->pvalue, &ptent->v.value); 1065232865Smelifaro } 1066232865Smelifaro 1067272840Smelifaro if (tei_buf != &tei) 1068272840Smelifaro free(tei_buf, M_TEMP); 1069272840Smelifaro 1070272840Smelifaro return (error); 1071272840Smelifaro} 1072272840Smelifaro 1073272840Smelifaro/* 1074272840Smelifaro * Looks up an entry in given table. 1075272840Smelifaro * Data layout (v0)(current): 1076272840Smelifaro * Request: [ ipfw_obj_header ipfw_obj_tentry ] 1077272840Smelifaro * Reply: [ ipfw_obj_header ipfw_obj_tentry ] 1078272840Smelifaro * 1079272840Smelifaro * Returns 0 on success 1080272840Smelifaro */ 1081272840Smelifarostatic int 1082272840Smelifarofind_table_entry(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 1083272840Smelifaro struct sockopt_data *sd) 1084272840Smelifaro{ 1085272840Smelifaro ipfw_obj_tentry *tent; 1086272840Smelifaro ipfw_obj_header *oh; 1087272840Smelifaro struct tid_info ti; 1088272840Smelifaro struct table_config *tc; 1089272840Smelifaro struct table_algo *ta; 1090272840Smelifaro struct table_info *kti; 1091307970Sae struct table_value *pval; 1092272840Smelifaro struct namedobj_instance *ni; 1093272840Smelifaro int error; 1094272840Smelifaro size_t sz; 1095272840Smelifaro 1096272840Smelifaro /* Check minimum header size */ 1097272840Smelifaro sz = sizeof(*oh) + sizeof(*tent); 1098272840Smelifaro if (sd->valsize != sz) 1099272840Smelifaro return (EINVAL); 1100272840Smelifaro 1101272840Smelifaro oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 1102272840Smelifaro tent = (ipfw_obj_tentry *)(oh + 1); 1103272840Smelifaro 1104272840Smelifaro /* Basic length checks for TLVs */ 1105272840Smelifaro if (oh->ntlv.head.length != sizeof(oh->ntlv)) 1106272840Smelifaro return (EINVAL); 1107272840Smelifaro 1108272840Smelifaro objheader_to_ti(oh, &ti); 1109272840Smelifaro ti.type = oh->ntlv.type; 1110272840Smelifaro ti.uidx = tent->idx; 1111272840Smelifaro 1112272840Smelifaro IPFW_UH_RLOCK(ch); 1113272840Smelifaro ni = CHAIN_TO_NI(ch); 1114272840Smelifaro 1115272840Smelifaro /* 1116272840Smelifaro * Find existing table and check its type . 1117272840Smelifaro */ 1118272840Smelifaro ta = NULL; 1119272840Smelifaro if ((tc = find_table(ni, &ti)) == NULL) { 1120272840Smelifaro IPFW_UH_RUNLOCK(ch); 1121200590Sluigi return (ESRCH); 1122200590Sluigi } 1123232865Smelifaro 1124272840Smelifaro /* check table type */ 1125282070Smelifaro if (tc->no.subtype != ti.type) { 1126272840Smelifaro IPFW_UH_RUNLOCK(ch); 1127232865Smelifaro return (EINVAL); 1128232865Smelifaro } 1129232865Smelifaro 1130272840Smelifaro kti = KIDX_TO_TI(ch, tc->no.kidx); 1131272840Smelifaro ta = tc->ta; 1132232865Smelifaro 1133272840Smelifaro if (ta->find_tentry == NULL) 1134272840Smelifaro return (ENOTSUP); 1135232865Smelifaro 1136272840Smelifaro error = ta->find_tentry(tc->astate, kti, tent); 1137307970Sae if (error == 0) { 1138307970Sae pval = get_table_value(ch, tc, tent->v.kidx); 1139307970Sae ipfw_export_table_value_v1(pval, &tent->v.value); 1140307970Sae } 1141272840Smelifaro IPFW_UH_RUNLOCK(ch); 1142272840Smelifaro 1143272840Smelifaro return (error); 1144200590Sluigi} 1145200590Sluigi 1146272840Smelifaro/* 1147272840Smelifaro * Flushes all entries or destroys given table. 1148272840Smelifaro * Data layout (v0)(current): 1149272840Smelifaro * Request: [ ipfw_obj_header ] 1150272840Smelifaro * 1151272840Smelifaro * Returns 0 on success 1152272840Smelifaro */ 1153200590Sluigistatic int 1154272840Smelifaroflush_table_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 1155272840Smelifaro struct sockopt_data *sd) 1156200590Sluigi{ 1157272840Smelifaro int error; 1158272840Smelifaro struct _ipfw_obj_header *oh; 1159272840Smelifaro struct tid_info ti; 1160200590Sluigi 1161272840Smelifaro if (sd->valsize != sizeof(*oh)) 1162272840Smelifaro return (EINVAL); 1163272840Smelifaro 1164272840Smelifaro oh = (struct _ipfw_obj_header *)op3; 1165272840Smelifaro objheader_to_ti(oh, &ti); 1166272840Smelifaro 1167272840Smelifaro if (op3->opcode == IP_FW_TABLE_XDESTROY) 1168272840Smelifaro error = destroy_table(ch, &ti); 1169272840Smelifaro else if (op3->opcode == IP_FW_TABLE_XFLUSH) 1170272840Smelifaro error = flush_table(ch, &ti); 1171272840Smelifaro else 1172272840Smelifaro return (ENOTSUP); 1173272840Smelifaro 1174272840Smelifaro return (error); 1175200590Sluigi} 1176200590Sluigi 1177272840Smelifarostatic void 1178272840Smelifarorestart_flush(void *object, struct op_state *_state) 1179272840Smelifaro{ 1180272840Smelifaro struct tableop_state *ts; 1181272840Smelifaro 1182272840Smelifaro ts = (struct tableop_state *)_state; 1183272840Smelifaro 1184272840Smelifaro if (ts->tc != object) 1185272840Smelifaro return; 1186272840Smelifaro 1187272840Smelifaro /* Indicate we've called */ 1188272840Smelifaro ts->modified = 1; 1189272840Smelifaro} 1190272840Smelifaro 1191272840Smelifaro/* 1192272840Smelifaro * Flushes given table. 1193272840Smelifaro * 1194272840Smelifaro * Function create new table instance with the same 1195272840Smelifaro * parameters, swaps it with old one and 1196272840Smelifaro * flushes state without holding runtime WLOCK. 1197272840Smelifaro * 1198272840Smelifaro * Returns 0 on success. 1199272840Smelifaro */ 1200200590Sluigiint 1201272840Smelifaroflush_table(struct ip_fw_chain *ch, struct tid_info *ti) 1202200590Sluigi{ 1203272840Smelifaro struct namedobj_instance *ni; 1204272840Smelifaro struct table_config *tc; 1205272840Smelifaro struct table_algo *ta; 1206272840Smelifaro struct table_info ti_old, ti_new, *tablestate; 1207272840Smelifaro void *astate_old, *astate_new; 1208272840Smelifaro char algostate[64], *pstate; 1209272840Smelifaro struct tableop_state ts; 1210278259Smelifaro int error, need_gc; 1211272840Smelifaro uint16_t kidx; 1212272840Smelifaro uint8_t tflags; 1213200590Sluigi 1214272840Smelifaro /* 1215298995Spfg * Stage 1: save table algorithm. 1216272840Smelifaro * Reference found table to ensure it won't disappear. 1217272840Smelifaro */ 1218272840Smelifaro IPFW_UH_WLOCK(ch); 1219272840Smelifaro ni = CHAIN_TO_NI(ch); 1220272840Smelifaro if ((tc = find_table(ni, ti)) == NULL) { 1221272840Smelifaro IPFW_UH_WUNLOCK(ch); 1222272840Smelifaro return (ESRCH); 1223272840Smelifaro } 1224278259Smelifaro need_gc = 0; 1225278259Smelifaro astate_new = NULL; 1226278259Smelifaro memset(&ti_new, 0, sizeof(ti_new)); 1227272840Smelifarorestart: 1228272840Smelifaro /* Set up swap handler */ 1229272840Smelifaro memset(&ts, 0, sizeof(ts)); 1230272840Smelifaro ts.opstate.func = restart_flush; 1231272840Smelifaro ts.tc = tc; 1232200590Sluigi 1233272840Smelifaro ta = tc->ta; 1234272840Smelifaro /* Do not flush readonly tables */ 1235272840Smelifaro if ((ta->flags & TA_FLAG_READONLY) != 0) { 1236272840Smelifaro IPFW_UH_WUNLOCK(ch); 1237272840Smelifaro return (EACCES); 1238272840Smelifaro } 1239272840Smelifaro /* Save startup algo parameters */ 1240272840Smelifaro if (ta->print_config != NULL) { 1241272840Smelifaro ta->print_config(tc->astate, KIDX_TO_TI(ch, tc->no.kidx), 1242272840Smelifaro algostate, sizeof(algostate)); 1243272840Smelifaro pstate = algostate; 1244272840Smelifaro } else 1245272840Smelifaro pstate = NULL; 1246272840Smelifaro tflags = tc->tflags; 1247272840Smelifaro tc->no.refcnt++; 1248272840Smelifaro add_toperation_state(ch, &ts); 1249272840Smelifaro IPFW_UH_WUNLOCK(ch); 1250272840Smelifaro 1251232865Smelifaro /* 1252278259Smelifaro * Stage 1.5: if this is not the first attempt, destroy previous state 1253278259Smelifaro */ 1254278259Smelifaro if (need_gc != 0) { 1255278259Smelifaro ta->destroy(astate_new, &ti_new); 1256278259Smelifaro need_gc = 0; 1257278259Smelifaro } 1258278259Smelifaro 1259278259Smelifaro /* 1260272840Smelifaro * Stage 2: allocate new table instance using same algo. 1261232865Smelifaro */ 1262272840Smelifaro memset(&ti_new, 0, sizeof(struct table_info)); 1263272840Smelifaro error = ta->init(ch, &astate_new, &ti_new, pstate, tflags); 1264232865Smelifaro 1265272840Smelifaro /* 1266272840Smelifaro * Stage 3: swap old state pointers with newly-allocated ones. 1267272840Smelifaro * Decrease refcount. 1268272840Smelifaro */ 1269272840Smelifaro IPFW_UH_WLOCK(ch); 1270272840Smelifaro tc->no.refcnt--; 1271272840Smelifaro del_toperation_state(ch, &ts); 1272232865Smelifaro 1273272840Smelifaro if (error != 0) { 1274272840Smelifaro IPFW_UH_WUNLOCK(ch); 1275272840Smelifaro return (error); 1276232865Smelifaro } 1277232865Smelifaro 1278272840Smelifaro /* 1279272840Smelifaro * Restart operation if table swap has happened: 1280272840Smelifaro * even if algo may be the same, algo init parameters 1281272840Smelifaro * may change. Restart operation instead of doing 1282272840Smelifaro * complex checks. 1283272840Smelifaro */ 1284272840Smelifaro if (ts.modified != 0) { 1285278259Smelifaro /* Delay destroying data since we're holding UH lock */ 1286278259Smelifaro need_gc = 1; 1287272840Smelifaro goto restart; 1288232865Smelifaro } 1289232865Smelifaro 1290272840Smelifaro ni = CHAIN_TO_NI(ch); 1291272840Smelifaro kidx = tc->no.kidx; 1292272840Smelifaro tablestate = (struct table_info *)ch->tablestate; 1293272840Smelifaro 1294272840Smelifaro IPFW_WLOCK(ch); 1295272840Smelifaro ti_old = tablestate[kidx]; 1296272840Smelifaro tablestate[kidx] = ti_new; 1297272840Smelifaro IPFW_WUNLOCK(ch); 1298272840Smelifaro 1299272840Smelifaro astate_old = tc->astate; 1300272840Smelifaro tc->astate = astate_new; 1301272840Smelifaro tc->ti_copy = ti_new; 1302272840Smelifaro tc->count = 0; 1303272840Smelifaro 1304272840Smelifaro /* Notify algo on real @ti address */ 1305272840Smelifaro if (ta->change_ti != NULL) 1306272840Smelifaro ta->change_ti(tc->astate, &tablestate[kidx]); 1307272840Smelifaro 1308272840Smelifaro /* 1309272840Smelifaro * Stage 4: unref values. 1310272840Smelifaro */ 1311272840Smelifaro ipfw_unref_table_values(ch, tc, ta, astate_old, &ti_old); 1312272840Smelifaro IPFW_UH_WUNLOCK(ch); 1313272840Smelifaro 1314272840Smelifaro /* 1315272840Smelifaro * Stage 5: perform real flush/destroy. 1316272840Smelifaro */ 1317272840Smelifaro ta->destroy(astate_old, &ti_old); 1318272840Smelifaro 1319200590Sluigi return (0); 1320200590Sluigi} 1321200590Sluigi 1322272840Smelifaro/* 1323272840Smelifaro * Swaps two tables. 1324272840Smelifaro * Data layout (v0)(current): 1325272840Smelifaro * Request: [ ipfw_obj_header ipfw_obj_ntlv ] 1326272840Smelifaro * 1327272840Smelifaro * Returns 0 on success 1328272840Smelifaro */ 1329272840Smelifarostatic int 1330272840Smelifaroswap_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 1331272840Smelifaro struct sockopt_data *sd) 1332200590Sluigi{ 1333272840Smelifaro int error; 1334272840Smelifaro struct _ipfw_obj_header *oh; 1335272840Smelifaro struct tid_info ti_a, ti_b; 1336200590Sluigi 1337272840Smelifaro if (sd->valsize != sizeof(*oh) + sizeof(ipfw_obj_ntlv)) 1338272840Smelifaro return (EINVAL); 1339200590Sluigi 1340272840Smelifaro oh = (struct _ipfw_obj_header *)op3; 1341272840Smelifaro ntlv_to_ti(&oh->ntlv, &ti_a); 1342272840Smelifaro ntlv_to_ti((ipfw_obj_ntlv *)(oh + 1), &ti_b); 1343272840Smelifaro 1344272840Smelifaro error = swap_tables(ch, &ti_a, &ti_b); 1345272840Smelifaro 1346272840Smelifaro return (error); 1347200590Sluigi} 1348200590Sluigi 1349272840Smelifaro/* 1350272840Smelifaro * Swaps two tables of the same type/valtype. 1351272840Smelifaro * 1352272840Smelifaro * Checks if tables are compatible and limits 1353272840Smelifaro * permits swap, than actually perform swap. 1354272840Smelifaro * 1355272840Smelifaro * Each table consists of 2 different parts: 1356272840Smelifaro * config: 1357272840Smelifaro * @tc (with name, set, kidx) and rule bindings, which is "stable". 1358272840Smelifaro * number of items 1359272840Smelifaro * table algo 1360272840Smelifaro * runtime: 1361272840Smelifaro * runtime data @ti (ch->tablestate) 1362272840Smelifaro * runtime cache in @tc 1363272840Smelifaro * algo-specific data (@tc->astate) 1364272840Smelifaro * 1365272840Smelifaro * So we switch: 1366272840Smelifaro * all runtime data 1367272840Smelifaro * number of items 1368272840Smelifaro * table algo 1369272840Smelifaro * 1370272840Smelifaro * After that we call @ti change handler for each table. 1371272840Smelifaro * 1372272840Smelifaro * Note that referencing @tc won't protect tc->ta from change. 1373272840Smelifaro * XXX: Do we need to restrict swap between locked tables? 1374272840Smelifaro * XXX: Do we need to exchange ftype? 1375272840Smelifaro * 1376272840Smelifaro * Returns 0 on success. 1377272840Smelifaro */ 1378272840Smelifarostatic int 1379272840Smelifaroswap_tables(struct ip_fw_chain *ch, struct tid_info *a, 1380272840Smelifaro struct tid_info *b) 1381232865Smelifaro{ 1382272840Smelifaro struct namedobj_instance *ni; 1383272840Smelifaro struct table_config *tc_a, *tc_b; 1384272840Smelifaro struct table_algo *ta; 1385272840Smelifaro struct table_info ti, *tablestate; 1386272840Smelifaro void *astate; 1387272840Smelifaro uint32_t count; 1388272840Smelifaro 1389272840Smelifaro /* 1390272840Smelifaro * Stage 1: find both tables and ensure they are of 1391272840Smelifaro * the same type. 1392272840Smelifaro */ 1393272840Smelifaro IPFW_UH_WLOCK(ch); 1394272840Smelifaro ni = CHAIN_TO_NI(ch); 1395272840Smelifaro if ((tc_a = find_table(ni, a)) == NULL) { 1396272840Smelifaro IPFW_UH_WUNLOCK(ch); 1397272840Smelifaro return (ESRCH); 1398272840Smelifaro } 1399272840Smelifaro if ((tc_b = find_table(ni, b)) == NULL) { 1400272840Smelifaro IPFW_UH_WUNLOCK(ch); 1401272840Smelifaro return (ESRCH); 1402272840Smelifaro } 1403272840Smelifaro 1404272840Smelifaro /* It is very easy to swap between the same table */ 1405272840Smelifaro if (tc_a == tc_b) { 1406272840Smelifaro IPFW_UH_WUNLOCK(ch); 1407272840Smelifaro return (0); 1408272840Smelifaro } 1409272840Smelifaro 1410272840Smelifaro /* Check type and value are the same */ 1411282070Smelifaro if (tc_a->no.subtype!=tc_b->no.subtype || tc_a->tflags!=tc_b->tflags) { 1412272840Smelifaro IPFW_UH_WUNLOCK(ch); 1413272840Smelifaro return (EINVAL); 1414272840Smelifaro } 1415272840Smelifaro 1416272840Smelifaro /* Check limits before swap */ 1417272840Smelifaro if ((tc_a->limit != 0 && tc_b->count > tc_a->limit) || 1418272840Smelifaro (tc_b->limit != 0 && tc_a->count > tc_b->limit)) { 1419272840Smelifaro IPFW_UH_WUNLOCK(ch); 1420272840Smelifaro return (EFBIG); 1421272840Smelifaro } 1422272840Smelifaro 1423272840Smelifaro /* Check if one of the tables is readonly */ 1424272840Smelifaro if (((tc_a->ta->flags | tc_b->ta->flags) & TA_FLAG_READONLY) != 0) { 1425272840Smelifaro IPFW_UH_WUNLOCK(ch); 1426272840Smelifaro return (EACCES); 1427272840Smelifaro } 1428272840Smelifaro 1429272840Smelifaro /* Notify we're going to swap */ 1430272840Smelifaro rollback_toperation_state(ch, tc_a); 1431272840Smelifaro rollback_toperation_state(ch, tc_b); 1432272840Smelifaro 1433272840Smelifaro /* Everything is fine, prepare to swap */ 1434272840Smelifaro tablestate = (struct table_info *)ch->tablestate; 1435272840Smelifaro ti = tablestate[tc_a->no.kidx]; 1436272840Smelifaro ta = tc_a->ta; 1437272840Smelifaro astate = tc_a->astate; 1438272840Smelifaro count = tc_a->count; 1439272840Smelifaro 1440272840Smelifaro IPFW_WLOCK(ch); 1441272840Smelifaro /* a <- b */ 1442272840Smelifaro tablestate[tc_a->no.kidx] = tablestate[tc_b->no.kidx]; 1443272840Smelifaro tc_a->ta = tc_b->ta; 1444272840Smelifaro tc_a->astate = tc_b->astate; 1445272840Smelifaro tc_a->count = tc_b->count; 1446272840Smelifaro /* b <- a */ 1447272840Smelifaro tablestate[tc_b->no.kidx] = ti; 1448272840Smelifaro tc_b->ta = ta; 1449272840Smelifaro tc_b->astate = astate; 1450272840Smelifaro tc_b->count = count; 1451272840Smelifaro IPFW_WUNLOCK(ch); 1452272840Smelifaro 1453272840Smelifaro /* Ensure tc.ti copies are in sync */ 1454272840Smelifaro tc_a->ti_copy = tablestate[tc_a->no.kidx]; 1455272840Smelifaro tc_b->ti_copy = tablestate[tc_b->no.kidx]; 1456272840Smelifaro 1457272840Smelifaro /* Notify both tables on @ti change */ 1458272840Smelifaro if (tc_a->ta->change_ti != NULL) 1459272840Smelifaro tc_a->ta->change_ti(tc_a->astate, &tablestate[tc_a->no.kidx]); 1460272840Smelifaro if (tc_b->ta->change_ti != NULL) 1461272840Smelifaro tc_b->ta->change_ti(tc_b->astate, &tablestate[tc_b->no.kidx]); 1462272840Smelifaro 1463272840Smelifaro IPFW_UH_WUNLOCK(ch); 1464272840Smelifaro 1465200590Sluigi return (0); 1466200590Sluigi} 1467200590Sluigi 1468272840Smelifaro/* 1469272840Smelifaro * Destroys table specified by @ti. 1470272840Smelifaro * Data layout (v0)(current): 1471272840Smelifaro * Request: [ ip_fw3_opheader ] 1472272840Smelifaro * 1473272840Smelifaro * Returns 0 on success 1474272840Smelifaro */ 1475272840Smelifarostatic int 1476272840Smelifarodestroy_table(struct ip_fw_chain *ch, struct tid_info *ti) 1477272840Smelifaro{ 1478272840Smelifaro struct namedobj_instance *ni; 1479272840Smelifaro struct table_config *tc; 1480272840Smelifaro 1481272840Smelifaro IPFW_UH_WLOCK(ch); 1482272840Smelifaro 1483272840Smelifaro ni = CHAIN_TO_NI(ch); 1484272840Smelifaro if ((tc = find_table(ni, ti)) == NULL) { 1485272840Smelifaro IPFW_UH_WUNLOCK(ch); 1486272840Smelifaro return (ESRCH); 1487272840Smelifaro } 1488272840Smelifaro 1489272840Smelifaro /* Do not permit destroying referenced tables */ 1490272840Smelifaro if (tc->no.refcnt > 0) { 1491272840Smelifaro IPFW_UH_WUNLOCK(ch); 1492272840Smelifaro return (EBUSY); 1493272840Smelifaro } 1494272840Smelifaro 1495272840Smelifaro IPFW_WLOCK(ch); 1496272840Smelifaro unlink_table(ch, tc); 1497272840Smelifaro IPFW_WUNLOCK(ch); 1498272840Smelifaro 1499272840Smelifaro /* Free obj index */ 1500272840Smelifaro if (ipfw_objhash_free_idx(ni, tc->no.kidx) != 0) 1501272840Smelifaro printf("Error unlinking kidx %d from table %s\n", 1502272840Smelifaro tc->no.kidx, tc->tablename); 1503272840Smelifaro 1504272840Smelifaro /* Unref values used in tables while holding UH lock */ 1505272840Smelifaro ipfw_unref_table_values(ch, tc, tc->ta, tc->astate, &tc->ti_copy); 1506272840Smelifaro IPFW_UH_WUNLOCK(ch); 1507272840Smelifaro 1508272840Smelifaro free_table_config(ni, tc); 1509272840Smelifaro 1510272840Smelifaro return (0); 1511272840Smelifaro} 1512272840Smelifaro 1513273274Smelifarostatic uint32_t 1514273274Smelifaroroundup2p(uint32_t v) 1515273274Smelifaro{ 1516273274Smelifaro 1517273274Smelifaro v--; 1518273274Smelifaro v |= v >> 1; 1519273274Smelifaro v |= v >> 2; 1520273274Smelifaro v |= v >> 4; 1521273274Smelifaro v |= v >> 8; 1522273274Smelifaro v |= v >> 16; 1523273274Smelifaro v++; 1524273274Smelifaro 1525273274Smelifaro return (v); 1526273274Smelifaro} 1527273274Smelifaro 1528272840Smelifaro/* 1529272840Smelifaro * Grow tables index. 1530272840Smelifaro * 1531272840Smelifaro * Returns 0 on success. 1532272840Smelifaro */ 1533200590Sluigiint 1534233478Smelifaroipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables) 1535233478Smelifaro{ 1536233478Smelifaro unsigned int ntables_old, tbl; 1537272840Smelifaro struct namedobj_instance *ni; 1538272840Smelifaro void *new_idx, *old_tablestate, *tablestate; 1539272840Smelifaro struct table_info *ti; 1540272840Smelifaro struct table_config *tc; 1541272840Smelifaro int i, new_blocks; 1542233478Smelifaro 1543233478Smelifaro /* Check new value for validity */ 1544273274Smelifaro if (ntables == 0) 1545273274Smelifaro return (EINVAL); 1546233478Smelifaro if (ntables > IPFW_TABLES_MAX) 1547233478Smelifaro ntables = IPFW_TABLES_MAX; 1548273274Smelifaro /* Alight to nearest power of 2 */ 1549273274Smelifaro ntables = (unsigned int)roundup2p(ntables); 1550233478Smelifaro 1551233478Smelifaro /* Allocate new pointers */ 1552272840Smelifaro tablestate = malloc(ntables * sizeof(struct table_info), 1553272840Smelifaro M_IPFW, M_WAITOK | M_ZERO); 1554233478Smelifaro 1555272840Smelifaro ipfw_objhash_bitmap_alloc(ntables, (void *)&new_idx, &new_blocks); 1556233478Smelifaro 1557272840Smelifaro IPFW_UH_WLOCK(ch); 1558272840Smelifaro 1559233478Smelifaro tbl = (ntables >= V_fw_tables_max) ? V_fw_tables_max : ntables; 1560272840Smelifaro ni = CHAIN_TO_NI(ch); 1561233478Smelifaro 1562272840Smelifaro /* Temporary restrict decreasing max_tables */ 1563272840Smelifaro if (ntables < V_fw_tables_max) { 1564233478Smelifaro 1565272840Smelifaro /* 1566272840Smelifaro * FIXME: Check if we really can shrink 1567272840Smelifaro */ 1568272840Smelifaro IPFW_UH_WUNLOCK(ch); 1569272840Smelifaro return (EINVAL); 1570272840Smelifaro } 1571233478Smelifaro 1572272840Smelifaro /* Copy table info/indices */ 1573272840Smelifaro memcpy(tablestate, ch->tablestate, sizeof(struct table_info) * tbl); 1574272840Smelifaro ipfw_objhash_bitmap_merge(ni, &new_idx, &new_blocks); 1575272840Smelifaro 1576272840Smelifaro IPFW_WLOCK(ch); 1577272840Smelifaro 1578272840Smelifaro /* Change pointers */ 1579272840Smelifaro old_tablestate = ch->tablestate; 1580272840Smelifaro ch->tablestate = tablestate; 1581272840Smelifaro ipfw_objhash_bitmap_swap(ni, &new_idx, &new_blocks); 1582272840Smelifaro 1583233478Smelifaro ntables_old = V_fw_tables_max; 1584233478Smelifaro V_fw_tables_max = ntables; 1585233478Smelifaro 1586233478Smelifaro IPFW_WUNLOCK(ch); 1587233478Smelifaro 1588272840Smelifaro /* Notify all consumers that their @ti pointer has changed */ 1589272840Smelifaro ti = (struct table_info *)ch->tablestate; 1590272840Smelifaro for (i = 0; i < tbl; i++, ti++) { 1591272840Smelifaro if (ti->lookup == NULL) 1592272840Smelifaro continue; 1593272840Smelifaro tc = (struct table_config *)ipfw_objhash_lookup_kidx(ni, i); 1594272840Smelifaro if (tc == NULL || tc->ta->change_ti == NULL) 1595272840Smelifaro continue; 1596272840Smelifaro 1597272840Smelifaro tc->ta->change_ti(tc->astate, ti); 1598272840Smelifaro } 1599272840Smelifaro 1600272840Smelifaro IPFW_UH_WUNLOCK(ch); 1601272840Smelifaro 1602272840Smelifaro /* Free old pointers */ 1603272840Smelifaro free(old_tablestate, M_IPFW); 1604272840Smelifaro ipfw_objhash_bitmap_free(new_idx, new_blocks); 1605272840Smelifaro 1606272840Smelifaro return (0); 1607272840Smelifaro} 1608272840Smelifaro 1609272840Smelifaro/* 1610316446Sae * Lookup table's named object by its @kidx. 1611316446Sae */ 1612316446Saestruct named_object * 1613316446Saeipfw_objhash_lookup_table_kidx(struct ip_fw_chain *ch, uint16_t kidx) 1614316446Sae{ 1615316446Sae 1616316446Sae return (ipfw_objhash_lookup_kidx(CHAIN_TO_NI(ch), kidx)); 1617316446Sae} 1618316446Sae 1619316446Sae/* 1620316446Sae * Take reference to table specified in @ntlv. 1621316446Sae * On success return its @kidx. 1622316446Sae */ 1623316446Saeint 1624316446Saeipfw_ref_table(struct ip_fw_chain *ch, ipfw_obj_ntlv *ntlv, uint16_t *kidx) 1625316446Sae{ 1626316446Sae struct tid_info ti; 1627316446Sae struct table_config *tc; 1628316446Sae int error; 1629316446Sae 1630316446Sae IPFW_UH_WLOCK_ASSERT(ch); 1631316446Sae 1632316446Sae ntlv_to_ti(ntlv, &ti); 1633316446Sae error = find_table_err(CHAIN_TO_NI(ch), &ti, &tc); 1634316446Sae if (error != 0) 1635316446Sae return (error); 1636316446Sae 1637316446Sae if (tc == NULL) 1638316446Sae return (ESRCH); 1639316446Sae 1640316446Sae tc_ref(tc); 1641316446Sae *kidx = tc->no.kidx; 1642316446Sae 1643316446Sae return (0); 1644316446Sae} 1645316446Sae 1646316446Saevoid 1647316446Saeipfw_unref_table(struct ip_fw_chain *ch, uint16_t kidx) 1648316446Sae{ 1649316446Sae 1650316446Sae struct namedobj_instance *ni; 1651316446Sae struct named_object *no; 1652316446Sae 1653316446Sae IPFW_UH_WLOCK_ASSERT(ch); 1654316446Sae ni = CHAIN_TO_NI(ch); 1655316446Sae no = ipfw_objhash_lookup_kidx(ni, kidx); 1656316446Sae KASSERT(no != NULL, ("Table with index %d not found", kidx)); 1657316446Sae no->refcnt--; 1658316446Sae} 1659316446Sae 1660316446Sae/* 1661338082Sloos * Lookup an arbitrary key @paddr of length @plen in table @tbl. 1662272840Smelifaro * Stores found value in @val. 1663272840Smelifaro * 1664272840Smelifaro * Returns 1 if key was found. 1665272840Smelifaro */ 1666272840Smelifaroint 1667315532Saeipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, uint16_t plen, 1668272840Smelifaro void *paddr, uint32_t *val) 1669272840Smelifaro{ 1670272840Smelifaro struct table_info *ti; 1671272840Smelifaro 1672272840Smelifaro ti = KIDX_TO_TI(ch, tbl); 1673272840Smelifaro 1674272840Smelifaro return (ti->lookup(ti, paddr, plen, val)); 1675272840Smelifaro} 1676272840Smelifaro 1677272840Smelifaro/* 1678272840Smelifaro * Info/List/dump support for tables. 1679272840Smelifaro * 1680272840Smelifaro */ 1681272840Smelifaro 1682272840Smelifaro/* 1683272840Smelifaro * High-level 'get' cmds sysctl handlers 1684272840Smelifaro */ 1685272840Smelifaro 1686272840Smelifaro/* 1687272840Smelifaro * Lists all tables currently available in kernel. 1688272840Smelifaro * Data layout (v0)(current): 1689272840Smelifaro * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size 1690272840Smelifaro * Reply: [ ipfw_obj_lheader ipfw_xtable_info x N ] 1691272840Smelifaro * 1692272840Smelifaro * Returns 0 on success 1693272840Smelifaro */ 1694272840Smelifarostatic int 1695272840Smelifarolist_tables(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 1696272840Smelifaro struct sockopt_data *sd) 1697272840Smelifaro{ 1698272840Smelifaro struct _ipfw_obj_lheader *olh; 1699272840Smelifaro int error; 1700272840Smelifaro 1701272840Smelifaro olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh)); 1702272840Smelifaro if (olh == NULL) 1703272840Smelifaro return (EINVAL); 1704272840Smelifaro if (sd->valsize < olh->size) 1705272840Smelifaro return (EINVAL); 1706272840Smelifaro 1707272840Smelifaro IPFW_UH_RLOCK(ch); 1708272840Smelifaro error = export_tables(ch, olh, sd); 1709272840Smelifaro IPFW_UH_RUNLOCK(ch); 1710272840Smelifaro 1711272840Smelifaro return (error); 1712272840Smelifaro} 1713272840Smelifaro 1714272840Smelifaro/* 1715272840Smelifaro * Store table info to buffer provided by @sd. 1716272840Smelifaro * Data layout (v0)(current): 1717272840Smelifaro * Request: [ ipfw_obj_header ipfw_xtable_info(empty)] 1718272840Smelifaro * Reply: [ ipfw_obj_header ipfw_xtable_info ] 1719272840Smelifaro * 1720272840Smelifaro * Returns 0 on success. 1721272840Smelifaro */ 1722272840Smelifarostatic int 1723272840Smelifarodescribe_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 1724272840Smelifaro struct sockopt_data *sd) 1725272840Smelifaro{ 1726272840Smelifaro struct _ipfw_obj_header *oh; 1727272840Smelifaro struct table_config *tc; 1728272840Smelifaro struct tid_info ti; 1729272840Smelifaro size_t sz; 1730272840Smelifaro 1731272840Smelifaro sz = sizeof(*oh) + sizeof(ipfw_xtable_info); 1732272840Smelifaro oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 1733272840Smelifaro if (oh == NULL) 1734272840Smelifaro return (EINVAL); 1735272840Smelifaro 1736272840Smelifaro objheader_to_ti(oh, &ti); 1737272840Smelifaro 1738272840Smelifaro IPFW_UH_RLOCK(ch); 1739272840Smelifaro if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) { 1740272840Smelifaro IPFW_UH_RUNLOCK(ch); 1741272840Smelifaro return (ESRCH); 1742200590Sluigi } 1743272840Smelifaro 1744272840Smelifaro export_table_info(ch, tc, (ipfw_xtable_info *)(oh + 1)); 1745272840Smelifaro IPFW_UH_RUNLOCK(ch); 1746272840Smelifaro 1747200590Sluigi return (0); 1748200590Sluigi} 1749200590Sluigi 1750272840Smelifaro/* 1751272840Smelifaro * Modifies existing table. 1752272840Smelifaro * Data layout (v0)(current): 1753272840Smelifaro * Request: [ ipfw_obj_header ipfw_xtable_info ] 1754272840Smelifaro * 1755272840Smelifaro * Returns 0 on success 1756272840Smelifaro */ 1757272840Smelifarostatic int 1758272840Smelifaromodify_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 1759272840Smelifaro struct sockopt_data *sd) 1760232865Smelifaro{ 1761272840Smelifaro struct _ipfw_obj_header *oh; 1762272840Smelifaro ipfw_xtable_info *i; 1763272840Smelifaro char *tname; 1764272840Smelifaro struct tid_info ti; 1765272840Smelifaro struct namedobj_instance *ni; 1766272840Smelifaro struct table_config *tc; 1767232865Smelifaro 1768272840Smelifaro if (sd->valsize != sizeof(*oh) + sizeof(ipfw_xtable_info)) 1769272840Smelifaro return (EINVAL); 1770232865Smelifaro 1771272840Smelifaro oh = (struct _ipfw_obj_header *)sd->kbuf; 1772272840Smelifaro i = (ipfw_xtable_info *)(oh + 1); 1773232865Smelifaro 1774272840Smelifaro /* 1775272840Smelifaro * Verify user-supplied strings. 1776272840Smelifaro * Check for null-terminated/zero-length strings/ 1777272840Smelifaro */ 1778272840Smelifaro tname = oh->ntlv.name; 1779290332Sae if (check_table_name(tname) != 0) 1780272840Smelifaro return (EINVAL); 1781232865Smelifaro 1782272840Smelifaro objheader_to_ti(oh, &ti); 1783272840Smelifaro ti.type = i->type; 1784272840Smelifaro 1785272840Smelifaro IPFW_UH_WLOCK(ch); 1786272840Smelifaro ni = CHAIN_TO_NI(ch); 1787272840Smelifaro if ((tc = find_table(ni, &ti)) == NULL) { 1788272840Smelifaro IPFW_UH_WUNLOCK(ch); 1789272840Smelifaro return (ESRCH); 1790232865Smelifaro } 1791232865Smelifaro 1792272840Smelifaro /* Do not support any modifications for readonly tables */ 1793272840Smelifaro if ((tc->ta->flags & TA_FLAG_READONLY) != 0) { 1794272840Smelifaro IPFW_UH_WUNLOCK(ch); 1795272840Smelifaro return (EACCES); 1796232865Smelifaro } 1797272840Smelifaro 1798272840Smelifaro if ((i->mflags & IPFW_TMFLAGS_LIMIT) != 0) 1799272840Smelifaro tc->limit = i->limit; 1800272840Smelifaro if ((i->mflags & IPFW_TMFLAGS_LOCK) != 0) 1801272840Smelifaro tc->locked = ((i->flags & IPFW_TGFLAGS_LOCKED) != 0); 1802272840Smelifaro IPFW_UH_WUNLOCK(ch); 1803272840Smelifaro 1804232865Smelifaro return (0); 1805232865Smelifaro} 1806232865Smelifaro 1807272840Smelifaro/* 1808272840Smelifaro * Creates new table. 1809272840Smelifaro * Data layout (v0)(current): 1810272840Smelifaro * Request: [ ipfw_obj_header ipfw_xtable_info ] 1811272840Smelifaro * 1812272840Smelifaro * Returns 0 on success 1813272840Smelifaro */ 1814200590Sluigistatic int 1815272840Smelifarocreate_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 1816272840Smelifaro struct sockopt_data *sd) 1817200590Sluigi{ 1818272840Smelifaro struct _ipfw_obj_header *oh; 1819272840Smelifaro ipfw_xtable_info *i; 1820272840Smelifaro char *tname, *aname; 1821272840Smelifaro struct tid_info ti; 1822272840Smelifaro struct namedobj_instance *ni; 1823200590Sluigi 1824272840Smelifaro if (sd->valsize != sizeof(*oh) + sizeof(ipfw_xtable_info)) 1825272840Smelifaro return (EINVAL); 1826272840Smelifaro 1827272840Smelifaro oh = (struct _ipfw_obj_header *)sd->kbuf; 1828272840Smelifaro i = (ipfw_xtable_info *)(oh + 1); 1829272840Smelifaro 1830272840Smelifaro /* 1831272840Smelifaro * Verify user-supplied strings. 1832272840Smelifaro * Check for null-terminated/zero-length strings/ 1833272840Smelifaro */ 1834272840Smelifaro tname = oh->ntlv.name; 1835272840Smelifaro aname = i->algoname; 1836290332Sae if (check_table_name(tname) != 0 || 1837272840Smelifaro strnlen(aname, sizeof(i->algoname)) == sizeof(i->algoname)) 1838272840Smelifaro return (EINVAL); 1839272840Smelifaro 1840272840Smelifaro if (aname[0] == '\0') { 1841272840Smelifaro /* Use default algorithm */ 1842272840Smelifaro aname = NULL; 1843272840Smelifaro } 1844272840Smelifaro 1845272840Smelifaro objheader_to_ti(oh, &ti); 1846272840Smelifaro ti.type = i->type; 1847272840Smelifaro 1848272840Smelifaro ni = CHAIN_TO_NI(ch); 1849272840Smelifaro 1850272840Smelifaro IPFW_UH_RLOCK(ch); 1851274087Smelifaro if (find_table(ni, &ti) != NULL) { 1852272840Smelifaro IPFW_UH_RUNLOCK(ch); 1853272840Smelifaro return (EEXIST); 1854272840Smelifaro } 1855272840Smelifaro IPFW_UH_RUNLOCK(ch); 1856272840Smelifaro 1857272840Smelifaro return (create_table_internal(ch, &ti, aname, i, NULL, 0)); 1858272840Smelifaro} 1859272840Smelifaro 1860272840Smelifaro/* 1861272840Smelifaro * Creates new table based on @ti and @aname. 1862272840Smelifaro * 1863272840Smelifaro * Assume @aname to be checked and valid. 1864272840Smelifaro * Stores allocated table kidx inside @pkidx (if non-NULL). 1865272840Smelifaro * Reference created table if @compat is non-zero. 1866272840Smelifaro * 1867272840Smelifaro * Returns 0 on success. 1868272840Smelifaro */ 1869272840Smelifarostatic int 1870272840Smelifarocreate_table_internal(struct ip_fw_chain *ch, struct tid_info *ti, 1871272840Smelifaro char *aname, ipfw_xtable_info *i, uint16_t *pkidx, int compat) 1872272840Smelifaro{ 1873272840Smelifaro struct namedobj_instance *ni; 1874272840Smelifaro struct table_config *tc, *tc_new, *tmp; 1875272840Smelifaro struct table_algo *ta; 1876272840Smelifaro uint16_t kidx; 1877272840Smelifaro 1878272840Smelifaro ni = CHAIN_TO_NI(ch); 1879272840Smelifaro 1880272840Smelifaro ta = find_table_algo(CHAIN_TO_TCFG(ch), ti, aname); 1881272840Smelifaro if (ta == NULL) 1882272840Smelifaro return (ENOTSUP); 1883272840Smelifaro 1884272840Smelifaro tc = alloc_table_config(ch, ti, ta, aname, i->tflags); 1885272840Smelifaro if (tc == NULL) 1886272840Smelifaro return (ENOMEM); 1887272840Smelifaro 1888272840Smelifaro tc->vmask = i->vmask; 1889272840Smelifaro tc->limit = i->limit; 1890272840Smelifaro if (ta->flags & TA_FLAG_READONLY) 1891272840Smelifaro tc->locked = 1; 1892272840Smelifaro else 1893272840Smelifaro tc->locked = (i->flags & IPFW_TGFLAGS_LOCKED) != 0; 1894272840Smelifaro 1895272840Smelifaro IPFW_UH_WLOCK(ch); 1896272840Smelifaro 1897272840Smelifaro /* Check if table has been already created */ 1898272840Smelifaro tc_new = find_table(ni, ti); 1899272840Smelifaro if (tc_new != NULL) { 1900272840Smelifaro 1901272840Smelifaro /* 1902272840Smelifaro * Compat: do not fail if we're 1903272840Smelifaro * requesting to create existing table 1904272840Smelifaro * which has the same type 1905272840Smelifaro */ 1906282070Smelifaro if (compat == 0 || tc_new->no.subtype != tc->no.subtype) { 1907272840Smelifaro IPFW_UH_WUNLOCK(ch); 1908272840Smelifaro free_table_config(ni, tc); 1909272840Smelifaro return (EEXIST); 1910272840Smelifaro } 1911272840Smelifaro 1912272840Smelifaro /* Exchange tc and tc_new for proper refcounting & freeing */ 1913272840Smelifaro tmp = tc; 1914272840Smelifaro tc = tc_new; 1915272840Smelifaro tc_new = tmp; 1916272840Smelifaro } else { 1917272840Smelifaro /* New table */ 1918272840Smelifaro if (ipfw_objhash_alloc_idx(ni, &kidx) != 0) { 1919272840Smelifaro IPFW_UH_WUNLOCK(ch); 1920272840Smelifaro printf("Unable to allocate table index." 1921272840Smelifaro " Consider increasing net.inet.ip.fw.tables_max"); 1922272840Smelifaro free_table_config(ni, tc); 1923272840Smelifaro return (EBUSY); 1924272840Smelifaro } 1925272840Smelifaro tc->no.kidx = kidx; 1926282070Smelifaro tc->no.etlv = IPFW_TLV_TBL_NAME; 1927272840Smelifaro 1928272840Smelifaro link_table(ch, tc); 1929272840Smelifaro } 1930272840Smelifaro 1931272840Smelifaro if (compat != 0) 1932272840Smelifaro tc->no.refcnt++; 1933272840Smelifaro if (pkidx != NULL) 1934272840Smelifaro *pkidx = tc->no.kidx; 1935272840Smelifaro 1936272840Smelifaro IPFW_UH_WUNLOCK(ch); 1937272840Smelifaro 1938272840Smelifaro if (tc_new != NULL) 1939272840Smelifaro free_table_config(ni, tc_new); 1940272840Smelifaro 1941200590Sluigi return (0); 1942200590Sluigi} 1943200590Sluigi 1944272840Smelifarostatic void 1945272840Smelifarontlv_to_ti(ipfw_obj_ntlv *ntlv, struct tid_info *ti) 1946272840Smelifaro{ 1947272840Smelifaro 1948272840Smelifaro memset(ti, 0, sizeof(struct tid_info)); 1949272840Smelifaro ti->set = ntlv->set; 1950272840Smelifaro ti->uidx = ntlv->idx; 1951272840Smelifaro ti->tlvs = ntlv; 1952272840Smelifaro ti->tlen = ntlv->head.length; 1953272840Smelifaro} 1954272840Smelifaro 1955272840Smelifarostatic void 1956272840Smelifaroobjheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti) 1957272840Smelifaro{ 1958272840Smelifaro 1959272840Smelifaro ntlv_to_ti(&oh->ntlv, ti); 1960272840Smelifaro} 1961272840Smelifaro 1962282070Smelifarostruct namedobj_instance * 1963282070Smelifaroipfw_get_table_objhash(struct ip_fw_chain *ch) 1964282070Smelifaro{ 1965282070Smelifaro 1966282070Smelifaro return (CHAIN_TO_NI(ch)); 1967282070Smelifaro} 1968282070Smelifaro 1969272840Smelifaro/* 1970272840Smelifaro * Exports basic table info as name TLV. 1971272840Smelifaro * Used inside dump_static_rules() to provide info 1972272840Smelifaro * about all tables referenced by current ruleset. 1973272840Smelifaro * 1974272840Smelifaro * Returns 0 on success. 1975272840Smelifaro */ 1976200590Sluigiint 1977272840Smelifaroipfw_export_table_ntlv(struct ip_fw_chain *ch, uint16_t kidx, 1978272840Smelifaro struct sockopt_data *sd) 1979200590Sluigi{ 1980272840Smelifaro struct namedobj_instance *ni; 1981272840Smelifaro struct named_object *no; 1982272840Smelifaro ipfw_obj_ntlv *ntlv; 1983200590Sluigi 1984272840Smelifaro ni = CHAIN_TO_NI(ch); 1985272840Smelifaro 1986272840Smelifaro no = ipfw_objhash_lookup_kidx(ni, kidx); 1987272840Smelifaro KASSERT(no != NULL, ("invalid table kidx passed")); 1988272840Smelifaro 1989272840Smelifaro ntlv = (ipfw_obj_ntlv *)ipfw_get_sopt_space(sd, sizeof(*ntlv)); 1990272840Smelifaro if (ntlv == NULL) 1991272840Smelifaro return (ENOMEM); 1992272840Smelifaro 1993272840Smelifaro ntlv->head.type = IPFW_TLV_TBL_NAME; 1994272840Smelifaro ntlv->head.length = sizeof(*ntlv); 1995272840Smelifaro ntlv->idx = no->kidx; 1996272840Smelifaro strlcpy(ntlv->name, no->name, sizeof(ntlv->name)); 1997272840Smelifaro 1998272840Smelifaro return (0); 1999272840Smelifaro} 2000272840Smelifaro 2001272840Smelifarostruct dump_args { 2002272840Smelifaro struct ip_fw_chain *ch; 2003272840Smelifaro struct table_info *ti; 2004272840Smelifaro struct table_config *tc; 2005272840Smelifaro struct sockopt_data *sd; 2006272840Smelifaro uint32_t cnt; 2007272840Smelifaro uint16_t uidx; 2008272840Smelifaro int error; 2009272840Smelifaro uint32_t size; 2010272840Smelifaro ipfw_table_entry *ent; 2011272840Smelifaro ta_foreach_f *f; 2012272840Smelifaro void *farg; 2013272840Smelifaro ipfw_obj_tentry tent; 2014272840Smelifaro}; 2015272840Smelifaro 2016272840Smelifarostatic int 2017272840Smelifarocount_ext_entries(void *e, void *arg) 2018272840Smelifaro{ 2019272840Smelifaro struct dump_args *da; 2020272840Smelifaro 2021272840Smelifaro da = (struct dump_args *)arg; 2022272840Smelifaro da->cnt++; 2023272840Smelifaro 2024272840Smelifaro return (0); 2025272840Smelifaro} 2026272840Smelifaro 2027272840Smelifaro/* 2028272840Smelifaro * Gets number of items from table either using 2029272840Smelifaro * internal counter or calling algo callback for 2030272840Smelifaro * externally-managed tables. 2031272840Smelifaro * 2032272840Smelifaro * Returns number of records. 2033272840Smelifaro */ 2034272840Smelifarostatic uint32_t 2035272840Smelifarotable_get_count(struct ip_fw_chain *ch, struct table_config *tc) 2036272840Smelifaro{ 2037272840Smelifaro struct table_info *ti; 2038272840Smelifaro struct table_algo *ta; 2039272840Smelifaro struct dump_args da; 2040272840Smelifaro 2041272840Smelifaro ti = KIDX_TO_TI(ch, tc->no.kidx); 2042272840Smelifaro ta = tc->ta; 2043272840Smelifaro 2044272840Smelifaro /* Use internal counter for self-managed tables */ 2045272840Smelifaro if ((ta->flags & TA_FLAG_READONLY) == 0) 2046272840Smelifaro return (tc->count); 2047272840Smelifaro 2048272840Smelifaro /* Use callback to quickly get number of items */ 2049272840Smelifaro if ((ta->flags & TA_FLAG_EXTCOUNTER) != 0) 2050272840Smelifaro return (ta->get_count(tc->astate, ti)); 2051272840Smelifaro 2052272840Smelifaro /* Count number of iterms ourselves */ 2053272840Smelifaro memset(&da, 0, sizeof(da)); 2054272840Smelifaro ta->foreach(tc->astate, ti, count_ext_entries, &da); 2055272840Smelifaro 2056272840Smelifaro return (da.cnt); 2057272840Smelifaro} 2058272840Smelifaro 2059272840Smelifaro/* 2060272840Smelifaro * Exports table @tc info into standard ipfw_xtable_info format. 2061272840Smelifaro */ 2062272840Smelifarostatic void 2063272840Smelifaroexport_table_info(struct ip_fw_chain *ch, struct table_config *tc, 2064272840Smelifaro ipfw_xtable_info *i) 2065272840Smelifaro{ 2066272840Smelifaro struct table_info *ti; 2067272840Smelifaro struct table_algo *ta; 2068272840Smelifaro 2069282070Smelifaro i->type = tc->no.subtype; 2070272840Smelifaro i->tflags = tc->tflags; 2071272840Smelifaro i->vmask = tc->vmask; 2072272840Smelifaro i->set = tc->no.set; 2073272840Smelifaro i->kidx = tc->no.kidx; 2074272840Smelifaro i->refcnt = tc->no.refcnt; 2075272840Smelifaro i->count = table_get_count(ch, tc); 2076272840Smelifaro i->limit = tc->limit; 2077272840Smelifaro i->flags |= (tc->locked != 0) ? IPFW_TGFLAGS_LOCKED : 0; 2078293625Smelifaro i->size = i->count * sizeof(ipfw_obj_tentry); 2079272840Smelifaro i->size += sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info); 2080272840Smelifaro strlcpy(i->tablename, tc->tablename, sizeof(i->tablename)); 2081272840Smelifaro ti = KIDX_TO_TI(ch, tc->no.kidx); 2082272840Smelifaro ta = tc->ta; 2083272840Smelifaro if (ta->print_config != NULL) { 2084272840Smelifaro /* Use algo function to print table config to string */ 2085272840Smelifaro ta->print_config(tc->astate, ti, i->algoname, 2086272840Smelifaro sizeof(i->algoname)); 2087272840Smelifaro } else 2088272840Smelifaro strlcpy(i->algoname, ta->name, sizeof(i->algoname)); 2089272840Smelifaro /* Dump algo-specific data, if possible */ 2090272840Smelifaro if (ta->dump_tinfo != NULL) { 2091272840Smelifaro ta->dump_tinfo(tc->astate, ti, &i->ta_info); 2092272840Smelifaro i->ta_info.flags |= IPFW_TATFLAGS_DATA; 2093272840Smelifaro } 2094272840Smelifaro} 2095272840Smelifaro 2096272840Smelifarostruct dump_table_args { 2097272840Smelifaro struct ip_fw_chain *ch; 2098272840Smelifaro struct sockopt_data *sd; 2099272840Smelifaro}; 2100272840Smelifaro 2101299152Saestatic int 2102272840Smelifaroexport_table_internal(struct namedobj_instance *ni, struct named_object *no, 2103272840Smelifaro void *arg) 2104272840Smelifaro{ 2105272840Smelifaro ipfw_xtable_info *i; 2106272840Smelifaro struct dump_table_args *dta; 2107272840Smelifaro 2108272840Smelifaro dta = (struct dump_table_args *)arg; 2109272840Smelifaro 2110272840Smelifaro i = (ipfw_xtable_info *)ipfw_get_sopt_space(dta->sd, sizeof(*i)); 2111298048Spfg KASSERT(i != NULL, ("previously checked buffer is not enough")); 2112272840Smelifaro 2113272840Smelifaro export_table_info(dta->ch, (struct table_config *)no, i); 2114299152Sae return (0); 2115272840Smelifaro} 2116272840Smelifaro 2117272840Smelifaro/* 2118272840Smelifaro * Export all tables as ipfw_xtable_info structures to 2119272840Smelifaro * storage provided by @sd. 2120272840Smelifaro * 2121272840Smelifaro * If supplied buffer is too small, fills in required size 2122272840Smelifaro * and returns ENOMEM. 2123272840Smelifaro * Returns 0 on success. 2124272840Smelifaro */ 2125272840Smelifarostatic int 2126272840Smelifaroexport_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh, 2127272840Smelifaro struct sockopt_data *sd) 2128272840Smelifaro{ 2129272840Smelifaro uint32_t size; 2130272840Smelifaro uint32_t count; 2131272840Smelifaro struct dump_table_args dta; 2132272840Smelifaro 2133272840Smelifaro count = ipfw_objhash_count(CHAIN_TO_NI(ch)); 2134272840Smelifaro size = count * sizeof(ipfw_xtable_info) + sizeof(ipfw_obj_lheader); 2135272840Smelifaro 2136272840Smelifaro /* Fill in header regadless of buffer size */ 2137272840Smelifaro olh->count = count; 2138272840Smelifaro olh->objsize = sizeof(ipfw_xtable_info); 2139272840Smelifaro 2140272840Smelifaro if (size > olh->size) { 2141272840Smelifaro olh->size = size; 2142272840Smelifaro return (ENOMEM); 2143272840Smelifaro } 2144272840Smelifaro 2145272840Smelifaro olh->size = size; 2146272840Smelifaro 2147272840Smelifaro dta.ch = ch; 2148272840Smelifaro dta.sd = sd; 2149272840Smelifaro 2150272840Smelifaro ipfw_objhash_foreach(CHAIN_TO_NI(ch), export_table_internal, &dta); 2151272840Smelifaro 2152272840Smelifaro return (0); 2153272840Smelifaro} 2154272840Smelifaro 2155272840Smelifaro/* 2156272840Smelifaro * Dumps all table data 2157272840Smelifaro * Data layout (v1)(current): 2158272840Smelifaro * Request: [ ipfw_obj_header ], size = ipfw_xtable_info.size 2159272840Smelifaro * Reply: [ ipfw_obj_header ipfw_xtable_info ipfw_obj_tentry x N ] 2160272840Smelifaro * 2161272840Smelifaro * Returns 0 on success 2162272840Smelifaro */ 2163272840Smelifarostatic int 2164272840Smelifarodump_table_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 2165272840Smelifaro struct sockopt_data *sd) 2166272840Smelifaro{ 2167272840Smelifaro struct _ipfw_obj_header *oh; 2168272840Smelifaro ipfw_xtable_info *i; 2169272840Smelifaro struct tid_info ti; 2170272840Smelifaro struct table_config *tc; 2171272840Smelifaro struct table_algo *ta; 2172272840Smelifaro struct dump_args da; 2173272840Smelifaro uint32_t sz; 2174272840Smelifaro 2175272840Smelifaro sz = sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info); 2176272840Smelifaro oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 2177272840Smelifaro if (oh == NULL) 2178200590Sluigi return (EINVAL); 2179272840Smelifaro 2180272840Smelifaro i = (ipfw_xtable_info *)(oh + 1); 2181272840Smelifaro objheader_to_ti(oh, &ti); 2182272840Smelifaro 2183272840Smelifaro IPFW_UH_RLOCK(ch); 2184272840Smelifaro if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) { 2185272840Smelifaro IPFW_UH_RUNLOCK(ch); 2186272840Smelifaro return (ESRCH); 2187272840Smelifaro } 2188272840Smelifaro export_table_info(ch, tc, i); 2189272840Smelifaro 2190272840Smelifaro if (sd->valsize < i->size) { 2191272840Smelifaro 2192272840Smelifaro /* 2193272840Smelifaro * Submitted buffer size is not enough. 2194272840Smelifaro * WE've already filled in @i structure with 2195272840Smelifaro * relevant table info including size, so we 2196272840Smelifaro * can return. Buffer will be flushed automatically. 2197272840Smelifaro */ 2198272840Smelifaro IPFW_UH_RUNLOCK(ch); 2199272840Smelifaro return (ENOMEM); 2200272840Smelifaro } 2201272840Smelifaro 2202272840Smelifaro /* 2203272840Smelifaro * Do the actual dump in eXtended format 2204272840Smelifaro */ 2205272840Smelifaro memset(&da, 0, sizeof(da)); 2206272840Smelifaro da.ch = ch; 2207272840Smelifaro da.ti = KIDX_TO_TI(ch, tc->no.kidx); 2208272840Smelifaro da.tc = tc; 2209272840Smelifaro da.sd = sd; 2210272840Smelifaro 2211272840Smelifaro ta = tc->ta; 2212272840Smelifaro 2213272840Smelifaro ta->foreach(tc->astate, da.ti, dump_table_tentry, &da); 2214272840Smelifaro IPFW_UH_RUNLOCK(ch); 2215272840Smelifaro 2216272840Smelifaro return (da.error); 2217272840Smelifaro} 2218272840Smelifaro 2219272840Smelifaro/* 2220272840Smelifaro * Dumps all table data 2221272840Smelifaro * Data layout (version 0)(legacy): 2222272840Smelifaro * Request: [ ipfw_xtable ], size = IP_FW_TABLE_XGETSIZE() 2223272840Smelifaro * Reply: [ ipfw_xtable ipfw_table_xentry x N ] 2224272840Smelifaro * 2225272840Smelifaro * Returns 0 on success 2226272840Smelifaro */ 2227272840Smelifarostatic int 2228272840Smelifarodump_table_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 2229272840Smelifaro struct sockopt_data *sd) 2230272840Smelifaro{ 2231272840Smelifaro ipfw_xtable *xtbl; 2232272840Smelifaro struct tid_info ti; 2233272840Smelifaro struct table_config *tc; 2234272840Smelifaro struct table_algo *ta; 2235272840Smelifaro struct dump_args da; 2236272840Smelifaro size_t sz, count; 2237272840Smelifaro 2238272840Smelifaro xtbl = (ipfw_xtable *)ipfw_get_sopt_header(sd, sizeof(ipfw_xtable)); 2239272840Smelifaro if (xtbl == NULL) 2240272840Smelifaro return (EINVAL); 2241272840Smelifaro 2242272840Smelifaro memset(&ti, 0, sizeof(ti)); 2243272840Smelifaro ti.uidx = xtbl->tbl; 2244272840Smelifaro 2245272840Smelifaro IPFW_UH_RLOCK(ch); 2246272840Smelifaro if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) { 2247272840Smelifaro IPFW_UH_RUNLOCK(ch); 2248232865Smelifaro return (0); 2249272840Smelifaro } 2250272840Smelifaro count = table_get_count(ch, tc); 2251272840Smelifaro sz = count * sizeof(ipfw_table_xentry) + sizeof(ipfw_xtable); 2252272840Smelifaro 2253272840Smelifaro xtbl->cnt = count; 2254272840Smelifaro xtbl->size = sz; 2255282070Smelifaro xtbl->type = tc->no.subtype; 2256272840Smelifaro xtbl->tbl = ti.uidx; 2257272840Smelifaro 2258272840Smelifaro if (sd->valsize < sz) { 2259272840Smelifaro 2260272840Smelifaro /* 2261272840Smelifaro * Submitted buffer size is not enough. 2262272840Smelifaro * WE've already filled in @i structure with 2263272840Smelifaro * relevant table info including size, so we 2264272840Smelifaro * can return. Buffer will be flushed automatically. 2265272840Smelifaro */ 2266272840Smelifaro IPFW_UH_RUNLOCK(ch); 2267272840Smelifaro return (ENOMEM); 2268272840Smelifaro } 2269272840Smelifaro 2270272840Smelifaro /* Do the actual dump in eXtended format */ 2271272840Smelifaro memset(&da, 0, sizeof(da)); 2272272840Smelifaro da.ch = ch; 2273272840Smelifaro da.ti = KIDX_TO_TI(ch, tc->no.kidx); 2274272840Smelifaro da.tc = tc; 2275272840Smelifaro da.sd = sd; 2276272840Smelifaro 2277272840Smelifaro ta = tc->ta; 2278272840Smelifaro 2279272840Smelifaro ta->foreach(tc->astate, da.ti, dump_table_xentry, &da); 2280272840Smelifaro IPFW_UH_RUNLOCK(ch); 2281272840Smelifaro 2282200590Sluigi return (0); 2283200590Sluigi} 2284200590Sluigi 2285272840Smelifaro/* 2286272840Smelifaro * Legacy function to retrieve number of items in table. 2287272840Smelifaro */ 2288200590Sluigistatic int 2289272840Smelifaroget_table_size(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 2290272840Smelifaro struct sockopt_data *sd) 2291200590Sluigi{ 2292272840Smelifaro uint32_t *tbl; 2293272840Smelifaro struct tid_info ti; 2294272840Smelifaro size_t sz; 2295272840Smelifaro int error; 2296200590Sluigi 2297272840Smelifaro sz = sizeof(*op3) + sizeof(uint32_t); 2298272840Smelifaro op3 = (ip_fw3_opheader *)ipfw_get_sopt_header(sd, sz); 2299272840Smelifaro if (op3 == NULL) 2300272840Smelifaro return (EINVAL); 2301272840Smelifaro 2302272840Smelifaro tbl = (uint32_t *)(op3 + 1); 2303272840Smelifaro memset(&ti, 0, sizeof(ti)); 2304272840Smelifaro ti.uidx = *tbl; 2305272840Smelifaro IPFW_UH_RLOCK(ch); 2306272840Smelifaro error = ipfw_count_xtable(ch, &ti, tbl); 2307272840Smelifaro IPFW_UH_RUNLOCK(ch); 2308272840Smelifaro return (error); 2309272840Smelifaro} 2310272840Smelifaro 2311272840Smelifaro/* 2312272840Smelifaro * Legacy IP_FW_TABLE_GETSIZE handler 2313272840Smelifaro */ 2314272840Smelifaroint 2315272840Smelifaroipfw_count_table(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt) 2316272840Smelifaro{ 2317272840Smelifaro struct table_config *tc; 2318272840Smelifaro 2319272840Smelifaro if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL) 2320272840Smelifaro return (ESRCH); 2321272840Smelifaro *cnt = table_get_count(ch, tc); 2322200590Sluigi return (0); 2323200590Sluigi} 2324200590Sluigi 2325272840Smelifaro/* 2326272840Smelifaro * Legacy IP_FW_TABLE_XGETSIZE handler 2327272840Smelifaro */ 2328200590Sluigiint 2329272840Smelifaroipfw_count_xtable(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt) 2330200590Sluigi{ 2331272840Smelifaro struct table_config *tc; 2332272840Smelifaro uint32_t count; 2333200590Sluigi 2334272840Smelifaro if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL) { 2335272840Smelifaro *cnt = 0; 2336272840Smelifaro return (0); /* 'table all list' requires success */ 2337272840Smelifaro } 2338272840Smelifaro 2339272840Smelifaro count = table_get_count(ch, tc); 2340272840Smelifaro *cnt = count * sizeof(ipfw_table_xentry); 2341272840Smelifaro if (count > 0) 2342272840Smelifaro *cnt += sizeof(ipfw_xtable); 2343200590Sluigi return (0); 2344200590Sluigi} 2345232865Smelifaro 2346232865Smelifarostatic int 2347272840Smelifarodump_table_entry(void *e, void *arg) 2348232865Smelifaro{ 2349272840Smelifaro struct dump_args *da; 2350272840Smelifaro struct table_config *tc; 2351272840Smelifaro struct table_algo *ta; 2352272840Smelifaro ipfw_table_entry *ent; 2353272840Smelifaro struct table_value *pval; 2354272840Smelifaro int error; 2355232865Smelifaro 2356272840Smelifaro da = (struct dump_args *)arg; 2357272840Smelifaro 2358272840Smelifaro tc = da->tc; 2359272840Smelifaro ta = tc->ta; 2360272840Smelifaro 2361272840Smelifaro /* Out of memory, returning */ 2362272840Smelifaro if (da->cnt == da->size) 2363272840Smelifaro return (1); 2364272840Smelifaro ent = da->ent++; 2365272840Smelifaro ent->tbl = da->uidx; 2366272840Smelifaro da->cnt++; 2367272840Smelifaro 2368272840Smelifaro error = ta->dump_tentry(tc->astate, da->ti, e, &da->tent); 2369272840Smelifaro if (error != 0) 2370272840Smelifaro return (error); 2371272840Smelifaro 2372272840Smelifaro ent->addr = da->tent.k.addr.s_addr; 2373272840Smelifaro ent->masklen = da->tent.masklen; 2374272840Smelifaro pval = get_table_value(da->ch, da->tc, da->tent.v.kidx); 2375272840Smelifaro ent->value = ipfw_export_table_value_legacy(pval); 2376272840Smelifaro 2377232865Smelifaro return (0); 2378232865Smelifaro} 2379232865Smelifaro 2380272840Smelifaro/* 2381272840Smelifaro * Dumps table in pre-8.1 legacy format. 2382272840Smelifaro */ 2383232865Smelifaroint 2384272840Smelifaroipfw_dump_table_legacy(struct ip_fw_chain *ch, struct tid_info *ti, 2385272840Smelifaro ipfw_table *tbl) 2386232865Smelifaro{ 2387272840Smelifaro struct table_config *tc; 2388272840Smelifaro struct table_algo *ta; 2389272840Smelifaro struct dump_args da; 2390232865Smelifaro 2391272840Smelifaro tbl->cnt = 0; 2392272840Smelifaro 2393272840Smelifaro if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL) 2394272840Smelifaro return (0); /* XXX: We should return ESRCH */ 2395272840Smelifaro 2396272840Smelifaro ta = tc->ta; 2397272840Smelifaro 2398272840Smelifaro /* This dump format supports IPv4 only */ 2399282070Smelifaro if (tc->no.subtype != IPFW_TABLE_ADDR) 2400272840Smelifaro return (0); 2401272840Smelifaro 2402272840Smelifaro memset(&da, 0, sizeof(da)); 2403272840Smelifaro da.ch = ch; 2404272840Smelifaro da.ti = KIDX_TO_TI(ch, tc->no.kidx); 2405272840Smelifaro da.tc = tc; 2406272840Smelifaro da.ent = &tbl->ent[0]; 2407272840Smelifaro da.size = tbl->size; 2408272840Smelifaro 2409272840Smelifaro tbl->cnt = 0; 2410272840Smelifaro ta->foreach(tc->astate, da.ti, dump_table_entry, &da); 2411272840Smelifaro tbl->cnt = da.cnt; 2412272840Smelifaro 2413232865Smelifaro return (0); 2414232865Smelifaro} 2415232865Smelifaro 2416272840Smelifaro/* 2417272840Smelifaro * Dumps table entry in eXtended format (v1)(current). 2418272840Smelifaro */ 2419232865Smelifarostatic int 2420272840Smelifarodump_table_tentry(void *e, void *arg) 2421232865Smelifaro{ 2422272840Smelifaro struct dump_args *da; 2423272840Smelifaro struct table_config *tc; 2424272840Smelifaro struct table_algo *ta; 2425272840Smelifaro struct table_value *pval; 2426272840Smelifaro ipfw_obj_tentry *tent; 2427272840Smelifaro int error; 2428232865Smelifaro 2429272840Smelifaro da = (struct dump_args *)arg; 2430272840Smelifaro 2431272840Smelifaro tc = da->tc; 2432272840Smelifaro ta = tc->ta; 2433272840Smelifaro 2434272840Smelifaro tent = (ipfw_obj_tentry *)ipfw_get_sopt_space(da->sd, sizeof(*tent)); 2435232865Smelifaro /* Out of memory, returning */ 2436272840Smelifaro if (tent == NULL) { 2437272840Smelifaro da->error = ENOMEM; 2438232865Smelifaro return (1); 2439272840Smelifaro } 2440272840Smelifaro tent->head.length = sizeof(ipfw_obj_tentry); 2441272840Smelifaro tent->idx = da->uidx; 2442272840Smelifaro 2443272840Smelifaro error = ta->dump_tentry(tc->astate, da->ti, e, tent); 2444272840Smelifaro if (error != 0) 2445272840Smelifaro return (error); 2446272840Smelifaro 2447272840Smelifaro pval = get_table_value(da->ch, da->tc, tent->v.kidx); 2448272840Smelifaro ipfw_export_table_value_v1(pval, &tent->v.value); 2449272840Smelifaro 2450232865Smelifaro return (0); 2451232865Smelifaro} 2452232865Smelifaro 2453272840Smelifaro/* 2454272840Smelifaro * Dumps table entry in eXtended format (v0). 2455272840Smelifaro */ 2456232865Smelifarostatic int 2457272840Smelifarodump_table_xentry(void *e, void *arg) 2458232865Smelifaro{ 2459272840Smelifaro struct dump_args *da; 2460272840Smelifaro struct table_config *tc; 2461272840Smelifaro struct table_algo *ta; 2462232865Smelifaro ipfw_table_xentry *xent; 2463272840Smelifaro ipfw_obj_tentry *tent; 2464272840Smelifaro struct table_value *pval; 2465272840Smelifaro int error; 2466272840Smelifaro 2467272840Smelifaro da = (struct dump_args *)arg; 2468272840Smelifaro 2469272840Smelifaro tc = da->tc; 2470272840Smelifaro ta = tc->ta; 2471272840Smelifaro 2472272840Smelifaro xent = (ipfw_table_xentry *)ipfw_get_sopt_space(da->sd, sizeof(*xent)); 2473232865Smelifaro /* Out of memory, returning */ 2474272840Smelifaro if (xent == NULL) 2475232865Smelifaro return (1); 2476232865Smelifaro xent->len = sizeof(ipfw_table_xentry); 2477272840Smelifaro xent->tbl = da->uidx; 2478232865Smelifaro 2479272840Smelifaro memset(&da->tent, 0, sizeof(da->tent)); 2480272840Smelifaro tent = &da->tent; 2481272840Smelifaro error = ta->dump_tentry(tc->astate, da->ti, e, tent); 2482272840Smelifaro if (error != 0) 2483272840Smelifaro return (error); 2484272840Smelifaro 2485272840Smelifaro /* Convert current format to previous one */ 2486272840Smelifaro xent->masklen = tent->masklen; 2487272840Smelifaro pval = get_table_value(da->ch, da->tc, da->tent.v.kidx); 2488272840Smelifaro xent->value = ipfw_export_table_value_legacy(pval); 2489272840Smelifaro /* Apply some hacks */ 2490282070Smelifaro if (tc->no.subtype == IPFW_TABLE_ADDR && tent->subtype == AF_INET) { 2491272840Smelifaro xent->k.addr6.s6_addr32[3] = tent->k.addr.s_addr; 2492272840Smelifaro xent->flags = IPFW_TCF_INET; 2493272840Smelifaro } else 2494272840Smelifaro memcpy(&xent->k, &tent->k, sizeof(xent->k)); 2495272840Smelifaro 2496272840Smelifaro return (0); 2497272840Smelifaro} 2498272840Smelifaro 2499272840Smelifaro/* 2500272840Smelifaro * Helper function to export table algo data 2501272840Smelifaro * to tentry format before calling user function. 2502272840Smelifaro * 2503272840Smelifaro * Returns 0 on success. 2504272840Smelifaro */ 2505272840Smelifarostatic int 2506272840Smelifaroprepare_table_tentry(void *e, void *arg) 2507272840Smelifaro{ 2508272840Smelifaro struct dump_args *da; 2509272840Smelifaro struct table_config *tc; 2510272840Smelifaro struct table_algo *ta; 2511272840Smelifaro int error; 2512272840Smelifaro 2513272840Smelifaro da = (struct dump_args *)arg; 2514272840Smelifaro 2515272840Smelifaro tc = da->tc; 2516272840Smelifaro ta = tc->ta; 2517272840Smelifaro 2518272840Smelifaro error = ta->dump_tentry(tc->astate, da->ti, e, &da->tent); 2519272840Smelifaro if (error != 0) 2520272840Smelifaro return (error); 2521272840Smelifaro 2522272840Smelifaro da->f(&da->tent, da->farg); 2523272840Smelifaro 2524272840Smelifaro return (0); 2525272840Smelifaro} 2526272840Smelifaro 2527272840Smelifaro/* 2528272840Smelifaro * Allow external consumers to read table entries in standard format. 2529272840Smelifaro */ 2530272840Smelifaroint 2531272840Smelifaroipfw_foreach_table_tentry(struct ip_fw_chain *ch, uint16_t kidx, 2532272840Smelifaro ta_foreach_f *f, void *arg) 2533272840Smelifaro{ 2534272840Smelifaro struct namedobj_instance *ni; 2535272840Smelifaro struct table_config *tc; 2536272840Smelifaro struct table_algo *ta; 2537272840Smelifaro struct dump_args da; 2538272840Smelifaro 2539272840Smelifaro ni = CHAIN_TO_NI(ch); 2540272840Smelifaro 2541272840Smelifaro tc = (struct table_config *)ipfw_objhash_lookup_kidx(ni, kidx); 2542272840Smelifaro if (tc == NULL) 2543272840Smelifaro return (ESRCH); 2544272840Smelifaro 2545272840Smelifaro ta = tc->ta; 2546272840Smelifaro 2547272840Smelifaro memset(&da, 0, sizeof(da)); 2548272840Smelifaro da.ch = ch; 2549272840Smelifaro da.ti = KIDX_TO_TI(ch, tc->no.kidx); 2550272840Smelifaro da.tc = tc; 2551272840Smelifaro da.f = f; 2552272840Smelifaro da.farg = arg; 2553272840Smelifaro 2554272840Smelifaro ta->foreach(tc->astate, da.ti, prepare_table_tentry, &da); 2555272840Smelifaro 2556272840Smelifaro return (0); 2557272840Smelifaro} 2558272840Smelifaro 2559272840Smelifaro/* 2560272840Smelifaro * Table algorithms 2561272840Smelifaro */ 2562272840Smelifaro 2563272840Smelifaro/* 2564298995Spfg * Finds algorithm by index, table type or supplied name. 2565272840Smelifaro * 2566272840Smelifaro * Returns pointer to algo or NULL. 2567272840Smelifaro */ 2568272840Smelifarostatic struct table_algo * 2569272840Smelifarofind_table_algo(struct tables_config *tcfg, struct tid_info *ti, char *name) 2570272840Smelifaro{ 2571272840Smelifaro int i, l; 2572272840Smelifaro struct table_algo *ta; 2573272840Smelifaro 2574272840Smelifaro if (ti->type > IPFW_TABLE_MAXTYPE) 2575272840Smelifaro return (NULL); 2576272840Smelifaro 2577272840Smelifaro /* Search by index */ 2578272840Smelifaro if (ti->atype != 0) { 2579272840Smelifaro if (ti->atype > tcfg->algo_count) 2580272840Smelifaro return (NULL); 2581272840Smelifaro return (tcfg->algo[ti->atype]); 2582272840Smelifaro } 2583272840Smelifaro 2584272840Smelifaro if (name == NULL) { 2585272840Smelifaro /* Return default algorithm for given type if set */ 2586272840Smelifaro return (tcfg->def_algo[ti->type]); 2587272840Smelifaro } 2588272840Smelifaro 2589272840Smelifaro /* Search by name */ 2590272840Smelifaro /* TODO: better search */ 2591272840Smelifaro for (i = 1; i <= tcfg->algo_count; i++) { 2592272840Smelifaro ta = tcfg->algo[i]; 2593272840Smelifaro 2594272840Smelifaro /* 2595272840Smelifaro * One can supply additional algorithm 2596272840Smelifaro * parameters so we compare only the first word 2597272840Smelifaro * of supplied name: 2598272840Smelifaro * 'addr:chash hsize=32' 2599272840Smelifaro * '^^^^^^^^^' 2600272840Smelifaro * 2601272840Smelifaro */ 2602272840Smelifaro l = strlen(ta->name); 2603272840Smelifaro if (strncmp(name, ta->name, l) != 0) 2604272840Smelifaro continue; 2605272840Smelifaro if (name[l] != '\0' && name[l] != ' ') 2606272840Smelifaro continue; 2607272840Smelifaro /* Check if we're requesting proper table type */ 2608272840Smelifaro if (ti->type != 0 && ti->type != ta->type) 2609272840Smelifaro return (NULL); 2610272840Smelifaro return (ta); 2611272840Smelifaro } 2612272840Smelifaro 2613272840Smelifaro return (NULL); 2614272840Smelifaro} 2615272840Smelifaro 2616272840Smelifaro/* 2617272840Smelifaro * Register new table algo @ta. 2618272840Smelifaro * Stores algo id inside @idx. 2619272840Smelifaro * 2620272840Smelifaro * Returns 0 on success. 2621272840Smelifaro */ 2622272840Smelifaroint 2623272840Smelifaroipfw_add_table_algo(struct ip_fw_chain *ch, struct table_algo *ta, size_t size, 2624272840Smelifaro int *idx) 2625272840Smelifaro{ 2626272840Smelifaro struct tables_config *tcfg; 2627272840Smelifaro struct table_algo *ta_new; 2628272840Smelifaro size_t sz; 2629272840Smelifaro 2630272840Smelifaro if (size > sizeof(struct table_algo)) 2631272840Smelifaro return (EINVAL); 2632272840Smelifaro 2633272840Smelifaro /* Check for the required on-stack size for add/del */ 2634272840Smelifaro sz = roundup2(ta->ta_buf_size, sizeof(void *)); 2635272840Smelifaro if (sz > TA_BUF_SZ) 2636272840Smelifaro return (EINVAL); 2637272840Smelifaro 2638272840Smelifaro KASSERT(ta->type <= IPFW_TABLE_MAXTYPE,("Increase IPFW_TABLE_MAXTYPE")); 2639272840Smelifaro 2640272840Smelifaro /* Copy algorithm data to stable storage. */ 2641272840Smelifaro ta_new = malloc(sizeof(struct table_algo), M_IPFW, M_WAITOK | M_ZERO); 2642272840Smelifaro memcpy(ta_new, ta, size); 2643272840Smelifaro 2644272840Smelifaro tcfg = CHAIN_TO_TCFG(ch); 2645272840Smelifaro 2646272840Smelifaro KASSERT(tcfg->algo_count < 255, ("Increase algo array size")); 2647272840Smelifaro 2648272840Smelifaro tcfg->algo[++tcfg->algo_count] = ta_new; 2649272840Smelifaro ta_new->idx = tcfg->algo_count; 2650272840Smelifaro 2651272840Smelifaro /* Set algorithm as default one for given type */ 2652272840Smelifaro if ((ta_new->flags & TA_FLAG_DEFAULT) != 0 && 2653272840Smelifaro tcfg->def_algo[ta_new->type] == NULL) 2654272840Smelifaro tcfg->def_algo[ta_new->type] = ta_new; 2655272840Smelifaro 2656272840Smelifaro *idx = ta_new->idx; 2657232865Smelifaro 2658272840Smelifaro return (0); 2659272840Smelifaro} 2660272840Smelifaro 2661272840Smelifaro/* 2662272840Smelifaro * Unregisters table algo using @idx as id. 2663272840Smelifaro * XXX: It is NOT safe to call this function in any place 2664272840Smelifaro * other than ipfw instance destroy handler. 2665272840Smelifaro */ 2666272840Smelifarovoid 2667272840Smelifaroipfw_del_table_algo(struct ip_fw_chain *ch, int idx) 2668272840Smelifaro{ 2669272840Smelifaro struct tables_config *tcfg; 2670272840Smelifaro struct table_algo *ta; 2671272840Smelifaro 2672272840Smelifaro tcfg = CHAIN_TO_TCFG(ch); 2673272840Smelifaro 2674272840Smelifaro KASSERT(idx <= tcfg->algo_count, ("algo idx %d out of range 1..%d", 2675272840Smelifaro idx, tcfg->algo_count)); 2676272840Smelifaro 2677272840Smelifaro ta = tcfg->algo[idx]; 2678272840Smelifaro KASSERT(ta != NULL, ("algo idx %d is NULL", idx)); 2679272840Smelifaro 2680272840Smelifaro if (tcfg->def_algo[ta->type] == ta) 2681272840Smelifaro tcfg->def_algo[ta->type] = NULL; 2682272840Smelifaro 2683272840Smelifaro free(ta, M_IPFW); 2684272840Smelifaro} 2685272840Smelifaro 2686272840Smelifaro/* 2687272840Smelifaro * Lists all table algorithms currently available. 2688272840Smelifaro * Data layout (v0)(current): 2689272840Smelifaro * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size 2690272840Smelifaro * Reply: [ ipfw_obj_lheader ipfw_ta_info x N ] 2691272840Smelifaro * 2692272840Smelifaro * Returns 0 on success 2693272840Smelifaro */ 2694272840Smelifarostatic int 2695272840Smelifarolist_table_algo(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 2696272840Smelifaro struct sockopt_data *sd) 2697272840Smelifaro{ 2698272840Smelifaro struct _ipfw_obj_lheader *olh; 2699272840Smelifaro struct tables_config *tcfg; 2700272840Smelifaro ipfw_ta_info *i; 2701272840Smelifaro struct table_algo *ta; 2702272840Smelifaro uint32_t count, n, size; 2703272840Smelifaro 2704272840Smelifaro olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh)); 2705272840Smelifaro if (olh == NULL) 2706272840Smelifaro return (EINVAL); 2707272840Smelifaro if (sd->valsize < olh->size) 2708272840Smelifaro return (EINVAL); 2709272840Smelifaro 2710272840Smelifaro IPFW_UH_RLOCK(ch); 2711272840Smelifaro tcfg = CHAIN_TO_TCFG(ch); 2712272840Smelifaro count = tcfg->algo_count; 2713272840Smelifaro size = count * sizeof(ipfw_ta_info) + sizeof(ipfw_obj_lheader); 2714272840Smelifaro 2715272840Smelifaro /* Fill in header regadless of buffer size */ 2716272840Smelifaro olh->count = count; 2717272840Smelifaro olh->objsize = sizeof(ipfw_ta_info); 2718272840Smelifaro 2719272840Smelifaro if (size > olh->size) { 2720272840Smelifaro olh->size = size; 2721272840Smelifaro IPFW_UH_RUNLOCK(ch); 2722272840Smelifaro return (ENOMEM); 2723232865Smelifaro } 2724272840Smelifaro olh->size = size; 2725232865Smelifaro 2726272840Smelifaro for (n = 1; n <= count; n++) { 2727272840Smelifaro i = (ipfw_ta_info *)ipfw_get_sopt_space(sd, sizeof(*i)); 2728298048Spfg KASSERT(i != NULL, ("previously checked buffer is not enough")); 2729272840Smelifaro ta = tcfg->algo[n]; 2730272840Smelifaro strlcpy(i->algoname, ta->name, sizeof(i->algoname)); 2731272840Smelifaro i->type = ta->type; 2732272840Smelifaro i->refcnt = ta->refcnt; 2733272840Smelifaro } 2734272840Smelifaro 2735272840Smelifaro IPFW_UH_RUNLOCK(ch); 2736272840Smelifaro 2737232865Smelifaro return (0); 2738232865Smelifaro} 2739232865Smelifaro 2740282070Smelifarostatic int 2741282070Smelifaroclassify_srcdst(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) 2742282070Smelifaro{ 2743282070Smelifaro /* Basic IPv4/IPv6 or u32 lookups */ 2744282070Smelifaro *puidx = cmd->arg1; 2745282070Smelifaro /* Assume ADDR by default */ 2746282070Smelifaro *ptype = IPFW_TABLE_ADDR; 2747282070Smelifaro int v; 2748282070Smelifaro 2749282070Smelifaro if (F_LEN(cmd) > F_INSN_SIZE(ipfw_insn_u32)) { 2750282070Smelifaro /* 2751282070Smelifaro * generic lookup. The key must be 2752282070Smelifaro * in 32bit big-endian format. 2753282070Smelifaro */ 2754282070Smelifaro v = ((ipfw_insn_u32 *)cmd)->d[1]; 2755282070Smelifaro switch (v) { 2756282070Smelifaro case 0: 2757282070Smelifaro case 1: 2758282070Smelifaro /* IPv4 src/dst */ 2759282070Smelifaro break; 2760282070Smelifaro case 2: 2761282070Smelifaro case 3: 2762282070Smelifaro /* src/dst port */ 2763282070Smelifaro *ptype = IPFW_TABLE_NUMBER; 2764282070Smelifaro break; 2765282070Smelifaro case 4: 2766282070Smelifaro /* uid/gid */ 2767282070Smelifaro *ptype = IPFW_TABLE_NUMBER; 2768282070Smelifaro break; 2769282070Smelifaro case 5: 2770282070Smelifaro /* jid */ 2771282070Smelifaro *ptype = IPFW_TABLE_NUMBER; 2772282070Smelifaro break; 2773282070Smelifaro case 6: 2774282070Smelifaro /* dscp */ 2775282070Smelifaro *ptype = IPFW_TABLE_NUMBER; 2776282070Smelifaro break; 2777282070Smelifaro } 2778282070Smelifaro } 2779272840Smelifaro 2780282070Smelifaro return (0); 2781282070Smelifaro} 2782282070Smelifaro 2783272840Smelifarostatic int 2784282070Smelifaroclassify_via(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) 2785272840Smelifaro{ 2786272840Smelifaro ipfw_insn_if *cmdif; 2787272840Smelifaro 2788282070Smelifaro /* Interface table, possibly */ 2789282070Smelifaro cmdif = (ipfw_insn_if *)cmd; 2790282070Smelifaro if (cmdif->name[0] != '\1') 2791282070Smelifaro return (1); 2792272840Smelifaro 2793282070Smelifaro *ptype = IPFW_TABLE_INTERFACE; 2794282070Smelifaro *puidx = cmdif->p.kidx; 2795272840Smelifaro 2796282070Smelifaro return (0); 2797282070Smelifaro} 2798272840Smelifaro 2799282070Smelifarostatic int 2800282070Smelifaroclassify_flow(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) 2801282070Smelifaro{ 2802282070Smelifaro 2803282070Smelifaro *puidx = cmd->arg1; 2804282070Smelifaro *ptype = IPFW_TABLE_FLOW; 2805282070Smelifaro 2806282070Smelifaro return (0); 2807272840Smelifaro} 2808272840Smelifaro 2809272840Smelifarostatic void 2810282070Smelifaroupdate_arg1(ipfw_insn *cmd, uint16_t idx) 2811272840Smelifaro{ 2812282070Smelifaro 2813282070Smelifaro cmd->arg1 = idx; 2814282070Smelifaro} 2815282070Smelifaro 2816282070Smelifarostatic void 2817282070Smelifaroupdate_via(ipfw_insn *cmd, uint16_t idx) 2818282070Smelifaro{ 2819272840Smelifaro ipfw_insn_if *cmdif; 2820272840Smelifaro 2821282070Smelifaro cmdif = (ipfw_insn_if *)cmd; 2822282070Smelifaro cmdif->p.kidx = idx; 2823272840Smelifaro} 2824272840Smelifaro 2825282070Smelifarostatic int 2826282070Smelifarotable_findbyname(struct ip_fw_chain *ch, struct tid_info *ti, 2827282070Smelifaro struct named_object **pno) 2828282070Smelifaro{ 2829282070Smelifaro struct table_config *tc; 2830282070Smelifaro int error; 2831282070Smelifaro 2832282070Smelifaro IPFW_UH_WLOCK_ASSERT(ch); 2833282070Smelifaro 2834282070Smelifaro error = find_table_err(CHAIN_TO_NI(ch), ti, &tc); 2835282070Smelifaro if (error != 0) 2836282070Smelifaro return (error); 2837282070Smelifaro 2838282070Smelifaro *pno = &tc->no; 2839282070Smelifaro return (0); 2840282070Smelifaro} 2841282070Smelifaro 2842282070Smelifaro/* XXX: sets-sets! */ 2843282070Smelifarostatic struct named_object * 2844282070Smelifarotable_findbykidx(struct ip_fw_chain *ch, uint16_t idx) 2845282070Smelifaro{ 2846282070Smelifaro struct namedobj_instance *ni; 2847282070Smelifaro struct table_config *tc; 2848282070Smelifaro 2849282070Smelifaro IPFW_UH_WLOCK_ASSERT(ch); 2850282070Smelifaro ni = CHAIN_TO_NI(ch); 2851282070Smelifaro tc = (struct table_config *)ipfw_objhash_lookup_kidx(ni, idx); 2852282070Smelifaro KASSERT(tc != NULL, ("Table with index %d not found", idx)); 2853282070Smelifaro 2854282070Smelifaro return (&tc->no); 2855282070Smelifaro} 2856282070Smelifaro 2857300021Saestatic int 2858300021Saetable_manage_sets(struct ip_fw_chain *ch, uint16_t set, uint8_t new_set, 2859300021Sae enum ipfw_sets_cmd cmd) 2860300021Sae{ 2861300021Sae 2862300021Sae switch (cmd) { 2863300021Sae case SWAP_ALL: 2864300021Sae case TEST_ALL: 2865306025Sae case MOVE_ALL: 2866300021Sae /* 2867306025Sae * Always return success, the real action and decision 2868306025Sae * should make table_manage_sets_all(). 2869300021Sae */ 2870306025Sae return (0); 2871300021Sae case TEST_ONE: 2872300021Sae case MOVE_ONE: 2873300021Sae /* 2874300021Sae * NOTE: we need to use ipfw_objhash_del/ipfw_objhash_add 2875300021Sae * if set number will be used in hash function. Currently 2876300021Sae * we can just use generic handler that replaces set value. 2877300021Sae */ 2878300021Sae if (V_fw_tables_sets == 0) 2879300021Sae return (0); 2880300021Sae break; 2881300021Sae case COUNT_ONE: 2882300021Sae /* 2883300021Sae * Return EOPNOTSUPP for COUNT_ONE when per-set sysctl is 2884300021Sae * disabled. This allow skip table's opcodes from additional 2885300021Sae * checks when specific rules moved to another set. 2886300021Sae */ 2887300021Sae if (V_fw_tables_sets == 0) 2888300021Sae return (EOPNOTSUPP); 2889300021Sae } 2890300021Sae /* Use generic sets handler when per-set sysctl is enabled. */ 2891300021Sae return (ipfw_obj_manage_sets(CHAIN_TO_NI(ch), IPFW_TLV_TBL_NAME, 2892300021Sae set, new_set, cmd)); 2893300021Sae} 2894300021Sae 2895306025Sae/* 2896306025Sae * We register several opcode rewriters for lookup tables. 2897306025Sae * All tables opcodes have the same ETLV type, but different subtype. 2898306025Sae * To avoid invoking sets handler several times for XXX_ALL commands, 2899306025Sae * we use separate manage_sets handler. O_RECV has the lowest value, 2900306025Sae * so it should be called first. 2901306025Sae */ 2902306025Saestatic int 2903306025Saetable_manage_sets_all(struct ip_fw_chain *ch, uint16_t set, uint8_t new_set, 2904306025Sae enum ipfw_sets_cmd cmd) 2905306025Sae{ 2906306025Sae 2907306025Sae switch (cmd) { 2908306025Sae case SWAP_ALL: 2909306025Sae case TEST_ALL: 2910306025Sae /* 2911306025Sae * Return success for TEST_ALL, since nothing prevents 2912306025Sae * move rules from one set to another. All tables are 2913306025Sae * accessible from all sets when per-set tables sysctl 2914306025Sae * is disabled. 2915306025Sae */ 2916306025Sae case MOVE_ALL: 2917306025Sae if (V_fw_tables_sets == 0) 2918306025Sae return (0); 2919306025Sae break; 2920306025Sae default: 2921306025Sae return (table_manage_sets(ch, set, new_set, cmd)); 2922306025Sae } 2923306025Sae /* Use generic sets handler when per-set sysctl is enabled. */ 2924306025Sae return (ipfw_obj_manage_sets(CHAIN_TO_NI(ch), IPFW_TLV_TBL_NAME, 2925306025Sae set, new_set, cmd)); 2926306025Sae} 2927306025Sae 2928282070Smelifarostatic struct opcode_obj_rewrite opcodes[] = { 2929282070Smelifaro { 2930300021Sae .opcode = O_IP_SRC_LOOKUP, 2931300021Sae .etlv = IPFW_TLV_TBL_NAME, 2932300021Sae .classifier = classify_srcdst, 2933300021Sae .update = update_arg1, 2934300021Sae .find_byname = table_findbyname, 2935300021Sae .find_bykidx = table_findbykidx, 2936300021Sae .create_object = create_table_compat, 2937300021Sae .manage_sets = table_manage_sets, 2938282070Smelifaro }, 2939282070Smelifaro { 2940300021Sae .opcode = O_IP_DST_LOOKUP, 2941300021Sae .etlv = IPFW_TLV_TBL_NAME, 2942300021Sae .classifier = classify_srcdst, 2943300021Sae .update = update_arg1, 2944300021Sae .find_byname = table_findbyname, 2945300021Sae .find_bykidx = table_findbykidx, 2946300021Sae .create_object = create_table_compat, 2947300021Sae .manage_sets = table_manage_sets, 2948282070Smelifaro }, 2949282070Smelifaro { 2950300021Sae .opcode = O_IP_FLOW_LOOKUP, 2951300021Sae .etlv = IPFW_TLV_TBL_NAME, 2952300021Sae .classifier = classify_flow, 2953300021Sae .update = update_arg1, 2954300021Sae .find_byname = table_findbyname, 2955300021Sae .find_bykidx = table_findbykidx, 2956300021Sae .create_object = create_table_compat, 2957300021Sae .manage_sets = table_manage_sets, 2958282070Smelifaro }, 2959282070Smelifaro { 2960300021Sae .opcode = O_XMIT, 2961300021Sae .etlv = IPFW_TLV_TBL_NAME, 2962300021Sae .classifier = classify_via, 2963300021Sae .update = update_via, 2964300021Sae .find_byname = table_findbyname, 2965300021Sae .find_bykidx = table_findbykidx, 2966300021Sae .create_object = create_table_compat, 2967300021Sae .manage_sets = table_manage_sets, 2968282070Smelifaro }, 2969282070Smelifaro { 2970300021Sae .opcode = O_RECV, 2971300021Sae .etlv = IPFW_TLV_TBL_NAME, 2972300021Sae .classifier = classify_via, 2973300021Sae .update = update_via, 2974300021Sae .find_byname = table_findbyname, 2975300021Sae .find_bykidx = table_findbykidx, 2976300021Sae .create_object = create_table_compat, 2977306025Sae .manage_sets = table_manage_sets_all, 2978282070Smelifaro }, 2979282070Smelifaro { 2980300021Sae .opcode = O_VIA, 2981300021Sae .etlv = IPFW_TLV_TBL_NAME, 2982300021Sae .classifier = classify_via, 2983300021Sae .update = update_via, 2984300021Sae .find_byname = table_findbyname, 2985300021Sae .find_bykidx = table_findbykidx, 2986300021Sae .create_object = create_table_compat, 2987300021Sae .manage_sets = table_manage_sets, 2988282070Smelifaro }, 2989282070Smelifaro}; 2990282070Smelifaro 2991300021Saestatic int 2992300021Saetest_sets_cb(struct namedobj_instance *ni __unused, struct named_object *no, 2993300021Sae void *arg __unused) 2994300021Sae{ 2995282070Smelifaro 2996300021Sae /* Check that there aren't any tables in not default set */ 2997300021Sae if (no->set != 0) 2998300021Sae return (EBUSY); 2999300021Sae return (0); 3000300021Sae} 3001300021Sae 3002272840Smelifaro/* 3003300021Sae * Switch between "set 0" and "rule's set" table binding, 3004300021Sae * Check all ruleset bindings and permits changing 3005300021Sae * IFF each binding has both rule AND table in default set (set 0). 3006300021Sae * 3007300021Sae * Returns 0 on success. 3008300021Sae */ 3009300021Saeint 3010300021Saeipfw_switch_tables_namespace(struct ip_fw_chain *ch, unsigned int sets) 3011300021Sae{ 3012300021Sae struct opcode_obj_rewrite *rw; 3013300021Sae struct namedobj_instance *ni; 3014300021Sae struct named_object *no; 3015300021Sae struct ip_fw *rule; 3016300021Sae ipfw_insn *cmd; 3017300021Sae int cmdlen, i, l; 3018300021Sae uint16_t kidx; 3019300021Sae uint8_t subtype; 3020300021Sae 3021300021Sae IPFW_UH_WLOCK(ch); 3022300021Sae 3023300021Sae if (V_fw_tables_sets == sets) { 3024300021Sae IPFW_UH_WUNLOCK(ch); 3025300021Sae return (0); 3026300021Sae } 3027300021Sae ni = CHAIN_TO_NI(ch); 3028300021Sae if (sets == 0) { 3029300021Sae /* 3030300021Sae * Prevent disabling sets support if we have some tables 3031300021Sae * in not default sets. 3032300021Sae */ 3033300021Sae if (ipfw_objhash_foreach_type(ni, test_sets_cb, 3034300021Sae NULL, IPFW_TLV_TBL_NAME) != 0) { 3035300021Sae IPFW_UH_WUNLOCK(ch); 3036300021Sae return (EBUSY); 3037300021Sae } 3038300021Sae } 3039300021Sae /* 3040300021Sae * Scan all rules and examine tables opcodes. 3041300021Sae */ 3042300021Sae for (i = 0; i < ch->n_rules; i++) { 3043300021Sae rule = ch->map[i]; 3044300021Sae 3045300021Sae l = rule->cmd_len; 3046300021Sae cmd = rule->cmd; 3047300021Sae cmdlen = 0; 3048300021Sae for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 3049300021Sae cmdlen = F_LEN(cmd); 3050300021Sae /* Check only tables opcodes */ 3051300021Sae for (kidx = 0, rw = opcodes; 3052300021Sae rw < opcodes + nitems(opcodes); rw++) { 3053300021Sae if (rw->opcode != cmd->opcode) 3054300021Sae continue; 3055300021Sae if (rw->classifier(cmd, &kidx, &subtype) == 0) 3056300021Sae break; 3057300021Sae } 3058300021Sae if (kidx == 0) 3059300021Sae continue; 3060300021Sae no = ipfw_objhash_lookup_kidx(ni, kidx); 3061300021Sae /* Check if both table object and rule has the set 0 */ 3062300021Sae if (no->set != 0 || rule->set != 0) { 3063300021Sae IPFW_UH_WUNLOCK(ch); 3064300021Sae return (EBUSY); 3065300021Sae } 3066300021Sae 3067300021Sae } 3068300021Sae } 3069300021Sae V_fw_tables_sets = sets; 3070300021Sae IPFW_UH_WUNLOCK(ch); 3071300021Sae return (0); 3072300021Sae} 3073300021Sae 3074300021Sae/* 3075272840Smelifaro * Checks table name for validity. 3076272840Smelifaro * Enforce basic length checks, the rest 3077272840Smelifaro * should be done in userland. 3078272840Smelifaro * 3079272840Smelifaro * Returns 0 if name is considered valid. 3080272840Smelifaro */ 3081290332Saestatic int 3082290332Saecheck_table_name(const char *name) 3083232865Smelifaro{ 3084232865Smelifaro 3085272840Smelifaro /* 3086272840Smelifaro * TODO: do some more complicated checks 3087272840Smelifaro */ 3088290332Sae return (ipfw_check_object_name_generic(name)); 3089232865Smelifaro} 3090232865Smelifaro 3091272840Smelifaro/* 3092272840Smelifaro * Finds table config based on either legacy index 3093272840Smelifaro * or name in ntlv. 3094272840Smelifaro * Note @ti structure contains unchecked data from userland. 3095272840Smelifaro * 3096282070Smelifaro * Returns 0 in success and fills in @tc with found config 3097272840Smelifaro */ 3098282070Smelifarostatic int 3099282070Smelifarofind_table_err(struct namedobj_instance *ni, struct tid_info *ti, 3100282070Smelifaro struct table_config **tc) 3101272840Smelifaro{ 3102272840Smelifaro char *name, bname[16]; 3103272840Smelifaro struct named_object *no; 3104272840Smelifaro ipfw_obj_ntlv *ntlv; 3105272840Smelifaro uint32_t set; 3106272840Smelifaro 3107272840Smelifaro if (ti->tlvs != NULL) { 3108299136Sae ntlv = ipfw_find_name_tlv_type(ti->tlvs, ti->tlen, ti->uidx, 3109299136Sae IPFW_TLV_TBL_NAME); 3110272840Smelifaro if (ntlv == NULL) 3111282070Smelifaro return (EINVAL); 3112272840Smelifaro name = ntlv->name; 3113272840Smelifaro 3114272840Smelifaro /* 3115272840Smelifaro * Use set provided by @ti instead of @ntlv one. 3116272840Smelifaro * This is needed due to different sets behavior 3117272840Smelifaro * controlled by V_fw_tables_sets. 3118272840Smelifaro */ 3119300021Sae set = (V_fw_tables_sets != 0) ? ti->set : 0; 3120272840Smelifaro } else { 3121272840Smelifaro snprintf(bname, sizeof(bname), "%d", ti->uidx); 3122272840Smelifaro name = bname; 3123272840Smelifaro set = 0; 3124272840Smelifaro } 3125272840Smelifaro 3126272840Smelifaro no = ipfw_objhash_lookup_name(ni, set, name); 3127282070Smelifaro *tc = (struct table_config *)no; 3128272840Smelifaro 3129282070Smelifaro return (0); 3130272840Smelifaro} 3131272840Smelifaro 3132272840Smelifaro/* 3133282070Smelifaro * Finds table config based on either legacy index 3134282070Smelifaro * or name in ntlv. 3135282070Smelifaro * Note @ti structure contains unchecked data from userland. 3136282070Smelifaro * 3137282070Smelifaro * Returns pointer to table_config or NULL. 3138282070Smelifaro */ 3139282070Smelifarostatic struct table_config * 3140282070Smelifarofind_table(struct namedobj_instance *ni, struct tid_info *ti) 3141282070Smelifaro{ 3142282070Smelifaro struct table_config *tc; 3143282070Smelifaro 3144282070Smelifaro if (find_table_err(ni, ti, &tc) != 0) 3145282070Smelifaro return (NULL); 3146282070Smelifaro 3147282070Smelifaro return (tc); 3148282070Smelifaro} 3149282070Smelifaro 3150282070Smelifaro/* 3151272840Smelifaro * Allocate new table config structure using 3152272840Smelifaro * specified @algo and @aname. 3153272840Smelifaro * 3154272840Smelifaro * Returns pointer to config or NULL. 3155272840Smelifaro */ 3156272840Smelifarostatic struct table_config * 3157272840Smelifaroalloc_table_config(struct ip_fw_chain *ch, struct tid_info *ti, 3158272840Smelifaro struct table_algo *ta, char *aname, uint8_t tflags) 3159272840Smelifaro{ 3160272840Smelifaro char *name, bname[16]; 3161272840Smelifaro struct table_config *tc; 3162272840Smelifaro int error; 3163272840Smelifaro ipfw_obj_ntlv *ntlv; 3164272840Smelifaro uint32_t set; 3165272840Smelifaro 3166272840Smelifaro if (ti->tlvs != NULL) { 3167299136Sae ntlv = ipfw_find_name_tlv_type(ti->tlvs, ti->tlen, ti->uidx, 3168299136Sae IPFW_TLV_TBL_NAME); 3169272840Smelifaro if (ntlv == NULL) 3170272840Smelifaro return (NULL); 3171272840Smelifaro name = ntlv->name; 3172332772Soleg set = (V_fw_tables_sets == 0) ? 0 : ntlv->set; 3173272840Smelifaro } else { 3174282070Smelifaro /* Compat part: convert number to string representation */ 3175272840Smelifaro snprintf(bname, sizeof(bname), "%d", ti->uidx); 3176272840Smelifaro name = bname; 3177272840Smelifaro set = 0; 3178272840Smelifaro } 3179272840Smelifaro 3180272840Smelifaro tc = malloc(sizeof(struct table_config), M_IPFW, M_WAITOK | M_ZERO); 3181272840Smelifaro tc->no.name = tc->tablename; 3182282070Smelifaro tc->no.subtype = ta->type; 3183272840Smelifaro tc->no.set = set; 3184272840Smelifaro tc->tflags = tflags; 3185272840Smelifaro tc->ta = ta; 3186272840Smelifaro strlcpy(tc->tablename, name, sizeof(tc->tablename)); 3187272840Smelifaro /* Set "shared" value type by default */ 3188272840Smelifaro tc->vshared = 1; 3189272840Smelifaro 3190272840Smelifaro /* Preallocate data structures for new tables */ 3191272840Smelifaro error = ta->init(ch, &tc->astate, &tc->ti_copy, aname, tflags); 3192272840Smelifaro if (error != 0) { 3193272840Smelifaro free(tc, M_IPFW); 3194272840Smelifaro return (NULL); 3195272840Smelifaro } 3196272840Smelifaro 3197272840Smelifaro return (tc); 3198272840Smelifaro} 3199272840Smelifaro 3200272840Smelifaro/* 3201272840Smelifaro * Destroys table state and config. 3202272840Smelifaro */ 3203272840Smelifarostatic void 3204272840Smelifarofree_table_config(struct namedobj_instance *ni, struct table_config *tc) 3205272840Smelifaro{ 3206272840Smelifaro 3207272840Smelifaro KASSERT(tc->linked == 0, ("free() on linked config")); 3208278259Smelifaro /* UH lock MUST NOT be held */ 3209272840Smelifaro 3210272840Smelifaro /* 3211272840Smelifaro * We're using ta without any locking/referencing. 3212272840Smelifaro * TODO: fix this if we're going to use unloadable algos. 3213272840Smelifaro */ 3214272840Smelifaro tc->ta->destroy(tc->astate, &tc->ti_copy); 3215272840Smelifaro free(tc, M_IPFW); 3216272840Smelifaro} 3217272840Smelifaro 3218272840Smelifaro/* 3219272840Smelifaro * Links @tc to @chain table named instance. 3220272840Smelifaro * Sets appropriate type/states in @chain table info. 3221272840Smelifaro */ 3222272840Smelifarostatic void 3223272840Smelifarolink_table(struct ip_fw_chain *ch, struct table_config *tc) 3224272840Smelifaro{ 3225272840Smelifaro struct namedobj_instance *ni; 3226272840Smelifaro struct table_info *ti; 3227272840Smelifaro uint16_t kidx; 3228272840Smelifaro 3229272840Smelifaro IPFW_UH_WLOCK_ASSERT(ch); 3230272840Smelifaro 3231272840Smelifaro ni = CHAIN_TO_NI(ch); 3232272840Smelifaro kidx = tc->no.kidx; 3233272840Smelifaro 3234272840Smelifaro ipfw_objhash_add(ni, &tc->no); 3235272840Smelifaro 3236272840Smelifaro ti = KIDX_TO_TI(ch, kidx); 3237272840Smelifaro *ti = tc->ti_copy; 3238272840Smelifaro 3239272840Smelifaro /* Notify algo on real @ti address */ 3240272840Smelifaro if (tc->ta->change_ti != NULL) 3241272840Smelifaro tc->ta->change_ti(tc->astate, ti); 3242272840Smelifaro 3243272840Smelifaro tc->linked = 1; 3244272840Smelifaro tc->ta->refcnt++; 3245272840Smelifaro} 3246272840Smelifaro 3247272840Smelifaro/* 3248272840Smelifaro * Unlinks @tc from @chain table named instance. 3249272840Smelifaro * Zeroes states in @chain and stores them in @tc. 3250272840Smelifaro */ 3251272840Smelifarostatic void 3252272840Smelifarounlink_table(struct ip_fw_chain *ch, struct table_config *tc) 3253272840Smelifaro{ 3254272840Smelifaro struct namedobj_instance *ni; 3255272840Smelifaro struct table_info *ti; 3256272840Smelifaro uint16_t kidx; 3257272840Smelifaro 3258272840Smelifaro IPFW_UH_WLOCK_ASSERT(ch); 3259272840Smelifaro IPFW_WLOCK_ASSERT(ch); 3260272840Smelifaro 3261272840Smelifaro ni = CHAIN_TO_NI(ch); 3262272840Smelifaro kidx = tc->no.kidx; 3263272840Smelifaro 3264272840Smelifaro /* Clear state. @ti copy is already saved inside @tc */ 3265272840Smelifaro ipfw_objhash_del(ni, &tc->no); 3266272840Smelifaro ti = KIDX_TO_TI(ch, kidx); 3267272840Smelifaro memset(ti, 0, sizeof(struct table_info)); 3268272840Smelifaro tc->linked = 0; 3269272840Smelifaro tc->ta->refcnt--; 3270272840Smelifaro 3271272840Smelifaro /* Notify algo on real @ti address */ 3272272840Smelifaro if (tc->ta->change_ti != NULL) 3273272840Smelifaro tc->ta->change_ti(tc->astate, NULL); 3274272840Smelifaro} 3275272840Smelifaro 3276272840Smelifarostatic struct ipfw_sopt_handler scodes[] = { 3277272840Smelifaro { IP_FW_TABLE_XCREATE, 0, HDIR_SET, create_table }, 3278272840Smelifaro { IP_FW_TABLE_XDESTROY, 0, HDIR_SET, flush_table_v0 }, 3279272840Smelifaro { IP_FW_TABLE_XFLUSH, 0, HDIR_SET, flush_table_v0 }, 3280272840Smelifaro { IP_FW_TABLE_XMODIFY, 0, HDIR_BOTH, modify_table }, 3281272840Smelifaro { IP_FW_TABLE_XINFO, 0, HDIR_GET, describe_table }, 3282272840Smelifaro { IP_FW_TABLES_XLIST, 0, HDIR_GET, list_tables }, 3283272840Smelifaro { IP_FW_TABLE_XLIST, 0, HDIR_GET, dump_table_v0 }, 3284272840Smelifaro { IP_FW_TABLE_XLIST, 1, HDIR_GET, dump_table_v1 }, 3285272840Smelifaro { IP_FW_TABLE_XADD, 0, HDIR_BOTH, manage_table_ent_v0 }, 3286272840Smelifaro { IP_FW_TABLE_XADD, 1, HDIR_BOTH, manage_table_ent_v1 }, 3287272840Smelifaro { IP_FW_TABLE_XDEL, 0, HDIR_BOTH, manage_table_ent_v0 }, 3288272840Smelifaro { IP_FW_TABLE_XDEL, 1, HDIR_BOTH, manage_table_ent_v1 }, 3289272840Smelifaro { IP_FW_TABLE_XFIND, 0, HDIR_GET, find_table_entry }, 3290272840Smelifaro { IP_FW_TABLE_XSWAP, 0, HDIR_SET, swap_table }, 3291272840Smelifaro { IP_FW_TABLES_ALIST, 0, HDIR_GET, list_table_algo }, 3292272840Smelifaro { IP_FW_TABLE_XGETSIZE, 0, HDIR_GET, get_table_size }, 3293272840Smelifaro}; 3294272840Smelifaro 3295299152Saestatic int 3296272840Smelifarodestroy_table_locked(struct namedobj_instance *ni, struct named_object *no, 3297272840Smelifaro void *arg) 3298272840Smelifaro{ 3299272840Smelifaro 3300272840Smelifaro unlink_table((struct ip_fw_chain *)arg, (struct table_config *)no); 3301272840Smelifaro if (ipfw_objhash_free_idx(ni, no->kidx) != 0) 3302272840Smelifaro printf("Error unlinking kidx %d from table %s\n", 3303272840Smelifaro no->kidx, no->name); 3304272840Smelifaro free_table_config(ni, (struct table_config *)no); 3305299152Sae return (0); 3306272840Smelifaro} 3307272840Smelifaro 3308272840Smelifaro/* 3309272840Smelifaro * Shuts tables module down. 3310272840Smelifaro */ 3311272840Smelifarovoid 3312272840Smelifaroipfw_destroy_tables(struct ip_fw_chain *ch, int last) 3313272840Smelifaro{ 3314272840Smelifaro 3315272840Smelifaro IPFW_DEL_SOPT_HANDLER(last, scodes); 3316282070Smelifaro IPFW_DEL_OBJ_REWRITER(last, opcodes); 3317272840Smelifaro 3318272840Smelifaro /* Remove all tables from working set */ 3319272840Smelifaro IPFW_UH_WLOCK(ch); 3320272840Smelifaro IPFW_WLOCK(ch); 3321272840Smelifaro ipfw_objhash_foreach(CHAIN_TO_NI(ch), destroy_table_locked, ch); 3322272840Smelifaro IPFW_WUNLOCK(ch); 3323272840Smelifaro IPFW_UH_WUNLOCK(ch); 3324272840Smelifaro 3325272840Smelifaro /* Free pointers itself */ 3326272840Smelifaro free(ch->tablestate, M_IPFW); 3327272840Smelifaro 3328272840Smelifaro ipfw_table_value_destroy(ch, last); 3329272840Smelifaro ipfw_table_algo_destroy(ch); 3330272840Smelifaro 3331272840Smelifaro ipfw_objhash_destroy(CHAIN_TO_NI(ch)); 3332272840Smelifaro free(CHAIN_TO_TCFG(ch), M_IPFW); 3333272840Smelifaro} 3334272840Smelifaro 3335272840Smelifaro/* 3336272840Smelifaro * Starts tables module. 3337272840Smelifaro */ 3338272840Smelifaroint 3339272840Smelifaroipfw_init_tables(struct ip_fw_chain *ch, int first) 3340272840Smelifaro{ 3341272840Smelifaro struct tables_config *tcfg; 3342272840Smelifaro 3343272840Smelifaro /* Allocate pointers */ 3344272840Smelifaro ch->tablestate = malloc(V_fw_tables_max * sizeof(struct table_info), 3345272840Smelifaro M_IPFW, M_WAITOK | M_ZERO); 3346272840Smelifaro 3347272840Smelifaro tcfg = malloc(sizeof(struct tables_config), M_IPFW, M_WAITOK | M_ZERO); 3348272840Smelifaro tcfg->namehash = ipfw_objhash_create(V_fw_tables_max); 3349272840Smelifaro ch->tblcfg = tcfg; 3350272840Smelifaro 3351272840Smelifaro ipfw_table_value_init(ch, first); 3352272840Smelifaro ipfw_table_algo_init(ch); 3353272840Smelifaro 3354282070Smelifaro IPFW_ADD_OBJ_REWRITER(first, opcodes); 3355272840Smelifaro IPFW_ADD_SOPT_HANDLER(first, scodes); 3356272840Smelifaro return (0); 3357272840Smelifaro} 3358272840Smelifaro 3359272840Smelifaro 3360272840Smelifaro 3361