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