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