npf_tableset.c revision 1.34 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