xref: /netbsd-src/sys/net/npf/npf_ctl.c (revision b899bfd96fd2cbaf2befc9ce4aaed9b9c230837a)
1 /*-
2  * Copyright (c) 2009-2020 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 device control.
32  *
33  * Implementation of (re)loading, construction of tables and rules.
34  * NPF nvlist(3) consumer.
35  */
36 
37 #ifdef _KERNEL
38 #include <sys/cdefs.h>
39 __KERNEL_RCSID(0, "$NetBSD: npf_ctl.c,v 1.60 2020/05/30 14:16:56 rmind Exp $");
40 
41 #include <sys/param.h>
42 #include <sys/conf.h>
43 #include <sys/kmem.h>
44 #include <net/bpf.h>
45 #endif
46 
47 #include "npf_impl.h"
48 #include "npf_conn.h"
49 
50 #define	NPF_ERR_DEBUG(e) \
51 	nvlist_add_string((e), "source-file", __FILE__); \
52 	nvlist_add_number((e), "source-line", __LINE__);
53 
54 static int __noinline
npf_mk_params(npf_t * npf,const nvlist_t * req,nvlist_t * resp,bool set)55 npf_mk_params(npf_t *npf, const nvlist_t *req, nvlist_t *resp, bool set)
56 {
57 	const nvlist_t *params;
58 	int type, error, val;
59 	const char *name;
60 	void *cookie;
61 
62 	params = dnvlist_get_nvlist(req, "params", NULL);
63 	if (params == NULL) {
64 		return 0;
65 	}
66 	cookie = NULL;
67 	while ((name = nvlist_next(params, &type, &cookie)) != NULL) {
68 		if (type != NV_TYPE_NUMBER) {
69 			NPF_ERR_DEBUG(resp);
70 			return EINVAL;
71 		}
72 		val = (int)nvlist_get_number(params, name);
73 		if (set) {
74 			/* Actually set the parameter. */
75 			error = npfk_param_set(npf, name, val);
76 			KASSERT(error == 0);
77 			continue;
78 		}
79 
80 		/* Validate the parameter and its value. */
81 		error = npf_param_check(npf, name, val);
82 		if (__predict_true(error == 0)) {
83 			continue;
84 		}
85 		if (error == ENOENT) {
86 			nvlist_add_stringf(resp, "error-msg",
87 			    "invalid parameter `%s`", name);
88 		}
89 		if (error == EINVAL) {
90 			nvlist_add_stringf(resp, "error-msg",
91 			    "invalid parameter `%s` value %d", name, val);
92 		}
93 		return error;
94 	}
95 	return 0;
96 }
97 
98 static int __noinline
npf_mk_table_entries(npf_table_t * t,const nvlist_t * req,nvlist_t * resp)99 npf_mk_table_entries(npf_table_t *t, const nvlist_t *req, nvlist_t *resp)
100 {
101 	const nvlist_t * const *entries;
102 	size_t nitems;
103 	int error = 0;
104 
105 	if (!nvlist_exists_nvlist_array(req, "entries")) {
106 		return 0;
107 	}
108 	entries = nvlist_get_nvlist_array(req, "entries", &nitems);
109 	for (unsigned i = 0; i < nitems; i++) {
110 		const nvlist_t *entry = entries[i];
111 		const npf_addr_t *addr;
112 		npf_netmask_t mask;
113 		size_t alen;
114 
115 		/* Get address and mask; add a table entry. */
116 		addr = dnvlist_get_binary(entry, "addr", &alen, NULL, 0);
117 		mask = dnvlist_get_number(entry, "mask", NPF_NO_NETMASK);
118 		if (addr == NULL || alen == 0) {
119 			NPF_ERR_DEBUG(resp);
120 			error = EINVAL;
121 			break;
122 		}
123 		error = npf_table_insert(t, alen, addr, mask);
124 		if (__predict_false(error)) {
125 			if (error == EEXIST) {
126 				nvlist_add_stringf(resp, "error-msg",
127 				    "table `%s' has a duplicate entry",
128 				    nvlist_get_string(req, "name"));
129 			} else {
130 				NPF_ERR_DEBUG(resp);
131 			}
132 			break;
133 		}
134 	}
135 	return error;
136 }
137 
138 /*
139  * npf_mk_table: create a table from provided nvlist.
140  */
141 static int __noinline
npf_mk_table(npf_t * npf,const nvlist_t * req,nvlist_t * resp,npf_tableset_t * tblset,npf_table_t ** tblp,bool replacing)142 npf_mk_table(npf_t *npf, const nvlist_t *req, nvlist_t *resp,
143     npf_tableset_t *tblset, npf_table_t **tblp, bool replacing)
144 {
145 	npf_table_t *t;
146 	const char *name;
147 	const void *blob;
148 	uint64_t tid;
149 	size_t size;
150 	int type;
151 	int error = 0;
152 
153 	KASSERT(tblp != NULL);
154 
155 	/* Table name, ID and type.  Validate them. */
156 	name = dnvlist_get_string(req, "name", NULL);
157 	if (!name) {
158 		NPF_ERR_DEBUG(resp);
159 		error = EINVAL;
160 		goto out;
161 	}
162 	tid = dnvlist_get_number(req, "id", UINT64_MAX);
163 	type = dnvlist_get_number(req, "type", UINT64_MAX);
164 	error = npf_table_check(tblset, name, tid, type, replacing);
165 	if (error) {
166 		NPF_ERR_DEBUG(resp);
167 		goto out;
168 	}
169 
170 	/* Get the entries or binary data. */
171 	blob = dnvlist_get_binary(req, "data", &size, NULL, 0);
172 	if (type == NPF_TABLE_CONST && (blob == NULL || size == 0)) {
173 		NPF_ERR_DEBUG(resp);
174 		error = EINVAL;
175 		goto out;
176 	}
177 
178 	t = npf_table_create(name, (unsigned)tid, type, blob, size);
179 	if (t == NULL) {
180 		NPF_ERR_DEBUG(resp);
181 		error = ENOMEM;
182 		goto out;
183 	}
184 
185 	if ((error = npf_mk_table_entries(t, req, resp)) != 0) {
186 		npf_table_destroy(t);
187 		goto out;
188 	}
189 
190 	*tblp = t;
191 out:
192 	return error;
193 }
194 
195 static int __noinline
npf_mk_tables(npf_t * npf,const nvlist_t * req,nvlist_t * resp,npf_config_t * nc)196 npf_mk_tables(npf_t *npf, const nvlist_t *req, nvlist_t *resp, npf_config_t *nc)
197 {
198 	const nvlist_t * const *tables;
199 	npf_tableset_t *tblset;
200 	size_t nitems;
201 	int error = 0;
202 
203 	if (nvlist_exists_nvlist_array(req, "tables")) {
204 		tables = nvlist_get_nvlist_array(req, "tables", &nitems);
205 		if (nitems > NPF_MAX_TABLES) {
206 			NPF_ERR_DEBUG(resp);
207 			return E2BIG;
208 		}
209 	} else {
210 		tables = NULL;
211 		nitems = 0;
212 	}
213 	tblset = npf_tableset_create(nitems);
214 	for (unsigned i = 0; i < nitems; i++) {
215 		const nvlist_t *table = tables[i];
216 		npf_table_t *t;
217 
218 		error = npf_mk_table(npf, table, resp, tblset, &t, 0);
219 		if (error) {
220 			break;
221 		}
222 
223 		error = npf_tableset_insert(tblset, t);
224 		KASSERT(error == 0);
225 	}
226 	nc->tableset = tblset;
227 	return error;
228 }
229 
230 static npf_rproc_t *
npf_mk_singlerproc(npf_t * npf,const nvlist_t * rproc,nvlist_t * resp)231 npf_mk_singlerproc(npf_t *npf, const nvlist_t *rproc, nvlist_t *resp)
232 {
233 	const nvlist_t * const *extcalls;
234 	size_t nitems;
235 	npf_rproc_t *rp;
236 
237 	if ((rp = npf_rproc_create(rproc)) == NULL) {
238 		NPF_ERR_DEBUG(resp);
239 		return NULL;
240 	}
241 	if (!nvlist_exists_nvlist_array(rproc, "extcalls")) {
242 		return rp;
243 	}
244 	extcalls = nvlist_get_nvlist_array(rproc, "extcalls", &nitems);
245 	for (unsigned i = 0; i < nitems; i++) {
246 		const nvlist_t *extcall = extcalls[i];
247 		const char *name;
248 
249 		name = dnvlist_get_string(extcall, "name", NULL);
250 		if (!name || npf_ext_construct(npf, name, rp, extcall)) {
251 			NPF_ERR_DEBUG(resp);
252 			npf_rproc_release(rp);
253 			rp = NULL;
254 			break;
255 		}
256 	}
257 	return rp;
258 }
259 
260 static int __noinline
npf_mk_rprocs(npf_t * npf,const nvlist_t * req,nvlist_t * resp,npf_config_t * nc)261 npf_mk_rprocs(npf_t *npf, const nvlist_t *req, nvlist_t *resp, npf_config_t *nc)
262 {
263 	const nvlist_t * const *rprocs;
264 	npf_rprocset_t *rpset;
265 	size_t nitems;
266 	int error = 0;
267 
268 	if (nvlist_exists_nvlist_array(req, "rprocs")) {
269 		rprocs = nvlist_get_nvlist_array(req, "rprocs", &nitems);
270 		if (nitems > NPF_MAX_RPROCS) {
271 			NPF_ERR_DEBUG(resp);
272 			return E2BIG;
273 		}
274 	} else {
275 		rprocs = NULL;
276 		nitems = 0;
277 	}
278 	rpset = npf_rprocset_create();
279 	for (unsigned i = 0; i < nitems; i++) {
280 		const nvlist_t *rproc = rprocs[i];
281 		npf_rproc_t *rp;
282 
283 		if ((rp = npf_mk_singlerproc(npf, rproc, resp)) == NULL) {
284 			error = EINVAL;
285 			break;
286 		}
287 		npf_rprocset_insert(rpset, rp);
288 	}
289 	nc->rule_procs = rpset;
290 	return error;
291 }
292 
293 static int __noinline
npf_mk_algs(npf_t * npf,const nvlist_t * req,nvlist_t * resp)294 npf_mk_algs(npf_t *npf, const nvlist_t *req, nvlist_t *resp)
295 {
296 	const nvlist_t * const *algs;
297 	size_t nitems;
298 
299 	if (nvlist_exists_nvlist_array(req, "algs")) {
300 		algs = nvlist_get_nvlist_array(req, "algs", &nitems);
301 	} else {
302 		algs = NULL;
303 		nitems = 0;
304 	}
305 	for (unsigned i = 0; i < nitems; i++) {
306 		const nvlist_t *alg = algs[i];
307 		const char *name;
308 
309 		name = dnvlist_get_string(alg, "name", NULL);
310 		if (!name) {
311 			NPF_ERR_DEBUG(resp);
312 			return EINVAL;
313 		}
314 		if (!npf_alg_construct(npf, name)) {
315 			NPF_ERR_DEBUG(resp);
316 			return EINVAL;
317 		}
318 	}
319 	return 0;
320 }
321 
322 static int __noinline
npf_mk_singlerule(npf_t * npf,const nvlist_t * req,nvlist_t * resp,npf_rprocset_t * rpset,npf_rule_t ** rlret)323 npf_mk_singlerule(npf_t *npf, const nvlist_t *req, nvlist_t *resp,
324     npf_rprocset_t *rpset, npf_rule_t **rlret)
325 {
326 	npf_rule_t *rl;
327 	const char *rname;
328 	const void *code;
329 	size_t clen;
330 	int error = 0;
331 
332 	if ((rl = npf_rule_alloc(npf, req)) == NULL) {
333 		NPF_ERR_DEBUG(resp);
334 		return EINVAL;
335 	}
336 
337 	/* Assign the rule procedure, if any. */
338 	if ((rname = dnvlist_get_string(req, "rproc", NULL)) != NULL) {
339 		npf_rproc_t *rp;
340 
341 		if (rpset == NULL) {
342 			NPF_ERR_DEBUG(resp);
343 			error = EINVAL;
344 			goto err;
345 		}
346 		if ((rp = npf_rprocset_lookup(rpset, rname)) == NULL) {
347 			NPF_ERR_DEBUG(resp);
348 			error = EINVAL;
349 			goto err;
350 		}
351 		npf_rule_setrproc(rl, rp);
352 	}
353 
354 	/* Filter byte-code (binary data). */
355 	code = dnvlist_get_binary(req, "code", &clen, NULL, 0);
356 	if (code) {
357 		void *bc;
358 		int type;
359 
360 		type = dnvlist_get_number(req, "code-type", UINT64_MAX);
361 		if (type != NPF_CODE_BPF) {
362 			NPF_ERR_DEBUG(resp);
363 			error = ENOTSUP;
364 			goto err;
365 		}
366 		if (clen == 0) {
367 			NPF_ERR_DEBUG(resp);
368 			error = EINVAL;
369 			goto err;
370 		}
371 		if (!npf_bpf_validate(code, clen)) {
372 			NPF_ERR_DEBUG(resp);
373 			error = EINVAL;
374 			goto err;
375 		}
376 		bc = kmem_alloc(clen, KM_SLEEP);
377 		memcpy(bc, code, clen); // XXX: use nvlist_take
378 		npf_rule_setcode(rl, type, bc, clen);
379 	}
380 
381 	*rlret = rl;
382 	return 0;
383 err:
384 	nvlist_add_number(resp, "id", dnvlist_get_number(req, "prio", 0));
385 	npf_rule_free(rl);
386 	return error;
387 }
388 
389 static int __noinline
npf_mk_rules(npf_t * npf,const nvlist_t * req,nvlist_t * resp,npf_config_t * nc)390 npf_mk_rules(npf_t *npf, const nvlist_t *req, nvlist_t *resp, npf_config_t *nc)
391 {
392 	const nvlist_t * const *rules;
393 	npf_ruleset_t *rlset;
394 	size_t nitems;
395 	int error = 0;
396 
397 	if (nvlist_exists_nvlist_array(req, "rules")) {
398 		rules = nvlist_get_nvlist_array(req, "rules", &nitems);
399 		if (nitems > NPF_MAX_RULES) {
400 			NPF_ERR_DEBUG(resp);
401 			return E2BIG;
402 		}
403 	} else {
404 		rules = NULL;
405 		nitems = 0;
406 	}
407 	rlset = npf_ruleset_create(nitems);
408 	for (unsigned i = 0; i < nitems; i++) {
409 		const nvlist_t *rule = rules[i];
410 		npf_rule_t *rl = NULL;
411 		const char *name;
412 
413 		error = npf_mk_singlerule(npf, rule, resp, nc->rule_procs, &rl);
414 		if (error) {
415 			break;
416 		}
417 		name = dnvlist_get_string(rule, "name", NULL);
418 		if (name && npf_ruleset_lookup(rlset, name)) {
419 			NPF_ERR_DEBUG(resp);
420 			npf_rule_free(rl);
421 			error = EEXIST;
422 			break;
423 		}
424 		npf_ruleset_insert(rlset, rl);
425 	}
426 	nc->ruleset = rlset;
427 	return error;
428 }
429 
430 static int __noinline
npf_mk_singlenat(npf_t * npf,const nvlist_t * nat,nvlist_t * resp,npf_ruleset_t * ntset,npf_tableset_t * tblset,npf_rule_t ** rlp)431 npf_mk_singlenat(npf_t *npf, const nvlist_t *nat, nvlist_t *resp,
432     npf_ruleset_t *ntset, npf_tableset_t *tblset, npf_rule_t **rlp)
433 {
434 	npf_rule_t *rl = NULL;
435 	npf_natpolicy_t *np;
436 	int error;
437 
438 	/*
439 	 * NAT rules are standard rules, plus the translation policy.
440 	 * We first construct the rule structure.
441 	 */
442 	error = npf_mk_singlerule(npf, nat, resp, NULL, &rl);
443 	if (error) {
444 		return error;
445 	}
446 	KASSERT(rl != NULL);
447 	*rlp = rl;
448 
449 	/* If this rule is named, then it is a group with NAT policies. */
450 	if (dnvlist_get_string(nat, "name", NULL)) {
451 		return 0;
452 	}
453 
454 	/* Check the table ID. */
455 	if (nvlist_exists_number(nat, "nat-table-id")) {
456 		unsigned tid = nvlist_get_number(nat, "nat-table-id");
457 
458 		if (!npf_tableset_getbyid(tblset, tid)) {
459 			NPF_ERR_DEBUG(resp);
460 			error = EINVAL;
461 			goto out;
462 		}
463 	}
464 
465 	/* Allocate a new NAT policy and assign it to the rule. */
466 	np = npf_natpolicy_create(npf, nat, ntset);
467 	if (np == NULL) {
468 		NPF_ERR_DEBUG(resp);
469 		error = ENOMEM;
470 		goto out;
471 	}
472 	npf_rule_setnat(rl, np);
473 out:
474 	if (error) {
475 		npf_rule_free(rl);
476 	}
477 	return error;
478 }
479 
480 static int __noinline
npf_mk_natlist(npf_t * npf,const nvlist_t * req,nvlist_t * resp,npf_config_t * nc)481 npf_mk_natlist(npf_t *npf, const nvlist_t *req, nvlist_t *resp, npf_config_t *nc)
482 {
483 	const nvlist_t * const *nat_rules;
484 	npf_ruleset_t *ntset;
485 	size_t nitems;
486 	int error = 0;
487 
488 	/*
489 	 * NAT policies must be an array, but enforce a limit.
490 	 */
491 	if (nvlist_exists_nvlist_array(req, "nat")) {
492 		nat_rules = nvlist_get_nvlist_array(req, "nat", &nitems);
493 		if (nitems > NPF_MAX_RULES) {
494 			NPF_ERR_DEBUG(resp);
495 			return E2BIG;
496 		}
497 	} else {
498 		nat_rules = NULL;
499 		nitems = 0;
500 	}
501 	ntset = npf_ruleset_create(nitems);
502 	for (unsigned i = 0; i < nitems; i++) {
503 		const nvlist_t *nat = nat_rules[i];
504 		npf_rule_t *rl = NULL;
505 
506 		error = npf_mk_singlenat(npf, nat, resp, ntset,
507 		    nc->tableset, &rl);
508 		if (error) {
509 			break;
510 		}
511 		npf_ruleset_insert(ntset, rl);
512 	}
513 	nc->nat_ruleset = ntset;
514 	return error;
515 }
516 
517 /*
518  * npf_mk_connlist: import a list of connections and load them.
519  */
520 static int __noinline
npf_mk_connlist(npf_t * npf,const nvlist_t * req,nvlist_t * resp,npf_config_t * nc,npf_conndb_t ** conndb)521 npf_mk_connlist(npf_t *npf, const nvlist_t *req, nvlist_t *resp,
522     npf_config_t *nc, npf_conndb_t **conndb)
523 {
524 	const nvlist_t * const *conns;
525 	npf_conndb_t *cd;
526 	size_t nitems;
527 	int error = 0;
528 
529 	if (!nvlist_exists_nvlist_array(req, "conn-list")) {
530 		*conndb = NULL;
531 		return 0;
532 	}
533 	cd = npf_conndb_create();
534 	conns = nvlist_get_nvlist_array(req, "conn-list", &nitems);
535 	for (unsigned i = 0; i < nitems; i++) {
536 		const nvlist_t *conn = conns[i];
537 
538 		/* Construct and insert the connection. */
539 		error = npf_conn_import(npf, cd, conn, nc->nat_ruleset);
540 		if (error) {
541 			NPF_ERR_DEBUG(resp);
542 			break;
543 		}
544 	}
545 	if (error) {
546 		npf_conndb_gc(npf, cd, true, false);
547 		npf_conndb_destroy(cd);
548 	} else {
549 		*conndb = cd;
550 	}
551 	return error;
552 }
553 
554 /*
555  * npfctl_load: store passed data i.e. the update settings, create the
556  * passed rules, tables, etc and atomically activate them all.
557  */
558 static int
npfctl_load(npf_t * npf,const nvlist_t * req,nvlist_t * resp)559 npfctl_load(npf_t *npf, const nvlist_t *req, nvlist_t *resp)
560 {
561 	npf_config_t *nc;
562 	npf_conndb_t *conndb = NULL;
563 	bool flush;
564 	int error;
565 
566 	nc = npf_config_create();
567 	error = npf_mk_params(npf, req, resp, false /* validate */);
568 	if (error) {
569 		goto fail;
570 	}
571 	error = npf_mk_algs(npf, req, resp);
572 	if (error) {
573 		goto fail;
574 	}
575 	error = npf_mk_tables(npf, req, resp, nc);
576 	if (error) {
577 		goto fail;
578 	}
579 	error = npf_mk_rprocs(npf, req, resp, nc);
580 	if (error) {
581 		goto fail;
582 	}
583 	error = npf_mk_natlist(npf, req, resp, nc);
584 	if (error) {
585 		goto fail;
586 	}
587 	error = npf_mk_rules(npf, req, resp, nc);
588 	if (error) {
589 		goto fail;
590 	}
591 	error = npf_mk_connlist(npf, req, resp, nc, &conndb);
592 	if (error) {
593 		goto fail;
594 	}
595 
596 	flush = dnvlist_get_bool(req, "flush", false);
597 	nc->default_pass = flush;
598 
599 	/*
600 	 * Finally - perform the load.
601 	 */
602 	npf_config_load(npf, nc, conndb, flush);
603 	npf_mk_params(npf, req, resp, true /* set the params */);
604 	return 0;
605 
606 fail:
607 	npf_config_destroy(nc);
608 	return error;
609 }
610 
611 /*
612  * npfctl_save: export the active configuration, including the current
613  * snapshot of the connections.  Additionally, set the version and indicate
614  * whether the ruleset is currently active.
615  */
616 static int
npfctl_save(npf_t * npf,const nvlist_t * req,nvlist_t * resp)617 npfctl_save(npf_t *npf, const nvlist_t *req, nvlist_t *resp)
618 {
619 	npf_config_t *nc;
620 	int error;
621 
622 	/*
623 	 * Serialize the whole NPF configuration, including connections.
624 	 */
625 	nvlist_add_number(resp, "version", NPF_VERSION);
626 	nc = npf_config_enter(npf);
627 	error = npf_params_export(npf, resp);
628 	if (error) {
629 		goto out;
630 	}
631 	error = npf_conndb_export(npf, resp);
632 	if (error) {
633 		goto out;
634 	}
635 	error = npf_ruleset_export(npf, nc->ruleset, "rules", resp);
636 	if (error) {
637 		goto out;
638 	}
639 	error = npf_ruleset_export(npf, nc->nat_ruleset, "nat", resp);
640 	if (error) {
641 		goto out;
642 	}
643 	error = npf_tableset_export(npf, nc->tableset, resp);
644 	if (error) {
645 		goto out;
646 	}
647 	error = npf_rprocset_export(nc->rule_procs, resp);
648 	if (error) {
649 		goto out;
650 	}
651 	error = npf_alg_export(npf, resp);
652 	if (error) {
653 		goto out;
654 	}
655 	nvlist_add_bool(resp, "active", npf_active_p());
656 out:
657 	npf_config_exit(npf);
658 	return error;
659 }
660 
661 /*
662  * npfctl_table_replace: atomically replace a table's contents with
663  * the passed table data.
664  */
665 static int __noinline
npfctl_table_replace(npf_t * npf,const nvlist_t * req,nvlist_t * resp)666 npfctl_table_replace(npf_t *npf, const nvlist_t *req, nvlist_t *resp)
667 {
668 	npf_table_t *tbl, *gc_tbl = NULL;
669 	npf_config_t *nc;
670 	int error = 0;
671 
672 	nc = npf_config_enter(npf);
673 	error = npf_mk_table(npf, req, resp, nc->tableset, &tbl, true);
674 	if (error) {
675 		goto err;
676 	}
677 	gc_tbl = npf_tableset_swap(nc->tableset, tbl);
678 	if (gc_tbl == NULL) {
679 		error = EINVAL;
680 		gc_tbl = tbl;
681 		goto err;
682 	}
683 	npf_config_sync(npf);
684 err:
685 	npf_config_exit(npf);
686 	if (gc_tbl) {
687 		npf_table_destroy(gc_tbl);
688 	}
689 	return error;
690 }
691 
692 /*
693  * npfctl_rule: add or remove dynamic rules in the specified ruleset.
694  */
695 static int
npfctl_rule(npf_t * npf,const nvlist_t * req,nvlist_t * resp)696 npfctl_rule(npf_t *npf, const nvlist_t *req, nvlist_t *resp)
697 {
698 	npf_ruleset_t *rlset;
699 	npf_rule_t *rl = NULL;
700 	const char *ruleset_name;
701 	npf_config_t *nc;
702 	uint32_t rcmd;
703 	int error = 0;
704 	bool natset;
705 
706 	rcmd = dnvlist_get_number(req, "command", 0);
707 	natset = dnvlist_get_bool(req, "nat-ruleset", false);
708 	ruleset_name = dnvlist_get_string(req, "ruleset-name", NULL);
709 	if (!ruleset_name) {
710 		error = EINVAL;
711 		goto out;
712 	}
713 
714 	nc = npf_config_enter(npf);
715 	rlset = natset ? nc->nat_ruleset : nc->ruleset;
716 	switch (rcmd) {
717 	case NPF_CMD_RULE_ADD: {
718 		if (natset) {
719 			/*
720 			 * Translation rule.
721 			 */
722 			error = npf_mk_singlenat(npf, req, resp, rlset,
723 			    nc->tableset, &rl);
724 		} else {
725 			/*
726 			 * Standard rule.
727 			 */
728 			error = npf_mk_singlerule(npf, req, resp, NULL, &rl);
729 		}
730 		if (error) {
731 			goto out;
732 		}
733 		if ((error = npf_ruleset_add(rlset, ruleset_name, rl)) == 0) {
734 			/* Success. */
735 			uint64_t id = npf_rule_getid(rl);
736 			nvlist_add_number(resp, "id", id);
737 			rl = NULL;
738 		}
739 		break;
740 	}
741 	case NPF_CMD_RULE_REMOVE: {
742 		uint64_t id = dnvlist_get_number(req, "id", UINT64_MAX);
743 		error = npf_ruleset_remove(rlset, ruleset_name, id);
744 		break;
745 	}
746 	case NPF_CMD_RULE_REMKEY: {
747 		const void *key;
748 		size_t len;
749 
750 		key = dnvlist_get_binary(req, "key", &len, NULL, 0);
751 		if (len == 0 || len > NPF_RULE_MAXKEYLEN) {
752 			error = EINVAL;
753 			break;
754 		}
755 		error = npf_ruleset_remkey(rlset, ruleset_name, key, len);
756 		break;
757 	}
758 	case NPF_CMD_RULE_LIST: {
759 		error = npf_ruleset_list(npf, rlset, ruleset_name, resp);
760 		break;
761 	}
762 	case NPF_CMD_RULE_FLUSH: {
763 		error = npf_ruleset_flush(rlset, ruleset_name);
764 		break;
765 	}
766 	default:
767 		error = EINVAL;
768 		break;
769 	}
770 
771 	/* Destroy any removed rules. */
772 	if (!error && rcmd != NPF_CMD_RULE_ADD && rcmd != NPF_CMD_RULE_LIST) {
773 		npf_config_sync(npf);
774 		npf_ruleset_gc(rlset);
775 	}
776 out:
777 	npf_config_exit(npf);
778 
779 	if (rl) {
780 		KASSERT(error);
781 		npf_rule_free(rl);
782 	}
783 	return error;
784 }
785 
786 /*
787  * npfctl_table: add, remove or query entries in the specified table.
788  *
789  * For maximum performance, the interface is using plain structures.
790  */
791 int
npfctl_table(npf_t * npf,void * data)792 npfctl_table(npf_t *npf, void *data)
793 {
794 	const npf_ioctl_table_t *nct = data;
795 	char tname[NPF_TABLE_MAXNAMELEN];
796 	npf_config_t *nc;
797 	npf_table_t *t;
798 	int error;
799 
800 	error = copyinstr(nct->nct_name, tname, sizeof(tname), NULL);
801 	if (error) {
802 		return error;
803 	}
804 
805 	nc = npf_config_enter(npf);
806 	if ((t = npf_tableset_getbyname(nc->tableset, tname)) == NULL) {
807 		npf_config_exit(npf);
808 		return EINVAL;
809 	}
810 
811 	switch (nct->nct_cmd) {
812 	case NPF_CMD_TABLE_LOOKUP:
813 		error = npf_table_lookup(t, nct->nct_data.ent.alen,
814 		    &nct->nct_data.ent.addr);
815 		break;
816 	case NPF_CMD_TABLE_ADD:
817 		error = npf_table_insert(t, nct->nct_data.ent.alen,
818 		    &nct->nct_data.ent.addr, nct->nct_data.ent.mask);
819 		break;
820 	case NPF_CMD_TABLE_REMOVE:
821 		error = npf_table_remove(t, nct->nct_data.ent.alen,
822 		    &nct->nct_data.ent.addr, nct->nct_data.ent.mask);
823 		break;
824 	case NPF_CMD_TABLE_LIST:
825 		error = npf_table_list(t, nct->nct_data.buf.buf,
826 		    nct->nct_data.buf.len);
827 		break;
828 	case NPF_CMD_TABLE_FLUSH:
829 		error = npf_table_flush(t);
830 		break;
831 	default:
832 		error = EINVAL;
833 		break;
834 	}
835 	npf_table_gc(npf, t);
836 	npf_config_exit(npf);
837 
838 	return error;
839 }
840 
841 /*
842  * npfctl_run_op: run a particular NPF operation with a given the request.
843  *
844  * => Checks the ABI version.
845  * => Sets the error number for the response.
846  */
847 int
npfctl_run_op(npf_t * npf,unsigned op,const nvlist_t * req,nvlist_t * resp)848 npfctl_run_op(npf_t *npf, unsigned op, const nvlist_t *req, nvlist_t *resp)
849 {
850 	uint64_t ver;
851 	int error;
852 
853 	ver = dnvlist_get_number(req, "version", UINT64_MAX);
854 	if (__predict_false(ver != UINT64_MAX && ver != NPF_VERSION)) {
855 		return EPROGMISMATCH;
856 	}
857 	switch (op) {
858 	case IOC_NPF_LOAD:
859 		error = npfctl_load(npf, req, resp);
860 		break;
861 	case IOC_NPF_SAVE:
862 		error = npfctl_save(npf, req, resp);
863 		break;
864 	case IOC_NPF_RULE:
865 		error = npfctl_rule(npf, req, resp);
866 		break;
867 	case IOC_NPF_CONN_LOOKUP:
868 		error = npf_conn_find(npf, req, resp);
869 		break;
870 	case IOC_NPF_TABLE_REPLACE:
871 		error = npfctl_table_replace(npf, req, resp);
872 		break;
873 	default:
874 		error = ENOTTY;
875 		break;
876 	}
877 	nvlist_add_number(resp, "errno", error);
878 	return error;
879 }
880