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