xref: /netbsd-src/sys/net/npf/npf_tableset.c (revision f3cfa6f6ce31685c6c4a758bc430e69eb99f50a4)
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