ip_fw_table_value.c revision 270982
1185377Ssam/*- 2187831Ssam * Copyright (c) 2004 Ruslan Ermilov and Vsevolod Lobko. 3185377Ssam * 4185377Ssam * Redistribution and use in source and binary forms, with or without 5185377Ssam * modification, are permitted provided that the following conditions 6185377Ssam * are met: 7185377Ssam * 1. Redistributions of source code must retain the above copyright 8185377Ssam * notice, this list of conditions and the following disclaimer. 9185377Ssam * 2. Redistributions in binary form must reproduce the above copyright 10185377Ssam * notice, this list of conditions and the following disclaimer in the 11185377Ssam * documentation and/or other materials provided with the distribution. 12185377Ssam * 13185377Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14185377Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15185377Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16185377Ssam * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17187831Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18185377Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19185377Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20185377Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21185377Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22185377Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23185377Ssam * SUCH DAMAGE. 24185377Ssam */ 25185377Ssam 26185377Ssam#include <sys/cdefs.h> 27185377Ssam__FBSDID("$FreeBSD: projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c 270407 2014-08-23 12:41:39Z melifaro $"); 28185380Ssam 29185380Ssam/* 30185377Ssam * Multi-field value support for ipfw tables. 31185377Ssam * 32185377Ssam * This file contains necessary functions to convert 33185377Ssam * large multi-field values into u32 indices suitable to be fed 34185377Ssam * to various table algorithms. Other machinery like proper refcounting, 35185377Ssam * internal structures resizing are also kept here. 36185377Ssam */ 37185377Ssam 38185377Ssam#include "opt_ipfw.h" 39185377Ssam 40185377Ssam#include <sys/param.h> 41185377Ssam#include <sys/systm.h> 42185377Ssam#include <sys/malloc.h> 43185377Ssam#include <sys/kernel.h> 44185377Ssam#include <sys/hash.h> 45185377Ssam#include <sys/lock.h> 46185377Ssam#include <sys/rwlock.h> 47185377Ssam#include <sys/socket.h> 48185377Ssam#include <sys/socketvar.h> 49185377Ssam#include <sys/queue.h> 50185377Ssam#include <net/if.h> /* ip_fw.h requires IFNAMSIZ */ 51185377Ssam 52185377Ssam#include <netinet/in.h> 53185377Ssam#include <netinet/ip_var.h> /* struct ipfw_rule_ref */ 54185377Ssam#include <netinet/ip_fw.h> 55185377Ssam 56185377Ssam#include <netpfil/ipfw/ip_fw_private.h> 57185377Ssam#include <netpfil/ipfw/ip_fw_table.h> 58185377Ssam 59185377Ssamstatic uint32_t hash_table_value(struct namedobj_instance *ni, void *key, 60185377Ssam uint32_t kopt); 61185377Ssamstatic int cmp_table_value(struct named_object *no, void *key, uint32_t kopt); 62185377Ssam 63185377Ssam#define CHAIN_TO_VI(chain) (CHAIN_TO_TCFG(chain)->valhash) 64185377Ssam 65185377Ssamstruct table_val_link 66185377Ssam{ 67185377Ssam struct named_object no; 68185377Ssam struct table_value *pval; /* Pointer to real table value */ 69185377Ssam}; 70185377Ssam#define VALDATA_START_SIZE 64 /* Allocate 64-items array by default */ 71185377Ssam 72185377Ssamstruct vdump_args { 73185377Ssam struct ip_fw_chain *ch; 74185377Ssam struct sockopt_data *sd; 75185377Ssam struct table_value *pval; 76185377Ssam int error; 77185377Ssam}; 78185377Ssam 79185377Ssam 80185377Ssamvoid 81185380Ssamipfw_table_value_init(struct ip_fw_chain *ch) 82185377Ssam{ 83185380Ssam struct tables_config *tcfg; 84185380Ssam 85185380Ssam ch->valuestate = malloc(VALDATA_START_SIZE * sizeof(struct table_value), 86185380Ssam M_IPFW, M_WAITOK | M_ZERO); 87185380Ssam 88185380Ssam tcfg = ch->tblcfg; 89185380Ssam 90185380Ssam tcfg->val_size = VALDATA_START_SIZE; 91185380Ssam tcfg->valhash = ipfw_objhash_create(tcfg->val_size); 92185380Ssam ipfw_objhash_set_funcs(tcfg->valhash, hash_table_value, 93185380Ssam cmp_table_value); 94185380Ssam} 95185380Ssam 96185380Ssamstatic void 97185380Ssamdestroy_value(struct namedobj_instance *ni, struct named_object *no, 98185380Ssam void *arg) 99185380Ssam{ 100185380Ssam 101185380Ssam free(no, M_IPFW); 102185380Ssam} 103185380Ssam 104185380Ssamvoid 105185380Ssamipfw_table_value_destroy(struct ip_fw_chain *ch) 106185380Ssam{ 107185380Ssam 108185380Ssam free(ch->valuestate, M_IPFW); 109185380Ssam ipfw_objhash_foreach(CHAIN_TO_VI(ch), destroy_value, ch); 110185380Ssam ipfw_objhash_destroy(CHAIN_TO_VI(ch)); 111185380Ssam} 112185380Ssam 113185380Ssamstatic uint32_t 114185380Ssamhash_table_value(struct namedobj_instance *ni, void *key, uint32_t kopt) 115185380Ssam{ 116185380Ssam 117185380Ssam return (hash32_buf(key, 56, 0)); 118185380Ssam} 119185380Ssam 120185380Ssamstatic int 121185380Ssamcmp_table_value(struct named_object *no, void *key, uint32_t kopt) 122185377Ssam{ 123185377Ssam 124185377Ssam return (memcmp(((struct table_val_link *)no)->pval, key, 56)); 125185377Ssam} 126185377Ssam 127185377Ssamstatic void 128185377Ssammask_table_value(struct table_value *src, struct table_value *dst, 129185377Ssam uint32_t mask) 130185377Ssam{ 131185377Ssam#define _MCPY(f, b) if ((mask & (b)) != 0) { dst->f = src->f; } 132185377Ssam 133185377Ssam memset(dst, 0, sizeof(*dst)); 134185377Ssam _MCPY(tag, IPFW_VTYPE_TAG); 135185377Ssam _MCPY(pipe, IPFW_VTYPE_PIPE); 136185377Ssam _MCPY(divert, IPFW_VTYPE_DIVERT); 137185377Ssam _MCPY(skipto, IPFW_VTYPE_SKIPTO); 138185377Ssam _MCPY(netgraph, IPFW_VTYPE_NETGRAPH); 139185377Ssam _MCPY(fib, IPFW_VTYPE_FIB); 140185377Ssam _MCPY(nat, IPFW_VTYPE_NAT); 141185377Ssam _MCPY(dscp, IPFW_VTYPE_DSCP); 142185377Ssam _MCPY(nh4, IPFW_VTYPE_NH4); 143185377Ssam _MCPY(nh6, IPFW_VTYPE_NH6); 144185377Ssam#undef _MCPY 145185377Ssam} 146185377Ssam 147185377Ssamstatic void 148185377Ssamget_value_ptrs(struct ip_fw_chain *ch, struct table_config *tc, int vshared, 149185377Ssam struct table_value **ptv, struct namedobj_instance **pvi) 150185377Ssam{ 151185377Ssam struct table_value *pval; 152185377Ssam struct namedobj_instance *vi; 153185377Ssam 154185377Ssam if (vshared != 0) { 155185377Ssam pval = (struct table_value *)ch->valuestate; 156185377Ssam vi = CHAIN_TO_VI(ch); 157185377Ssam } else { 158185377Ssam pval = NULL; 159185377Ssam vi = NULL; 160185377Ssam //pval = (struct table_value *)&tc->ti.data; 161188974Ssam } 162185377Ssam 163185377Ssam if (ptv != NULL) 164185377Ssam *ptv = pval; 165185377Ssam if (pvi != NULL) 166185377Ssam *pvi = vi; 167185377Ssam} 168185377Ssam 169185377Ssam/* 170185377Ssam * Update pointers to real vaues after @pval change. 171185377Ssam */ 172185377Ssamstatic void 173185377Ssamupdate_tvalue(struct namedobj_instance *ni, struct named_object *no, void *arg) 174185377Ssam{ 175185377Ssam struct vdump_args *da; 176185377Ssam struct table_val_link *ptv; 177185377Ssam struct table_value *pval; 178185377Ssam 179185377Ssam da = (struct vdump_args *)arg; 180185377Ssam ptv = (struct table_val_link *)no; 181185377Ssam 182185377Ssam pval = da->pval; 183185377Ssam ptv->pval = &pval[ptv->no.kidx]; 184185377Ssam 185185377Ssam} 186185377Ssam 187185377Ssam/* 188185377Ssam * Grows value storage shared among all tables. 189185377Ssam * Drops/reacquires UH locks. 190185377Ssam * Notifies other running adds on @ch shared storage resize. 191185377Ssam * Note function does not guarantee that free space 192185377Ssam * will be available after invocation, so one caller needs 193185377Ssam * to roll cycle himself. 194185377Ssam * 195185377Ssam * Returns 0 if case of no errors. 196185377Ssam */ 197185377Ssamstatic int 198185377Ssamresize_shared_value_storage(struct ip_fw_chain *ch) 199185377Ssam{ 200185377Ssam struct tables_config *tcfg; 201185377Ssam struct namedobj_instance *vi; 202185377Ssam struct table_value *pval, *valuestate, *old_valuestate; 203185377Ssam void *new_idx; 204185377Ssam struct vdump_args da; 205185377Ssam int new_blocks; 206185377Ssam int val_size, val_size_old; 207185377Ssam 208185377Ssam IPFW_UH_WLOCK_ASSERT(ch); 209185377Ssam 210185377Ssam valuestate = NULL; 211185377Ssam new_idx = NULL; 212185377Ssam 213185377Ssam pval = (struct table_value *)ch->valuestate; 214185377Ssam vi = CHAIN_TO_VI(ch); 215185377Ssam tcfg = CHAIN_TO_TCFG(ch); 216185377Ssam 217185377Ssam val_size = tcfg->val_size * 2; 218185377Ssam 219185377Ssam if (val_size == (1 << 30)) 220185377Ssam return (ENOSPC); 221185377Ssam 222185377Ssam IPFW_UH_WUNLOCK(ch); 223185377Ssam 224185377Ssam valuestate = malloc(sizeof(struct table_value) * val_size, M_IPFW, 225185377Ssam M_WAITOK | M_ZERO); 226185377Ssam ipfw_objhash_bitmap_alloc(val_size, (void *)&new_idx, 227185377Ssam &new_blocks); 228185377Ssam 229185377Ssam IPFW_UH_WLOCK(ch); 230185377Ssam 231185377Ssam /* 232185377Ssam * Check if we still need to resize 233185377Ssam */ 234185377Ssam if (tcfg->val_size >= val_size) 235185377Ssam goto done; 236185377Ssam 237185377Ssam /* Update pointers and notify everyone we're changing @ch */ 238185377Ssam pval = (struct table_value *)ch->valuestate; 239185377Ssam rollback_toperation_state(ch, ch); 240185377Ssam 241185377Ssam /* Good. Let's merge */ 242185377Ssam memcpy(valuestate, pval, sizeof(struct table_value) * tcfg->val_size); 243185377Ssam ipfw_objhash_bitmap_merge(CHAIN_TO_VI(ch), &new_idx, &new_blocks); 244185377Ssam 245185377Ssam IPFW_WLOCK(ch); 246185377Ssam /* Change pointers */ 247185377Ssam old_valuestate = ch->valuestate; 248185377Ssam ch->valuestate = valuestate; 249185377Ssam valuestate = old_valuestate; 250185377Ssam ipfw_objhash_bitmap_swap(CHAIN_TO_VI(ch), &new_idx, &new_blocks); 251185377Ssam 252185377Ssam val_size_old = tcfg->val_size; 253185377Ssam tcfg->val_size = val_size; 254185377Ssam val_size = val_size_old; 255185377Ssam IPFW_WUNLOCK(ch); 256185377Ssam /* Update pointers to reflect resize */ 257185377Ssam memset(&da, 0, sizeof(da)); 258185377Ssam da.pval = (struct table_value *)ch->valuestate; 259185377Ssam ipfw_objhash_foreach(vi, update_tvalue, &da); 260185377Ssam 261185377Ssamdone: 262185377Ssam free(valuestate, M_IPFW); 263185377Ssam ipfw_objhash_bitmap_free(new_idx, new_blocks); 264185377Ssam 265185377Ssam return (0); 266185377Ssam} 267185377Ssam 268185377Ssam/* 269185377Ssam * Drops reference for table value with index @kidx, stored in @pval and 270185377Ssam * @vi. Frees value if it has no references. 271185377Ssam */ 272185377Ssamstatic void 273185377Ssamunref_table_value(struct namedobj_instance *vi, struct table_value *pval, 274185377Ssam uint32_t kidx) 275185377Ssam{ 276185377Ssam struct table_val_link *ptvl; 277185377Ssam 278185377Ssam if (pval[kidx].refcnt > 1) { 279185377Ssam pval[kidx].refcnt--; 280185377Ssam return; 281185377Ssam } 282185377Ssam 283185377Ssam /* Last reference, delete item */ 284185377Ssam ptvl = (struct table_val_link *)ipfw_objhash_lookup_kidx(vi, kidx); 285185377Ssam KASSERT(ptvl != NULL, ("lookup on value kidx %d failed", kidx)); 286185377Ssam ipfw_objhash_del(vi, &ptvl->no); 287185377Ssam ipfw_objhash_free_idx(vi, kidx); 288185377Ssam free(ptvl, M_IPFW); 289185377Ssam} 290185377Ssam 291185377Ssamstruct flush_args { 292185377Ssam struct ip_fw_chain *ch; 293185377Ssam struct table_algo *ta; 294185377Ssam struct table_info *ti; 295185377Ssam void *astate; 296185377Ssam ipfw_obj_tentry tent; 297185377Ssam}; 298185377Ssam 299185377Ssamstatic int 300185377Ssamunref_table_value_cb(void *e, void *arg) 301185377Ssam{ 302185377Ssam struct flush_args *fa; 303185377Ssam struct ip_fw_chain *ch; 304185377Ssam struct table_algo *ta; 305185377Ssam ipfw_obj_tentry *tent; 306185377Ssam int error; 307185377Ssam 308185377Ssam fa = (struct flush_args *)arg; 309185377Ssam 310185377Ssam ta = fa->ta; 311185377Ssam memset(&fa->tent, 0, sizeof(fa->tent)); 312185377Ssam tent = &fa->tent; 313185377Ssam error = ta->dump_tentry(fa->astate, fa->ti, e, tent); 314185377Ssam if (error != 0) 315185377Ssam return (error); 316185377Ssam 317185377Ssam ch = fa->ch; 318185377Ssam 319185377Ssam unref_table_value(CHAIN_TO_VI(ch), 320185377Ssam (struct table_value *)ch->valuestate, tent->v.kidx); 321185377Ssam 322185377Ssam return (0); 323185377Ssam} 324185377Ssam 325185377Ssam/* 326185377Ssam * Drop references for each value used in @tc. 327185377Ssam */ 328185377Ssamvoid 329185377Ssamipfw_unref_table_values(struct ip_fw_chain *ch, struct table_config *tc, 330185377Ssam struct table_algo *ta, void *astate, struct table_info *ti) 331185377Ssam{ 332185377Ssam struct flush_args fa; 333185377Ssam 334185377Ssam memset(&fa, 0, sizeof(fa)); 335185377Ssam fa.ch = ch; 336185377Ssam fa.ta = ta; 337185377Ssam fa.astate = astate; 338185377Ssam fa.ti = ti; 339185377Ssam 340185377Ssam ta->foreach(astate, ti, unref_table_value_cb, &fa); 341185377Ssam} 342185377Ssam 343185377Ssam/* 344185377Ssam * Table operation state handler. 345185380Ssam * Called when we are going to change something in @tc which 346185380Ssam * may lead to inconsistencies in on-going table data addition. 347185380Ssam * 348185380Ssam * Here we rollback all already committed state (table values, currently) 349185377Ssam * and set "modified" field to non-zero value to indicate 350185377Ssam * that we need to restart original operation. 351185377Ssam */ 352185377Ssamvoid 353185377Ssamrollback_table_values(struct tableop_state *ts) 354185377Ssam{ 355185377Ssam struct ip_fw_chain *ch; 356185377Ssam struct table_value *pval; 357185377Ssam struct tentry_info *ptei; 358185377Ssam struct namedobj_instance *vi; 359185377Ssam int i; 360185377Ssam 361185377Ssam ch = ts->ch; 362185377Ssam 363185377Ssam IPFW_UH_WLOCK_ASSERT(ch); 364185377Ssam 365185377Ssam /* Get current table value pointer */ 366185377Ssam get_value_ptrs(ch, ts->tc, ts->vshared, &pval, &vi); 367185377Ssam 368185377Ssam for (i = 0; i < ts->count; i++) { 369185377Ssam ptei = &ts->tei[i]; 370185377Ssam 371185377Ssam if (ptei->value == 0) 372185377Ssam continue; 373185377Ssam 374185377Ssam unref_table_value(vi, pval, ptei->value); 375185377Ssam } 376185377Ssam} 377185377Ssam 378185377Ssam/* 379185377Ssam * Allocate new value index in either shared or per-table array. 380185377Ssam * Function may drop/reacquire UH lock. 381185377Ssam * 382185377Ssam * Returns 0 on success. 383185377Ssam */ 384185377Ssamstatic int 385185377Ssamalloc_table_vidx(struct ip_fw_chain *ch, struct tableop_state *ts, 386185377Ssam struct namedobj_instance *vi, uint16_t *pvidx) 387185377Ssam{ 388185377Ssam int error, vlimit; 389185377Ssam uint16_t vidx; 390185377Ssam 391185377Ssam IPFW_UH_WLOCK_ASSERT(ch); 392185377Ssam 393185377Ssam error = ipfw_objhash_alloc_idx(vi, &vidx); 394185377Ssam if (error != 0) { 395185377Ssam 396185377Ssam /* 397185377Ssam * We need to resize array. This involves 398185377Ssam * lock/unlock, so we need to check "modified" 399185377Ssam * state. 400185377Ssam */ 401185377Ssam ts->opstate.func(ts->tc, &ts->opstate); 402185377Ssam error = resize_shared_value_storage(ch); 403185377Ssam return (error); /* ts->modified should be set, we will restart */ 404185377Ssam } 405185377Ssam 406185377Ssam vlimit = ts->ta->vlimit; 407185377Ssam if (vlimit != 0 && vidx >= vlimit) { 408185377Ssam 409185377Ssam /* 410185377Ssam * Algorithm is not able to store given index. 411185377Ssam * We have to rollback state, start using 412185377Ssam * per-table value array or return error 413185377Ssam * if we're already using it. 414185377Ssam * 415185377Ssam * TODO: do not rollback state if 416185377Ssam * atomicity is not required. 417185377Ssam */ 418185377Ssam if (ts->vshared != 0) { 419185377Ssam /* shared -> per-table */ 420185377Ssam return (ENOSPC); /* TODO: proper error */ 421185377Ssam } 422185377Ssam 423185377Ssam /* per-table. Fail for now. */ 424185377Ssam return (ENOSPC); /* TODO: proper error */ 425185377Ssam } 426185377Ssam 427185377Ssam *pvidx = vidx; 428185377Ssam return (0); 429185377Ssam} 430185377Ssam 431185377Ssam/* 432185377Ssam * Drops value reference for unused values (updates, deletes, partially 433185377Ssam * successful adds or rollbacks). 434185377Ssam */ 435185377Ssamvoid 436185377Ssamipfw_garbage_table_values(struct ip_fw_chain *ch, struct table_config *tc, 437185377Ssam struct tentry_info *tei, uint32_t count, int rollback) 438185377Ssam{ 439185377Ssam int i; 440185377Ssam struct tentry_info *ptei; 441185377Ssam struct table_value *pval; 442185377Ssam struct namedobj_instance *vi; 443185377Ssam 444185377Ssam /* 445185377Ssam * We have two slightly different ADD cases here: 446185377Ssam * either (1) we are successful / partially successful, 447185377Ssam * in that case we need 448185377Ssam * * to ignore ADDED entries values 449185377Ssam * * rollback every other values (either UPDATED since 450185377Ssam * old value has been stored there, or some failure like 451185377Ssam * EXISTS or LIMIT or simply "ignored" case. 452185377Ssam * 453185377Ssam * (2): atomic rollback of partially successful operation 454185377Ssam * in that case we simply need to unref all entries. 455185377Ssam * 456185377Ssam * DELETE case is simpler: no atomic support there, so 457185377Ssam * we simply unref all non-zero values. 458185377Ssam */ 459185377Ssam 460185377Ssam /* 461185377Ssam * Get current table value pointers. 462185377Ssam * XXX: Properly read vshared 463185377Ssam */ 464185377Ssam get_value_ptrs(ch, tc, 1, &pval, &vi); 465185377Ssam 466185377Ssam for (i = 0; i < count; i++) { 467185377Ssam ptei = &tei[i]; 468185377Ssam 469185377Ssam if (ptei->value == 0) { 470185377Ssam 471185377Ssam /* 472185377Ssam * We may be deleting non-existing record. 473185377Ssam * Skip. 474185377Ssam */ 475185377Ssam continue; 476185377Ssam } 477185377Ssam 478185377Ssam if ((ptei->flags & TEI_FLAGS_ADDED) != 0 && rollback == 0) { 479185377Ssam ptei->value = 0; 480185377Ssam continue; 481185377Ssam } 482185377Ssam 483185377Ssam unref_table_value(vi, pval, ptei->value); 484185377Ssam ptei->value = 0; 485185377Ssam } 486185377Ssam} 487185377Ssam 488185377Ssam/* 489185377Ssam * Main function used to link values of entries going to be added, 490185377Ssam * to the index. Since we may perform many UH locks drops/acquires, 491185377Ssam * handle changes by checking tablestate "modified" field. 492185377Ssam * 493185377Ssam * Success: return 0. 494185377Ssam */ 495185377Ssamint 496185377Ssamipfw_link_table_values(struct ip_fw_chain *ch, struct tableop_state *ts) 497185377Ssam{ 498185377Ssam int error, i, found; 499185377Ssam struct namedobj_instance *vi; 500185377Ssam struct table_config *tc; 501185377Ssam struct tentry_info *tei, *ptei; 502185377Ssam uint32_t count, vlimit; 503185377Ssam uint16_t vidx; 504185377Ssam struct table_val_link *ptv; 505185377Ssam struct table_value tval, *pval; 506185377Ssam 507185377Ssam /* 508185377Ssam * Stage 1: reference all existing values and 509185377Ssam * save their indices. 510185377Ssam */ 511185377Ssam IPFW_UH_WLOCK_ASSERT(ch); 512185377Ssam get_value_ptrs(ch, ts->tc, ts->vshared, &pval, &vi); 513185377Ssam 514185377Ssam error = 0; 515185377Ssam found = 0; 516185377Ssam vlimit = ts->ta->vlimit; 517185377Ssam tc = ts->tc; 518185377Ssam tei = ts->tei; 519185377Ssam count = ts->count; 520185377Ssam for (i = 0; i < count; i++) { 521185377Ssam ptei = &tei[i]; 522185377Ssam ptei->value = 0; /* Ensure value is always 0 in the beginnig */ 523185377Ssam mask_table_value(ptei->pvalue, &tval, ts->vmask); 524185377Ssam ptv = (struct table_val_link *)ipfw_objhash_lookup_name(vi, 0, 525185377Ssam (char *)&tval); 526185377Ssam if (ptv == NULL) 527185377Ssam continue; 528185377Ssam /* Deal with vlimit later */ 529185377Ssam if (vlimit > 0 && vlimit <= ptv->no.kidx) 530185377Ssam continue; 531185377Ssam 532185377Ssam /* Value found. Bump refcount */ 533185377Ssam ptv->pval->refcnt++; 534185377Ssam ptei->value = ptv->no.kidx; 535185377Ssam found++; 536185377Ssam } 537185377Ssam 538185377Ssam if (ts->count == found) { 539185377Ssam /* We've found all values , no need ts create new ones */ 540185377Ssam return (0); 541185377Ssam } 542185377Ssam 543185377Ssam /* 544185377Ssam * we have added some state here, let's attach operation 545185377Ssam * state ts the list ts be able ts rollback if necessary. 546185377Ssam */ 547185377Ssam add_toperation_state(ch, ts); 548185377Ssam /* Ensure table won't disappear */ 549185377Ssam tc_ref(tc); 550185377Ssam IPFW_UH_WUNLOCK(ch); 551185377Ssam 552185377Ssam /* 553185377Ssam * Stage 2: allocate objects for non-existing values. 554185377Ssam */ 555185377Ssam for (i = 0; i < count; i++) { 556185377Ssam ptei = &tei[i]; 557185377Ssam if (ptei->value != 0) 558185377Ssam continue; 559185377Ssam if (ptei->ptv != NULL) 560185377Ssam continue; 561185377Ssam ptei->ptv = malloc(sizeof(struct table_val_link), M_IPFW, 562185377Ssam M_WAITOK | M_ZERO); 563185377Ssam } 564217684Sadrian 565187831Ssam /* 566185377Ssam * Stage 3: allocate index numbers for new values 567185377Ssam * and link them to index. 568185377Ssam */ 569185377Ssam IPFW_UH_WLOCK(ch); 570217684Sadrian tc_unref(tc); 571217684Sadrian del_toperation_state(ch, ts); 572217684Sadrian if (ts->modified != 0) 573217684Sadrian return (0); 574217684Sadrian 575185377Ssam KASSERT(pval == ch->tablestate, ("resize_storage() notify failure")); 576185377Ssam 577185377Ssam /* Let's try to link values */ 578185377Ssam for (i = 0; i < count; i++) { 579185377Ssam ptei = &tei[i]; 580185377Ssam if (ptei->value != 0) 581185377Ssam continue; 582185377Ssam 583185377Ssam /* Check if record has appeared */ 584185377Ssam mask_table_value(ptei->pvalue, &tval, ts->vmask); 585185377Ssam ptv = (struct table_val_link *)ipfw_objhash_lookup_name(vi, 0, 586243317Sadrian (char *)&tval); 587185377Ssam if (ptv != NULL) { 588243317Sadrian ptv->pval->refcnt++; 589243317Sadrian ptei->value = ptv->no.kidx; 590243317Sadrian continue; 591185377Ssam } 592185377Ssam 593185377Ssam /* May perform UH unlock/lock */ 594185377Ssam error = alloc_table_vidx(ch, ts, vi, &vidx); 595185377Ssam if (error != 0) { 596185377Ssam ts->opstate.func(ts->tc, &ts->opstate); 597185377Ssam return (error); 598185377Ssam } 599185377Ssam /* value storage resize has happened, return */ 600185377Ssam if (ts->modified != 0) 601185377Ssam return (0); 602185377Ssam 603185377Ssam /* Finally, we have allocated valid index, let's add entry */ 604185377Ssam ptei->value = vidx; 605185377Ssam ptv = (struct table_val_link *)ptei->ptv; 606185377Ssam ptei->ptv = NULL; 607185377Ssam 608185377Ssam ptv->no.kidx = vidx; 609185377Ssam ptv->no.name = (char *)&pval[vidx]; 610185377Ssam ptv->pval = &pval[vidx]; 611185377Ssam memcpy(ptv->pval, &tval, sizeof(struct table_value)); 612185377Ssam pval[vidx].refcnt = 1; 613243317Sadrian ipfw_objhash_add(vi, &ptv->no); 614185377Ssam } 615185377Ssam 616185377Ssam return (0); 617185377Ssam} 618185377Ssam 619185377Ssam/* 620185377Ssam * Compability function used to import data from old 621185377Ssam * IP_FW_TABLE_ADD / IP_FW_TABLE_XADD opcodes. 622185377Ssam */ 623185377Ssamvoid 624185377Ssamipfw_import_table_value_legacy(uint32_t value, struct table_value *v) 625185377Ssam{ 626185377Ssam 627185377Ssam memset(v, 0, sizeof(*v)); 628185377Ssam v->tag = value; 629185377Ssam v->pipe = value; 630185377Ssam v->divert = value; 631185377Ssam v->skipto = value; 632185377Ssam v->netgraph = value; 633185377Ssam v->fib = value; 634185377Ssam v->nat = value; 635185377Ssam v->nh4 = value; /* host format */ 636185377Ssam v->dscp = value; 637185377Ssam v->limit = value; 638185377Ssam} 639185377Ssam 640185377Ssam/* 641185377Ssam * Export data to legacy table dumps opcodes. 642185377Ssam */ 643185377Ssamuint32_t 644185377Ssamipfw_export_table_value_legacy(struct table_value *v) 645185377Ssam{ 646185377Ssam 647185377Ssam /* 648185377Ssam * TODO: provide more compatibility depending on 649185377Ssam * vmask value. 650185377Ssam */ 651234873Sadrian return (v->tag); 652234873Sadrian} 653234873Sadrian 654234873Sadrian/* 655234873Sadrian * Imports table value from current userland format. 656234873Sadrian * Saves value in kernel format to the same place. 657234873Sadrian */ 658234873Sadrianvoid 659234873Sadrianipfw_import_table_value_v1(ipfw_table_value *iv) 660234873Sadrian{ 661234873Sadrian struct table_value v; 662234873Sadrian 663234873Sadrian memset(&v, 0, sizeof(v)); 664234873Sadrian v.tag = iv->tag; 665234873Sadrian v.pipe = iv->pipe; 666234873Sadrian v.divert = iv->divert; 667234873Sadrian v.skipto = iv->skipto; 668234873Sadrian v.netgraph = iv->netgraph; 669234873Sadrian v.fib = iv->fib; 670234873Sadrian v.nat = iv->nat; 671234873Sadrian v.dscp = iv->dscp; 672235206Sadrian v.nh4 = iv->nh4; 673235206Sadrian v.nh6 = iv->nh6; 674247286Sadrian v.limit = iv->limit; 675247286Sadrian 676247286Sadrian memcpy(iv, &v, sizeof(ipfw_table_value)); 677247286Sadrian} 678247286Sadrian 679247286Sadrian/* 680235206Sadrian * Export real table value @v to current userland format. 681235206Sadrian * Note that @v and @piv may point to the same memory. 682235206Sadrian */ 683235206Sadrianvoid 684235206Sadrianipfw_export_table_value_v1(struct table_value *v, ipfw_table_value *piv) 685235206Sadrian{ 686235206Sadrian ipfw_table_value iv; 687235206Sadrian 688243317Sadrian memset(&iv, 0, sizeof(iv)); 689243317Sadrian iv.tag = v->tag; 690243317Sadrian iv.pipe = v->pipe; 691243317Sadrian iv.divert = v->divert; 692243317Sadrian iv.skipto = v->skipto; 693243317Sadrian iv.netgraph = v->netgraph; 694243317Sadrian iv.fib = v->fib; 695243317Sadrian iv.nat = v->nat; 696243317Sadrian iv.dscp = v->dscp; 697243317Sadrian iv.limit = v->limit; 698243317Sadrian iv.nh4 = v->nh4; 699243317Sadrian iv.nh6 = v->nh6; 700243317Sadrian 701243317Sadrian memcpy(piv, &iv, sizeof(iv)); 702243317Sadrian} 703 704/* 705 * Exports real value data into ipfw_table_value structure. 706 * Utilizes "spare1" field to store kernel index. 707 */ 708static void 709dump_tvalue(struct namedobj_instance *ni, struct named_object *no, void *arg) 710{ 711 struct vdump_args *da; 712 struct table_val_link *ptv; 713 struct table_value *v; 714 715 da = (struct vdump_args *)arg; 716 ptv = (struct table_val_link *)no; 717 718 v = (struct table_value *)ipfw_get_sopt_space(da->sd, sizeof(*v)); 719 /* Out of memory, returning */ 720 if (v == NULL) { 721 da->error = ENOMEM; 722 return; 723 } 724 725 memcpy(v, ptv->pval, sizeof(*v)); 726 v->spare1 = ptv->no.kidx; 727} 728 729/* 730 * Dumps all shared/table value data 731 * Data layout (v1)(current): 732 * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size 733 * Reply: [ ipfw_obj_lheader ipfw_table_value x N ] 734 * 735 * Returns 0 on success 736 */ 737int 738ipfw_list_table_values(struct ip_fw_chain *ch, ip_fw3_opheader *op3, 739 struct sockopt_data *sd) 740{ 741 struct _ipfw_obj_lheader *olh; 742 struct namedobj_instance *vi; 743 struct vdump_args da; 744 uint32_t count, size; 745 746 olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh)); 747 if (olh == NULL) 748 return (EINVAL); 749 if (sd->valsize < olh->size) 750 return (EINVAL); 751 752 IPFW_UH_RLOCK(ch); 753 vi = CHAIN_TO_VI(ch); 754 755 count = ipfw_objhash_count(vi); 756 size = count * sizeof(ipfw_table_value) + sizeof(ipfw_obj_lheader); 757 758 /* Fill in header regadless of buffer size */ 759 olh->count = count; 760 olh->objsize = sizeof(ipfw_table_value); 761 762 if (size > olh->size) { 763 olh->size = size; 764 IPFW_UH_RUNLOCK(ch); 765 return (ENOMEM); 766 } 767 olh->size = size; 768 769 /* 770 * Do the actual value dump 771 */ 772 memset(&da, 0, sizeof(da)); 773 da.ch = ch; 774 da.sd = sd; 775 ipfw_objhash_foreach(vi, dump_tvalue, &da); 776 777 IPFW_UH_RUNLOCK(ch); 778 779 return (0); 780} 781 782