1 /*- 2 * Copyright (c) 2009-2019 The NetBSD Foundation, Inc. 3 * All rights reserved. 4 * 5 * This material is based upon work partially supported by The 6 * NetBSD Foundation under a contract with Mindaugas Rasiukevicius. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 /* 31 * NPF tableset module. 32 * 33 * Notes 34 * 35 * The tableset is an array of tables. After the creation, the array 36 * is immutable. The caller is responsible to synchronise the access 37 * to the tableset. 38 * 39 * Warning (not applicable for the userspace npfkern): 40 * 41 * The thmap_put()/thmap_del() are not called from the interrupt 42 * context and are protected by a mutex(9), therefore they do not 43 * SPL wrappers -- see the comment at the top of the npf_conndb.c 44 * source file. 45 */ 46 47 #ifdef _KERNEL 48 #include <sys/cdefs.h> 49 __KERNEL_RCSID(0, "$NetBSD: npf_tableset.c,v 1.34 2019/08/21 21:45:47 rmind Exp $"); 50 51 #include <sys/param.h> 52 #include <sys/types.h> 53 54 #include <sys/atomic.h> 55 #include <sys/cdbr.h> 56 #include <sys/kmem.h> 57 #include <sys/pool.h> 58 #include <sys/queue.h> 59 #include <sys/mutex.h> 60 #include <sys/thmap.h> 61 62 #include "lpm.h" 63 #endif 64 65 #include "npf_impl.h" 66 67 typedef struct npf_tblent { 68 LIST_ENTRY(npf_tblent) te_listent; 69 uint16_t te_preflen; 70 uint16_t te_alen; 71 npf_addr_t te_addr; 72 } npf_tblent_t; 73 74 #define NPF_ADDRLEN2IDX(alen) ((alen) >> 4) 75 #define NPF_ADDR_SLOTS (2) 76 77 struct npf_table { 78 /* 79 * The storage type can be: a) hashmap b) LPM c) cdb. 80 * There are separate trees for IPv4 and IPv6. 81 */ 82 union { 83 struct { 84 thmap_t * t_map; 85 LIST_HEAD(, npf_tblent) t_gc; 86 }; 87 lpm_t * t_lpm; 88 struct { 89 void * t_blob; 90 size_t t_bsize; 91 struct cdbr * t_cdb; 92 }; 93 struct { 94 npf_tblent_t ** t_elements[NPF_ADDR_SLOTS]; 95 unsigned t_allocated[NPF_ADDR_SLOTS]; 96 unsigned t_used[NPF_ADDR_SLOTS]; 97 }; 98 } /* C11 */; 99 LIST_HEAD(, npf_tblent) t_list; 100 unsigned t_nitems; 101 102 /* 103 * Table ID, type and lock. The ID may change during the 104 * config reload, it is protected by the npf_config_lock. 105 */ 106 int t_type; 107 unsigned t_id; 108 kmutex_t t_lock; 109 110 /* Reference count and table name. */ 111 unsigned t_refcnt; 112 char t_name[NPF_TABLE_MAXNAMELEN]; 113 }; 114 115 struct npf_tableset { 116 unsigned ts_nitems; 117 npf_table_t * ts_map[]; 118 }; 119 120 #define NPF_TABLESET_SIZE(n) \ 121 (offsetof(npf_tableset_t, ts_map[n]) * sizeof(npf_table_t *)) 122 123 #define NPF_IFADDR_STEP 4 124 125 static pool_cache_t tblent_cache __read_mostly; 126 127 /* 128 * npf_table_sysinit: initialise tableset structures. 129 */ 130 void 131 npf_tableset_sysinit(void) 132 { 133 tblent_cache = pool_cache_init(sizeof(npf_tblent_t), 0, 134 0, 0, "npftblpl", NULL, IPL_NONE, NULL, NULL, NULL); 135 } 136 137 void 138 npf_tableset_sysfini(void) 139 { 140 pool_cache_destroy(tblent_cache); 141 } 142 143 npf_tableset_t * 144 npf_tableset_create(u_int nitems) 145 { 146 npf_tableset_t *ts = kmem_zalloc(NPF_TABLESET_SIZE(nitems), KM_SLEEP); 147 ts->ts_nitems = nitems; 148 return ts; 149 } 150 151 void 152 npf_tableset_destroy(npf_tableset_t *ts) 153 { 154 /* 155 * Destroy all tables (no references should be held, since the 156 * ruleset should be destroyed before). 157 */ 158 for (u_int tid = 0; tid < ts->ts_nitems; tid++) { 159 npf_table_t *t = ts->ts_map[tid]; 160 161 if (t && atomic_dec_uint_nv(&t->t_refcnt) == 0) { 162 npf_table_destroy(t); 163 } 164 } 165 kmem_free(ts, NPF_TABLESET_SIZE(ts->ts_nitems)); 166 } 167 168 /* 169 * npf_tableset_insert: insert the table into the specified tableset. 170 * 171 * => Returns 0 on success. Fails and returns error if ID is already used. 172 */ 173 int 174 npf_tableset_insert(npf_tableset_t *ts, npf_table_t *t) 175 { 176 const u_int tid = t->t_id; 177 int error; 178 179 KASSERT((u_int)tid < ts->ts_nitems); 180 181 if (ts->ts_map[tid] == NULL) { 182 atomic_inc_uint(&t->t_refcnt); 183 ts->ts_map[tid] = t; 184 error = 0; 185 } else { 186 error = EEXIST; 187 } 188 return error; 189 } 190 191 npf_table_t * 192 npf_tableset_swap(npf_tableset_t *ts, npf_table_t *newt) 193 { 194 const u_int tid = newt->t_id; 195 npf_table_t *oldt = ts->ts_map[tid]; 196 197 KASSERT(tid < ts->ts_nitems); 198 KASSERT(oldt->t_id == newt->t_id); 199 200 newt->t_refcnt = oldt->t_refcnt; 201 oldt->t_refcnt = 0; 202 203 return atomic_swap_ptr(&ts->ts_map[tid], newt); 204 } 205 206 /* 207 * npf_tableset_getbyname: look for a table in the set given the name. 208 */ 209 npf_table_t * 210 npf_tableset_getbyname(npf_tableset_t *ts, const char *name) 211 { 212 npf_table_t *t; 213 214 for (u_int tid = 0; tid < ts->ts_nitems; tid++) { 215 if ((t = ts->ts_map[tid]) == NULL) 216 continue; 217 if (strcmp(name, t->t_name) == 0) 218 return t; 219 } 220 return NULL; 221 } 222 223 npf_table_t * 224 npf_tableset_getbyid(npf_tableset_t *ts, u_int tid) 225 { 226 if (__predict_true(tid < ts->ts_nitems)) { 227 return ts->ts_map[tid]; 228 } 229 return NULL; 230 } 231 232 /* 233 * npf_tableset_reload: iterate all tables and if the new table is of the 234 * same type and has no items, then we preserve the old one and its entries. 235 * 236 * => The caller is responsible for providing synchronisation. 237 */ 238 void 239 npf_tableset_reload(npf_t *npf, npf_tableset_t *nts, npf_tableset_t *ots) 240 { 241 for (u_int tid = 0; tid < nts->ts_nitems; tid++) { 242 npf_table_t *t, *ot; 243 244 if ((t = nts->ts_map[tid]) == NULL) { 245 continue; 246 } 247 248 /* If our table has entries, just load it. */ 249 if (t->t_nitems) { 250 continue; 251 } 252 253 /* Look for a currently existing table with such name. */ 254 ot = npf_tableset_getbyname(ots, t->t_name); 255 if (ot == NULL) { 256 /* Not found: we have a new table. */ 257 continue; 258 } 259 260 /* Found. Did the type change? */ 261 if (t->t_type != ot->t_type) { 262 /* Yes, load the new. */ 263 continue; 264 } 265 266 /* 267 * Preserve the current table. Acquire a reference since 268 * we are keeping it in the old table set. Update its ID. 269 */ 270 atomic_inc_uint(&ot->t_refcnt); 271 nts->ts_map[tid] = ot; 272 273 KASSERT(npf_config_locked_p(npf)); 274 ot->t_id = tid; 275 276 /* Destroy the new table (we hold the only reference). */ 277 t->t_refcnt--; 278 npf_table_destroy(t); 279 } 280 } 281 282 int 283 npf_tableset_export(npf_t *npf, const npf_tableset_t *ts, nvlist_t *npf_dict) 284 { 285 const npf_table_t *t; 286 287 KASSERT(npf_config_locked_p(npf)); 288 289 for (u_int tid = 0; tid < ts->ts_nitems; tid++) { 290 nvlist_t *table; 291 292 if ((t = ts->ts_map[tid]) == NULL) { 293 continue; 294 } 295 table = nvlist_create(0); 296 nvlist_add_string(table, "name", t->t_name); 297 nvlist_add_number(table, "type", t->t_type); 298 nvlist_add_number(table, "id", tid); 299 300 nvlist_append_nvlist_array(npf_dict, "tables", table); 301 nvlist_destroy(table); 302 } 303 return 0; 304 } 305 306 /* 307 * Few helper routines. 308 */ 309 310 static void 311 table_ipset_flush(npf_table_t *t) 312 { 313 npf_tblent_t *ent; 314 315 while ((ent = LIST_FIRST(&t->t_list)) != NULL) { 316 thmap_del(t->t_map, &ent->te_addr, ent->te_alen); 317 LIST_REMOVE(ent, te_listent); 318 pool_cache_put(tblent_cache, ent); 319 } 320 t->t_nitems = 0; 321 } 322 323 static void 324 table_tree_flush(npf_table_t *t) 325 { 326 npf_tblent_t *ent; 327 328 while ((ent = LIST_FIRST(&t->t_list)) != NULL) { 329 LIST_REMOVE(ent, te_listent); 330 pool_cache_put(tblent_cache, ent); 331 } 332 lpm_clear(t->t_lpm, NULL, NULL); 333 t->t_nitems = 0; 334 } 335 336 static void 337 table_ifaddr_flush(npf_table_t *t) 338 { 339 npf_tblent_t *ent; 340 341 for (unsigned i = 0; i < NPF_ADDR_SLOTS; i++) { 342 size_t len; 343 344 if (!t->t_allocated[i]) { 345 KASSERT(t->t_elements[i] == NULL); 346 continue; 347 } 348 len = t->t_allocated[i] * sizeof(npf_tblent_t *); 349 kmem_free(t->t_elements[i], len); 350 t->t_elements[i] = NULL; 351 t->t_allocated[i] = 0; 352 t->t_used[i] = 0; 353 } 354 while ((ent = LIST_FIRST(&t->t_list)) != NULL) { 355 LIST_REMOVE(ent, te_listent); 356 pool_cache_put(tblent_cache, ent); 357 } 358 t->t_nitems = 0; 359 } 360 361 /* 362 * npf_table_create: create table with a specified ID. 363 */ 364 npf_table_t * 365 npf_table_create(const char *name, u_int tid, int type, 366 const void *blob, size_t size) 367 { 368 npf_table_t *t; 369 370 t = kmem_zalloc(sizeof(npf_table_t), KM_SLEEP); 371 strlcpy(t->t_name, name, NPF_TABLE_MAXNAMELEN); 372 373 switch (type) { 374 case NPF_TABLE_LPM: 375 t->t_lpm = lpm_create(KM_NOSLEEP); 376 if (t->t_lpm == NULL) { 377 goto out; 378 } 379 LIST_INIT(&t->t_list); 380 break; 381 case NPF_TABLE_IPSET: 382 t->t_map = thmap_create(0, NULL, THMAP_NOCOPY); 383 if (t->t_map == NULL) { 384 goto out; 385 } 386 break; 387 case NPF_TABLE_CONST: 388 t->t_blob = kmem_alloc(size, KM_SLEEP); 389 if (t->t_blob == NULL) { 390 goto out; 391 } 392 memcpy(t->t_blob, blob, size); 393 t->t_bsize = size; 394 395 t->t_cdb = cdbr_open_mem(t->t_blob, size, 396 CDBR_DEFAULT, NULL, NULL); 397 if (t->t_cdb == NULL) { 398 kmem_free(t->t_blob, t->t_bsize); 399 goto out; 400 } 401 t->t_nitems = cdbr_entries(t->t_cdb); 402 break; 403 case NPF_TABLE_IFADDR: 404 break; 405 default: 406 KASSERT(false); 407 } 408 mutex_init(&t->t_lock, MUTEX_DEFAULT, IPL_NET); 409 t->t_type = type; 410 t->t_id = tid; 411 return t; 412 out: 413 kmem_free(t, sizeof(npf_table_t)); 414 return NULL; 415 } 416 417 /* 418 * npf_table_destroy: free all table entries and table itself. 419 */ 420 void 421 npf_table_destroy(npf_table_t *t) 422 { 423 KASSERT(t->t_refcnt == 0); 424 425 switch (t->t_type) { 426 case NPF_TABLE_IPSET: 427 table_ipset_flush(t); 428 npf_table_gc(NULL, t); 429 thmap_destroy(t->t_map); 430 break; 431 case NPF_TABLE_LPM: 432 table_tree_flush(t); 433 lpm_destroy(t->t_lpm); 434 break; 435 case NPF_TABLE_CONST: 436 cdbr_close(t->t_cdb); 437 kmem_free(t->t_blob, t->t_bsize); 438 break; 439 case NPF_TABLE_IFADDR: 440 table_ifaddr_flush(t); 441 break; 442 default: 443 KASSERT(false); 444 } 445 mutex_destroy(&t->t_lock); 446 kmem_free(t, sizeof(npf_table_t)); 447 } 448 449 u_int 450 npf_table_getid(npf_table_t *t) 451 { 452 return t->t_id; 453 } 454 455 /* 456 * npf_table_check: validate the name, ID and type. 457 */ 458 int 459 npf_table_check(npf_tableset_t *ts, const char *name, uint64_t tid, 460 uint64_t type, bool replacing) 461 { 462 const npf_table_t *t; 463 464 if (tid >= ts->ts_nitems) { 465 return EINVAL; 466 } 467 if (!replacing && ts->ts_map[tid] != NULL) { 468 return EEXIST; 469 } 470 switch (type) { 471 case NPF_TABLE_LPM: 472 case NPF_TABLE_IPSET: 473 case NPF_TABLE_CONST: 474 case NPF_TABLE_IFADDR: 475 break; 476 default: 477 return EINVAL; 478 } 479 if (strlen(name) >= NPF_TABLE_MAXNAMELEN) { 480 return ENAMETOOLONG; 481 } 482 if ((t = npf_tableset_getbyname(ts, name)) != NULL) { 483 if (!replacing || t->t_id != tid) { 484 return EEXIST; 485 } 486 } 487 return 0; 488 } 489 490 static int 491 table_ifaddr_insert(npf_table_t *t, const int alen, npf_tblent_t *ent) 492 { 493 const unsigned aidx = NPF_ADDRLEN2IDX(alen); 494 const unsigned allocated = t->t_allocated[aidx]; 495 const unsigned used = t->t_used[aidx]; 496 497 /* 498 * No need to check for duplicates. 499 */ 500 if (allocated <= used) { 501 npf_tblent_t **old_elements = t->t_elements[aidx]; 502 npf_tblent_t **elements; 503 size_t toalloc, newsize; 504 505 toalloc = roundup2(allocated + 1, NPF_IFADDR_STEP); 506 newsize = toalloc * sizeof(npf_tblent_t *); 507 508 elements = kmem_zalloc(newsize, KM_NOSLEEP); 509 if (elements == NULL) { 510 return ENOMEM; 511 } 512 for (unsigned i = 0; i < used; i++) { 513 elements[i] = old_elements[i]; 514 } 515 if (allocated) { 516 const size_t len = allocated * sizeof(npf_tblent_t *); 517 KASSERT(old_elements != NULL); 518 kmem_free(old_elements, len); 519 } 520 t->t_elements[aidx] = elements; 521 t->t_allocated[aidx] = toalloc; 522 } 523 t->t_elements[aidx][used] = ent; 524 t->t_used[aidx]++; 525 return 0; 526 } 527 528 /* 529 * npf_table_insert: add an IP CIDR entry into the table. 530 */ 531 int 532 npf_table_insert(npf_table_t *t, const int alen, 533 const npf_addr_t *addr, const npf_netmask_t mask) 534 { 535 npf_tblent_t *ent; 536 int error; 537 538 error = npf_netmask_check(alen, mask); 539 if (error) { 540 return error; 541 } 542 ent = pool_cache_get(tblent_cache, PR_WAITOK); 543 memcpy(&ent->te_addr, addr, alen); 544 ent->te_alen = alen; 545 ent->te_preflen = 0; 546 547 /* 548 * Insert the entry. Return an error on duplicate. 549 */ 550 mutex_enter(&t->t_lock); 551 switch (t->t_type) { 552 case NPF_TABLE_IPSET: 553 /* 554 * Hashmap supports only IPs. 555 * 556 * Note: the key must be already persistent, since we 557 * use THMAP_NOCOPY. 558 */ 559 if (mask != NPF_NO_NETMASK) { 560 error = EINVAL; 561 break; 562 } 563 if (thmap_put(t->t_map, &ent->te_addr, alen, ent) == ent) { 564 LIST_INSERT_HEAD(&t->t_list, ent, te_listent); 565 t->t_nitems++; 566 } else { 567 error = EEXIST; 568 } 569 break; 570 case NPF_TABLE_LPM: { 571 const unsigned preflen = 572 (mask == NPF_NO_NETMASK) ? (alen * 8) : mask; 573 ent->te_preflen = preflen; 574 575 if (lpm_lookup(t->t_lpm, addr, alen) == NULL && 576 lpm_insert(t->t_lpm, addr, alen, preflen, ent) == 0) { 577 LIST_INSERT_HEAD(&t->t_list, ent, te_listent); 578 t->t_nitems++; 579 error = 0; 580 } else { 581 error = EEXIST; 582 } 583 break; 584 } 585 case NPF_TABLE_CONST: 586 error = EINVAL; 587 break; 588 case NPF_TABLE_IFADDR: 589 if ((error = table_ifaddr_insert(t, alen, ent)) != 0) { 590 break; 591 } 592 LIST_INSERT_HEAD(&t->t_list, ent, te_listent); 593 t->t_nitems++; 594 break; 595 default: 596 KASSERT(false); 597 } 598 mutex_exit(&t->t_lock); 599 600 if (error) { 601 pool_cache_put(tblent_cache, ent); 602 } 603 return error; 604 } 605 606 /* 607 * npf_table_remove: remove the IP CIDR entry from the table. 608 */ 609 int 610 npf_table_remove(npf_table_t *t, const int alen, 611 const npf_addr_t *addr, const npf_netmask_t mask) 612 { 613 npf_tblent_t *ent = NULL; 614 int error; 615 616 error = npf_netmask_check(alen, mask); 617 if (error) { 618 return error; 619 } 620 621 mutex_enter(&t->t_lock); 622 switch (t->t_type) { 623 case NPF_TABLE_IPSET: 624 ent = thmap_del(t->t_map, addr, alen); 625 if (__predict_true(ent != NULL)) { 626 LIST_REMOVE(ent, te_listent); 627 LIST_INSERT_HEAD(&t->t_gc, ent, te_listent); 628 ent = NULL; // to be G/C'ed 629 t->t_nitems--; 630 } else { 631 error = ENOENT; 632 } 633 break; 634 case NPF_TABLE_LPM: 635 ent = lpm_lookup(t->t_lpm, addr, alen); 636 if (__predict_true(ent != NULL)) { 637 LIST_REMOVE(ent, te_listent); 638 lpm_remove(t->t_lpm, &ent->te_addr, 639 ent->te_alen, ent->te_preflen); 640 t->t_nitems--; 641 } else { 642 error = ENOENT; 643 } 644 break; 645 case NPF_TABLE_CONST: 646 case NPF_TABLE_IFADDR: 647 error = EINVAL; 648 break; 649 default: 650 KASSERT(false); 651 ent = NULL; 652 } 653 mutex_exit(&t->t_lock); 654 655 if (ent) { 656 pool_cache_put(tblent_cache, ent); 657 } 658 return error; 659 } 660 661 /* 662 * npf_table_lookup: find the table according to ID, lookup and match 663 * the contents with the specified IP address. 664 */ 665 int 666 npf_table_lookup(npf_table_t *t, const int alen, const npf_addr_t *addr) 667 { 668 const void *data; 669 size_t dlen; 670 bool found; 671 int error; 672 673 error = npf_netmask_check(alen, NPF_NO_NETMASK); 674 if (error) { 675 return error; 676 } 677 678 switch (t->t_type) { 679 case NPF_TABLE_IPSET: 680 found = thmap_get(t->t_map, addr, alen) != NULL; 681 break; 682 case NPF_TABLE_LPM: 683 mutex_enter(&t->t_lock); 684 found = lpm_lookup(t->t_lpm, addr, alen) != NULL; 685 mutex_exit(&t->t_lock); 686 break; 687 case NPF_TABLE_CONST: 688 if (cdbr_find(t->t_cdb, addr, alen, &data, &dlen) == 0) { 689 found = dlen == (unsigned)alen && 690 memcmp(addr, data, dlen) == 0; 691 } else { 692 found = false; 693 } 694 break; 695 case NPF_TABLE_IFADDR: { 696 const unsigned aidx = NPF_ADDRLEN2IDX(alen); 697 698 found = false; 699 for (unsigned i = 0; i < t->t_used[aidx]; i++) { 700 const npf_tblent_t *elm = t->t_elements[aidx][i]; 701 702 KASSERT(elm->te_alen == alen); 703 704 if (memcmp(&elm->te_addr, addr, alen) == 0) { 705 found = true; 706 break; 707 } 708 } 709 break; 710 } 711 default: 712 KASSERT(false); 713 found = false; 714 } 715 716 return found ? 0 : ENOENT; 717 } 718 719 npf_addr_t * 720 npf_table_getsome(npf_table_t *t, const int alen, unsigned idx) 721 { 722 const unsigned aidx = NPF_ADDRLEN2IDX(alen); 723 npf_tblent_t *elm; 724 unsigned nitems; 725 726 KASSERT(t->t_type == NPF_TABLE_IFADDR); 727 KASSERT(aidx < NPF_ADDR_SLOTS); 728 729 nitems = t->t_used[aidx]; 730 if (nitems == 0) { 731 return NULL; 732 } 733 734 /* 735 * No need to acquire the lock, since the table is immutable. 736 */ 737 elm = t->t_elements[aidx][idx % nitems]; 738 return &elm->te_addr; 739 } 740 741 static int 742 table_ent_copyout(const npf_addr_t *addr, const int alen, npf_netmask_t mask, 743 void *ubuf, size_t len, size_t *off) 744 { 745 void *ubufp = (uint8_t *)ubuf + *off; 746 npf_ioctl_ent_t uent; 747 748 if ((*off += sizeof(npf_ioctl_ent_t)) > len) { 749 return ENOMEM; 750 } 751 uent.alen = alen; 752 memcpy(&uent.addr, addr, sizeof(npf_addr_t)); 753 uent.mask = mask; 754 755 return copyout(&uent, ubufp, sizeof(npf_ioctl_ent_t)); 756 } 757 758 static int 759 table_generic_list(const npf_table_t *t, void *ubuf, size_t len) 760 { 761 npf_tblent_t *ent; 762 size_t off = 0; 763 int error = 0; 764 765 LIST_FOREACH(ent, &t->t_list, te_listent) { 766 error = table_ent_copyout(&ent->te_addr, 767 ent->te_alen, ent->te_preflen, ubuf, len, &off); 768 if (error) 769 break; 770 } 771 return error; 772 } 773 774 static int 775 table_cdb_list(npf_table_t *t, void *ubuf, size_t len) 776 { 777 size_t off = 0, dlen; 778 const void *data; 779 int error = 0; 780 781 for (size_t i = 0; i < t->t_nitems; i++) { 782 if (cdbr_get(t->t_cdb, i, &data, &dlen) != 0) { 783 return EINVAL; 784 } 785 error = table_ent_copyout(data, dlen, 0, ubuf, len, &off); 786 if (error) 787 break; 788 } 789 return error; 790 } 791 792 /* 793 * npf_table_list: copy a list of all table entries into a userspace buffer. 794 */ 795 int 796 npf_table_list(npf_table_t *t, void *ubuf, size_t len) 797 { 798 int error = 0; 799 800 mutex_enter(&t->t_lock); 801 switch (t->t_type) { 802 case NPF_TABLE_IPSET: 803 error = table_generic_list(t, ubuf, len); 804 break; 805 case NPF_TABLE_LPM: 806 error = table_generic_list(t, ubuf, len); 807 break; 808 case NPF_TABLE_CONST: 809 error = table_cdb_list(t, ubuf, len); 810 break; 811 case NPF_TABLE_IFADDR: 812 error = table_generic_list(t, ubuf, len); 813 break; 814 default: 815 KASSERT(false); 816 } 817 mutex_exit(&t->t_lock); 818 819 return error; 820 } 821 822 /* 823 * npf_table_flush: remove all table entries. 824 */ 825 int 826 npf_table_flush(npf_table_t *t) 827 { 828 int error = 0; 829 830 mutex_enter(&t->t_lock); 831 switch (t->t_type) { 832 case NPF_TABLE_IPSET: 833 table_ipset_flush(t); 834 break; 835 case NPF_TABLE_LPM: 836 table_tree_flush(t); 837 break; 838 case NPF_TABLE_CONST: 839 case NPF_TABLE_IFADDR: 840 error = EINVAL; 841 break; 842 default: 843 KASSERT(false); 844 } 845 mutex_exit(&t->t_lock); 846 return error; 847 } 848 849 void 850 npf_table_gc(npf_t *npf, npf_table_t *t) 851 { 852 npf_tblent_t *ent; 853 void *ref; 854 855 if (t->t_type != NPF_TABLE_IPSET || LIST_EMPTY(&t->t_gc)) { 856 return; 857 } 858 859 ref = thmap_stage_gc(t->t_map); 860 if (npf) { 861 npf_config_locked_p(npf); 862 npf_config_sync(npf); 863 } 864 thmap_gc(t->t_map, ref); 865 866 while ((ent = LIST_FIRST(&t->t_gc)) != NULL) { 867 LIST_REMOVE(ent, te_listent); 868 pool_cache_put(tblent_cache, ent); 869 } 870 } 871