ip_fw_table.c revision 278259
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: head/sys/netpfil/ipfw/ip_fw_table.c 278259 2015-02-05 13:49:04Z melifaro $"); 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 */ 56200590Sluigi 57200590Sluigi#include <netinet/in.h> 58201732Sluigi#include <netinet/ip_var.h> /* struct ipfw_rule_ref */ 59200590Sluigi#include <netinet/ip_fw.h> 60200590Sluigi 61240494Sglebius#include <netpfil/ipfw/ip_fw_private.h> 62272840Smelifaro#include <netpfil/ipfw/ip_fw_table.h> 63240494Sglebius 64272840Smelifaro /* 65272840Smelifaro * Table has the following `type` concepts: 66272840Smelifaro * 67272840Smelifaro * `no.type` represents lookup key type (addr, ifp, uid, etc..) 68272840Smelifaro * vmask represents bitmask of table values which are present at the moment. 69272840Smelifaro * Special IPFW_VTYPE_LEGACY ( (uint32_t)-1 ) represents old 70272840Smelifaro * single-value-for-all approach. 71272840Smelifaro */ 72272840Smelifarostruct table_config { 73272840Smelifaro struct named_object no; 74272840Smelifaro uint8_t tflags; /* type flags */ 75272840Smelifaro uint8_t locked; /* 1 if locked from changes */ 76272840Smelifaro uint8_t linked; /* 1 if already linked */ 77272840Smelifaro uint8_t ochanged; /* used by set swapping */ 78272840Smelifaro uint8_t vshared; /* 1 if using shared value array */ 79272840Smelifaro uint8_t spare[3]; 80272840Smelifaro uint32_t count; /* Number of records */ 81272840Smelifaro uint32_t limit; /* Max number of records */ 82272840Smelifaro uint32_t vmask; /* bitmask with supported values */ 83272840Smelifaro uint32_t ocount; /* used by set swapping */ 84272840Smelifaro uint64_t gencnt; /* generation count */ 85272840Smelifaro char tablename[64]; /* table name */ 86272840Smelifaro struct table_algo *ta; /* Callbacks for given algo */ 87272840Smelifaro void *astate; /* algorithm state */ 88272840Smelifaro struct table_info ti_copy; /* data to put to table_info */ 89272840Smelifaro struct namedobj_instance *vi; 90272840Smelifaro}; 91200590Sluigi 92272840Smelifarostatic struct table_config *find_table(struct namedobj_instance *ni, 93272840Smelifaro struct tid_info *ti); 94272840Smelifarostatic struct table_config *alloc_table_config(struct ip_fw_chain *ch, 95272840Smelifaro struct tid_info *ti, struct table_algo *ta, char *adata, uint8_t tflags); 96272840Smelifarostatic void free_table_config(struct namedobj_instance *ni, 97272840Smelifaro struct table_config *tc); 98272840Smelifarostatic int create_table_internal(struct ip_fw_chain *ch, struct tid_info *ti, 99272840Smelifaro char *aname, ipfw_xtable_info *i, uint16_t *pkidx, int ref); 100272840Smelifarostatic void link_table(struct ip_fw_chain *ch, struct table_config *tc); 101272840Smelifarostatic void unlink_table(struct ip_fw_chain *ch, struct table_config *tc); 102272840Smelifarostatic int find_ref_table(struct ip_fw_chain *ch, struct tid_info *ti, 103272840Smelifaro struct tentry_info *tei, uint32_t count, int op, struct table_config **ptc); 104272840Smelifaro#define OP_ADD 1 105272840Smelifaro#define OP_DEL 0 106272840Smelifarostatic int export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh, 107272840Smelifaro struct sockopt_data *sd); 108272840Smelifarostatic void export_table_info(struct ip_fw_chain *ch, struct table_config *tc, 109272840Smelifaro ipfw_xtable_info *i); 110272840Smelifarostatic int dump_table_tentry(void *e, void *arg); 111272840Smelifarostatic int dump_table_xentry(void *e, void *arg); 112200590Sluigi 113272840Smelifarostatic int swap_tables(struct ip_fw_chain *ch, struct tid_info *a, 114272840Smelifaro struct tid_info *b); 115200590Sluigi 116272840Smelifarostatic int check_table_space(struct ip_fw_chain *ch, struct tableop_state *ts, 117272840Smelifaro struct table_config *tc, struct table_info *ti, uint32_t count); 118272840Smelifarostatic int destroy_table(struct ip_fw_chain *ch, struct tid_info *ti); 119232865Smelifaro 120272840Smelifarostatic struct table_algo *find_table_algo(struct tables_config *tableconf, 121272840Smelifaro struct tid_info *ti, char *name); 122232865Smelifaro 123272840Smelifarostatic void objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti); 124272840Smelifarostatic void ntlv_to_ti(struct _ipfw_obj_ntlv *ntlv, struct tid_info *ti); 125272840Smelifarostatic int classify_table_opcode(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype); 126272840Smelifaro 127272840Smelifaro#define CHAIN_TO_NI(chain) (CHAIN_TO_TCFG(chain)->namehash) 128272840Smelifaro#define KIDX_TO_TI(ch, k) (&(((struct table_info *)(ch)->tablestate)[k])) 129272840Smelifaro 130272840Smelifaro#define TA_BUF_SZ 128 /* On-stack buffer for add/delete state */ 131272840Smelifaro 132272840Smelifarovoid 133272840Smelifarorollback_toperation_state(struct ip_fw_chain *ch, void *object) 134272840Smelifaro{ 135272840Smelifaro struct tables_config *tcfg; 136272840Smelifaro struct op_state *os; 137272840Smelifaro 138272840Smelifaro tcfg = CHAIN_TO_TCFG(ch); 139272840Smelifaro TAILQ_FOREACH(os, &tcfg->state_list, next) 140272840Smelifaro os->func(object, os); 141272840Smelifaro} 142272840Smelifaro 143272840Smelifarovoid 144272840Smelifaroadd_toperation_state(struct ip_fw_chain *ch, struct tableop_state *ts) 145272840Smelifaro{ 146272840Smelifaro struct tables_config *tcfg; 147272840Smelifaro 148272840Smelifaro tcfg = CHAIN_TO_TCFG(ch); 149272840Smelifaro TAILQ_INSERT_HEAD(&tcfg->state_list, &ts->opstate, next); 150272840Smelifaro} 151272840Smelifaro 152272840Smelifarovoid 153272840Smelifarodel_toperation_state(struct ip_fw_chain *ch, struct tableop_state *ts) 154272840Smelifaro{ 155272840Smelifaro struct tables_config *tcfg; 156272840Smelifaro 157272840Smelifaro tcfg = CHAIN_TO_TCFG(ch); 158272840Smelifaro TAILQ_REMOVE(&tcfg->state_list, &ts->opstate, next); 159272840Smelifaro} 160272840Smelifaro 161272840Smelifarovoid 162272840Smelifarotc_ref(struct table_config *tc) 163272840Smelifaro{ 164272840Smelifaro 165272840Smelifaro tc->no.refcnt++; 166272840Smelifaro} 167272840Smelifaro 168272840Smelifarovoid 169272840Smelifarotc_unref(struct table_config *tc) 170272840Smelifaro{ 171272840Smelifaro 172272840Smelifaro tc->no.refcnt--; 173272840Smelifaro} 174272840Smelifaro 175272840Smelifarostatic struct table_value * 176272840Smelifaroget_table_value(struct ip_fw_chain *ch, struct table_config *tc, uint32_t kidx) 177272840Smelifaro{ 178272840Smelifaro struct table_value *pval; 179272840Smelifaro 180272840Smelifaro pval = (struct table_value *)ch->valuestate; 181272840Smelifaro 182272840Smelifaro return (&pval[kidx]); 183272840Smelifaro} 184272840Smelifaro 185272840Smelifaro 186201120Sluigi/* 187272840Smelifaro * Checks if we're able to insert/update entry @tei into table 188272840Smelifaro * w.r.t @tc limits. 189272840Smelifaro * May alter @tei to indicate insertion error / insert 190272840Smelifaro * options. 191272840Smelifaro * 192272840Smelifaro * Returns 0 if operation can be performed/ 193201120Sluigi */ 194272840Smelifarostatic int 195272840Smelifarocheck_table_limit(struct table_config *tc, struct tentry_info *tei) 196272840Smelifaro{ 197272840Smelifaro 198272840Smelifaro if (tc->limit == 0 || tc->count < tc->limit) 199272840Smelifaro return (0); 200272840Smelifaro 201272840Smelifaro if ((tei->flags & TEI_FLAGS_UPDATE) == 0) { 202272840Smelifaro /* Notify userland on error cause */ 203272840Smelifaro tei->flags |= TEI_FLAGS_LIMIT; 204272840Smelifaro return (EFBIG); 205272840Smelifaro } 206272840Smelifaro 207272840Smelifaro /* 208272840Smelifaro * We have UPDATE flag set. 209272840Smelifaro * Permit updating record (if found), 210272840Smelifaro * but restrict adding new one since we've 211272840Smelifaro * already hit the limit. 212272840Smelifaro */ 213272840Smelifaro tei->flags |= TEI_FLAGS_DONTADD; 214272840Smelifaro 215272840Smelifaro return (0); 216272840Smelifaro} 217272840Smelifaro 218232865Smelifaro/* 219272840Smelifaro * Convert algorithm callback return code into 220272840Smelifaro * one of pre-defined states known by userland. 221232865Smelifaro */ 222272840Smelifarostatic void 223272840Smelifarostore_tei_result(struct tentry_info *tei, int op, int error, uint32_t num) 224272840Smelifaro{ 225272840Smelifaro int flag; 226201120Sluigi 227272840Smelifaro flag = 0; 228232865Smelifaro 229272840Smelifaro switch (error) { 230272840Smelifaro case 0: 231272840Smelifaro if (op == OP_ADD && num != 0) 232272840Smelifaro flag = TEI_FLAGS_ADDED; 233272840Smelifaro if (op == OP_DEL) 234272840Smelifaro flag = TEI_FLAGS_DELETED; 235272840Smelifaro break; 236272840Smelifaro case ENOENT: 237272840Smelifaro flag = TEI_FLAGS_NOTFOUND; 238272840Smelifaro break; 239272840Smelifaro case EEXIST: 240272840Smelifaro flag = TEI_FLAGS_EXISTS; 241272840Smelifaro break; 242272840Smelifaro default: 243272840Smelifaro flag = TEI_FLAGS_ERROR; 244272840Smelifaro } 245232865Smelifaro 246272840Smelifaro tei->flags |= flag; 247272840Smelifaro} 248272840Smelifaro 249272840Smelifaro/* 250272840Smelifaro * Creates and references table with default parameters. 251272840Smelifaro * Saves table config, algo and allocated kidx info @ptc, @pta and 252272840Smelifaro * @pkidx if non-zero. 253272840Smelifaro * Used for table auto-creation to support old binaries. 254272840Smelifaro * 255272840Smelifaro * Returns 0 on success. 256272840Smelifaro */ 257272840Smelifarostatic int 258272840Smelifarocreate_table_compat(struct ip_fw_chain *ch, struct tid_info *ti, 259272840Smelifaro uint16_t *pkidx) 260232865Smelifaro{ 261272840Smelifaro ipfw_xtable_info xi; 262272840Smelifaro int error; 263232865Smelifaro 264272840Smelifaro memset(&xi, 0, sizeof(xi)); 265272840Smelifaro /* Set default value mask for legacy clients */ 266272840Smelifaro xi.vmask = IPFW_VTYPE_LEGACY; 267272840Smelifaro 268272840Smelifaro error = create_table_internal(ch, ti, NULL, &xi, pkidx, 1); 269272840Smelifaro if (error != 0) 270272840Smelifaro return (error); 271272840Smelifaro 272272840Smelifaro return (0); 273232865Smelifaro} 274232865Smelifaro 275272840Smelifaro/* 276272840Smelifaro * Find and reference existing table optionally 277272840Smelifaro * creating new one. 278272840Smelifaro * 279272840Smelifaro * Saves found table config into @ptc. 280272840Smelifaro * Note function may drop/acquire UH_WLOCK. 281272840Smelifaro * Returns 0 if table was found/created and referenced 282272840Smelifaro * or non-zero return code. 283272840Smelifaro */ 284272840Smelifarostatic int 285272840Smelifarofind_ref_table(struct ip_fw_chain *ch, struct tid_info *ti, 286272840Smelifaro struct tentry_info *tei, uint32_t count, int op, 287272840Smelifaro struct table_config **ptc) 288200590Sluigi{ 289272840Smelifaro struct namedobj_instance *ni; 290272840Smelifaro struct table_config *tc; 291272840Smelifaro uint16_t kidx; 292272840Smelifaro int error; 293200590Sluigi 294272840Smelifaro IPFW_UH_WLOCK_ASSERT(ch); 295232865Smelifaro 296272840Smelifaro ni = CHAIN_TO_NI(ch); 297272840Smelifaro tc = NULL; 298272840Smelifaro if ((tc = find_table(ni, ti)) != NULL) { 299272840Smelifaro /* check table type */ 300272840Smelifaro if (tc->no.type != ti->type) 301232865Smelifaro return (EINVAL); 302272840Smelifaro 303272840Smelifaro if (tc->locked != 0) 304272840Smelifaro return (EACCES); 305272840Smelifaro 306272840Smelifaro /* Try to exit early on limit hit */ 307272840Smelifaro if (op == OP_ADD && count == 1 && 308272840Smelifaro check_table_limit(tc, tei) != 0) 309272840Smelifaro return (EFBIG); 310272840Smelifaro 311272840Smelifaro /* Reference and return */ 312272840Smelifaro tc->no.refcnt++; 313272840Smelifaro *ptc = tc; 314272840Smelifaro return (0); 315272840Smelifaro } 316272840Smelifaro 317272840Smelifaro if (op == OP_DEL) 318272840Smelifaro return (ESRCH); 319272840Smelifaro 320272840Smelifaro /* Compability mode: create new table for old clients */ 321272840Smelifaro if ((tei->flags & TEI_FLAGS_COMPAT) == 0) 322272840Smelifaro return (ESRCH); 323272840Smelifaro 324272840Smelifaro IPFW_UH_WUNLOCK(ch); 325272840Smelifaro error = create_table_compat(ch, ti, &kidx); 326272840Smelifaro IPFW_UH_WLOCK(ch); 327272840Smelifaro 328272840Smelifaro if (error != 0) 329272840Smelifaro return (error); 330272840Smelifaro 331272840Smelifaro tc = (struct table_config *)ipfw_objhash_lookup_kidx(ni, kidx); 332272840Smelifaro KASSERT(tc != NULL, ("create_table_compat returned bad idx %d", kidx)); 333272840Smelifaro 334272840Smelifaro /* OK, now we've got referenced table. */ 335272840Smelifaro *ptc = tc; 336272840Smelifaro return (0); 337272840Smelifaro} 338272840Smelifaro 339272840Smelifaro/* 340272840Smelifaro * Rolls back already @added to @tc entries using state array @ta_buf_m. 341272840Smelifaro * Assume the following layout: 342272840Smelifaro * 1) ADD state (ta_buf_m[0] ... t_buf_m[added - 1]) for handling update cases 343272840Smelifaro * 2) DEL state (ta_buf_m[count[ ... t_buf_m[count + added - 1]) 344272840Smelifaro * for storing deleted state 345272840Smelifaro */ 346272840Smelifarostatic void 347272840Smelifarorollback_added_entries(struct ip_fw_chain *ch, struct table_config *tc, 348272840Smelifaro struct table_info *tinfo, struct tentry_info *tei, caddr_t ta_buf_m, 349272840Smelifaro uint32_t count, uint32_t added) 350272840Smelifaro{ 351272840Smelifaro struct table_algo *ta; 352272840Smelifaro struct tentry_info *ptei; 353272840Smelifaro caddr_t v, vv; 354272840Smelifaro size_t ta_buf_sz; 355272840Smelifaro int error, i; 356272840Smelifaro uint32_t num; 357272840Smelifaro 358272840Smelifaro IPFW_UH_WLOCK_ASSERT(ch); 359272840Smelifaro 360272840Smelifaro ta = tc->ta; 361272840Smelifaro ta_buf_sz = ta->ta_buf_size; 362272840Smelifaro v = ta_buf_m; 363272840Smelifaro vv = v + count * ta_buf_sz; 364272840Smelifaro for (i = 0; i < added; i++, v += ta_buf_sz, vv += ta_buf_sz) { 365272840Smelifaro ptei = &tei[i]; 366272840Smelifaro if ((ptei->flags & TEI_FLAGS_UPDATED) != 0) { 367272840Smelifaro 368272840Smelifaro /* 369272840Smelifaro * We have old value stored by previous 370272840Smelifaro * call in @ptei->value. Do add once again 371272840Smelifaro * to restore it. 372272840Smelifaro */ 373272840Smelifaro error = ta->add(tc->astate, tinfo, ptei, v, &num); 374272840Smelifaro KASSERT(error == 0, ("rollback UPDATE fail")); 375272840Smelifaro KASSERT(num == 0, ("rollback UPDATE fail2")); 376272840Smelifaro continue; 377232865Smelifaro } 378272840Smelifaro 379272840Smelifaro error = ta->prepare_del(ch, ptei, vv); 380272840Smelifaro KASSERT(error == 0, ("pre-rollback INSERT failed")); 381272840Smelifaro error = ta->del(tc->astate, tinfo, ptei, vv, &num); 382272840Smelifaro KASSERT(error == 0, ("rollback INSERT failed")); 383272840Smelifaro tc->count -= num; 384272840Smelifaro } 385272840Smelifaro} 386272840Smelifaro 387272840Smelifaro/* 388272840Smelifaro * Prepares add/del state for all @count entries in @tei. 389272840Smelifaro * Uses either stack buffer (@ta_buf) or allocates a new one. 390272840Smelifaro * Stores pointer to allocated buffer back to @ta_buf. 391272840Smelifaro * 392272840Smelifaro * Returns 0 on success. 393272840Smelifaro */ 394272840Smelifarostatic int 395272840Smelifaroprepare_batch_buffer(struct ip_fw_chain *ch, struct table_algo *ta, 396272840Smelifaro struct tentry_info *tei, uint32_t count, int op, caddr_t *ta_buf) 397272840Smelifaro{ 398272840Smelifaro caddr_t ta_buf_m, v; 399272840Smelifaro size_t ta_buf_sz, sz; 400272840Smelifaro struct tentry_info *ptei; 401272840Smelifaro int error, i; 402272840Smelifaro 403272840Smelifaro error = 0; 404272840Smelifaro ta_buf_sz = ta->ta_buf_size; 405272840Smelifaro if (count == 1) { 406272840Smelifaro /* Sigle add/delete, use on-stack buffer */ 407272840Smelifaro memset(*ta_buf, 0, TA_BUF_SZ); 408272840Smelifaro ta_buf_m = *ta_buf; 409272840Smelifaro } else { 410272840Smelifaro 411272840Smelifaro /* 412272840Smelifaro * Multiple adds/deletes, allocate larger buffer 413272840Smelifaro * 414272840Smelifaro * Note we need 2xcount buffer for add case: 415272840Smelifaro * we have hold both ADD state 416272840Smelifaro * and DELETE state (this may be needed 417272840Smelifaro * if we need to rollback all changes) 418272840Smelifaro */ 419272840Smelifaro sz = count * ta_buf_sz; 420272840Smelifaro ta_buf_m = malloc((op == OP_ADD) ? sz * 2 : sz, M_TEMP, 421272840Smelifaro M_WAITOK | M_ZERO); 422272840Smelifaro } 423272840Smelifaro 424272840Smelifaro v = ta_buf_m; 425272840Smelifaro for (i = 0; i < count; i++, v += ta_buf_sz) { 426272840Smelifaro ptei = &tei[i]; 427272840Smelifaro error = (op == OP_ADD) ? 428272840Smelifaro ta->prepare_add(ch, ptei, v) : ta->prepare_del(ch, ptei, v); 429272840Smelifaro 430272840Smelifaro /* 431272840Smelifaro * Some syntax error (incorrect mask, or address, or 432272840Smelifaro * anything). Return error regardless of atomicity 433272840Smelifaro * settings. 434272840Smelifaro */ 435272840Smelifaro if (error != 0) 436272840Smelifaro break; 437272840Smelifaro } 438272840Smelifaro 439272840Smelifaro *ta_buf = ta_buf_m; 440272840Smelifaro return (error); 441272840Smelifaro} 442272840Smelifaro 443272840Smelifaro/* 444272840Smelifaro * Flushes allocated state for each @count entries in @tei. 445272840Smelifaro * Frees @ta_buf_m if differs from stack buffer @ta_buf. 446272840Smelifaro */ 447272840Smelifarostatic void 448272840Smelifaroflush_batch_buffer(struct ip_fw_chain *ch, struct table_algo *ta, 449272840Smelifaro struct tentry_info *tei, uint32_t count, int rollback, 450272840Smelifaro caddr_t ta_buf_m, caddr_t ta_buf) 451272840Smelifaro{ 452272840Smelifaro caddr_t v; 453272840Smelifaro struct tentry_info *ptei; 454272840Smelifaro size_t ta_buf_sz; 455272840Smelifaro int i; 456272840Smelifaro 457272840Smelifaro ta_buf_sz = ta->ta_buf_size; 458272840Smelifaro 459272840Smelifaro /* Run cleaning callback anyway */ 460272840Smelifaro v = ta_buf_m; 461272840Smelifaro for (i = 0; i < count; i++, v += ta_buf_sz) { 462272840Smelifaro ptei = &tei[i]; 463272840Smelifaro ta->flush_entry(ch, ptei, v); 464272840Smelifaro if (ptei->ptv != NULL) { 465272840Smelifaro free(ptei->ptv, M_IPFW); 466272840Smelifaro ptei->ptv = NULL; 467272840Smelifaro } 468272840Smelifaro } 469272840Smelifaro 470272840Smelifaro /* Clean up "deleted" state in case of rollback */ 471272840Smelifaro if (rollback != 0) { 472272840Smelifaro v = ta_buf_m + count * ta_buf_sz; 473272840Smelifaro for (i = 0; i < count; i++, v += ta_buf_sz) 474272840Smelifaro ta->flush_entry(ch, &tei[i], v); 475272840Smelifaro } 476272840Smelifaro 477272840Smelifaro if (ta_buf_m != ta_buf) 478272840Smelifaro free(ta_buf_m, M_TEMP); 479272840Smelifaro} 480272840Smelifaro 481272840Smelifaro 482272840Smelifarostatic void 483272840Smelifarorollback_add_entry(void *object, struct op_state *_state) 484272840Smelifaro{ 485272840Smelifaro struct ip_fw_chain *ch; 486272840Smelifaro struct tableop_state *ts; 487272840Smelifaro 488272840Smelifaro ts = (struct tableop_state *)_state; 489272840Smelifaro 490272840Smelifaro if (ts->tc != object && ts->ch != object) 491272840Smelifaro return; 492272840Smelifaro 493272840Smelifaro ch = ts->ch; 494272840Smelifaro 495272840Smelifaro IPFW_UH_WLOCK_ASSERT(ch); 496272840Smelifaro 497272840Smelifaro /* Call specifid unlockers */ 498272840Smelifaro rollback_table_values(ts); 499272840Smelifaro 500272840Smelifaro /* Indicate we've called */ 501272840Smelifaro ts->modified = 1; 502272840Smelifaro} 503272840Smelifaro 504272840Smelifaro/* 505272840Smelifaro * Adds/updates one or more entries in table @ti. 506272840Smelifaro * 507272840Smelifaro * Function may drop/reacquire UH wlock multiple times due to 508272840Smelifaro * items alloc, algorithm callbacks (check_space), value linkage 509272840Smelifaro * (new values, value storage realloc), etc.. 510272840Smelifaro * Other processes like other adds (which may involve storage resize), 511272840Smelifaro * table swaps (which changes table data and may change algo type), 512272840Smelifaro * table modify (which may change value mask) may be executed 513272840Smelifaro * simultaneously so we need to deal with it. 514272840Smelifaro * 515272840Smelifaro * The following approach was implemented: 516272840Smelifaro * we have per-chain linked list, protected with UH lock. 517272840Smelifaro * add_table_entry prepares special on-stack structure wthich is passed 518272840Smelifaro * to its descendants. Users add this structure to this list before unlock. 519272840Smelifaro * After performing needed operations and acquiring UH lock back, each user 520272840Smelifaro * checks if structure has changed. If true, it rolls local state back and 521272840Smelifaro * returns without error to the caller. 522272840Smelifaro * add_table_entry() on its own checks if structure has changed and restarts 523272840Smelifaro * its operation from the beginning (goto restart). 524272840Smelifaro * 525272840Smelifaro * Functions which are modifying fields of interest (currently 526272840Smelifaro * resize_shared_value_storage() and swap_tables() ) 527272840Smelifaro * traverses given list while holding UH lock immediately before 528272840Smelifaro * performing their operations calling function provided be list entry 529272840Smelifaro * ( currently rollback_add_entry ) which performs rollback for all necessary 530272840Smelifaro * state and sets appropriate values in structure indicating rollback 531272840Smelifaro * has happened. 532272840Smelifaro * 533272840Smelifaro * Algo interaction: 534272840Smelifaro * Function references @ti first to ensure table won't 535272840Smelifaro * disappear or change its type. 536272840Smelifaro * After that, prepare_add callback is called for each @tei entry. 537272840Smelifaro * Next, we try to add each entry under UH+WHLOCK 538272840Smelifaro * using add() callback. 539272840Smelifaro * Finally, we free all state by calling flush_entry callback 540272840Smelifaro * for each @tei. 541272840Smelifaro * 542272840Smelifaro * Returns 0 on success. 543272840Smelifaro */ 544272840Smelifaroint 545272840Smelifaroadd_table_entry(struct ip_fw_chain *ch, struct tid_info *ti, 546272840Smelifaro struct tentry_info *tei, uint8_t flags, uint32_t count) 547272840Smelifaro{ 548272840Smelifaro struct table_config *tc; 549272840Smelifaro struct table_algo *ta; 550272840Smelifaro uint16_t kidx; 551272840Smelifaro int error, first_error, i, rollback; 552272840Smelifaro uint32_t num, numadd; 553272840Smelifaro struct tentry_info *ptei; 554272840Smelifaro struct tableop_state ts; 555272840Smelifaro char ta_buf[TA_BUF_SZ]; 556272840Smelifaro caddr_t ta_buf_m, v; 557272840Smelifaro 558272840Smelifaro memset(&ts, 0, sizeof(ts)); 559272840Smelifaro ta = NULL; 560272840Smelifaro IPFW_UH_WLOCK(ch); 561272840Smelifaro 562272840Smelifaro /* 563272840Smelifaro * Find and reference existing table. 564272840Smelifaro */ 565272840Smelifarorestart: 566272840Smelifaro if (ts.modified != 0) { 567272840Smelifaro IPFW_UH_WUNLOCK(ch); 568272840Smelifaro flush_batch_buffer(ch, ta, tei, count, rollback, 569272840Smelifaro ta_buf_m, ta_buf); 570272840Smelifaro memset(&ts, 0, sizeof(ts)); 571272840Smelifaro ta = NULL; 572272840Smelifaro IPFW_UH_WLOCK(ch); 573272840Smelifaro } 574272840Smelifaro 575272840Smelifaro error = find_ref_table(ch, ti, tei, count, OP_ADD, &tc); 576272840Smelifaro if (error != 0) { 577272840Smelifaro IPFW_UH_WUNLOCK(ch); 578272840Smelifaro return (error); 579272840Smelifaro } 580272840Smelifaro ta = tc->ta; 581272840Smelifaro 582272840Smelifaro /* Fill in tablestate */ 583272840Smelifaro ts.ch = ch; 584272840Smelifaro ts.opstate.func = rollback_add_entry; 585272840Smelifaro ts.tc = tc; 586272840Smelifaro ts.vshared = tc->vshared; 587272840Smelifaro ts.vmask = tc->vmask; 588272840Smelifaro ts.ta = ta; 589272840Smelifaro ts.tei = tei; 590272840Smelifaro ts.count = count; 591272840Smelifaro rollback = 0; 592272840Smelifaro add_toperation_state(ch, &ts); 593272840Smelifaro IPFW_UH_WUNLOCK(ch); 594272840Smelifaro 595272840Smelifaro /* Allocate memory and prepare record(s) */ 596272840Smelifaro /* Pass stack buffer by default */ 597272840Smelifaro ta_buf_m = ta_buf; 598272840Smelifaro error = prepare_batch_buffer(ch, ta, tei, count, OP_ADD, &ta_buf_m); 599272840Smelifaro if (error != 0) 600272840Smelifaro goto cleanup; 601272840Smelifaro 602272840Smelifaro IPFW_UH_WLOCK(ch); 603272840Smelifaro /* Drop reference we've used in first search */ 604272840Smelifaro tc->no.refcnt--; 605272840Smelifaro 606272840Smelifaro /* 607272840Smelifaro * Check if table swap has happened. 608272840Smelifaro * (so table algo might be changed). 609272840Smelifaro * Restart operation to achieve consistent behavior. 610272840Smelifaro */ 611272840Smelifaro del_toperation_state(ch, &ts); 612272840Smelifaro if (ts.modified != 0) 613272840Smelifaro goto restart; 614272840Smelifaro 615272840Smelifaro /* 616272840Smelifaro * Link all values values to shared/per-table value array. 617272840Smelifaro * 618272840Smelifaro * May release/reacquire UH_WLOCK. 619272840Smelifaro */ 620272840Smelifaro error = ipfw_link_table_values(ch, &ts); 621272840Smelifaro if (error != 0) 622272840Smelifaro goto cleanup; 623272840Smelifaro if (ts.modified != 0) 624272840Smelifaro goto restart; 625272840Smelifaro 626272840Smelifaro /* 627272840Smelifaro * Ensure we are able to add all entries without additional 628272840Smelifaro * memory allocations. May release/reacquire UH_WLOCK. 629272840Smelifaro */ 630272840Smelifaro kidx = tc->no.kidx; 631272840Smelifaro error = check_table_space(ch, &ts, tc, KIDX_TO_TI(ch, kidx), count); 632272840Smelifaro if (error != 0) 633272840Smelifaro goto cleanup; 634272840Smelifaro if (ts.modified != 0) 635272840Smelifaro goto restart; 636272840Smelifaro 637272840Smelifaro /* We've got valid table in @tc. Let's try to add data */ 638272840Smelifaro kidx = tc->no.kidx; 639272840Smelifaro ta = tc->ta; 640272840Smelifaro numadd = 0; 641272840Smelifaro first_error = 0; 642272840Smelifaro 643272840Smelifaro IPFW_WLOCK(ch); 644272840Smelifaro 645272840Smelifaro v = ta_buf_m; 646272840Smelifaro for (i = 0; i < count; i++, v += ta->ta_buf_size) { 647272840Smelifaro ptei = &tei[i]; 648272840Smelifaro num = 0; 649272840Smelifaro /* check limit before adding */ 650272840Smelifaro if ((error = check_table_limit(tc, ptei)) == 0) { 651272840Smelifaro error = ta->add(tc->astate, KIDX_TO_TI(ch, kidx), 652272840Smelifaro ptei, v, &num); 653272840Smelifaro /* Set status flag to inform userland */ 654272840Smelifaro store_tei_result(ptei, OP_ADD, error, num); 655272840Smelifaro } 656272840Smelifaro if (error == 0) { 657272840Smelifaro /* Update number of records to ease limit checking */ 658272840Smelifaro tc->count += num; 659272840Smelifaro numadd += num; 660272840Smelifaro continue; 661272840Smelifaro } 662272840Smelifaro 663272840Smelifaro if (first_error == 0) 664272840Smelifaro first_error = error; 665272840Smelifaro 666272840Smelifaro /* 667272840Smelifaro * Some error have happened. Check our atomicity 668272840Smelifaro * settings: continue if atomicity is not required, 669272840Smelifaro * rollback changes otherwise. 670272840Smelifaro */ 671272840Smelifaro if ((flags & IPFW_CTF_ATOMIC) == 0) 672272840Smelifaro continue; 673272840Smelifaro 674272840Smelifaro rollback_added_entries(ch, tc, KIDX_TO_TI(ch, kidx), 675272840Smelifaro tei, ta_buf_m, count, i); 676272840Smelifaro 677272840Smelifaro rollback = 1; 678232865Smelifaro break; 679272840Smelifaro } 680272840Smelifaro 681272840Smelifaro IPFW_WUNLOCK(ch); 682272840Smelifaro 683272840Smelifaro ipfw_garbage_table_values(ch, tc, tei, count, rollback); 684272840Smelifaro 685272840Smelifaro /* Permit post-add algorithm grow/rehash. */ 686272840Smelifaro if (numadd != 0) 687272840Smelifaro check_table_space(ch, NULL, tc, KIDX_TO_TI(ch, kidx), 0); 688272840Smelifaro 689272840Smelifaro /* Return first error to user, if any */ 690272840Smelifaro error = first_error; 691272840Smelifaro 692272840Smelifarocleanup: 693272840Smelifaro IPFW_UH_WUNLOCK(ch); 694272840Smelifaro 695272840Smelifaro flush_batch_buffer(ch, ta, tei, count, rollback, ta_buf_m, ta_buf); 696232865Smelifaro 697272840Smelifaro return (error); 698272840Smelifaro} 699232865Smelifaro 700272840Smelifaro/* 701272840Smelifaro * Deletes one or more entries in table @ti. 702272840Smelifaro * 703272840Smelifaro * Returns 0 on success. 704272840Smelifaro */ 705272840Smelifaroint 706272840Smelifarodel_table_entry(struct ip_fw_chain *ch, struct tid_info *ti, 707272840Smelifaro struct tentry_info *tei, uint8_t flags, uint32_t count) 708272840Smelifaro{ 709272840Smelifaro struct table_config *tc; 710272840Smelifaro struct table_algo *ta; 711272840Smelifaro struct tentry_info *ptei; 712272840Smelifaro uint16_t kidx; 713272840Smelifaro int error, first_error, i; 714272840Smelifaro uint32_t num, numdel; 715272840Smelifaro char ta_buf[TA_BUF_SZ]; 716272840Smelifaro caddr_t ta_buf_m, v; 717232865Smelifaro 718272840Smelifaro /* 719272840Smelifaro * Find and reference existing table. 720272840Smelifaro */ 721272840Smelifaro IPFW_UH_WLOCK(ch); 722272840Smelifaro error = find_ref_table(ch, ti, tei, count, OP_DEL, &tc); 723272840Smelifaro if (error != 0) { 724272840Smelifaro IPFW_UH_WUNLOCK(ch); 725272840Smelifaro return (error); 726272840Smelifaro } 727272840Smelifaro ta = tc->ta; 728272840Smelifaro IPFW_UH_WUNLOCK(ch); 729232865Smelifaro 730272840Smelifaro /* Allocate memory and prepare record(s) */ 731272840Smelifaro /* Pass stack buffer by default */ 732272840Smelifaro ta_buf_m = ta_buf; 733272840Smelifaro error = prepare_batch_buffer(ch, ta, tei, count, OP_DEL, &ta_buf_m); 734272840Smelifaro if (error != 0) 735272840Smelifaro goto cleanup; 736272840Smelifaro 737272840Smelifaro IPFW_UH_WLOCK(ch); 738272840Smelifaro 739272840Smelifaro /* Drop reference we've used in first search */ 740272840Smelifaro tc->no.refcnt--; 741272840Smelifaro 742272840Smelifaro /* 743272840Smelifaro * Check if table algo is still the same. 744272840Smelifaro * (changed ta may be the result of table swap). 745272840Smelifaro */ 746272840Smelifaro if (ta != tc->ta) { 747272840Smelifaro IPFW_UH_WUNLOCK(ch); 748272840Smelifaro error = EINVAL; 749272840Smelifaro goto cleanup; 750232865Smelifaro } 751232865Smelifaro 752272840Smelifaro kidx = tc->no.kidx; 753272840Smelifaro numdel = 0; 754272840Smelifaro first_error = 0; 755272840Smelifaro 756200590Sluigi IPFW_WLOCK(ch); 757272840Smelifaro v = ta_buf_m; 758272840Smelifaro for (i = 0; i < count; i++, v += ta->ta_buf_size) { 759272840Smelifaro ptei = &tei[i]; 760272840Smelifaro num = 0; 761272840Smelifaro error = ta->del(tc->astate, KIDX_TO_TI(ch, kidx), ptei, v, 762272840Smelifaro &num); 763272840Smelifaro /* Save state for userland */ 764272840Smelifaro store_tei_result(ptei, OP_DEL, error, num); 765272840Smelifaro if (error != 0 && first_error == 0) 766272840Smelifaro first_error = error; 767272840Smelifaro tc->count -= num; 768272840Smelifaro numdel += num; 769272840Smelifaro } 770272840Smelifaro IPFW_WUNLOCK(ch); 771232865Smelifaro 772272840Smelifaro /* Unlink non-used values */ 773272840Smelifaro ipfw_garbage_table_values(ch, tc, tei, count, 0); 774272840Smelifaro 775272840Smelifaro if (numdel != 0) { 776272840Smelifaro /* Run post-del hook to permit shrinking */ 777272840Smelifaro check_table_space(ch, NULL, tc, KIDX_TO_TI(ch, kidx), 0); 778232865Smelifaro } 779232865Smelifaro 780272840Smelifaro IPFW_UH_WUNLOCK(ch); 781272840Smelifaro 782272840Smelifaro /* Return first error to user, if any */ 783272840Smelifaro error = first_error; 784272840Smelifaro 785272840Smelifarocleanup: 786272840Smelifaro flush_batch_buffer(ch, ta, tei, count, 0, ta_buf_m, ta_buf); 787272840Smelifaro 788272840Smelifaro return (error); 789272840Smelifaro} 790272840Smelifaro 791272840Smelifaro/* 792272840Smelifaro * Ensure that table @tc has enough space to add @count entries without 793272840Smelifaro * need for reallocation. 794272840Smelifaro * 795272840Smelifaro * Callbacks order: 796272840Smelifaro * 0) need_modify() (UH_WLOCK) - checks if @count items can be added w/o resize. 797272840Smelifaro * 798272840Smelifaro * 1) alloc_modify (no locks, M_WAITOK) - alloc new state based on @pflags. 799272840Smelifaro * 2) prepare_modifyt (UH_WLOCK) - copy old data into new storage 800272840Smelifaro * 3) modify (UH_WLOCK + WLOCK) - switch pointers 801272840Smelifaro * 4) flush_modify (UH_WLOCK) - free state, if needed 802272840Smelifaro * 803272840Smelifaro * Returns 0 on success. 804272840Smelifaro */ 805272840Smelifarostatic int 806272840Smelifarocheck_table_space(struct ip_fw_chain *ch, struct tableop_state *ts, 807272840Smelifaro struct table_config *tc, struct table_info *ti, uint32_t count) 808272840Smelifaro{ 809272840Smelifaro struct table_algo *ta; 810272840Smelifaro uint64_t pflags; 811272840Smelifaro char ta_buf[TA_BUF_SZ]; 812272840Smelifaro int error; 813272840Smelifaro 814272840Smelifaro IPFW_UH_WLOCK_ASSERT(ch); 815272840Smelifaro 816272840Smelifaro error = 0; 817272840Smelifaro ta = tc->ta; 818272840Smelifaro if (ta->need_modify == NULL) 819272840Smelifaro return (0); 820272840Smelifaro 821272840Smelifaro /* Acquire reference not to loose @tc between locks/unlocks */ 822272840Smelifaro tc->no.refcnt++; 823272840Smelifaro 824272840Smelifaro /* 825272840Smelifaro * TODO: think about avoiding race between large add/large delete 826272840Smelifaro * operation on algorithm which implements shrinking along with 827272840Smelifaro * growing. 828272840Smelifaro */ 829272840Smelifaro while (true) { 830272840Smelifaro pflags = 0; 831272840Smelifaro if (ta->need_modify(tc->astate, ti, count, &pflags) == 0) { 832272840Smelifaro error = 0; 833272840Smelifaro break; 834232865Smelifaro } 835232865Smelifaro 836272840Smelifaro /* We have to shrink/grow table */ 837272840Smelifaro if (ts != NULL) 838272840Smelifaro add_toperation_state(ch, ts); 839272840Smelifaro IPFW_UH_WUNLOCK(ch); 840272840Smelifaro 841272840Smelifaro memset(&ta_buf, 0, sizeof(ta_buf)); 842272840Smelifaro error = ta->prepare_mod(ta_buf, &pflags); 843272840Smelifaro 844272840Smelifaro IPFW_UH_WLOCK(ch); 845272840Smelifaro if (ts != NULL) 846272840Smelifaro del_toperation_state(ch, ts); 847272840Smelifaro 848272840Smelifaro if (error != 0) 849272840Smelifaro break; 850272840Smelifaro 851272840Smelifaro if (ts != NULL && ts->modified != 0) { 852272840Smelifaro 853272840Smelifaro /* 854272840Smelifaro * Swap operation has happened 855272840Smelifaro * so we're currently operating on other 856272840Smelifaro * table data. Stop doing this. 857232865Smelifaro */ 858272840Smelifaro ta->flush_mod(ta_buf); 859272840Smelifaro break; 860232865Smelifaro } 861272840Smelifaro 862272840Smelifaro /* Check if we still need to alter table */ 863272840Smelifaro ti = KIDX_TO_TI(ch, tc->no.kidx); 864272840Smelifaro if (ta->need_modify(tc->astate, ti, count, &pflags) == 0) { 865272840Smelifaro IPFW_UH_WUNLOCK(ch); 866272840Smelifaro 867272840Smelifaro /* 868272840Smelifaro * Other thread has already performed resize. 869272840Smelifaro * Flush our state and return. 870272840Smelifaro */ 871272840Smelifaro ta->flush_mod(ta_buf); 872272840Smelifaro break; 873272840Smelifaro } 874272840Smelifaro 875272840Smelifaro error = ta->fill_mod(tc->astate, ti, ta_buf, &pflags); 876272840Smelifaro if (error == 0) { 877272840Smelifaro /* Do actual modification */ 878272840Smelifaro IPFW_WLOCK(ch); 879272840Smelifaro ta->modify(tc->astate, ti, ta_buf, pflags); 880272840Smelifaro IPFW_WUNLOCK(ch); 881272840Smelifaro } 882272840Smelifaro 883272840Smelifaro /* Anyway, flush data and retry */ 884272840Smelifaro ta->flush_mod(ta_buf); 885232865Smelifaro } 886232865Smelifaro 887272840Smelifaro tc->no.refcnt--; 888272840Smelifaro return (error); 889272840Smelifaro} 890232865Smelifaro 891272840Smelifaro/* 892272840Smelifaro * Adds or deletes record in table. 893272840Smelifaro * Data layout (v0): 894272840Smelifaro * Request: [ ip_fw3_opheader ipfw_table_xentry ] 895272840Smelifaro * 896272840Smelifaro * Returns 0 on success 897272840Smelifaro */ 898272840Smelifarostatic int 899272840Smelifaromanage_table_ent_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 900272840Smelifaro struct sockopt_data *sd) 901272840Smelifaro{ 902272840Smelifaro ipfw_table_xentry *xent; 903272840Smelifaro struct tentry_info tei; 904272840Smelifaro struct tid_info ti; 905272840Smelifaro struct table_value v; 906272840Smelifaro int error, hdrlen, read; 907272840Smelifaro 908272840Smelifaro hdrlen = offsetof(ipfw_table_xentry, k); 909272840Smelifaro 910272840Smelifaro /* Check minimum header size */ 911272840Smelifaro if (sd->valsize < (sizeof(*op3) + hdrlen)) 912272840Smelifaro return (EINVAL); 913272840Smelifaro 914272840Smelifaro read = sizeof(ip_fw3_opheader); 915272840Smelifaro 916272840Smelifaro /* Check if xentry len field is valid */ 917272840Smelifaro xent = (ipfw_table_xentry *)(op3 + 1); 918272840Smelifaro if (xent->len < hdrlen || xent->len + read > sd->valsize) 919272840Smelifaro return (EINVAL); 920272840Smelifaro 921272840Smelifaro memset(&tei, 0, sizeof(tei)); 922272840Smelifaro tei.paddr = &xent->k; 923272840Smelifaro tei.masklen = xent->masklen; 924272840Smelifaro ipfw_import_table_value_legacy(xent->value, &v); 925272840Smelifaro tei.pvalue = &v; 926272840Smelifaro /* Old requests compability */ 927272840Smelifaro tei.flags = TEI_FLAGS_COMPAT; 928272840Smelifaro if (xent->type == IPFW_TABLE_ADDR) { 929272840Smelifaro if (xent->len - hdrlen == sizeof(in_addr_t)) 930272840Smelifaro tei.subtype = AF_INET; 931272840Smelifaro else 932272840Smelifaro tei.subtype = AF_INET6; 933200590Sluigi } 934272840Smelifaro 935272840Smelifaro memset(&ti, 0, sizeof(ti)); 936272840Smelifaro ti.uidx = xent->tbl; 937272840Smelifaro ti.type = xent->type; 938272840Smelifaro 939272840Smelifaro error = (op3->opcode == IP_FW_TABLE_XADD) ? 940272840Smelifaro add_table_entry(ch, &ti, &tei, 0, 1) : 941272840Smelifaro del_table_entry(ch, &ti, &tei, 0, 1); 942272840Smelifaro 943272840Smelifaro return (error); 944200590Sluigi} 945200590Sluigi 946272840Smelifaro/* 947272840Smelifaro * Adds or deletes record in table. 948272840Smelifaro * Data layout (v1)(current): 949272840Smelifaro * Request: [ ipfw_obj_header 950272840Smelifaro * ipfw_obj_ctlv(IPFW_TLV_TBLENT_LIST) [ ipfw_obj_tentry x N ] 951272840Smelifaro * ] 952272840Smelifaro * 953272840Smelifaro * Returns 0 on success 954272840Smelifaro */ 955272840Smelifarostatic int 956272840Smelifaromanage_table_ent_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 957272840Smelifaro struct sockopt_data *sd) 958200590Sluigi{ 959272840Smelifaro ipfw_obj_tentry *tent, *ptent; 960272840Smelifaro ipfw_obj_ctlv *ctlv; 961272840Smelifaro ipfw_obj_header *oh; 962272840Smelifaro struct tentry_info *ptei, tei, *tei_buf; 963272840Smelifaro struct tid_info ti; 964272840Smelifaro int error, i, kidx, read; 965200590Sluigi 966272840Smelifaro /* Check minimum header size */ 967272840Smelifaro if (sd->valsize < (sizeof(*oh) + sizeof(*ctlv))) 968200590Sluigi return (EINVAL); 969232865Smelifaro 970272840Smelifaro /* Check if passed data is too long */ 971272840Smelifaro if (sd->valsize != sd->kavail) 972272840Smelifaro return (EINVAL); 973232865Smelifaro 974272840Smelifaro oh = (ipfw_obj_header *)sd->kbuf; 975272840Smelifaro 976272840Smelifaro /* Basic length checks for TLVs */ 977272840Smelifaro if (oh->ntlv.head.length != sizeof(oh->ntlv)) 978272840Smelifaro return (EINVAL); 979272840Smelifaro 980272840Smelifaro read = sizeof(*oh); 981272840Smelifaro 982272840Smelifaro ctlv = (ipfw_obj_ctlv *)(oh + 1); 983272840Smelifaro if (ctlv->head.length + read != sd->valsize) 984272840Smelifaro return (EINVAL); 985272840Smelifaro 986272840Smelifaro read += sizeof(*ctlv); 987272840Smelifaro tent = (ipfw_obj_tentry *)(ctlv + 1); 988272840Smelifaro if (ctlv->count * sizeof(*tent) + read != sd->valsize) 989272840Smelifaro return (EINVAL); 990272840Smelifaro 991272840Smelifaro if (ctlv->count == 0) 992272840Smelifaro return (0); 993272840Smelifaro 994272840Smelifaro /* 995272840Smelifaro * Mark entire buffer as "read". 996272840Smelifaro * This instructs sopt api write it back 997272840Smelifaro * after function return. 998272840Smelifaro */ 999272840Smelifaro ipfw_get_sopt_header(sd, sd->valsize); 1000272840Smelifaro 1001272840Smelifaro /* Perform basic checks for each entry */ 1002272840Smelifaro ptent = tent; 1003272840Smelifaro kidx = tent->idx; 1004272840Smelifaro for (i = 0; i < ctlv->count; i++, ptent++) { 1005272840Smelifaro if (ptent->head.length != sizeof(*ptent)) 1006232865Smelifaro return (EINVAL); 1007272840Smelifaro if (ptent->idx != kidx) 1008272840Smelifaro return (ENOTSUP); 1009272840Smelifaro } 1010232865Smelifaro 1011272840Smelifaro /* Convert data into kernel request objects */ 1012272840Smelifaro objheader_to_ti(oh, &ti); 1013272840Smelifaro ti.type = oh->ntlv.type; 1014272840Smelifaro ti.uidx = kidx; 1015232865Smelifaro 1016272840Smelifaro /* Use on-stack buffer for single add/del */ 1017272840Smelifaro if (ctlv->count == 1) { 1018272840Smelifaro memset(&tei, 0, sizeof(tei)); 1019272840Smelifaro tei_buf = &tei; 1020272840Smelifaro } else 1021272840Smelifaro tei_buf = malloc(ctlv->count * sizeof(tei), M_TEMP, 1022272840Smelifaro M_WAITOK | M_ZERO); 1023238265Smelifaro 1024272840Smelifaro ptei = tei_buf; 1025272840Smelifaro ptent = tent; 1026272840Smelifaro for (i = 0; i < ctlv->count; i++, ptent++, ptei++) { 1027272840Smelifaro ptei->paddr = &ptent->k; 1028272840Smelifaro ptei->subtype = ptent->subtype; 1029272840Smelifaro ptei->masklen = ptent->masklen; 1030272840Smelifaro if (ptent->head.flags & IPFW_TF_UPDATE) 1031272840Smelifaro ptei->flags |= TEI_FLAGS_UPDATE; 1032232865Smelifaro 1033272840Smelifaro ipfw_import_table_value_v1(&ptent->v.value); 1034272840Smelifaro ptei->pvalue = (struct table_value *)&ptent->v.value; 1035272840Smelifaro } 1036232865Smelifaro 1037272840Smelifaro error = (oh->opheader.opcode == IP_FW_TABLE_XADD) ? 1038272840Smelifaro add_table_entry(ch, &ti, tei_buf, ctlv->flags, ctlv->count) : 1039272840Smelifaro del_table_entry(ch, &ti, tei_buf, ctlv->flags, ctlv->count); 1040272840Smelifaro 1041272840Smelifaro /* Translate result back to userland */ 1042272840Smelifaro ptei = tei_buf; 1043272840Smelifaro ptent = tent; 1044272840Smelifaro for (i = 0; i < ctlv->count; i++, ptent++, ptei++) { 1045272840Smelifaro if (ptei->flags & TEI_FLAGS_ADDED) 1046272840Smelifaro ptent->result = IPFW_TR_ADDED; 1047272840Smelifaro else if (ptei->flags & TEI_FLAGS_DELETED) 1048272840Smelifaro ptent->result = IPFW_TR_DELETED; 1049272840Smelifaro else if (ptei->flags & TEI_FLAGS_UPDATED) 1050272840Smelifaro ptent->result = IPFW_TR_UPDATED; 1051272840Smelifaro else if (ptei->flags & TEI_FLAGS_LIMIT) 1052272840Smelifaro ptent->result = IPFW_TR_LIMIT; 1053272840Smelifaro else if (ptei->flags & TEI_FLAGS_ERROR) 1054272840Smelifaro ptent->result = IPFW_TR_ERROR; 1055272840Smelifaro else if (ptei->flags & TEI_FLAGS_NOTFOUND) 1056272840Smelifaro ptent->result = IPFW_TR_NOTFOUND; 1057272840Smelifaro else if (ptei->flags & TEI_FLAGS_EXISTS) 1058272840Smelifaro ptent->result = IPFW_TR_EXISTS; 1059272840Smelifaro ipfw_export_table_value_v1(ptei->pvalue, &ptent->v.value); 1060232865Smelifaro } 1061232865Smelifaro 1062272840Smelifaro if (tei_buf != &tei) 1063272840Smelifaro free(tei_buf, M_TEMP); 1064272840Smelifaro 1065272840Smelifaro return (error); 1066272840Smelifaro} 1067272840Smelifaro 1068272840Smelifaro/* 1069272840Smelifaro * Looks up an entry in given table. 1070272840Smelifaro * Data layout (v0)(current): 1071272840Smelifaro * Request: [ ipfw_obj_header ipfw_obj_tentry ] 1072272840Smelifaro * Reply: [ ipfw_obj_header ipfw_obj_tentry ] 1073272840Smelifaro * 1074272840Smelifaro * Returns 0 on success 1075272840Smelifaro */ 1076272840Smelifarostatic int 1077272840Smelifarofind_table_entry(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 1078272840Smelifaro struct sockopt_data *sd) 1079272840Smelifaro{ 1080272840Smelifaro ipfw_obj_tentry *tent; 1081272840Smelifaro ipfw_obj_header *oh; 1082272840Smelifaro struct tid_info ti; 1083272840Smelifaro struct table_config *tc; 1084272840Smelifaro struct table_algo *ta; 1085272840Smelifaro struct table_info *kti; 1086272840Smelifaro struct namedobj_instance *ni; 1087272840Smelifaro int error; 1088272840Smelifaro size_t sz; 1089272840Smelifaro 1090272840Smelifaro /* Check minimum header size */ 1091272840Smelifaro sz = sizeof(*oh) + sizeof(*tent); 1092272840Smelifaro if (sd->valsize != sz) 1093272840Smelifaro return (EINVAL); 1094272840Smelifaro 1095272840Smelifaro oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 1096272840Smelifaro tent = (ipfw_obj_tentry *)(oh + 1); 1097272840Smelifaro 1098272840Smelifaro /* Basic length checks for TLVs */ 1099272840Smelifaro if (oh->ntlv.head.length != sizeof(oh->ntlv)) 1100272840Smelifaro return (EINVAL); 1101272840Smelifaro 1102272840Smelifaro objheader_to_ti(oh, &ti); 1103272840Smelifaro ti.type = oh->ntlv.type; 1104272840Smelifaro ti.uidx = tent->idx; 1105272840Smelifaro 1106272840Smelifaro IPFW_UH_RLOCK(ch); 1107272840Smelifaro ni = CHAIN_TO_NI(ch); 1108272840Smelifaro 1109272840Smelifaro /* 1110272840Smelifaro * Find existing table and check its type . 1111272840Smelifaro */ 1112272840Smelifaro ta = NULL; 1113272840Smelifaro if ((tc = find_table(ni, &ti)) == NULL) { 1114272840Smelifaro IPFW_UH_RUNLOCK(ch); 1115200590Sluigi return (ESRCH); 1116200590Sluigi } 1117232865Smelifaro 1118272840Smelifaro /* check table type */ 1119272840Smelifaro if (tc->no.type != ti.type) { 1120272840Smelifaro IPFW_UH_RUNLOCK(ch); 1121232865Smelifaro return (EINVAL); 1122232865Smelifaro } 1123232865Smelifaro 1124272840Smelifaro kti = KIDX_TO_TI(ch, tc->no.kidx); 1125272840Smelifaro ta = tc->ta; 1126232865Smelifaro 1127272840Smelifaro if (ta->find_tentry == NULL) 1128272840Smelifaro return (ENOTSUP); 1129232865Smelifaro 1130272840Smelifaro error = ta->find_tentry(tc->astate, kti, tent); 1131272840Smelifaro 1132272840Smelifaro IPFW_UH_RUNLOCK(ch); 1133272840Smelifaro 1134272840Smelifaro return (error); 1135200590Sluigi} 1136200590Sluigi 1137272840Smelifaro/* 1138272840Smelifaro * Flushes all entries or destroys given table. 1139272840Smelifaro * Data layout (v0)(current): 1140272840Smelifaro * Request: [ ipfw_obj_header ] 1141272840Smelifaro * 1142272840Smelifaro * Returns 0 on success 1143272840Smelifaro */ 1144200590Sluigistatic int 1145272840Smelifaroflush_table_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 1146272840Smelifaro struct sockopt_data *sd) 1147200590Sluigi{ 1148272840Smelifaro int error; 1149272840Smelifaro struct _ipfw_obj_header *oh; 1150272840Smelifaro struct tid_info ti; 1151200590Sluigi 1152272840Smelifaro if (sd->valsize != sizeof(*oh)) 1153272840Smelifaro return (EINVAL); 1154272840Smelifaro 1155272840Smelifaro oh = (struct _ipfw_obj_header *)op3; 1156272840Smelifaro objheader_to_ti(oh, &ti); 1157272840Smelifaro 1158272840Smelifaro if (op3->opcode == IP_FW_TABLE_XDESTROY) 1159272840Smelifaro error = destroy_table(ch, &ti); 1160272840Smelifaro else if (op3->opcode == IP_FW_TABLE_XFLUSH) 1161272840Smelifaro error = flush_table(ch, &ti); 1162272840Smelifaro else 1163272840Smelifaro return (ENOTSUP); 1164272840Smelifaro 1165272840Smelifaro return (error); 1166200590Sluigi} 1167200590Sluigi 1168272840Smelifarostatic void 1169272840Smelifarorestart_flush(void *object, struct op_state *_state) 1170272840Smelifaro{ 1171272840Smelifaro struct tableop_state *ts; 1172272840Smelifaro 1173272840Smelifaro ts = (struct tableop_state *)_state; 1174272840Smelifaro 1175272840Smelifaro if (ts->tc != object) 1176272840Smelifaro return; 1177272840Smelifaro 1178272840Smelifaro /* Indicate we've called */ 1179272840Smelifaro ts->modified = 1; 1180272840Smelifaro} 1181272840Smelifaro 1182272840Smelifaro/* 1183272840Smelifaro * Flushes given table. 1184272840Smelifaro * 1185272840Smelifaro * Function create new table instance with the same 1186272840Smelifaro * parameters, swaps it with old one and 1187272840Smelifaro * flushes state without holding runtime WLOCK. 1188272840Smelifaro * 1189272840Smelifaro * Returns 0 on success. 1190272840Smelifaro */ 1191200590Sluigiint 1192272840Smelifaroflush_table(struct ip_fw_chain *ch, struct tid_info *ti) 1193200590Sluigi{ 1194272840Smelifaro struct namedobj_instance *ni; 1195272840Smelifaro struct table_config *tc; 1196272840Smelifaro struct table_algo *ta; 1197272840Smelifaro struct table_info ti_old, ti_new, *tablestate; 1198272840Smelifaro void *astate_old, *astate_new; 1199272840Smelifaro char algostate[64], *pstate; 1200272840Smelifaro struct tableop_state ts; 1201278259Smelifaro int error, need_gc; 1202272840Smelifaro uint16_t kidx; 1203272840Smelifaro uint8_t tflags; 1204200590Sluigi 1205272840Smelifaro /* 1206272840Smelifaro * Stage 1: save table algoritm. 1207272840Smelifaro * Reference found table to ensure it won't disappear. 1208272840Smelifaro */ 1209272840Smelifaro IPFW_UH_WLOCK(ch); 1210272840Smelifaro ni = CHAIN_TO_NI(ch); 1211272840Smelifaro if ((tc = find_table(ni, ti)) == NULL) { 1212272840Smelifaro IPFW_UH_WUNLOCK(ch); 1213272840Smelifaro return (ESRCH); 1214272840Smelifaro } 1215278259Smelifaro need_gc = 0; 1216278259Smelifaro astate_new = NULL; 1217278259Smelifaro memset(&ti_new, 0, sizeof(ti_new)); 1218272840Smelifarorestart: 1219272840Smelifaro /* Set up swap handler */ 1220272840Smelifaro memset(&ts, 0, sizeof(ts)); 1221272840Smelifaro ts.opstate.func = restart_flush; 1222272840Smelifaro ts.tc = tc; 1223200590Sluigi 1224272840Smelifaro ta = tc->ta; 1225272840Smelifaro /* Do not flush readonly tables */ 1226272840Smelifaro if ((ta->flags & TA_FLAG_READONLY) != 0) { 1227272840Smelifaro IPFW_UH_WUNLOCK(ch); 1228272840Smelifaro return (EACCES); 1229272840Smelifaro } 1230272840Smelifaro /* Save startup algo parameters */ 1231272840Smelifaro if (ta->print_config != NULL) { 1232272840Smelifaro ta->print_config(tc->astate, KIDX_TO_TI(ch, tc->no.kidx), 1233272840Smelifaro algostate, sizeof(algostate)); 1234272840Smelifaro pstate = algostate; 1235272840Smelifaro } else 1236272840Smelifaro pstate = NULL; 1237272840Smelifaro tflags = tc->tflags; 1238272840Smelifaro tc->no.refcnt++; 1239272840Smelifaro add_toperation_state(ch, &ts); 1240272840Smelifaro IPFW_UH_WUNLOCK(ch); 1241272840Smelifaro 1242232865Smelifaro /* 1243278259Smelifaro * Stage 1.5: if this is not the first attempt, destroy previous state 1244278259Smelifaro */ 1245278259Smelifaro if (need_gc != 0) { 1246278259Smelifaro ta->destroy(astate_new, &ti_new); 1247278259Smelifaro need_gc = 0; 1248278259Smelifaro } 1249278259Smelifaro 1250278259Smelifaro /* 1251272840Smelifaro * Stage 2: allocate new table instance using same algo. 1252232865Smelifaro */ 1253272840Smelifaro memset(&ti_new, 0, sizeof(struct table_info)); 1254272840Smelifaro error = ta->init(ch, &astate_new, &ti_new, pstate, tflags); 1255232865Smelifaro 1256272840Smelifaro /* 1257272840Smelifaro * Stage 3: swap old state pointers with newly-allocated ones. 1258272840Smelifaro * Decrease refcount. 1259272840Smelifaro */ 1260272840Smelifaro IPFW_UH_WLOCK(ch); 1261272840Smelifaro tc->no.refcnt--; 1262272840Smelifaro del_toperation_state(ch, &ts); 1263232865Smelifaro 1264272840Smelifaro if (error != 0) { 1265272840Smelifaro IPFW_UH_WUNLOCK(ch); 1266272840Smelifaro return (error); 1267232865Smelifaro } 1268232865Smelifaro 1269272840Smelifaro /* 1270272840Smelifaro * Restart operation if table swap has happened: 1271272840Smelifaro * even if algo may be the same, algo init parameters 1272272840Smelifaro * may change. Restart operation instead of doing 1273272840Smelifaro * complex checks. 1274272840Smelifaro */ 1275272840Smelifaro if (ts.modified != 0) { 1276278259Smelifaro /* Delay destroying data since we're holding UH lock */ 1277278259Smelifaro need_gc = 1; 1278272840Smelifaro goto restart; 1279232865Smelifaro } 1280232865Smelifaro 1281272840Smelifaro ni = CHAIN_TO_NI(ch); 1282272840Smelifaro kidx = tc->no.kidx; 1283272840Smelifaro tablestate = (struct table_info *)ch->tablestate; 1284272840Smelifaro 1285272840Smelifaro IPFW_WLOCK(ch); 1286272840Smelifaro ti_old = tablestate[kidx]; 1287272840Smelifaro tablestate[kidx] = ti_new; 1288272840Smelifaro IPFW_WUNLOCK(ch); 1289272840Smelifaro 1290272840Smelifaro astate_old = tc->astate; 1291272840Smelifaro tc->astate = astate_new; 1292272840Smelifaro tc->ti_copy = ti_new; 1293272840Smelifaro tc->count = 0; 1294272840Smelifaro 1295272840Smelifaro /* Notify algo on real @ti address */ 1296272840Smelifaro if (ta->change_ti != NULL) 1297272840Smelifaro ta->change_ti(tc->astate, &tablestate[kidx]); 1298272840Smelifaro 1299272840Smelifaro /* 1300272840Smelifaro * Stage 4: unref values. 1301272840Smelifaro */ 1302272840Smelifaro ipfw_unref_table_values(ch, tc, ta, astate_old, &ti_old); 1303272840Smelifaro IPFW_UH_WUNLOCK(ch); 1304272840Smelifaro 1305272840Smelifaro /* 1306272840Smelifaro * Stage 5: perform real flush/destroy. 1307272840Smelifaro */ 1308272840Smelifaro ta->destroy(astate_old, &ti_old); 1309272840Smelifaro 1310200590Sluigi return (0); 1311200590Sluigi} 1312200590Sluigi 1313272840Smelifaro/* 1314272840Smelifaro * Swaps two tables. 1315272840Smelifaro * Data layout (v0)(current): 1316272840Smelifaro * Request: [ ipfw_obj_header ipfw_obj_ntlv ] 1317272840Smelifaro * 1318272840Smelifaro * Returns 0 on success 1319272840Smelifaro */ 1320272840Smelifarostatic int 1321272840Smelifaroswap_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 1322272840Smelifaro struct sockopt_data *sd) 1323200590Sluigi{ 1324272840Smelifaro int error; 1325272840Smelifaro struct _ipfw_obj_header *oh; 1326272840Smelifaro struct tid_info ti_a, ti_b; 1327200590Sluigi 1328272840Smelifaro if (sd->valsize != sizeof(*oh) + sizeof(ipfw_obj_ntlv)) 1329272840Smelifaro return (EINVAL); 1330200590Sluigi 1331272840Smelifaro oh = (struct _ipfw_obj_header *)op3; 1332272840Smelifaro ntlv_to_ti(&oh->ntlv, &ti_a); 1333272840Smelifaro ntlv_to_ti((ipfw_obj_ntlv *)(oh + 1), &ti_b); 1334272840Smelifaro 1335272840Smelifaro error = swap_tables(ch, &ti_a, &ti_b); 1336272840Smelifaro 1337272840Smelifaro return (error); 1338200590Sluigi} 1339200590Sluigi 1340272840Smelifaro/* 1341272840Smelifaro * Swaps two tables of the same type/valtype. 1342272840Smelifaro * 1343272840Smelifaro * Checks if tables are compatible and limits 1344272840Smelifaro * permits swap, than actually perform swap. 1345272840Smelifaro * 1346272840Smelifaro * Each table consists of 2 different parts: 1347272840Smelifaro * config: 1348272840Smelifaro * @tc (with name, set, kidx) and rule bindings, which is "stable". 1349272840Smelifaro * number of items 1350272840Smelifaro * table algo 1351272840Smelifaro * runtime: 1352272840Smelifaro * runtime data @ti (ch->tablestate) 1353272840Smelifaro * runtime cache in @tc 1354272840Smelifaro * algo-specific data (@tc->astate) 1355272840Smelifaro * 1356272840Smelifaro * So we switch: 1357272840Smelifaro * all runtime data 1358272840Smelifaro * number of items 1359272840Smelifaro * table algo 1360272840Smelifaro * 1361272840Smelifaro * After that we call @ti change handler for each table. 1362272840Smelifaro * 1363272840Smelifaro * Note that referencing @tc won't protect tc->ta from change. 1364272840Smelifaro * XXX: Do we need to restrict swap between locked tables? 1365272840Smelifaro * XXX: Do we need to exchange ftype? 1366272840Smelifaro * 1367272840Smelifaro * Returns 0 on success. 1368272840Smelifaro */ 1369272840Smelifarostatic int 1370272840Smelifaroswap_tables(struct ip_fw_chain *ch, struct tid_info *a, 1371272840Smelifaro struct tid_info *b) 1372232865Smelifaro{ 1373272840Smelifaro struct namedobj_instance *ni; 1374272840Smelifaro struct table_config *tc_a, *tc_b; 1375272840Smelifaro struct table_algo *ta; 1376272840Smelifaro struct table_info ti, *tablestate; 1377272840Smelifaro void *astate; 1378272840Smelifaro uint32_t count; 1379272840Smelifaro 1380272840Smelifaro /* 1381272840Smelifaro * Stage 1: find both tables and ensure they are of 1382272840Smelifaro * the same type. 1383272840Smelifaro */ 1384272840Smelifaro IPFW_UH_WLOCK(ch); 1385272840Smelifaro ni = CHAIN_TO_NI(ch); 1386272840Smelifaro if ((tc_a = find_table(ni, a)) == NULL) { 1387272840Smelifaro IPFW_UH_WUNLOCK(ch); 1388272840Smelifaro return (ESRCH); 1389272840Smelifaro } 1390272840Smelifaro if ((tc_b = find_table(ni, b)) == NULL) { 1391272840Smelifaro IPFW_UH_WUNLOCK(ch); 1392272840Smelifaro return (ESRCH); 1393272840Smelifaro } 1394272840Smelifaro 1395272840Smelifaro /* It is very easy to swap between the same table */ 1396272840Smelifaro if (tc_a == tc_b) { 1397272840Smelifaro IPFW_UH_WUNLOCK(ch); 1398272840Smelifaro return (0); 1399272840Smelifaro } 1400272840Smelifaro 1401272840Smelifaro /* Check type and value are the same */ 1402272840Smelifaro if (tc_a->no.type != tc_b->no.type || tc_a->tflags != tc_b->tflags) { 1403272840Smelifaro IPFW_UH_WUNLOCK(ch); 1404272840Smelifaro return (EINVAL); 1405272840Smelifaro } 1406272840Smelifaro 1407272840Smelifaro /* Check limits before swap */ 1408272840Smelifaro if ((tc_a->limit != 0 && tc_b->count > tc_a->limit) || 1409272840Smelifaro (tc_b->limit != 0 && tc_a->count > tc_b->limit)) { 1410272840Smelifaro IPFW_UH_WUNLOCK(ch); 1411272840Smelifaro return (EFBIG); 1412272840Smelifaro } 1413272840Smelifaro 1414272840Smelifaro /* Check if one of the tables is readonly */ 1415272840Smelifaro if (((tc_a->ta->flags | tc_b->ta->flags) & TA_FLAG_READONLY) != 0) { 1416272840Smelifaro IPFW_UH_WUNLOCK(ch); 1417272840Smelifaro return (EACCES); 1418272840Smelifaro } 1419272840Smelifaro 1420272840Smelifaro /* Notify we're going to swap */ 1421272840Smelifaro rollback_toperation_state(ch, tc_a); 1422272840Smelifaro rollback_toperation_state(ch, tc_b); 1423272840Smelifaro 1424272840Smelifaro /* Everything is fine, prepare to swap */ 1425272840Smelifaro tablestate = (struct table_info *)ch->tablestate; 1426272840Smelifaro ti = tablestate[tc_a->no.kidx]; 1427272840Smelifaro ta = tc_a->ta; 1428272840Smelifaro astate = tc_a->astate; 1429272840Smelifaro count = tc_a->count; 1430272840Smelifaro 1431272840Smelifaro IPFW_WLOCK(ch); 1432272840Smelifaro /* a <- b */ 1433272840Smelifaro tablestate[tc_a->no.kidx] = tablestate[tc_b->no.kidx]; 1434272840Smelifaro tc_a->ta = tc_b->ta; 1435272840Smelifaro tc_a->astate = tc_b->astate; 1436272840Smelifaro tc_a->count = tc_b->count; 1437272840Smelifaro /* b <- a */ 1438272840Smelifaro tablestate[tc_b->no.kidx] = ti; 1439272840Smelifaro tc_b->ta = ta; 1440272840Smelifaro tc_b->astate = astate; 1441272840Smelifaro tc_b->count = count; 1442272840Smelifaro IPFW_WUNLOCK(ch); 1443272840Smelifaro 1444272840Smelifaro /* Ensure tc.ti copies are in sync */ 1445272840Smelifaro tc_a->ti_copy = tablestate[tc_a->no.kidx]; 1446272840Smelifaro tc_b->ti_copy = tablestate[tc_b->no.kidx]; 1447272840Smelifaro 1448272840Smelifaro /* Notify both tables on @ti change */ 1449272840Smelifaro if (tc_a->ta->change_ti != NULL) 1450272840Smelifaro tc_a->ta->change_ti(tc_a->astate, &tablestate[tc_a->no.kidx]); 1451272840Smelifaro if (tc_b->ta->change_ti != NULL) 1452272840Smelifaro tc_b->ta->change_ti(tc_b->astate, &tablestate[tc_b->no.kidx]); 1453272840Smelifaro 1454272840Smelifaro IPFW_UH_WUNLOCK(ch); 1455272840Smelifaro 1456200590Sluigi return (0); 1457200590Sluigi} 1458200590Sluigi 1459272840Smelifaro/* 1460272840Smelifaro * Destroys table specified by @ti. 1461272840Smelifaro * Data layout (v0)(current): 1462272840Smelifaro * Request: [ ip_fw3_opheader ] 1463272840Smelifaro * 1464272840Smelifaro * Returns 0 on success 1465272840Smelifaro */ 1466272840Smelifarostatic int 1467272840Smelifarodestroy_table(struct ip_fw_chain *ch, struct tid_info *ti) 1468272840Smelifaro{ 1469272840Smelifaro struct namedobj_instance *ni; 1470272840Smelifaro struct table_config *tc; 1471272840Smelifaro 1472272840Smelifaro IPFW_UH_WLOCK(ch); 1473272840Smelifaro 1474272840Smelifaro ni = CHAIN_TO_NI(ch); 1475272840Smelifaro if ((tc = find_table(ni, ti)) == NULL) { 1476272840Smelifaro IPFW_UH_WUNLOCK(ch); 1477272840Smelifaro return (ESRCH); 1478272840Smelifaro } 1479272840Smelifaro 1480272840Smelifaro /* Do not permit destroying referenced tables */ 1481272840Smelifaro if (tc->no.refcnt > 0) { 1482272840Smelifaro IPFW_UH_WUNLOCK(ch); 1483272840Smelifaro return (EBUSY); 1484272840Smelifaro } 1485272840Smelifaro 1486272840Smelifaro IPFW_WLOCK(ch); 1487272840Smelifaro unlink_table(ch, tc); 1488272840Smelifaro IPFW_WUNLOCK(ch); 1489272840Smelifaro 1490272840Smelifaro /* Free obj index */ 1491272840Smelifaro if (ipfw_objhash_free_idx(ni, tc->no.kidx) != 0) 1492272840Smelifaro printf("Error unlinking kidx %d from table %s\n", 1493272840Smelifaro tc->no.kidx, tc->tablename); 1494272840Smelifaro 1495272840Smelifaro /* Unref values used in tables while holding UH lock */ 1496272840Smelifaro ipfw_unref_table_values(ch, tc, tc->ta, tc->astate, &tc->ti_copy); 1497272840Smelifaro IPFW_UH_WUNLOCK(ch); 1498272840Smelifaro 1499272840Smelifaro free_table_config(ni, tc); 1500272840Smelifaro 1501272840Smelifaro return (0); 1502272840Smelifaro} 1503272840Smelifaro 1504273274Smelifarostatic uint32_t 1505273274Smelifaroroundup2p(uint32_t v) 1506273274Smelifaro{ 1507273274Smelifaro 1508273274Smelifaro v--; 1509273274Smelifaro v |= v >> 1; 1510273274Smelifaro v |= v >> 2; 1511273274Smelifaro v |= v >> 4; 1512273274Smelifaro v |= v >> 8; 1513273274Smelifaro v |= v >> 16; 1514273274Smelifaro v++; 1515273274Smelifaro 1516273274Smelifaro return (v); 1517273274Smelifaro} 1518273274Smelifaro 1519272840Smelifaro/* 1520272840Smelifaro * Grow tables index. 1521272840Smelifaro * 1522272840Smelifaro * Returns 0 on success. 1523272840Smelifaro */ 1524200590Sluigiint 1525233478Smelifaroipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables) 1526233478Smelifaro{ 1527233478Smelifaro unsigned int ntables_old, tbl; 1528272840Smelifaro struct namedobj_instance *ni; 1529272840Smelifaro void *new_idx, *old_tablestate, *tablestate; 1530272840Smelifaro struct table_info *ti; 1531272840Smelifaro struct table_config *tc; 1532272840Smelifaro int i, new_blocks; 1533233478Smelifaro 1534233478Smelifaro /* Check new value for validity */ 1535273274Smelifaro if (ntables == 0) 1536273274Smelifaro return (EINVAL); 1537233478Smelifaro if (ntables > IPFW_TABLES_MAX) 1538233478Smelifaro ntables = IPFW_TABLES_MAX; 1539273274Smelifaro /* Alight to nearest power of 2 */ 1540273274Smelifaro ntables = (unsigned int)roundup2p(ntables); 1541233478Smelifaro 1542233478Smelifaro /* Allocate new pointers */ 1543272840Smelifaro tablestate = malloc(ntables * sizeof(struct table_info), 1544272840Smelifaro M_IPFW, M_WAITOK | M_ZERO); 1545233478Smelifaro 1546272840Smelifaro ipfw_objhash_bitmap_alloc(ntables, (void *)&new_idx, &new_blocks); 1547233478Smelifaro 1548272840Smelifaro IPFW_UH_WLOCK(ch); 1549272840Smelifaro 1550233478Smelifaro tbl = (ntables >= V_fw_tables_max) ? V_fw_tables_max : ntables; 1551272840Smelifaro ni = CHAIN_TO_NI(ch); 1552233478Smelifaro 1553272840Smelifaro /* Temporary restrict decreasing max_tables */ 1554272840Smelifaro if (ntables < V_fw_tables_max) { 1555233478Smelifaro 1556272840Smelifaro /* 1557272840Smelifaro * FIXME: Check if we really can shrink 1558272840Smelifaro */ 1559272840Smelifaro IPFW_UH_WUNLOCK(ch); 1560272840Smelifaro return (EINVAL); 1561272840Smelifaro } 1562233478Smelifaro 1563272840Smelifaro /* Copy table info/indices */ 1564272840Smelifaro memcpy(tablestate, ch->tablestate, sizeof(struct table_info) * tbl); 1565272840Smelifaro ipfw_objhash_bitmap_merge(ni, &new_idx, &new_blocks); 1566272840Smelifaro 1567272840Smelifaro IPFW_WLOCK(ch); 1568272840Smelifaro 1569272840Smelifaro /* Change pointers */ 1570272840Smelifaro old_tablestate = ch->tablestate; 1571272840Smelifaro ch->tablestate = tablestate; 1572272840Smelifaro ipfw_objhash_bitmap_swap(ni, &new_idx, &new_blocks); 1573272840Smelifaro 1574233478Smelifaro ntables_old = V_fw_tables_max; 1575233478Smelifaro V_fw_tables_max = ntables; 1576233478Smelifaro 1577233478Smelifaro IPFW_WUNLOCK(ch); 1578233478Smelifaro 1579272840Smelifaro /* Notify all consumers that their @ti pointer has changed */ 1580272840Smelifaro ti = (struct table_info *)ch->tablestate; 1581272840Smelifaro for (i = 0; i < tbl; i++, ti++) { 1582272840Smelifaro if (ti->lookup == NULL) 1583272840Smelifaro continue; 1584272840Smelifaro tc = (struct table_config *)ipfw_objhash_lookup_kidx(ni, i); 1585272840Smelifaro if (tc == NULL || tc->ta->change_ti == NULL) 1586272840Smelifaro continue; 1587272840Smelifaro 1588272840Smelifaro tc->ta->change_ti(tc->astate, ti); 1589272840Smelifaro } 1590272840Smelifaro 1591272840Smelifaro IPFW_UH_WUNLOCK(ch); 1592272840Smelifaro 1593272840Smelifaro /* Free old pointers */ 1594272840Smelifaro free(old_tablestate, M_IPFW); 1595272840Smelifaro ipfw_objhash_bitmap_free(new_idx, new_blocks); 1596272840Smelifaro 1597272840Smelifaro return (0); 1598272840Smelifaro} 1599272840Smelifaro 1600272840Smelifaro/* 1601272840Smelifaro * Switch between "set 0" and "rule's set" table binding, 1602272840Smelifaro * Check all ruleset bindings and permits changing 1603272840Smelifaro * IFF each binding has both rule AND table in default set (set 0). 1604272840Smelifaro * 1605272840Smelifaro * Returns 0 on success. 1606272840Smelifaro */ 1607272840Smelifaroint 1608272840Smelifaroipfw_switch_tables_namespace(struct ip_fw_chain *ch, unsigned int sets) 1609272840Smelifaro{ 1610272840Smelifaro struct namedobj_instance *ni; 1611272840Smelifaro struct named_object *no; 1612272840Smelifaro struct ip_fw *rule; 1613272840Smelifaro ipfw_insn *cmd; 1614272840Smelifaro int cmdlen, i, l; 1615272840Smelifaro uint16_t kidx; 1616272840Smelifaro uint8_t type; 1617272840Smelifaro 1618272840Smelifaro IPFW_UH_WLOCK(ch); 1619272840Smelifaro 1620272840Smelifaro if (V_fw_tables_sets == sets) { 1621272840Smelifaro IPFW_UH_WUNLOCK(ch); 1622272840Smelifaro return (0); 1623272840Smelifaro } 1624272840Smelifaro 1625272840Smelifaro ni = CHAIN_TO_NI(ch); 1626272840Smelifaro 1627272840Smelifaro /* 1628272840Smelifaro * Scan all rules and examine tables opcodes. 1629272840Smelifaro */ 1630272840Smelifaro for (i = 0; i < ch->n_rules; i++) { 1631272840Smelifaro rule = ch->map[i]; 1632272840Smelifaro 1633272840Smelifaro l = rule->cmd_len; 1634272840Smelifaro cmd = rule->cmd; 1635272840Smelifaro cmdlen = 0; 1636272840Smelifaro for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 1637272840Smelifaro cmdlen = F_LEN(cmd); 1638272840Smelifaro 1639272840Smelifaro if (classify_table_opcode(cmd, &kidx, &type) != 0) 1640272840Smelifaro continue; 1641272840Smelifaro 1642272840Smelifaro no = ipfw_objhash_lookup_kidx(ni, kidx); 1643272840Smelifaro 1644272840Smelifaro /* Check if both table object and rule has the set 0 */ 1645272840Smelifaro if (no->set != 0 || rule->set != 0) { 1646272840Smelifaro IPFW_UH_WUNLOCK(ch); 1647272840Smelifaro return (EBUSY); 1648233478Smelifaro } 1649233478Smelifaro 1650233478Smelifaro } 1651233478Smelifaro } 1652272840Smelifaro V_fw_tables_sets = sets; 1653233478Smelifaro 1654272840Smelifaro IPFW_UH_WUNLOCK(ch); 1655233478Smelifaro 1656233478Smelifaro return (0); 1657233478Smelifaro} 1658233478Smelifaro 1659272840Smelifaro/* 1660272840Smelifaro * Lookup an IP @addr in table @tbl. 1661272840Smelifaro * Stores found value in @val. 1662272840Smelifaro * 1663272840Smelifaro * Returns 1 if @addr was found. 1664272840Smelifaro */ 1665233478Smelifaroint 1666200590Sluigiipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, 1667200590Sluigi uint32_t *val) 1668200590Sluigi{ 1669272840Smelifaro struct table_info *ti; 1670200590Sluigi 1671272840Smelifaro ti = KIDX_TO_TI(ch, tbl); 1672272840Smelifaro 1673272840Smelifaro return (ti->lookup(ti, &addr, sizeof(in_addr_t), val)); 1674272840Smelifaro} 1675272840Smelifaro 1676272840Smelifaro/* 1677272840Smelifaro * Lookup an arbtrary key @paddr of legth @plen in table @tbl. 1678272840Smelifaro * Stores found value in @val. 1679272840Smelifaro * 1680272840Smelifaro * Returns 1 if key was found. 1681272840Smelifaro */ 1682272840Smelifaroint 1683272840Smelifaroipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, uint16_t plen, 1684272840Smelifaro void *paddr, uint32_t *val) 1685272840Smelifaro{ 1686272840Smelifaro struct table_info *ti; 1687272840Smelifaro 1688272840Smelifaro ti = KIDX_TO_TI(ch, tbl); 1689272840Smelifaro 1690272840Smelifaro return (ti->lookup(ti, paddr, plen, val)); 1691272840Smelifaro} 1692272840Smelifaro 1693272840Smelifaro/* 1694272840Smelifaro * Info/List/dump support for tables. 1695272840Smelifaro * 1696272840Smelifaro */ 1697272840Smelifaro 1698272840Smelifaro/* 1699272840Smelifaro * High-level 'get' cmds sysctl handlers 1700272840Smelifaro */ 1701272840Smelifaro 1702272840Smelifaro/* 1703272840Smelifaro * Lists all tables currently available in kernel. 1704272840Smelifaro * Data layout (v0)(current): 1705272840Smelifaro * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size 1706272840Smelifaro * Reply: [ ipfw_obj_lheader ipfw_xtable_info x N ] 1707272840Smelifaro * 1708272840Smelifaro * Returns 0 on success 1709272840Smelifaro */ 1710272840Smelifarostatic int 1711272840Smelifarolist_tables(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 1712272840Smelifaro struct sockopt_data *sd) 1713272840Smelifaro{ 1714272840Smelifaro struct _ipfw_obj_lheader *olh; 1715272840Smelifaro int error; 1716272840Smelifaro 1717272840Smelifaro olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh)); 1718272840Smelifaro if (olh == NULL) 1719272840Smelifaro return (EINVAL); 1720272840Smelifaro if (sd->valsize < olh->size) 1721272840Smelifaro return (EINVAL); 1722272840Smelifaro 1723272840Smelifaro IPFW_UH_RLOCK(ch); 1724272840Smelifaro error = export_tables(ch, olh, sd); 1725272840Smelifaro IPFW_UH_RUNLOCK(ch); 1726272840Smelifaro 1727272840Smelifaro return (error); 1728272840Smelifaro} 1729272840Smelifaro 1730272840Smelifaro/* 1731272840Smelifaro * Store table info to buffer provided by @sd. 1732272840Smelifaro * Data layout (v0)(current): 1733272840Smelifaro * Request: [ ipfw_obj_header ipfw_xtable_info(empty)] 1734272840Smelifaro * Reply: [ ipfw_obj_header ipfw_xtable_info ] 1735272840Smelifaro * 1736272840Smelifaro * Returns 0 on success. 1737272840Smelifaro */ 1738272840Smelifarostatic int 1739272840Smelifarodescribe_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 1740272840Smelifaro struct sockopt_data *sd) 1741272840Smelifaro{ 1742272840Smelifaro struct _ipfw_obj_header *oh; 1743272840Smelifaro struct table_config *tc; 1744272840Smelifaro struct tid_info ti; 1745272840Smelifaro size_t sz; 1746272840Smelifaro 1747272840Smelifaro sz = sizeof(*oh) + sizeof(ipfw_xtable_info); 1748272840Smelifaro oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 1749272840Smelifaro if (oh == NULL) 1750272840Smelifaro return (EINVAL); 1751272840Smelifaro 1752272840Smelifaro objheader_to_ti(oh, &ti); 1753272840Smelifaro 1754272840Smelifaro IPFW_UH_RLOCK(ch); 1755272840Smelifaro if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) { 1756272840Smelifaro IPFW_UH_RUNLOCK(ch); 1757272840Smelifaro return (ESRCH); 1758200590Sluigi } 1759272840Smelifaro 1760272840Smelifaro export_table_info(ch, tc, (ipfw_xtable_info *)(oh + 1)); 1761272840Smelifaro IPFW_UH_RUNLOCK(ch); 1762272840Smelifaro 1763200590Sluigi return (0); 1764200590Sluigi} 1765200590Sluigi 1766272840Smelifaro/* 1767272840Smelifaro * Modifies existing table. 1768272840Smelifaro * Data layout (v0)(current): 1769272840Smelifaro * Request: [ ipfw_obj_header ipfw_xtable_info ] 1770272840Smelifaro * 1771272840Smelifaro * Returns 0 on success 1772272840Smelifaro */ 1773272840Smelifarostatic int 1774272840Smelifaromodify_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 1775272840Smelifaro struct sockopt_data *sd) 1776232865Smelifaro{ 1777272840Smelifaro struct _ipfw_obj_header *oh; 1778272840Smelifaro ipfw_xtable_info *i; 1779272840Smelifaro char *tname; 1780272840Smelifaro struct tid_info ti; 1781272840Smelifaro struct namedobj_instance *ni; 1782272840Smelifaro struct table_config *tc; 1783232865Smelifaro 1784272840Smelifaro if (sd->valsize != sizeof(*oh) + sizeof(ipfw_xtable_info)) 1785272840Smelifaro return (EINVAL); 1786232865Smelifaro 1787272840Smelifaro oh = (struct _ipfw_obj_header *)sd->kbuf; 1788272840Smelifaro i = (ipfw_xtable_info *)(oh + 1); 1789232865Smelifaro 1790272840Smelifaro /* 1791272840Smelifaro * Verify user-supplied strings. 1792272840Smelifaro * Check for null-terminated/zero-length strings/ 1793272840Smelifaro */ 1794272840Smelifaro tname = oh->ntlv.name; 1795272840Smelifaro if (ipfw_check_table_name(tname) != 0) 1796272840Smelifaro return (EINVAL); 1797232865Smelifaro 1798272840Smelifaro objheader_to_ti(oh, &ti); 1799272840Smelifaro ti.type = i->type; 1800272840Smelifaro 1801272840Smelifaro IPFW_UH_WLOCK(ch); 1802272840Smelifaro ni = CHAIN_TO_NI(ch); 1803272840Smelifaro if ((tc = find_table(ni, &ti)) == NULL) { 1804272840Smelifaro IPFW_UH_WUNLOCK(ch); 1805272840Smelifaro return (ESRCH); 1806232865Smelifaro } 1807232865Smelifaro 1808272840Smelifaro /* Do not support any modifications for readonly tables */ 1809272840Smelifaro if ((tc->ta->flags & TA_FLAG_READONLY) != 0) { 1810272840Smelifaro IPFW_UH_WUNLOCK(ch); 1811272840Smelifaro return (EACCES); 1812232865Smelifaro } 1813272840Smelifaro 1814272840Smelifaro if ((i->mflags & IPFW_TMFLAGS_LIMIT) != 0) 1815272840Smelifaro tc->limit = i->limit; 1816272840Smelifaro if ((i->mflags & IPFW_TMFLAGS_LOCK) != 0) 1817272840Smelifaro tc->locked = ((i->flags & IPFW_TGFLAGS_LOCKED) != 0); 1818272840Smelifaro IPFW_UH_WUNLOCK(ch); 1819272840Smelifaro 1820232865Smelifaro return (0); 1821232865Smelifaro} 1822232865Smelifaro 1823272840Smelifaro/* 1824272840Smelifaro * Creates new table. 1825272840Smelifaro * Data layout (v0)(current): 1826272840Smelifaro * Request: [ ipfw_obj_header ipfw_xtable_info ] 1827272840Smelifaro * 1828272840Smelifaro * Returns 0 on success 1829272840Smelifaro */ 1830200590Sluigistatic int 1831272840Smelifarocreate_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 1832272840Smelifaro struct sockopt_data *sd) 1833200590Sluigi{ 1834272840Smelifaro struct _ipfw_obj_header *oh; 1835272840Smelifaro ipfw_xtable_info *i; 1836272840Smelifaro char *tname, *aname; 1837272840Smelifaro struct tid_info ti; 1838272840Smelifaro struct namedobj_instance *ni; 1839200590Sluigi 1840272840Smelifaro if (sd->valsize != sizeof(*oh) + sizeof(ipfw_xtable_info)) 1841272840Smelifaro return (EINVAL); 1842272840Smelifaro 1843272840Smelifaro oh = (struct _ipfw_obj_header *)sd->kbuf; 1844272840Smelifaro i = (ipfw_xtable_info *)(oh + 1); 1845272840Smelifaro 1846272840Smelifaro /* 1847272840Smelifaro * Verify user-supplied strings. 1848272840Smelifaro * Check for null-terminated/zero-length strings/ 1849272840Smelifaro */ 1850272840Smelifaro tname = oh->ntlv.name; 1851272840Smelifaro aname = i->algoname; 1852272840Smelifaro if (ipfw_check_table_name(tname) != 0 || 1853272840Smelifaro strnlen(aname, sizeof(i->algoname)) == sizeof(i->algoname)) 1854272840Smelifaro return (EINVAL); 1855272840Smelifaro 1856272840Smelifaro if (aname[0] == '\0') { 1857272840Smelifaro /* Use default algorithm */ 1858272840Smelifaro aname = NULL; 1859272840Smelifaro } 1860272840Smelifaro 1861272840Smelifaro objheader_to_ti(oh, &ti); 1862272840Smelifaro ti.type = i->type; 1863272840Smelifaro 1864272840Smelifaro ni = CHAIN_TO_NI(ch); 1865272840Smelifaro 1866272840Smelifaro IPFW_UH_RLOCK(ch); 1867274087Smelifaro if (find_table(ni, &ti) != NULL) { 1868272840Smelifaro IPFW_UH_RUNLOCK(ch); 1869272840Smelifaro return (EEXIST); 1870272840Smelifaro } 1871272840Smelifaro IPFW_UH_RUNLOCK(ch); 1872272840Smelifaro 1873272840Smelifaro return (create_table_internal(ch, &ti, aname, i, NULL, 0)); 1874272840Smelifaro} 1875272840Smelifaro 1876272840Smelifaro/* 1877272840Smelifaro * Creates new table based on @ti and @aname. 1878272840Smelifaro * 1879272840Smelifaro * Relies on table name checking inside find_name_tlv() 1880272840Smelifaro * Assume @aname to be checked and valid. 1881272840Smelifaro * Stores allocated table kidx inside @pkidx (if non-NULL). 1882272840Smelifaro * Reference created table if @compat is non-zero. 1883272840Smelifaro * 1884272840Smelifaro * Returns 0 on success. 1885272840Smelifaro */ 1886272840Smelifarostatic int 1887272840Smelifarocreate_table_internal(struct ip_fw_chain *ch, struct tid_info *ti, 1888272840Smelifaro char *aname, ipfw_xtable_info *i, uint16_t *pkidx, int compat) 1889272840Smelifaro{ 1890272840Smelifaro struct namedobj_instance *ni; 1891272840Smelifaro struct table_config *tc, *tc_new, *tmp; 1892272840Smelifaro struct table_algo *ta; 1893272840Smelifaro uint16_t kidx; 1894272840Smelifaro 1895272840Smelifaro ni = CHAIN_TO_NI(ch); 1896272840Smelifaro 1897272840Smelifaro ta = find_table_algo(CHAIN_TO_TCFG(ch), ti, aname); 1898272840Smelifaro if (ta == NULL) 1899272840Smelifaro return (ENOTSUP); 1900272840Smelifaro 1901272840Smelifaro tc = alloc_table_config(ch, ti, ta, aname, i->tflags); 1902272840Smelifaro if (tc == NULL) 1903272840Smelifaro return (ENOMEM); 1904272840Smelifaro 1905272840Smelifaro tc->vmask = i->vmask; 1906272840Smelifaro tc->limit = i->limit; 1907272840Smelifaro if (ta->flags & TA_FLAG_READONLY) 1908272840Smelifaro tc->locked = 1; 1909272840Smelifaro else 1910272840Smelifaro tc->locked = (i->flags & IPFW_TGFLAGS_LOCKED) != 0; 1911272840Smelifaro 1912272840Smelifaro IPFW_UH_WLOCK(ch); 1913272840Smelifaro 1914272840Smelifaro /* Check if table has been already created */ 1915272840Smelifaro tc_new = find_table(ni, ti); 1916272840Smelifaro if (tc_new != NULL) { 1917272840Smelifaro 1918272840Smelifaro /* 1919272840Smelifaro * Compat: do not fail if we're 1920272840Smelifaro * requesting to create existing table 1921272840Smelifaro * which has the same type 1922272840Smelifaro */ 1923272840Smelifaro if (compat == 0 || tc_new->no.type != tc->no.type) { 1924272840Smelifaro IPFW_UH_WUNLOCK(ch); 1925272840Smelifaro free_table_config(ni, tc); 1926272840Smelifaro return (EEXIST); 1927272840Smelifaro } 1928272840Smelifaro 1929272840Smelifaro /* Exchange tc and tc_new for proper refcounting & freeing */ 1930272840Smelifaro tmp = tc; 1931272840Smelifaro tc = tc_new; 1932272840Smelifaro tc_new = tmp; 1933272840Smelifaro } else { 1934272840Smelifaro /* New table */ 1935272840Smelifaro if (ipfw_objhash_alloc_idx(ni, &kidx) != 0) { 1936272840Smelifaro IPFW_UH_WUNLOCK(ch); 1937272840Smelifaro printf("Unable to allocate table index." 1938272840Smelifaro " Consider increasing net.inet.ip.fw.tables_max"); 1939272840Smelifaro free_table_config(ni, tc); 1940272840Smelifaro return (EBUSY); 1941272840Smelifaro } 1942272840Smelifaro tc->no.kidx = kidx; 1943272840Smelifaro 1944272840Smelifaro IPFW_WLOCK(ch); 1945272840Smelifaro link_table(ch, tc); 1946272840Smelifaro IPFW_WUNLOCK(ch); 1947272840Smelifaro } 1948272840Smelifaro 1949272840Smelifaro if (compat != 0) 1950272840Smelifaro tc->no.refcnt++; 1951272840Smelifaro if (pkidx != NULL) 1952272840Smelifaro *pkidx = tc->no.kidx; 1953272840Smelifaro 1954272840Smelifaro IPFW_UH_WUNLOCK(ch); 1955272840Smelifaro 1956272840Smelifaro if (tc_new != NULL) 1957272840Smelifaro free_table_config(ni, tc_new); 1958272840Smelifaro 1959200590Sluigi return (0); 1960200590Sluigi} 1961200590Sluigi 1962272840Smelifarostatic void 1963272840Smelifarontlv_to_ti(ipfw_obj_ntlv *ntlv, struct tid_info *ti) 1964272840Smelifaro{ 1965272840Smelifaro 1966272840Smelifaro memset(ti, 0, sizeof(struct tid_info)); 1967272840Smelifaro ti->set = ntlv->set; 1968272840Smelifaro ti->uidx = ntlv->idx; 1969272840Smelifaro ti->tlvs = ntlv; 1970272840Smelifaro ti->tlen = ntlv->head.length; 1971272840Smelifaro} 1972272840Smelifaro 1973272840Smelifarostatic void 1974272840Smelifaroobjheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti) 1975272840Smelifaro{ 1976272840Smelifaro 1977272840Smelifaro ntlv_to_ti(&oh->ntlv, ti); 1978272840Smelifaro} 1979272840Smelifaro 1980272840Smelifaro/* 1981272840Smelifaro * Exports basic table info as name TLV. 1982272840Smelifaro * Used inside dump_static_rules() to provide info 1983272840Smelifaro * about all tables referenced by current ruleset. 1984272840Smelifaro * 1985272840Smelifaro * Returns 0 on success. 1986272840Smelifaro */ 1987200590Sluigiint 1988272840Smelifaroipfw_export_table_ntlv(struct ip_fw_chain *ch, uint16_t kidx, 1989272840Smelifaro struct sockopt_data *sd) 1990200590Sluigi{ 1991272840Smelifaro struct namedobj_instance *ni; 1992272840Smelifaro struct named_object *no; 1993272840Smelifaro ipfw_obj_ntlv *ntlv; 1994200590Sluigi 1995272840Smelifaro ni = CHAIN_TO_NI(ch); 1996272840Smelifaro 1997272840Smelifaro no = ipfw_objhash_lookup_kidx(ni, kidx); 1998272840Smelifaro KASSERT(no != NULL, ("invalid table kidx passed")); 1999272840Smelifaro 2000272840Smelifaro ntlv = (ipfw_obj_ntlv *)ipfw_get_sopt_space(sd, sizeof(*ntlv)); 2001272840Smelifaro if (ntlv == NULL) 2002272840Smelifaro return (ENOMEM); 2003272840Smelifaro 2004272840Smelifaro ntlv->head.type = IPFW_TLV_TBL_NAME; 2005272840Smelifaro ntlv->head.length = sizeof(*ntlv); 2006272840Smelifaro ntlv->idx = no->kidx; 2007272840Smelifaro strlcpy(ntlv->name, no->name, sizeof(ntlv->name)); 2008272840Smelifaro 2009272840Smelifaro return (0); 2010272840Smelifaro} 2011272840Smelifaro 2012272840Smelifaro/* 2013272840Smelifaro * Marks every table kidx used in @rule with bit in @bmask. 2014272840Smelifaro * Used to generate bitmask of referenced tables for given ruleset. 2015272840Smelifaro * 2016272840Smelifaro * Returns number of newly-referenced tables. 2017272840Smelifaro */ 2018272840Smelifaroint 2019272840Smelifaroipfw_mark_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule, 2020272840Smelifaro uint32_t *bmask) 2021272840Smelifaro{ 2022272840Smelifaro int cmdlen, l, count; 2023272840Smelifaro ipfw_insn *cmd; 2024272840Smelifaro uint16_t kidx; 2025272840Smelifaro uint8_t type; 2026272840Smelifaro 2027272840Smelifaro l = rule->cmd_len; 2028272840Smelifaro cmd = rule->cmd; 2029272840Smelifaro cmdlen = 0; 2030272840Smelifaro count = 0; 2031272840Smelifaro for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 2032272840Smelifaro cmdlen = F_LEN(cmd); 2033272840Smelifaro 2034272840Smelifaro if (classify_table_opcode(cmd, &kidx, &type) != 0) 2035272840Smelifaro continue; 2036272840Smelifaro 2037272840Smelifaro if ((bmask[kidx / 32] & (1 << (kidx % 32))) == 0) 2038272840Smelifaro count++; 2039272840Smelifaro 2040272840Smelifaro bmask[kidx / 32] |= 1 << (kidx % 32); 2041272840Smelifaro } 2042272840Smelifaro 2043272840Smelifaro return (count); 2044272840Smelifaro} 2045272840Smelifaro 2046272840Smelifarostruct dump_args { 2047272840Smelifaro struct ip_fw_chain *ch; 2048272840Smelifaro struct table_info *ti; 2049272840Smelifaro struct table_config *tc; 2050272840Smelifaro struct sockopt_data *sd; 2051272840Smelifaro uint32_t cnt; 2052272840Smelifaro uint16_t uidx; 2053272840Smelifaro int error; 2054272840Smelifaro uint32_t size; 2055272840Smelifaro ipfw_table_entry *ent; 2056272840Smelifaro ta_foreach_f *f; 2057272840Smelifaro void *farg; 2058272840Smelifaro ipfw_obj_tentry tent; 2059272840Smelifaro}; 2060272840Smelifaro 2061272840Smelifarostatic int 2062272840Smelifarocount_ext_entries(void *e, void *arg) 2063272840Smelifaro{ 2064272840Smelifaro struct dump_args *da; 2065272840Smelifaro 2066272840Smelifaro da = (struct dump_args *)arg; 2067272840Smelifaro da->cnt++; 2068272840Smelifaro 2069272840Smelifaro return (0); 2070272840Smelifaro} 2071272840Smelifaro 2072272840Smelifaro/* 2073272840Smelifaro * Gets number of items from table either using 2074272840Smelifaro * internal counter or calling algo callback for 2075272840Smelifaro * externally-managed tables. 2076272840Smelifaro * 2077272840Smelifaro * Returns number of records. 2078272840Smelifaro */ 2079272840Smelifarostatic uint32_t 2080272840Smelifarotable_get_count(struct ip_fw_chain *ch, struct table_config *tc) 2081272840Smelifaro{ 2082272840Smelifaro struct table_info *ti; 2083272840Smelifaro struct table_algo *ta; 2084272840Smelifaro struct dump_args da; 2085272840Smelifaro 2086272840Smelifaro ti = KIDX_TO_TI(ch, tc->no.kidx); 2087272840Smelifaro ta = tc->ta; 2088272840Smelifaro 2089272840Smelifaro /* Use internal counter for self-managed tables */ 2090272840Smelifaro if ((ta->flags & TA_FLAG_READONLY) == 0) 2091272840Smelifaro return (tc->count); 2092272840Smelifaro 2093272840Smelifaro /* Use callback to quickly get number of items */ 2094272840Smelifaro if ((ta->flags & TA_FLAG_EXTCOUNTER) != 0) 2095272840Smelifaro return (ta->get_count(tc->astate, ti)); 2096272840Smelifaro 2097272840Smelifaro /* Count number of iterms ourselves */ 2098272840Smelifaro memset(&da, 0, sizeof(da)); 2099272840Smelifaro ta->foreach(tc->astate, ti, count_ext_entries, &da); 2100272840Smelifaro 2101272840Smelifaro return (da.cnt); 2102272840Smelifaro} 2103272840Smelifaro 2104272840Smelifaro/* 2105272840Smelifaro * Exports table @tc info into standard ipfw_xtable_info format. 2106272840Smelifaro */ 2107272840Smelifarostatic void 2108272840Smelifaroexport_table_info(struct ip_fw_chain *ch, struct table_config *tc, 2109272840Smelifaro ipfw_xtable_info *i) 2110272840Smelifaro{ 2111272840Smelifaro struct table_info *ti; 2112272840Smelifaro struct table_algo *ta; 2113272840Smelifaro 2114272840Smelifaro i->type = tc->no.type; 2115272840Smelifaro i->tflags = tc->tflags; 2116272840Smelifaro i->vmask = tc->vmask; 2117272840Smelifaro i->set = tc->no.set; 2118272840Smelifaro i->kidx = tc->no.kidx; 2119272840Smelifaro i->refcnt = tc->no.refcnt; 2120272840Smelifaro i->count = table_get_count(ch, tc); 2121272840Smelifaro i->limit = tc->limit; 2122272840Smelifaro i->flags |= (tc->locked != 0) ? IPFW_TGFLAGS_LOCKED : 0; 2123272840Smelifaro i->size = tc->count * sizeof(ipfw_obj_tentry); 2124272840Smelifaro i->size += sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info); 2125272840Smelifaro strlcpy(i->tablename, tc->tablename, sizeof(i->tablename)); 2126272840Smelifaro ti = KIDX_TO_TI(ch, tc->no.kidx); 2127272840Smelifaro ta = tc->ta; 2128272840Smelifaro if (ta->print_config != NULL) { 2129272840Smelifaro /* Use algo function to print table config to string */ 2130272840Smelifaro ta->print_config(tc->astate, ti, i->algoname, 2131272840Smelifaro sizeof(i->algoname)); 2132272840Smelifaro } else 2133272840Smelifaro strlcpy(i->algoname, ta->name, sizeof(i->algoname)); 2134272840Smelifaro /* Dump algo-specific data, if possible */ 2135272840Smelifaro if (ta->dump_tinfo != NULL) { 2136272840Smelifaro ta->dump_tinfo(tc->astate, ti, &i->ta_info); 2137272840Smelifaro i->ta_info.flags |= IPFW_TATFLAGS_DATA; 2138272840Smelifaro } 2139272840Smelifaro} 2140272840Smelifaro 2141272840Smelifarostruct dump_table_args { 2142272840Smelifaro struct ip_fw_chain *ch; 2143272840Smelifaro struct sockopt_data *sd; 2144272840Smelifaro}; 2145272840Smelifaro 2146272840Smelifarostatic void 2147272840Smelifaroexport_table_internal(struct namedobj_instance *ni, struct named_object *no, 2148272840Smelifaro void *arg) 2149272840Smelifaro{ 2150272840Smelifaro ipfw_xtable_info *i; 2151272840Smelifaro struct dump_table_args *dta; 2152272840Smelifaro 2153272840Smelifaro dta = (struct dump_table_args *)arg; 2154272840Smelifaro 2155272840Smelifaro i = (ipfw_xtable_info *)ipfw_get_sopt_space(dta->sd, sizeof(*i)); 2156272840Smelifaro KASSERT(i != 0, ("previously checked buffer is not enough")); 2157272840Smelifaro 2158272840Smelifaro export_table_info(dta->ch, (struct table_config *)no, i); 2159272840Smelifaro} 2160272840Smelifaro 2161272840Smelifaro/* 2162272840Smelifaro * Export all tables as ipfw_xtable_info structures to 2163272840Smelifaro * storage provided by @sd. 2164272840Smelifaro * 2165272840Smelifaro * If supplied buffer is too small, fills in required size 2166272840Smelifaro * and returns ENOMEM. 2167272840Smelifaro * Returns 0 on success. 2168272840Smelifaro */ 2169272840Smelifarostatic int 2170272840Smelifaroexport_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh, 2171272840Smelifaro struct sockopt_data *sd) 2172272840Smelifaro{ 2173272840Smelifaro uint32_t size; 2174272840Smelifaro uint32_t count; 2175272840Smelifaro struct dump_table_args dta; 2176272840Smelifaro 2177272840Smelifaro count = ipfw_objhash_count(CHAIN_TO_NI(ch)); 2178272840Smelifaro size = count * sizeof(ipfw_xtable_info) + sizeof(ipfw_obj_lheader); 2179272840Smelifaro 2180272840Smelifaro /* Fill in header regadless of buffer size */ 2181272840Smelifaro olh->count = count; 2182272840Smelifaro olh->objsize = sizeof(ipfw_xtable_info); 2183272840Smelifaro 2184272840Smelifaro if (size > olh->size) { 2185272840Smelifaro olh->size = size; 2186272840Smelifaro return (ENOMEM); 2187272840Smelifaro } 2188272840Smelifaro 2189272840Smelifaro olh->size = size; 2190272840Smelifaro 2191272840Smelifaro dta.ch = ch; 2192272840Smelifaro dta.sd = sd; 2193272840Smelifaro 2194272840Smelifaro ipfw_objhash_foreach(CHAIN_TO_NI(ch), export_table_internal, &dta); 2195272840Smelifaro 2196272840Smelifaro return (0); 2197272840Smelifaro} 2198272840Smelifaro 2199272840Smelifaro/* 2200272840Smelifaro * Dumps all table data 2201272840Smelifaro * Data layout (v1)(current): 2202272840Smelifaro * Request: [ ipfw_obj_header ], size = ipfw_xtable_info.size 2203272840Smelifaro * Reply: [ ipfw_obj_header ipfw_xtable_info ipfw_obj_tentry x N ] 2204272840Smelifaro * 2205272840Smelifaro * Returns 0 on success 2206272840Smelifaro */ 2207272840Smelifarostatic int 2208272840Smelifarodump_table_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 2209272840Smelifaro struct sockopt_data *sd) 2210272840Smelifaro{ 2211272840Smelifaro struct _ipfw_obj_header *oh; 2212272840Smelifaro ipfw_xtable_info *i; 2213272840Smelifaro struct tid_info ti; 2214272840Smelifaro struct table_config *tc; 2215272840Smelifaro struct table_algo *ta; 2216272840Smelifaro struct dump_args da; 2217272840Smelifaro uint32_t sz; 2218272840Smelifaro 2219272840Smelifaro sz = sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info); 2220272840Smelifaro oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 2221272840Smelifaro if (oh == NULL) 2222200590Sluigi return (EINVAL); 2223272840Smelifaro 2224272840Smelifaro i = (ipfw_xtable_info *)(oh + 1); 2225272840Smelifaro objheader_to_ti(oh, &ti); 2226272840Smelifaro 2227272840Smelifaro IPFW_UH_RLOCK(ch); 2228272840Smelifaro if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) { 2229272840Smelifaro IPFW_UH_RUNLOCK(ch); 2230272840Smelifaro return (ESRCH); 2231272840Smelifaro } 2232272840Smelifaro export_table_info(ch, tc, i); 2233272840Smelifaro 2234272840Smelifaro if (sd->valsize < i->size) { 2235272840Smelifaro 2236272840Smelifaro /* 2237272840Smelifaro * Submitted buffer size is not enough. 2238272840Smelifaro * WE've already filled in @i structure with 2239272840Smelifaro * relevant table info including size, so we 2240272840Smelifaro * can return. Buffer will be flushed automatically. 2241272840Smelifaro */ 2242272840Smelifaro IPFW_UH_RUNLOCK(ch); 2243272840Smelifaro return (ENOMEM); 2244272840Smelifaro } 2245272840Smelifaro 2246272840Smelifaro /* 2247272840Smelifaro * Do the actual dump in eXtended format 2248272840Smelifaro */ 2249272840Smelifaro memset(&da, 0, sizeof(da)); 2250272840Smelifaro da.ch = ch; 2251272840Smelifaro da.ti = KIDX_TO_TI(ch, tc->no.kidx); 2252272840Smelifaro da.tc = tc; 2253272840Smelifaro da.sd = sd; 2254272840Smelifaro 2255272840Smelifaro ta = tc->ta; 2256272840Smelifaro 2257272840Smelifaro ta->foreach(tc->astate, da.ti, dump_table_tentry, &da); 2258272840Smelifaro IPFW_UH_RUNLOCK(ch); 2259272840Smelifaro 2260272840Smelifaro return (da.error); 2261272840Smelifaro} 2262272840Smelifaro 2263272840Smelifaro/* 2264272840Smelifaro * Dumps all table data 2265272840Smelifaro * Data layout (version 0)(legacy): 2266272840Smelifaro * Request: [ ipfw_xtable ], size = IP_FW_TABLE_XGETSIZE() 2267272840Smelifaro * Reply: [ ipfw_xtable ipfw_table_xentry x N ] 2268272840Smelifaro * 2269272840Smelifaro * Returns 0 on success 2270272840Smelifaro */ 2271272840Smelifarostatic int 2272272840Smelifarodump_table_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 2273272840Smelifaro struct sockopt_data *sd) 2274272840Smelifaro{ 2275272840Smelifaro ipfw_xtable *xtbl; 2276272840Smelifaro struct tid_info ti; 2277272840Smelifaro struct table_config *tc; 2278272840Smelifaro struct table_algo *ta; 2279272840Smelifaro struct dump_args da; 2280272840Smelifaro size_t sz, count; 2281272840Smelifaro 2282272840Smelifaro xtbl = (ipfw_xtable *)ipfw_get_sopt_header(sd, sizeof(ipfw_xtable)); 2283272840Smelifaro if (xtbl == NULL) 2284272840Smelifaro return (EINVAL); 2285272840Smelifaro 2286272840Smelifaro memset(&ti, 0, sizeof(ti)); 2287272840Smelifaro ti.uidx = xtbl->tbl; 2288272840Smelifaro 2289272840Smelifaro IPFW_UH_RLOCK(ch); 2290272840Smelifaro if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) { 2291272840Smelifaro IPFW_UH_RUNLOCK(ch); 2292232865Smelifaro return (0); 2293272840Smelifaro } 2294272840Smelifaro count = table_get_count(ch, tc); 2295272840Smelifaro sz = count * sizeof(ipfw_table_xentry) + sizeof(ipfw_xtable); 2296272840Smelifaro 2297272840Smelifaro xtbl->cnt = count; 2298272840Smelifaro xtbl->size = sz; 2299272840Smelifaro xtbl->type = tc->no.type; 2300272840Smelifaro xtbl->tbl = ti.uidx; 2301272840Smelifaro 2302272840Smelifaro if (sd->valsize < sz) { 2303272840Smelifaro 2304272840Smelifaro /* 2305272840Smelifaro * Submitted buffer size is not enough. 2306272840Smelifaro * WE've already filled in @i structure with 2307272840Smelifaro * relevant table info including size, so we 2308272840Smelifaro * can return. Buffer will be flushed automatically. 2309272840Smelifaro */ 2310272840Smelifaro IPFW_UH_RUNLOCK(ch); 2311272840Smelifaro return (ENOMEM); 2312272840Smelifaro } 2313272840Smelifaro 2314272840Smelifaro /* Do the actual dump in eXtended format */ 2315272840Smelifaro memset(&da, 0, sizeof(da)); 2316272840Smelifaro da.ch = ch; 2317272840Smelifaro da.ti = KIDX_TO_TI(ch, tc->no.kidx); 2318272840Smelifaro da.tc = tc; 2319272840Smelifaro da.sd = sd; 2320272840Smelifaro 2321272840Smelifaro ta = tc->ta; 2322272840Smelifaro 2323272840Smelifaro ta->foreach(tc->astate, da.ti, dump_table_xentry, &da); 2324272840Smelifaro IPFW_UH_RUNLOCK(ch); 2325272840Smelifaro 2326200590Sluigi return (0); 2327200590Sluigi} 2328200590Sluigi 2329272840Smelifaro/* 2330272840Smelifaro * Legacy function to retrieve number of items in table. 2331272840Smelifaro */ 2332200590Sluigistatic int 2333272840Smelifaroget_table_size(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 2334272840Smelifaro struct sockopt_data *sd) 2335200590Sluigi{ 2336272840Smelifaro uint32_t *tbl; 2337272840Smelifaro struct tid_info ti; 2338272840Smelifaro size_t sz; 2339272840Smelifaro int error; 2340200590Sluigi 2341272840Smelifaro sz = sizeof(*op3) + sizeof(uint32_t); 2342272840Smelifaro op3 = (ip_fw3_opheader *)ipfw_get_sopt_header(sd, sz); 2343272840Smelifaro if (op3 == NULL) 2344272840Smelifaro return (EINVAL); 2345272840Smelifaro 2346272840Smelifaro tbl = (uint32_t *)(op3 + 1); 2347272840Smelifaro memset(&ti, 0, sizeof(ti)); 2348272840Smelifaro ti.uidx = *tbl; 2349272840Smelifaro IPFW_UH_RLOCK(ch); 2350272840Smelifaro error = ipfw_count_xtable(ch, &ti, tbl); 2351272840Smelifaro IPFW_UH_RUNLOCK(ch); 2352272840Smelifaro return (error); 2353272840Smelifaro} 2354272840Smelifaro 2355272840Smelifaro/* 2356272840Smelifaro * Legacy IP_FW_TABLE_GETSIZE handler 2357272840Smelifaro */ 2358272840Smelifaroint 2359272840Smelifaroipfw_count_table(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt) 2360272840Smelifaro{ 2361272840Smelifaro struct table_config *tc; 2362272840Smelifaro 2363272840Smelifaro if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL) 2364272840Smelifaro return (ESRCH); 2365272840Smelifaro *cnt = table_get_count(ch, tc); 2366200590Sluigi return (0); 2367200590Sluigi} 2368200590Sluigi 2369272840Smelifaro/* 2370272840Smelifaro * Legacy IP_FW_TABLE_XGETSIZE handler 2371272840Smelifaro */ 2372200590Sluigiint 2373272840Smelifaroipfw_count_xtable(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt) 2374200590Sluigi{ 2375272840Smelifaro struct table_config *tc; 2376272840Smelifaro uint32_t count; 2377200590Sluigi 2378272840Smelifaro if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL) { 2379272840Smelifaro *cnt = 0; 2380272840Smelifaro return (0); /* 'table all list' requires success */ 2381272840Smelifaro } 2382272840Smelifaro 2383272840Smelifaro count = table_get_count(ch, tc); 2384272840Smelifaro *cnt = count * sizeof(ipfw_table_xentry); 2385272840Smelifaro if (count > 0) 2386272840Smelifaro *cnt += sizeof(ipfw_xtable); 2387200590Sluigi return (0); 2388200590Sluigi} 2389232865Smelifaro 2390232865Smelifarostatic int 2391272840Smelifarodump_table_entry(void *e, void *arg) 2392232865Smelifaro{ 2393272840Smelifaro struct dump_args *da; 2394272840Smelifaro struct table_config *tc; 2395272840Smelifaro struct table_algo *ta; 2396272840Smelifaro ipfw_table_entry *ent; 2397272840Smelifaro struct table_value *pval; 2398272840Smelifaro int error; 2399232865Smelifaro 2400272840Smelifaro da = (struct dump_args *)arg; 2401272840Smelifaro 2402272840Smelifaro tc = da->tc; 2403272840Smelifaro ta = tc->ta; 2404272840Smelifaro 2405272840Smelifaro /* Out of memory, returning */ 2406272840Smelifaro if (da->cnt == da->size) 2407272840Smelifaro return (1); 2408272840Smelifaro ent = da->ent++; 2409272840Smelifaro ent->tbl = da->uidx; 2410272840Smelifaro da->cnt++; 2411272840Smelifaro 2412272840Smelifaro error = ta->dump_tentry(tc->astate, da->ti, e, &da->tent); 2413272840Smelifaro if (error != 0) 2414272840Smelifaro return (error); 2415272840Smelifaro 2416272840Smelifaro ent->addr = da->tent.k.addr.s_addr; 2417272840Smelifaro ent->masklen = da->tent.masklen; 2418272840Smelifaro pval = get_table_value(da->ch, da->tc, da->tent.v.kidx); 2419272840Smelifaro ent->value = ipfw_export_table_value_legacy(pval); 2420272840Smelifaro 2421232865Smelifaro return (0); 2422232865Smelifaro} 2423232865Smelifaro 2424272840Smelifaro/* 2425272840Smelifaro * Dumps table in pre-8.1 legacy format. 2426272840Smelifaro */ 2427232865Smelifaroint 2428272840Smelifaroipfw_dump_table_legacy(struct ip_fw_chain *ch, struct tid_info *ti, 2429272840Smelifaro ipfw_table *tbl) 2430232865Smelifaro{ 2431272840Smelifaro struct table_config *tc; 2432272840Smelifaro struct table_algo *ta; 2433272840Smelifaro struct dump_args da; 2434232865Smelifaro 2435272840Smelifaro tbl->cnt = 0; 2436272840Smelifaro 2437272840Smelifaro if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL) 2438272840Smelifaro return (0); /* XXX: We should return ESRCH */ 2439272840Smelifaro 2440272840Smelifaro ta = tc->ta; 2441272840Smelifaro 2442272840Smelifaro /* This dump format supports IPv4 only */ 2443272840Smelifaro if (tc->no.type != IPFW_TABLE_ADDR) 2444272840Smelifaro return (0); 2445272840Smelifaro 2446272840Smelifaro memset(&da, 0, sizeof(da)); 2447272840Smelifaro da.ch = ch; 2448272840Smelifaro da.ti = KIDX_TO_TI(ch, tc->no.kidx); 2449272840Smelifaro da.tc = tc; 2450272840Smelifaro da.ent = &tbl->ent[0]; 2451272840Smelifaro da.size = tbl->size; 2452272840Smelifaro 2453272840Smelifaro tbl->cnt = 0; 2454272840Smelifaro ta->foreach(tc->astate, da.ti, dump_table_entry, &da); 2455272840Smelifaro tbl->cnt = da.cnt; 2456272840Smelifaro 2457232865Smelifaro return (0); 2458232865Smelifaro} 2459232865Smelifaro 2460272840Smelifaro/* 2461272840Smelifaro * Dumps table entry in eXtended format (v1)(current). 2462272840Smelifaro */ 2463232865Smelifarostatic int 2464272840Smelifarodump_table_tentry(void *e, void *arg) 2465232865Smelifaro{ 2466272840Smelifaro struct dump_args *da; 2467272840Smelifaro struct table_config *tc; 2468272840Smelifaro struct table_algo *ta; 2469272840Smelifaro struct table_value *pval; 2470272840Smelifaro ipfw_obj_tentry *tent; 2471272840Smelifaro int error; 2472232865Smelifaro 2473272840Smelifaro da = (struct dump_args *)arg; 2474272840Smelifaro 2475272840Smelifaro tc = da->tc; 2476272840Smelifaro ta = tc->ta; 2477272840Smelifaro 2478272840Smelifaro tent = (ipfw_obj_tentry *)ipfw_get_sopt_space(da->sd, sizeof(*tent)); 2479232865Smelifaro /* Out of memory, returning */ 2480272840Smelifaro if (tent == NULL) { 2481272840Smelifaro da->error = ENOMEM; 2482232865Smelifaro return (1); 2483272840Smelifaro } 2484272840Smelifaro tent->head.length = sizeof(ipfw_obj_tentry); 2485272840Smelifaro tent->idx = da->uidx; 2486272840Smelifaro 2487272840Smelifaro error = ta->dump_tentry(tc->astate, da->ti, e, tent); 2488272840Smelifaro if (error != 0) 2489272840Smelifaro return (error); 2490272840Smelifaro 2491272840Smelifaro pval = get_table_value(da->ch, da->tc, tent->v.kidx); 2492272840Smelifaro ipfw_export_table_value_v1(pval, &tent->v.value); 2493272840Smelifaro 2494232865Smelifaro return (0); 2495232865Smelifaro} 2496232865Smelifaro 2497272840Smelifaro/* 2498272840Smelifaro * Dumps table entry in eXtended format (v0). 2499272840Smelifaro */ 2500232865Smelifarostatic int 2501272840Smelifarodump_table_xentry(void *e, void *arg) 2502232865Smelifaro{ 2503272840Smelifaro struct dump_args *da; 2504272840Smelifaro struct table_config *tc; 2505272840Smelifaro struct table_algo *ta; 2506232865Smelifaro ipfw_table_xentry *xent; 2507272840Smelifaro ipfw_obj_tentry *tent; 2508272840Smelifaro struct table_value *pval; 2509272840Smelifaro int error; 2510272840Smelifaro 2511272840Smelifaro da = (struct dump_args *)arg; 2512272840Smelifaro 2513272840Smelifaro tc = da->tc; 2514272840Smelifaro ta = tc->ta; 2515272840Smelifaro 2516272840Smelifaro xent = (ipfw_table_xentry *)ipfw_get_sopt_space(da->sd, sizeof(*xent)); 2517232865Smelifaro /* Out of memory, returning */ 2518272840Smelifaro if (xent == NULL) 2519232865Smelifaro return (1); 2520232865Smelifaro xent->len = sizeof(ipfw_table_xentry); 2521272840Smelifaro xent->tbl = da->uidx; 2522232865Smelifaro 2523272840Smelifaro memset(&da->tent, 0, sizeof(da->tent)); 2524272840Smelifaro tent = &da->tent; 2525272840Smelifaro error = ta->dump_tentry(tc->astate, da->ti, e, tent); 2526272840Smelifaro if (error != 0) 2527272840Smelifaro return (error); 2528272840Smelifaro 2529272840Smelifaro /* Convert current format to previous one */ 2530272840Smelifaro xent->masklen = tent->masklen; 2531272840Smelifaro pval = get_table_value(da->ch, da->tc, da->tent.v.kidx); 2532272840Smelifaro xent->value = ipfw_export_table_value_legacy(pval); 2533272840Smelifaro /* Apply some hacks */ 2534272840Smelifaro if (tc->no.type == IPFW_TABLE_ADDR && tent->subtype == AF_INET) { 2535272840Smelifaro xent->k.addr6.s6_addr32[3] = tent->k.addr.s_addr; 2536272840Smelifaro xent->flags = IPFW_TCF_INET; 2537272840Smelifaro } else 2538272840Smelifaro memcpy(&xent->k, &tent->k, sizeof(xent->k)); 2539272840Smelifaro 2540272840Smelifaro return (0); 2541272840Smelifaro} 2542272840Smelifaro 2543272840Smelifaro/* 2544272840Smelifaro * Helper function to export table algo data 2545272840Smelifaro * to tentry format before calling user function. 2546272840Smelifaro * 2547272840Smelifaro * Returns 0 on success. 2548272840Smelifaro */ 2549272840Smelifarostatic int 2550272840Smelifaroprepare_table_tentry(void *e, void *arg) 2551272840Smelifaro{ 2552272840Smelifaro struct dump_args *da; 2553272840Smelifaro struct table_config *tc; 2554272840Smelifaro struct table_algo *ta; 2555272840Smelifaro int error; 2556272840Smelifaro 2557272840Smelifaro da = (struct dump_args *)arg; 2558272840Smelifaro 2559272840Smelifaro tc = da->tc; 2560272840Smelifaro ta = tc->ta; 2561272840Smelifaro 2562272840Smelifaro error = ta->dump_tentry(tc->astate, da->ti, e, &da->tent); 2563272840Smelifaro if (error != 0) 2564272840Smelifaro return (error); 2565272840Smelifaro 2566272840Smelifaro da->f(&da->tent, da->farg); 2567272840Smelifaro 2568272840Smelifaro return (0); 2569272840Smelifaro} 2570272840Smelifaro 2571272840Smelifaro/* 2572272840Smelifaro * Allow external consumers to read table entries in standard format. 2573272840Smelifaro */ 2574272840Smelifaroint 2575272840Smelifaroipfw_foreach_table_tentry(struct ip_fw_chain *ch, uint16_t kidx, 2576272840Smelifaro ta_foreach_f *f, void *arg) 2577272840Smelifaro{ 2578272840Smelifaro struct namedobj_instance *ni; 2579272840Smelifaro struct table_config *tc; 2580272840Smelifaro struct table_algo *ta; 2581272840Smelifaro struct dump_args da; 2582272840Smelifaro 2583272840Smelifaro ni = CHAIN_TO_NI(ch); 2584272840Smelifaro 2585272840Smelifaro tc = (struct table_config *)ipfw_objhash_lookup_kidx(ni, kidx); 2586272840Smelifaro if (tc == NULL) 2587272840Smelifaro return (ESRCH); 2588272840Smelifaro 2589272840Smelifaro ta = tc->ta; 2590272840Smelifaro 2591272840Smelifaro memset(&da, 0, sizeof(da)); 2592272840Smelifaro da.ch = ch; 2593272840Smelifaro da.ti = KIDX_TO_TI(ch, tc->no.kidx); 2594272840Smelifaro da.tc = tc; 2595272840Smelifaro da.f = f; 2596272840Smelifaro da.farg = arg; 2597272840Smelifaro 2598272840Smelifaro ta->foreach(tc->astate, da.ti, prepare_table_tentry, &da); 2599272840Smelifaro 2600272840Smelifaro return (0); 2601272840Smelifaro} 2602272840Smelifaro 2603272840Smelifaro/* 2604272840Smelifaro * Table algorithms 2605272840Smelifaro */ 2606272840Smelifaro 2607272840Smelifaro/* 2608272840Smelifaro * Finds algoritm by index, table type or supplied name. 2609272840Smelifaro * 2610272840Smelifaro * Returns pointer to algo or NULL. 2611272840Smelifaro */ 2612272840Smelifarostatic struct table_algo * 2613272840Smelifarofind_table_algo(struct tables_config *tcfg, struct tid_info *ti, char *name) 2614272840Smelifaro{ 2615272840Smelifaro int i, l; 2616272840Smelifaro struct table_algo *ta; 2617272840Smelifaro 2618272840Smelifaro if (ti->type > IPFW_TABLE_MAXTYPE) 2619272840Smelifaro return (NULL); 2620272840Smelifaro 2621272840Smelifaro /* Search by index */ 2622272840Smelifaro if (ti->atype != 0) { 2623272840Smelifaro if (ti->atype > tcfg->algo_count) 2624272840Smelifaro return (NULL); 2625272840Smelifaro return (tcfg->algo[ti->atype]); 2626272840Smelifaro } 2627272840Smelifaro 2628272840Smelifaro if (name == NULL) { 2629272840Smelifaro /* Return default algorithm for given type if set */ 2630272840Smelifaro return (tcfg->def_algo[ti->type]); 2631272840Smelifaro } 2632272840Smelifaro 2633272840Smelifaro /* Search by name */ 2634272840Smelifaro /* TODO: better search */ 2635272840Smelifaro for (i = 1; i <= tcfg->algo_count; i++) { 2636272840Smelifaro ta = tcfg->algo[i]; 2637272840Smelifaro 2638272840Smelifaro /* 2639272840Smelifaro * One can supply additional algorithm 2640272840Smelifaro * parameters so we compare only the first word 2641272840Smelifaro * of supplied name: 2642272840Smelifaro * 'addr:chash hsize=32' 2643272840Smelifaro * '^^^^^^^^^' 2644272840Smelifaro * 2645272840Smelifaro */ 2646272840Smelifaro l = strlen(ta->name); 2647272840Smelifaro if (strncmp(name, ta->name, l) != 0) 2648272840Smelifaro continue; 2649272840Smelifaro if (name[l] != '\0' && name[l] != ' ') 2650272840Smelifaro continue; 2651272840Smelifaro /* Check if we're requesting proper table type */ 2652272840Smelifaro if (ti->type != 0 && ti->type != ta->type) 2653272840Smelifaro return (NULL); 2654272840Smelifaro return (ta); 2655272840Smelifaro } 2656272840Smelifaro 2657272840Smelifaro return (NULL); 2658272840Smelifaro} 2659272840Smelifaro 2660272840Smelifaro/* 2661272840Smelifaro * Register new table algo @ta. 2662272840Smelifaro * Stores algo id inside @idx. 2663272840Smelifaro * 2664272840Smelifaro * Returns 0 on success. 2665272840Smelifaro */ 2666272840Smelifaroint 2667272840Smelifaroipfw_add_table_algo(struct ip_fw_chain *ch, struct table_algo *ta, size_t size, 2668272840Smelifaro int *idx) 2669272840Smelifaro{ 2670272840Smelifaro struct tables_config *tcfg; 2671272840Smelifaro struct table_algo *ta_new; 2672272840Smelifaro size_t sz; 2673272840Smelifaro 2674272840Smelifaro if (size > sizeof(struct table_algo)) 2675272840Smelifaro return (EINVAL); 2676272840Smelifaro 2677272840Smelifaro /* Check for the required on-stack size for add/del */ 2678272840Smelifaro sz = roundup2(ta->ta_buf_size, sizeof(void *)); 2679272840Smelifaro if (sz > TA_BUF_SZ) 2680272840Smelifaro return (EINVAL); 2681272840Smelifaro 2682272840Smelifaro KASSERT(ta->type <= IPFW_TABLE_MAXTYPE,("Increase IPFW_TABLE_MAXTYPE")); 2683272840Smelifaro 2684272840Smelifaro /* Copy algorithm data to stable storage. */ 2685272840Smelifaro ta_new = malloc(sizeof(struct table_algo), M_IPFW, M_WAITOK | M_ZERO); 2686272840Smelifaro memcpy(ta_new, ta, size); 2687272840Smelifaro 2688272840Smelifaro tcfg = CHAIN_TO_TCFG(ch); 2689272840Smelifaro 2690272840Smelifaro KASSERT(tcfg->algo_count < 255, ("Increase algo array size")); 2691272840Smelifaro 2692272840Smelifaro tcfg->algo[++tcfg->algo_count] = ta_new; 2693272840Smelifaro ta_new->idx = tcfg->algo_count; 2694272840Smelifaro 2695272840Smelifaro /* Set algorithm as default one for given type */ 2696272840Smelifaro if ((ta_new->flags & TA_FLAG_DEFAULT) != 0 && 2697272840Smelifaro tcfg->def_algo[ta_new->type] == NULL) 2698272840Smelifaro tcfg->def_algo[ta_new->type] = ta_new; 2699272840Smelifaro 2700272840Smelifaro *idx = ta_new->idx; 2701232865Smelifaro 2702272840Smelifaro return (0); 2703272840Smelifaro} 2704272840Smelifaro 2705272840Smelifaro/* 2706272840Smelifaro * Unregisters table algo using @idx as id. 2707272840Smelifaro * XXX: It is NOT safe to call this function in any place 2708272840Smelifaro * other than ipfw instance destroy handler. 2709272840Smelifaro */ 2710272840Smelifarovoid 2711272840Smelifaroipfw_del_table_algo(struct ip_fw_chain *ch, int idx) 2712272840Smelifaro{ 2713272840Smelifaro struct tables_config *tcfg; 2714272840Smelifaro struct table_algo *ta; 2715272840Smelifaro 2716272840Smelifaro tcfg = CHAIN_TO_TCFG(ch); 2717272840Smelifaro 2718272840Smelifaro KASSERT(idx <= tcfg->algo_count, ("algo idx %d out of range 1..%d", 2719272840Smelifaro idx, tcfg->algo_count)); 2720272840Smelifaro 2721272840Smelifaro ta = tcfg->algo[idx]; 2722272840Smelifaro KASSERT(ta != NULL, ("algo idx %d is NULL", idx)); 2723272840Smelifaro 2724272840Smelifaro if (tcfg->def_algo[ta->type] == ta) 2725272840Smelifaro tcfg->def_algo[ta->type] = NULL; 2726272840Smelifaro 2727272840Smelifaro free(ta, M_IPFW); 2728272840Smelifaro} 2729272840Smelifaro 2730272840Smelifaro/* 2731272840Smelifaro * Lists all table algorithms currently available. 2732272840Smelifaro * Data layout (v0)(current): 2733272840Smelifaro * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size 2734272840Smelifaro * Reply: [ ipfw_obj_lheader ipfw_ta_info x N ] 2735272840Smelifaro * 2736272840Smelifaro * Returns 0 on success 2737272840Smelifaro */ 2738272840Smelifarostatic int 2739272840Smelifarolist_table_algo(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 2740272840Smelifaro struct sockopt_data *sd) 2741272840Smelifaro{ 2742272840Smelifaro struct _ipfw_obj_lheader *olh; 2743272840Smelifaro struct tables_config *tcfg; 2744272840Smelifaro ipfw_ta_info *i; 2745272840Smelifaro struct table_algo *ta; 2746272840Smelifaro uint32_t count, n, size; 2747272840Smelifaro 2748272840Smelifaro olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh)); 2749272840Smelifaro if (olh == NULL) 2750272840Smelifaro return (EINVAL); 2751272840Smelifaro if (sd->valsize < olh->size) 2752272840Smelifaro return (EINVAL); 2753272840Smelifaro 2754272840Smelifaro IPFW_UH_RLOCK(ch); 2755272840Smelifaro tcfg = CHAIN_TO_TCFG(ch); 2756272840Smelifaro count = tcfg->algo_count; 2757272840Smelifaro size = count * sizeof(ipfw_ta_info) + sizeof(ipfw_obj_lheader); 2758272840Smelifaro 2759272840Smelifaro /* Fill in header regadless of buffer size */ 2760272840Smelifaro olh->count = count; 2761272840Smelifaro olh->objsize = sizeof(ipfw_ta_info); 2762272840Smelifaro 2763272840Smelifaro if (size > olh->size) { 2764272840Smelifaro olh->size = size; 2765272840Smelifaro IPFW_UH_RUNLOCK(ch); 2766272840Smelifaro return (ENOMEM); 2767232865Smelifaro } 2768272840Smelifaro olh->size = size; 2769232865Smelifaro 2770272840Smelifaro for (n = 1; n <= count; n++) { 2771272840Smelifaro i = (ipfw_ta_info *)ipfw_get_sopt_space(sd, sizeof(*i)); 2772272840Smelifaro KASSERT(i != 0, ("previously checked buffer is not enough")); 2773272840Smelifaro ta = tcfg->algo[n]; 2774272840Smelifaro strlcpy(i->algoname, ta->name, sizeof(i->algoname)); 2775272840Smelifaro i->type = ta->type; 2776272840Smelifaro i->refcnt = ta->refcnt; 2777272840Smelifaro } 2778272840Smelifaro 2779272840Smelifaro IPFW_UH_RUNLOCK(ch); 2780272840Smelifaro 2781232865Smelifaro return (0); 2782232865Smelifaro} 2783232865Smelifaro 2784272840Smelifaro/* 2785272840Smelifaro * Tables rewriting code 2786272840Smelifaro */ 2787272840Smelifaro 2788272840Smelifaro/* 2789272840Smelifaro * Determine table number and lookup type for @cmd. 2790272840Smelifaro * Fill @tbl and @type with appropriate values. 2791272840Smelifaro * Returns 0 for relevant opcodes, 1 otherwise. 2792272840Smelifaro */ 2793272840Smelifarostatic int 2794272840Smelifaroclassify_table_opcode(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) 2795272840Smelifaro{ 2796272840Smelifaro ipfw_insn_if *cmdif; 2797272840Smelifaro int skip; 2798272840Smelifaro uint16_t v; 2799272840Smelifaro 2800272840Smelifaro skip = 1; 2801272840Smelifaro 2802272840Smelifaro switch (cmd->opcode) { 2803272840Smelifaro case O_IP_SRC_LOOKUP: 2804272840Smelifaro case O_IP_DST_LOOKUP: 2805272840Smelifaro /* Basic IPv4/IPv6 or u32 lookups */ 2806272840Smelifaro *puidx = cmd->arg1; 2807272840Smelifaro /* Assume ADDR by default */ 2808272840Smelifaro *ptype = IPFW_TABLE_ADDR; 2809272840Smelifaro skip = 0; 2810272840Smelifaro 2811272840Smelifaro if (F_LEN(cmd) > F_INSN_SIZE(ipfw_insn_u32)) { 2812272840Smelifaro /* 2813272840Smelifaro * generic lookup. The key must be 2814272840Smelifaro * in 32bit big-endian format. 2815272840Smelifaro */ 2816272840Smelifaro v = ((ipfw_insn_u32 *)cmd)->d[1]; 2817272840Smelifaro switch (v) { 2818272840Smelifaro case 0: 2819272840Smelifaro case 1: 2820272840Smelifaro /* IPv4 src/dst */ 2821272840Smelifaro break; 2822272840Smelifaro case 2: 2823272840Smelifaro case 3: 2824272840Smelifaro /* src/dst port */ 2825272840Smelifaro *ptype = IPFW_TABLE_NUMBER; 2826272840Smelifaro break; 2827272840Smelifaro case 4: 2828272840Smelifaro /* uid/gid */ 2829272840Smelifaro *ptype = IPFW_TABLE_NUMBER; 2830272840Smelifaro break; 2831272840Smelifaro case 5: 2832272840Smelifaro /* jid */ 2833272840Smelifaro *ptype = IPFW_TABLE_NUMBER; 2834272840Smelifaro break; 2835272840Smelifaro case 6: 2836272840Smelifaro /* dscp */ 2837272840Smelifaro *ptype = IPFW_TABLE_NUMBER; 2838272840Smelifaro break; 2839272840Smelifaro } 2840272840Smelifaro } 2841272840Smelifaro break; 2842272840Smelifaro case O_XMIT: 2843272840Smelifaro case O_RECV: 2844272840Smelifaro case O_VIA: 2845272840Smelifaro /* Interface table, possibly */ 2846272840Smelifaro cmdif = (ipfw_insn_if *)cmd; 2847272840Smelifaro if (cmdif->name[0] != '\1') 2848272840Smelifaro break; 2849272840Smelifaro 2850272840Smelifaro *ptype = IPFW_TABLE_INTERFACE; 2851272840Smelifaro *puidx = cmdif->p.kidx; 2852272840Smelifaro skip = 0; 2853272840Smelifaro break; 2854272840Smelifaro case O_IP_FLOW_LOOKUP: 2855272840Smelifaro *puidx = cmd->arg1; 2856272840Smelifaro *ptype = IPFW_TABLE_FLOW; 2857272840Smelifaro skip = 0; 2858272840Smelifaro break; 2859272840Smelifaro } 2860272840Smelifaro 2861272840Smelifaro return (skip); 2862272840Smelifaro} 2863272840Smelifaro 2864272840Smelifaro/* 2865272840Smelifaro * Sets new table value for given opcode. 2866272840Smelifaro * Assume the same opcodes as classify_table_opcode() 2867272840Smelifaro */ 2868272840Smelifarostatic void 2869272840Smelifaroupdate_table_opcode(ipfw_insn *cmd, uint16_t idx) 2870272840Smelifaro{ 2871272840Smelifaro ipfw_insn_if *cmdif; 2872272840Smelifaro 2873272840Smelifaro switch (cmd->opcode) { 2874272840Smelifaro case O_IP_SRC_LOOKUP: 2875272840Smelifaro case O_IP_DST_LOOKUP: 2876272840Smelifaro /* Basic IPv4/IPv6 or u32 lookups */ 2877272840Smelifaro cmd->arg1 = idx; 2878272840Smelifaro break; 2879272840Smelifaro case O_XMIT: 2880272840Smelifaro case O_RECV: 2881272840Smelifaro case O_VIA: 2882272840Smelifaro /* Interface table, possibly */ 2883272840Smelifaro cmdif = (ipfw_insn_if *)cmd; 2884272840Smelifaro cmdif->p.kidx = idx; 2885272840Smelifaro break; 2886272840Smelifaro case O_IP_FLOW_LOOKUP: 2887272840Smelifaro cmd->arg1 = idx; 2888272840Smelifaro break; 2889272840Smelifaro } 2890272840Smelifaro} 2891272840Smelifaro 2892272840Smelifaro/* 2893272840Smelifaro * Checks table name for validity. 2894272840Smelifaro * Enforce basic length checks, the rest 2895272840Smelifaro * should be done in userland. 2896272840Smelifaro * 2897272840Smelifaro * Returns 0 if name is considered valid. 2898272840Smelifaro */ 2899232865Smelifaroint 2900272840Smelifaroipfw_check_table_name(char *name) 2901232865Smelifaro{ 2902272840Smelifaro int nsize; 2903272840Smelifaro ipfw_obj_ntlv *ntlv = NULL; 2904232865Smelifaro 2905272840Smelifaro nsize = sizeof(ntlv->name); 2906272840Smelifaro 2907272840Smelifaro if (strnlen(name, nsize) == nsize) 2908232865Smelifaro return (EINVAL); 2909272840Smelifaro 2910272840Smelifaro if (name[0] == '\0') 2911272840Smelifaro return (EINVAL); 2912272840Smelifaro 2913272840Smelifaro /* 2914272840Smelifaro * TODO: do some more complicated checks 2915272840Smelifaro */ 2916272840Smelifaro 2917232865Smelifaro return (0); 2918232865Smelifaro} 2919232865Smelifaro 2920272840Smelifaro/* 2921272840Smelifaro * Find tablename TLV by @uid. 2922272840Smelifaro * Check @tlvs for valid data inside. 2923272840Smelifaro * 2924272840Smelifaro * Returns pointer to found TLV or NULL. 2925272840Smelifaro */ 2926272840Smelifarostatic ipfw_obj_ntlv * 2927272840Smelifarofind_name_tlv(void *tlvs, int len, uint16_t uidx) 2928272840Smelifaro{ 2929272840Smelifaro ipfw_obj_ntlv *ntlv; 2930272840Smelifaro uintptr_t pa, pe; 2931272840Smelifaro int l; 2932272840Smelifaro 2933272840Smelifaro pa = (uintptr_t)tlvs; 2934272840Smelifaro pe = pa + len; 2935272840Smelifaro l = 0; 2936272840Smelifaro for (; pa < pe; pa += l) { 2937272840Smelifaro ntlv = (ipfw_obj_ntlv *)pa; 2938272840Smelifaro l = ntlv->head.length; 2939272840Smelifaro 2940272840Smelifaro if (l != sizeof(*ntlv)) 2941272840Smelifaro return (NULL); 2942272840Smelifaro 2943272840Smelifaro if (ntlv->head.type != IPFW_TLV_TBL_NAME) 2944272840Smelifaro continue; 2945272840Smelifaro 2946272840Smelifaro if (ntlv->idx != uidx) 2947272840Smelifaro continue; 2948272840Smelifaro 2949272840Smelifaro if (ipfw_check_table_name(ntlv->name) != 0) 2950272840Smelifaro return (NULL); 2951272840Smelifaro 2952272840Smelifaro return (ntlv); 2953272840Smelifaro } 2954272840Smelifaro 2955272840Smelifaro return (NULL); 2956272840Smelifaro} 2957272840Smelifaro 2958272840Smelifaro/* 2959272840Smelifaro * Finds table config based on either legacy index 2960272840Smelifaro * or name in ntlv. 2961272840Smelifaro * Note @ti structure contains unchecked data from userland. 2962272840Smelifaro * 2963272840Smelifaro * Returns pointer to table_config or NULL. 2964272840Smelifaro */ 2965272840Smelifarostatic struct table_config * 2966272840Smelifarofind_table(struct namedobj_instance *ni, struct tid_info *ti) 2967272840Smelifaro{ 2968272840Smelifaro char *name, bname[16]; 2969272840Smelifaro struct named_object *no; 2970272840Smelifaro ipfw_obj_ntlv *ntlv; 2971272840Smelifaro uint32_t set; 2972272840Smelifaro 2973272840Smelifaro if (ti->tlvs != NULL) { 2974272840Smelifaro ntlv = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx); 2975272840Smelifaro if (ntlv == NULL) 2976272840Smelifaro return (NULL); 2977272840Smelifaro name = ntlv->name; 2978272840Smelifaro 2979272840Smelifaro /* 2980272840Smelifaro * Use set provided by @ti instead of @ntlv one. 2981272840Smelifaro * This is needed due to different sets behavior 2982272840Smelifaro * controlled by V_fw_tables_sets. 2983272840Smelifaro */ 2984272840Smelifaro set = ti->set; 2985272840Smelifaro } else { 2986272840Smelifaro snprintf(bname, sizeof(bname), "%d", ti->uidx); 2987272840Smelifaro name = bname; 2988272840Smelifaro set = 0; 2989272840Smelifaro } 2990272840Smelifaro 2991272840Smelifaro no = ipfw_objhash_lookup_name(ni, set, name); 2992272840Smelifaro 2993272840Smelifaro return ((struct table_config *)no); 2994272840Smelifaro} 2995272840Smelifaro 2996272840Smelifaro/* 2997272840Smelifaro * Allocate new table config structure using 2998272840Smelifaro * specified @algo and @aname. 2999272840Smelifaro * 3000272840Smelifaro * Returns pointer to config or NULL. 3001272840Smelifaro */ 3002272840Smelifarostatic struct table_config * 3003272840Smelifaroalloc_table_config(struct ip_fw_chain *ch, struct tid_info *ti, 3004272840Smelifaro struct table_algo *ta, char *aname, uint8_t tflags) 3005272840Smelifaro{ 3006272840Smelifaro char *name, bname[16]; 3007272840Smelifaro struct table_config *tc; 3008272840Smelifaro int error; 3009272840Smelifaro ipfw_obj_ntlv *ntlv; 3010272840Smelifaro uint32_t set; 3011272840Smelifaro 3012272840Smelifaro if (ti->tlvs != NULL) { 3013272840Smelifaro ntlv = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx); 3014272840Smelifaro if (ntlv == NULL) 3015272840Smelifaro return (NULL); 3016272840Smelifaro name = ntlv->name; 3017272840Smelifaro set = ntlv->set; 3018272840Smelifaro } else { 3019272840Smelifaro snprintf(bname, sizeof(bname), "%d", ti->uidx); 3020272840Smelifaro name = bname; 3021272840Smelifaro set = 0; 3022272840Smelifaro } 3023272840Smelifaro 3024272840Smelifaro tc = malloc(sizeof(struct table_config), M_IPFW, M_WAITOK | M_ZERO); 3025272840Smelifaro tc->no.name = tc->tablename; 3026272840Smelifaro tc->no.type = ta->type; 3027272840Smelifaro tc->no.set = set; 3028272840Smelifaro tc->tflags = tflags; 3029272840Smelifaro tc->ta = ta; 3030272840Smelifaro strlcpy(tc->tablename, name, sizeof(tc->tablename)); 3031272840Smelifaro /* Set "shared" value type by default */ 3032272840Smelifaro tc->vshared = 1; 3033272840Smelifaro 3034272840Smelifaro if (ti->tlvs == NULL) { 3035272840Smelifaro tc->no.compat = 1; 3036272840Smelifaro tc->no.uidx = ti->uidx; 3037272840Smelifaro } 3038272840Smelifaro 3039272840Smelifaro /* Preallocate data structures for new tables */ 3040272840Smelifaro error = ta->init(ch, &tc->astate, &tc->ti_copy, aname, tflags); 3041272840Smelifaro if (error != 0) { 3042272840Smelifaro free(tc, M_IPFW); 3043272840Smelifaro return (NULL); 3044272840Smelifaro } 3045272840Smelifaro 3046272840Smelifaro return (tc); 3047272840Smelifaro} 3048272840Smelifaro 3049272840Smelifaro/* 3050272840Smelifaro * Destroys table state and config. 3051272840Smelifaro */ 3052272840Smelifarostatic void 3053272840Smelifarofree_table_config(struct namedobj_instance *ni, struct table_config *tc) 3054272840Smelifaro{ 3055272840Smelifaro 3056272840Smelifaro KASSERT(tc->linked == 0, ("free() on linked config")); 3057278259Smelifaro /* UH lock MUST NOT be held */ 3058272840Smelifaro 3059272840Smelifaro /* 3060272840Smelifaro * We're using ta without any locking/referencing. 3061272840Smelifaro * TODO: fix this if we're going to use unloadable algos. 3062272840Smelifaro */ 3063272840Smelifaro tc->ta->destroy(tc->astate, &tc->ti_copy); 3064272840Smelifaro free(tc, M_IPFW); 3065272840Smelifaro} 3066272840Smelifaro 3067272840Smelifaro/* 3068272840Smelifaro * Links @tc to @chain table named instance. 3069272840Smelifaro * Sets appropriate type/states in @chain table info. 3070272840Smelifaro */ 3071272840Smelifarostatic void 3072272840Smelifarolink_table(struct ip_fw_chain *ch, struct table_config *tc) 3073272840Smelifaro{ 3074272840Smelifaro struct namedobj_instance *ni; 3075272840Smelifaro struct table_info *ti; 3076272840Smelifaro uint16_t kidx; 3077272840Smelifaro 3078272840Smelifaro IPFW_UH_WLOCK_ASSERT(ch); 3079272840Smelifaro IPFW_WLOCK_ASSERT(ch); 3080272840Smelifaro 3081272840Smelifaro ni = CHAIN_TO_NI(ch); 3082272840Smelifaro kidx = tc->no.kidx; 3083272840Smelifaro 3084272840Smelifaro ipfw_objhash_add(ni, &tc->no); 3085272840Smelifaro 3086272840Smelifaro ti = KIDX_TO_TI(ch, kidx); 3087272840Smelifaro *ti = tc->ti_copy; 3088272840Smelifaro 3089272840Smelifaro /* Notify algo on real @ti address */ 3090272840Smelifaro if (tc->ta->change_ti != NULL) 3091272840Smelifaro tc->ta->change_ti(tc->astate, ti); 3092272840Smelifaro 3093272840Smelifaro tc->linked = 1; 3094272840Smelifaro tc->ta->refcnt++; 3095272840Smelifaro} 3096272840Smelifaro 3097272840Smelifaro/* 3098272840Smelifaro * Unlinks @tc from @chain table named instance. 3099272840Smelifaro * Zeroes states in @chain and stores them in @tc. 3100272840Smelifaro */ 3101272840Smelifarostatic void 3102272840Smelifarounlink_table(struct ip_fw_chain *ch, struct table_config *tc) 3103272840Smelifaro{ 3104272840Smelifaro struct namedobj_instance *ni; 3105272840Smelifaro struct table_info *ti; 3106272840Smelifaro uint16_t kidx; 3107272840Smelifaro 3108272840Smelifaro IPFW_UH_WLOCK_ASSERT(ch); 3109272840Smelifaro IPFW_WLOCK_ASSERT(ch); 3110272840Smelifaro 3111272840Smelifaro ni = CHAIN_TO_NI(ch); 3112272840Smelifaro kidx = tc->no.kidx; 3113272840Smelifaro 3114272840Smelifaro /* Clear state. @ti copy is already saved inside @tc */ 3115272840Smelifaro ipfw_objhash_del(ni, &tc->no); 3116272840Smelifaro ti = KIDX_TO_TI(ch, kidx); 3117272840Smelifaro memset(ti, 0, sizeof(struct table_info)); 3118272840Smelifaro tc->linked = 0; 3119272840Smelifaro tc->ta->refcnt--; 3120272840Smelifaro 3121272840Smelifaro /* Notify algo on real @ti address */ 3122272840Smelifaro if (tc->ta->change_ti != NULL) 3123272840Smelifaro tc->ta->change_ti(tc->astate, NULL); 3124272840Smelifaro} 3125272840Smelifaro 3126272840Smelifarostruct swap_table_args { 3127272840Smelifaro int set; 3128272840Smelifaro int new_set; 3129272840Smelifaro int mv; 3130272840Smelifaro}; 3131272840Smelifaro 3132272840Smelifaro/* 3133272840Smelifaro * Change set for each matching table. 3134272840Smelifaro * 3135272840Smelifaro * Ensure we dispatch each table once by setting/checking ochange 3136272840Smelifaro * fields. 3137272840Smelifaro */ 3138272840Smelifarostatic void 3139272840Smelifaroswap_table_set(struct namedobj_instance *ni, struct named_object *no, 3140272840Smelifaro void *arg) 3141272840Smelifaro{ 3142272840Smelifaro struct table_config *tc; 3143272840Smelifaro struct swap_table_args *sta; 3144272840Smelifaro 3145272840Smelifaro tc = (struct table_config *)no; 3146272840Smelifaro sta = (struct swap_table_args *)arg; 3147272840Smelifaro 3148272840Smelifaro if (no->set != sta->set && (no->set != sta->new_set || sta->mv != 0)) 3149272840Smelifaro return; 3150272840Smelifaro 3151272840Smelifaro if (tc->ochanged != 0) 3152272840Smelifaro return; 3153272840Smelifaro 3154272840Smelifaro tc->ochanged = 1; 3155272840Smelifaro ipfw_objhash_del(ni, no); 3156272840Smelifaro if (no->set == sta->set) 3157272840Smelifaro no->set = sta->new_set; 3158272840Smelifaro else 3159272840Smelifaro no->set = sta->set; 3160272840Smelifaro ipfw_objhash_add(ni, no); 3161272840Smelifaro} 3162272840Smelifaro 3163272840Smelifaro/* 3164272840Smelifaro * Cleans up ochange field for all tables. 3165272840Smelifaro */ 3166272840Smelifarostatic void 3167272840Smelifaroclean_table_set_data(struct namedobj_instance *ni, struct named_object *no, 3168272840Smelifaro void *arg) 3169272840Smelifaro{ 3170272840Smelifaro struct table_config *tc; 3171272840Smelifaro struct swap_table_args *sta; 3172272840Smelifaro 3173272840Smelifaro tc = (struct table_config *)no; 3174272840Smelifaro sta = (struct swap_table_args *)arg; 3175272840Smelifaro 3176272840Smelifaro tc->ochanged = 0; 3177272840Smelifaro} 3178272840Smelifaro 3179272840Smelifaro/* 3180272840Smelifaro * Swaps tables within two sets. 3181272840Smelifaro */ 3182272840Smelifarovoid 3183272840Smelifaroipfw_swap_tables_sets(struct ip_fw_chain *ch, uint32_t set, 3184272840Smelifaro uint32_t new_set, int mv) 3185272840Smelifaro{ 3186272840Smelifaro struct swap_table_args sta; 3187272840Smelifaro 3188272840Smelifaro IPFW_UH_WLOCK_ASSERT(ch); 3189272840Smelifaro 3190272840Smelifaro sta.set = set; 3191272840Smelifaro sta.new_set = new_set; 3192272840Smelifaro sta.mv = mv; 3193272840Smelifaro 3194272840Smelifaro ipfw_objhash_foreach(CHAIN_TO_NI(ch), swap_table_set, &sta); 3195272840Smelifaro ipfw_objhash_foreach(CHAIN_TO_NI(ch), clean_table_set_data, &sta); 3196272840Smelifaro} 3197272840Smelifaro 3198272840Smelifaro/* 3199272840Smelifaro * Move all tables which are reference by rules in @rr to set @new_set. 3200272840Smelifaro * Makes sure that all relevant tables are referenced ONLLY by given rules. 3201272840Smelifaro * 3202272840Smelifaro * Retuns 0 on success, 3203272840Smelifaro */ 3204272840Smelifaroint 3205272840Smelifaroipfw_move_tables_sets(struct ip_fw_chain *ch, ipfw_range_tlv *rt, 3206272840Smelifaro uint32_t new_set) 3207272840Smelifaro{ 3208272840Smelifaro struct ip_fw *rule; 3209272840Smelifaro struct table_config *tc; 3210272840Smelifaro struct named_object *no; 3211272840Smelifaro struct namedobj_instance *ni; 3212272840Smelifaro int bad, i, l, cmdlen; 3213272840Smelifaro uint16_t kidx; 3214272840Smelifaro uint8_t type; 3215272840Smelifaro ipfw_insn *cmd; 3216272840Smelifaro 3217272840Smelifaro IPFW_UH_WLOCK_ASSERT(ch); 3218272840Smelifaro 3219272840Smelifaro ni = CHAIN_TO_NI(ch); 3220272840Smelifaro 3221272840Smelifaro /* Stage 1: count number of references by given rules */ 3222272840Smelifaro for (i = 0; i < ch->n_rules - 1; i++) { 3223272840Smelifaro rule = ch->map[i]; 3224272840Smelifaro if (ipfw_match_range(rule, rt) == 0) 3225272840Smelifaro continue; 3226272840Smelifaro 3227272840Smelifaro l = rule->cmd_len; 3228272840Smelifaro cmd = rule->cmd; 3229272840Smelifaro cmdlen = 0; 3230272840Smelifaro for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 3231272840Smelifaro cmdlen = F_LEN(cmd); 3232272840Smelifaro if (classify_table_opcode(cmd, &kidx, &type) != 0) 3233272840Smelifaro continue; 3234272840Smelifaro no = ipfw_objhash_lookup_kidx(ni, kidx); 3235272840Smelifaro KASSERT(no != NULL, 3236272840Smelifaro ("objhash lookup failed on index %d", kidx)); 3237272840Smelifaro tc = (struct table_config *)no; 3238272840Smelifaro tc->ocount++; 3239272840Smelifaro } 3240272840Smelifaro 3241272840Smelifaro } 3242272840Smelifaro 3243272840Smelifaro /* Stage 2: verify "ownership" */ 3244272840Smelifaro bad = 0; 3245272840Smelifaro for (i = 0; i < ch->n_rules - 1; i++) { 3246272840Smelifaro rule = ch->map[i]; 3247272840Smelifaro if (ipfw_match_range(rule, rt) == 0) 3248272840Smelifaro continue; 3249272840Smelifaro 3250272840Smelifaro l = rule->cmd_len; 3251272840Smelifaro cmd = rule->cmd; 3252272840Smelifaro cmdlen = 0; 3253272840Smelifaro for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 3254272840Smelifaro cmdlen = F_LEN(cmd); 3255272840Smelifaro if (classify_table_opcode(cmd, &kidx, &type) != 0) 3256272840Smelifaro continue; 3257272840Smelifaro no = ipfw_objhash_lookup_kidx(ni, kidx); 3258272840Smelifaro KASSERT(no != NULL, 3259272840Smelifaro ("objhash lookup failed on index %d", kidx)); 3260272840Smelifaro tc = (struct table_config *)no; 3261272840Smelifaro if (tc->no.refcnt != tc->ocount) { 3262272840Smelifaro 3263272840Smelifaro /* 3264272840Smelifaro * Number of references differ: 3265272840Smelifaro * Other rule(s) are holding reference to given 3266272840Smelifaro * table, so it is not possible to change its set. 3267272840Smelifaro * 3268272840Smelifaro * Note that refcnt may account 3269272840Smelifaro * references to some going-to-be-added rules. 3270272840Smelifaro * Since we don't know their numbers (and event 3271272840Smelifaro * if they will be added) it is perfectly OK 3272272840Smelifaro * to return error here. 3273272840Smelifaro */ 3274272840Smelifaro bad = 1; 3275272840Smelifaro break; 3276272840Smelifaro } 3277272840Smelifaro } 3278272840Smelifaro 3279272840Smelifaro if (bad != 0) 3280272840Smelifaro break; 3281272840Smelifaro } 3282272840Smelifaro 3283272840Smelifaro /* Stage 3: change set or cleanup */ 3284272840Smelifaro for (i = 0; i < ch->n_rules - 1; i++) { 3285272840Smelifaro rule = ch->map[i]; 3286272840Smelifaro if (ipfw_match_range(rule, rt) == 0) 3287272840Smelifaro continue; 3288272840Smelifaro 3289272840Smelifaro l = rule->cmd_len; 3290272840Smelifaro cmd = rule->cmd; 3291272840Smelifaro cmdlen = 0; 3292272840Smelifaro for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 3293272840Smelifaro cmdlen = F_LEN(cmd); 3294272840Smelifaro if (classify_table_opcode(cmd, &kidx, &type) != 0) 3295272840Smelifaro continue; 3296272840Smelifaro no = ipfw_objhash_lookup_kidx(ni, kidx); 3297272840Smelifaro KASSERT(no != NULL, 3298272840Smelifaro ("objhash lookup failed on index %d", kidx)); 3299272840Smelifaro tc = (struct table_config *)no; 3300272840Smelifaro 3301272840Smelifaro tc->ocount = 0; 3302272840Smelifaro if (bad != 0) 3303272840Smelifaro continue; 3304272840Smelifaro 3305272840Smelifaro /* Actually change set. */ 3306272840Smelifaro ipfw_objhash_del(ni, no); 3307272840Smelifaro no->set = new_set; 3308272840Smelifaro ipfw_objhash_add(ni, no); 3309272840Smelifaro } 3310272840Smelifaro } 3311272840Smelifaro 3312272840Smelifaro return (bad); 3313272840Smelifaro} 3314272840Smelifaro 3315272840Smelifaro/* 3316272840Smelifaro * Finds and bumps refcount for tables referenced by given @rule. 3317272840Smelifaro * Auto-creates non-existing tables. 3318272840Smelifaro * Fills in @oib array with userland/kernel indexes. 3319272840Smelifaro * First free oidx pointer is saved back in @oib. 3320272840Smelifaro * 3321272840Smelifaro * Returns 0 on success. 3322272840Smelifaro */ 3323272840Smelifarostatic int 3324272840Smelifarofind_ref_rule_tables(struct ip_fw_chain *ch, struct ip_fw *rule, 3325272840Smelifaro struct rule_check_info *ci, struct obj_idx **oib, struct tid_info *ti) 3326272840Smelifaro{ 3327272840Smelifaro struct table_config *tc; 3328272840Smelifaro struct namedobj_instance *ni; 3329272840Smelifaro struct named_object *no; 3330272840Smelifaro int cmdlen, error, l, numnew; 3331272840Smelifaro uint16_t kidx; 3332272840Smelifaro ipfw_insn *cmd; 3333272840Smelifaro struct obj_idx *pidx, *pidx_first, *p; 3334272840Smelifaro 3335272840Smelifaro pidx_first = *oib; 3336272840Smelifaro pidx = pidx_first; 3337272840Smelifaro l = rule->cmd_len; 3338272840Smelifaro cmd = rule->cmd; 3339272840Smelifaro cmdlen = 0; 3340272840Smelifaro error = 0; 3341272840Smelifaro numnew = 0; 3342272840Smelifaro 3343272840Smelifaro IPFW_UH_WLOCK(ch); 3344272840Smelifaro ni = CHAIN_TO_NI(ch); 3345272840Smelifaro 3346272840Smelifaro /* Increase refcount on each existing referenced table. */ 3347272840Smelifaro for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 3348272840Smelifaro cmdlen = F_LEN(cmd); 3349272840Smelifaro 3350272840Smelifaro if (classify_table_opcode(cmd, &ti->uidx, &ti->type) != 0) 3351272840Smelifaro continue; 3352272840Smelifaro 3353272840Smelifaro pidx->uidx = ti->uidx; 3354272840Smelifaro pidx->type = ti->type; 3355272840Smelifaro 3356272840Smelifaro if ((tc = find_table(ni, ti)) != NULL) { 3357272840Smelifaro if (tc->no.type != ti->type) { 3358272840Smelifaro /* Incompatible types */ 3359272840Smelifaro error = EINVAL; 3360272840Smelifaro break; 3361272840Smelifaro } 3362272840Smelifaro 3363272840Smelifaro /* Reference found table and save kidx */ 3364272840Smelifaro tc->no.refcnt++; 3365272840Smelifaro pidx->kidx = tc->no.kidx; 3366272840Smelifaro pidx++; 3367272840Smelifaro continue; 3368272840Smelifaro } 3369272840Smelifaro 3370272840Smelifaro /* 3371272840Smelifaro * Compability stuff for old clients: 3372272840Smelifaro * prepare to manually create non-existing tables. 3373272840Smelifaro */ 3374272840Smelifaro pidx++; 3375272840Smelifaro numnew++; 3376272840Smelifaro } 3377272840Smelifaro 3378272840Smelifaro if (error != 0) { 3379272840Smelifaro /* Unref everything we have already done */ 3380272840Smelifaro for (p = *oib; p < pidx; p++) { 3381272840Smelifaro if (p->kidx == 0) 3382272840Smelifaro continue; 3383272840Smelifaro 3384272840Smelifaro /* Find & unref by existing idx */ 3385272840Smelifaro no = ipfw_objhash_lookup_kidx(ni, p->kidx); 3386272840Smelifaro KASSERT(no != NULL, ("Ref'd table %d disappeared", 3387272840Smelifaro p->kidx)); 3388272840Smelifaro 3389272840Smelifaro no->refcnt--; 3390272840Smelifaro } 3391272840Smelifaro } 3392272840Smelifaro 3393272840Smelifaro IPFW_UH_WUNLOCK(ch); 3394272840Smelifaro 3395272840Smelifaro if (numnew == 0) { 3396272840Smelifaro *oib = pidx; 3397272840Smelifaro return (error); 3398272840Smelifaro } 3399272840Smelifaro 3400272840Smelifaro /* 3401272840Smelifaro * Compatibility stuff: do actual creation for non-existing, 3402272840Smelifaro * but referenced tables. 3403272840Smelifaro */ 3404272840Smelifaro for (p = pidx_first; p < pidx; p++) { 3405272840Smelifaro if (p->kidx != 0) 3406272840Smelifaro continue; 3407272840Smelifaro 3408272840Smelifaro ti->uidx = p->uidx; 3409272840Smelifaro ti->type = p->type; 3410272840Smelifaro ti->atype = 0; 3411272840Smelifaro 3412272840Smelifaro error = create_table_compat(ch, ti, &kidx); 3413272840Smelifaro if (error == 0) { 3414272840Smelifaro p->kidx = kidx; 3415272840Smelifaro continue; 3416272840Smelifaro } 3417272840Smelifaro 3418272840Smelifaro /* Error. We have to drop references */ 3419272840Smelifaro IPFW_UH_WLOCK(ch); 3420272840Smelifaro for (p = pidx_first; p < pidx; p++) { 3421272840Smelifaro if (p->kidx == 0) 3422272840Smelifaro continue; 3423272840Smelifaro 3424272840Smelifaro /* Find & unref by existing idx */ 3425272840Smelifaro no = ipfw_objhash_lookup_kidx(ni, p->kidx); 3426272840Smelifaro KASSERT(no != NULL, ("Ref'd table %d disappeared", 3427272840Smelifaro p->kidx)); 3428272840Smelifaro 3429272840Smelifaro no->refcnt--; 3430272840Smelifaro } 3431272840Smelifaro IPFW_UH_WUNLOCK(ch); 3432272840Smelifaro 3433272840Smelifaro return (error); 3434272840Smelifaro } 3435272840Smelifaro 3436272840Smelifaro *oib = pidx; 3437272840Smelifaro 3438272840Smelifaro return (error); 3439272840Smelifaro} 3440272840Smelifaro 3441272840Smelifaro/* 3442272840Smelifaro * Remove references from every table used in @rule. 3443272840Smelifaro */ 3444272840Smelifarovoid 3445272840Smelifaroipfw_unref_rule_tables(struct ip_fw_chain *chain, struct ip_fw *rule) 3446272840Smelifaro{ 3447272840Smelifaro int cmdlen, l; 3448272840Smelifaro ipfw_insn *cmd; 3449272840Smelifaro struct namedobj_instance *ni; 3450272840Smelifaro struct named_object *no; 3451272840Smelifaro uint16_t kidx; 3452272840Smelifaro uint8_t type; 3453272840Smelifaro 3454272840Smelifaro IPFW_UH_WLOCK_ASSERT(chain); 3455272840Smelifaro ni = CHAIN_TO_NI(chain); 3456272840Smelifaro 3457272840Smelifaro l = rule->cmd_len; 3458272840Smelifaro cmd = rule->cmd; 3459272840Smelifaro cmdlen = 0; 3460272840Smelifaro for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 3461272840Smelifaro cmdlen = F_LEN(cmd); 3462272840Smelifaro 3463272840Smelifaro if (classify_table_opcode(cmd, &kidx, &type) != 0) 3464272840Smelifaro continue; 3465272840Smelifaro 3466272840Smelifaro no = ipfw_objhash_lookup_kidx(ni, kidx); 3467272840Smelifaro 3468272840Smelifaro KASSERT(no != NULL, ("table id %d not found", kidx)); 3469272840Smelifaro KASSERT(no->type == type, ("wrong type %d (%d) for table id %d", 3470272840Smelifaro no->type, type, kidx)); 3471272840Smelifaro KASSERT(no->refcnt > 0, ("refcount for table %d is %d", 3472272840Smelifaro kidx, no->refcnt)); 3473272840Smelifaro 3474272840Smelifaro no->refcnt--; 3475272840Smelifaro } 3476272840Smelifaro} 3477272840Smelifaro 3478272840Smelifaro/* 3479272840Smelifaro * Compatibility function for old ipfw(8) binaries. 3480272840Smelifaro * Rewrites table kernel indices with userland ones. 3481272840Smelifaro * Convert tables matching '/^\d+$/' to their atoi() value. 3482272840Smelifaro * Use number 65535 for other tables. 3483272840Smelifaro * 3484272840Smelifaro * Returns 0 on success. 3485272840Smelifaro */ 3486272840Smelifaroint 3487272840Smelifaroipfw_rewrite_table_kidx(struct ip_fw_chain *chain, struct ip_fw_rule0 *rule) 3488272840Smelifaro{ 3489272840Smelifaro int cmdlen, error, l; 3490272840Smelifaro ipfw_insn *cmd; 3491272840Smelifaro uint16_t kidx, uidx; 3492272840Smelifaro uint8_t type; 3493272840Smelifaro struct named_object *no; 3494272840Smelifaro struct namedobj_instance *ni; 3495272840Smelifaro 3496272840Smelifaro ni = CHAIN_TO_NI(chain); 3497272840Smelifaro error = 0; 3498272840Smelifaro 3499272840Smelifaro l = rule->cmd_len; 3500272840Smelifaro cmd = rule->cmd; 3501272840Smelifaro cmdlen = 0; 3502272840Smelifaro for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 3503272840Smelifaro cmdlen = F_LEN(cmd); 3504272840Smelifaro 3505272840Smelifaro if (classify_table_opcode(cmd, &kidx, &type) != 0) 3506272840Smelifaro continue; 3507272840Smelifaro 3508272840Smelifaro if ((no = ipfw_objhash_lookup_kidx(ni, kidx)) == NULL) 3509272840Smelifaro return (1); 3510272840Smelifaro 3511272840Smelifaro uidx = no->uidx; 3512272840Smelifaro if (no->compat == 0) { 3513272840Smelifaro 3514272840Smelifaro /* 3515272840Smelifaro * We are called via legacy opcode. 3516272840Smelifaro * Save error and show table as fake number 3517272840Smelifaro * not to make ipfw(8) hang. 3518272840Smelifaro */ 3519272840Smelifaro uidx = 65535; 3520272840Smelifaro error = 2; 3521272840Smelifaro } 3522272840Smelifaro 3523272840Smelifaro update_table_opcode(cmd, uidx); 3524272840Smelifaro } 3525272840Smelifaro 3526272840Smelifaro return (error); 3527272840Smelifaro} 3528272840Smelifaro 3529272840Smelifaro/* 3530272840Smelifaro * Checks is opcode is referencing table of appropriate type. 3531272840Smelifaro * Adds reference count for found table if true. 3532272840Smelifaro * Rewrites user-supplied opcode values with kernel ones. 3533272840Smelifaro * 3534272840Smelifaro * Returns 0 on success and appropriate error code otherwise. 3535272840Smelifaro */ 3536272840Smelifaroint 3537272840Smelifaroipfw_rewrite_table_uidx(struct ip_fw_chain *chain, 3538272840Smelifaro struct rule_check_info *ci) 3539272840Smelifaro{ 3540272840Smelifaro int cmdlen, error, l; 3541272840Smelifaro ipfw_insn *cmd; 3542272840Smelifaro uint16_t uidx; 3543272840Smelifaro uint8_t type; 3544272840Smelifaro struct namedobj_instance *ni; 3545272840Smelifaro struct obj_idx *p, *pidx_first, *pidx_last; 3546272840Smelifaro struct tid_info ti; 3547272840Smelifaro 3548272840Smelifaro ni = CHAIN_TO_NI(chain); 3549272840Smelifaro 3550272840Smelifaro /* 3551272840Smelifaro * Prepare an array for storing opcode indices. 3552272840Smelifaro * Use stack allocation by default. 3553272840Smelifaro */ 3554272840Smelifaro if (ci->table_opcodes <= (sizeof(ci->obuf)/sizeof(ci->obuf[0]))) { 3555272840Smelifaro /* Stack */ 3556272840Smelifaro pidx_first = ci->obuf; 3557272840Smelifaro } else 3558272840Smelifaro pidx_first = malloc(ci->table_opcodes * sizeof(struct obj_idx), 3559272840Smelifaro M_IPFW, M_WAITOK | M_ZERO); 3560272840Smelifaro 3561272840Smelifaro pidx_last = pidx_first; 3562272840Smelifaro error = 0; 3563272840Smelifaro type = 0; 3564272840Smelifaro memset(&ti, 0, sizeof(ti)); 3565272840Smelifaro 3566272840Smelifaro /* 3567272840Smelifaro * Use default set for looking up tables (old way) or 3568272840Smelifaro * use set rule is assigned to (new way). 3569272840Smelifaro */ 3570272840Smelifaro ti.set = (V_fw_tables_sets != 0) ? ci->krule->set : 0; 3571272840Smelifaro if (ci->ctlv != NULL) { 3572272840Smelifaro ti.tlvs = (void *)(ci->ctlv + 1); 3573272840Smelifaro ti.tlen = ci->ctlv->head.length - sizeof(ipfw_obj_ctlv); 3574272840Smelifaro } 3575272840Smelifaro 3576272840Smelifaro /* Reference all used tables */ 3577272840Smelifaro error = find_ref_rule_tables(chain, ci->krule, ci, &pidx_last, &ti); 3578272840Smelifaro if (error != 0) 3579272840Smelifaro goto free; 3580272840Smelifaro 3581272840Smelifaro IPFW_UH_WLOCK(chain); 3582272840Smelifaro 3583272840Smelifaro /* Perform rule rewrite */ 3584272840Smelifaro l = ci->krule->cmd_len; 3585272840Smelifaro cmd = ci->krule->cmd; 3586272840Smelifaro cmdlen = 0; 3587272840Smelifaro p = pidx_first; 3588272840Smelifaro for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { 3589272840Smelifaro cmdlen = F_LEN(cmd); 3590272840Smelifaro if (classify_table_opcode(cmd, &uidx, &type) != 0) 3591272840Smelifaro continue; 3592272840Smelifaro update_table_opcode(cmd, p->kidx); 3593272840Smelifaro p++; 3594272840Smelifaro } 3595272840Smelifaro 3596272840Smelifaro IPFW_UH_WUNLOCK(chain); 3597272840Smelifaro 3598272840Smelifarofree: 3599272840Smelifaro if (pidx_first != ci->obuf) 3600272840Smelifaro free(pidx_first, M_IPFW); 3601272840Smelifaro 3602272840Smelifaro return (error); 3603272840Smelifaro} 3604272840Smelifaro 3605272840Smelifarostatic struct ipfw_sopt_handler scodes[] = { 3606272840Smelifaro { IP_FW_TABLE_XCREATE, 0, HDIR_SET, create_table }, 3607272840Smelifaro { IP_FW_TABLE_XDESTROY, 0, HDIR_SET, flush_table_v0 }, 3608272840Smelifaro { IP_FW_TABLE_XFLUSH, 0, HDIR_SET, flush_table_v0 }, 3609272840Smelifaro { IP_FW_TABLE_XMODIFY, 0, HDIR_BOTH, modify_table }, 3610272840Smelifaro { IP_FW_TABLE_XINFO, 0, HDIR_GET, describe_table }, 3611272840Smelifaro { IP_FW_TABLES_XLIST, 0, HDIR_GET, list_tables }, 3612272840Smelifaro { IP_FW_TABLE_XLIST, 0, HDIR_GET, dump_table_v0 }, 3613272840Smelifaro { IP_FW_TABLE_XLIST, 1, HDIR_GET, dump_table_v1 }, 3614272840Smelifaro { IP_FW_TABLE_XADD, 0, HDIR_BOTH, manage_table_ent_v0 }, 3615272840Smelifaro { IP_FW_TABLE_XADD, 1, HDIR_BOTH, manage_table_ent_v1 }, 3616272840Smelifaro { IP_FW_TABLE_XDEL, 0, HDIR_BOTH, manage_table_ent_v0 }, 3617272840Smelifaro { IP_FW_TABLE_XDEL, 1, HDIR_BOTH, manage_table_ent_v1 }, 3618272840Smelifaro { IP_FW_TABLE_XFIND, 0, HDIR_GET, find_table_entry }, 3619272840Smelifaro { IP_FW_TABLE_XSWAP, 0, HDIR_SET, swap_table }, 3620272840Smelifaro { IP_FW_TABLES_ALIST, 0, HDIR_GET, list_table_algo }, 3621272840Smelifaro { IP_FW_TABLE_XGETSIZE, 0, HDIR_GET, get_table_size }, 3622272840Smelifaro}; 3623272840Smelifaro 3624272840Smelifarostatic void 3625272840Smelifarodestroy_table_locked(struct namedobj_instance *ni, struct named_object *no, 3626272840Smelifaro void *arg) 3627272840Smelifaro{ 3628272840Smelifaro 3629272840Smelifaro unlink_table((struct ip_fw_chain *)arg, (struct table_config *)no); 3630272840Smelifaro if (ipfw_objhash_free_idx(ni, no->kidx) != 0) 3631272840Smelifaro printf("Error unlinking kidx %d from table %s\n", 3632272840Smelifaro no->kidx, no->name); 3633272840Smelifaro free_table_config(ni, (struct table_config *)no); 3634272840Smelifaro} 3635272840Smelifaro 3636272840Smelifaro/* 3637272840Smelifaro * Shuts tables module down. 3638272840Smelifaro */ 3639272840Smelifarovoid 3640272840Smelifaroipfw_destroy_tables(struct ip_fw_chain *ch, int last) 3641272840Smelifaro{ 3642272840Smelifaro 3643272840Smelifaro IPFW_DEL_SOPT_HANDLER(last, scodes); 3644272840Smelifaro 3645272840Smelifaro /* Remove all tables from working set */ 3646272840Smelifaro IPFW_UH_WLOCK(ch); 3647272840Smelifaro IPFW_WLOCK(ch); 3648272840Smelifaro ipfw_objhash_foreach(CHAIN_TO_NI(ch), destroy_table_locked, ch); 3649272840Smelifaro IPFW_WUNLOCK(ch); 3650272840Smelifaro IPFW_UH_WUNLOCK(ch); 3651272840Smelifaro 3652272840Smelifaro /* Free pointers itself */ 3653272840Smelifaro free(ch->tablestate, M_IPFW); 3654272840Smelifaro 3655272840Smelifaro ipfw_table_value_destroy(ch, last); 3656272840Smelifaro ipfw_table_algo_destroy(ch); 3657272840Smelifaro 3658272840Smelifaro ipfw_objhash_destroy(CHAIN_TO_NI(ch)); 3659272840Smelifaro free(CHAIN_TO_TCFG(ch), M_IPFW); 3660272840Smelifaro} 3661272840Smelifaro 3662272840Smelifaro/* 3663272840Smelifaro * Starts tables module. 3664272840Smelifaro */ 3665272840Smelifaroint 3666272840Smelifaroipfw_init_tables(struct ip_fw_chain *ch, int first) 3667272840Smelifaro{ 3668272840Smelifaro struct tables_config *tcfg; 3669272840Smelifaro 3670272840Smelifaro /* Allocate pointers */ 3671272840Smelifaro ch->tablestate = malloc(V_fw_tables_max * sizeof(struct table_info), 3672272840Smelifaro M_IPFW, M_WAITOK | M_ZERO); 3673272840Smelifaro 3674272840Smelifaro tcfg = malloc(sizeof(struct tables_config), M_IPFW, M_WAITOK | M_ZERO); 3675272840Smelifaro tcfg->namehash = ipfw_objhash_create(V_fw_tables_max); 3676272840Smelifaro ch->tblcfg = tcfg; 3677272840Smelifaro 3678272840Smelifaro ipfw_table_value_init(ch, first); 3679272840Smelifaro ipfw_table_algo_init(ch); 3680272840Smelifaro 3681272840Smelifaro IPFW_ADD_SOPT_HANDLER(first, scodes); 3682272840Smelifaro return (0); 3683272840Smelifaro} 3684272840Smelifaro 3685272840Smelifaro 3686272840Smelifaro 3687