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