npfctl.c revision 1.55.2.1 1 /*-
2 * Copyright (c) 2009-2014 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 #include <sys/cdefs.h>
31 __RCSID("$NetBSD: npfctl.c,v 1.55.2.1 2019/06/10 22:10:34 christos Exp $");
32
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <sys/mman.h>
36 #ifdef __NetBSD__
37 #include <sha1.h>
38 #include <sys/ioctl.h>
39 #include <sys/module.h>
40 #define SHA_DIGEST_LENGTH SHA1_DIGEST_LENGTH
41 #else
42 #include <openssl/sha.h>
43 #endif
44
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <err.h>
49 #include <fcntl.h>
50 #include <unistd.h>
51 #include <errno.h>
52
53 #include <arpa/inet.h>
54
55 #include "npfctl.h"
56
57 extern void npf_yyparse_string(const char *);
58
59 enum {
60 NPFCTL_START,
61 NPFCTL_STOP,
62 NPFCTL_RELOAD,
63 NPFCTL_SHOWCONF,
64 NPFCTL_FLUSH,
65 NPFCTL_VALIDATE,
66 NPFCTL_TABLE,
67 NPFCTL_RULE,
68 NPFCTL_STATS,
69 NPFCTL_SAVE,
70 NPFCTL_LOAD,
71 NPFCTL_DEBUG,
72 NPFCTL_CONN_LIST,
73 };
74
75 static const struct operations_s {
76 const char * cmd;
77 int action;
78 } operations[] = {
79 /* Start, stop, reload */
80 { "start", NPFCTL_START },
81 { "stop", NPFCTL_STOP },
82 { "reload", NPFCTL_RELOAD },
83 { "show", NPFCTL_SHOWCONF, },
84 { "flush", NPFCTL_FLUSH },
85 /* Table */
86 { "table", NPFCTL_TABLE },
87 /* Rule */
88 { "rule", NPFCTL_RULE },
89 /* Stats */
90 { "stats", NPFCTL_STATS },
91 /* Full state save/load */
92 { "save", NPFCTL_SAVE },
93 { "load", NPFCTL_LOAD },
94 { "list", NPFCTL_CONN_LIST },
95 /* Misc. */
96 { "valid", NPFCTL_VALIDATE },
97 { "debug", NPFCTL_DEBUG },
98 /* --- */
99 { NULL, 0 }
100 };
101
102 bool
103 join(char *buf, size_t buflen, int count, char **args, const char *sep)
104 {
105 const u_int seplen = strlen(sep);
106 char *s = buf, *p = NULL;
107
108 for (int i = 0; i < count; i++) {
109 size_t len;
110
111 p = stpncpy(s, args[i], buflen);
112 len = p - s + seplen;
113 if (len >= buflen) {
114 return false;
115 }
116 buflen -= len;
117 strcpy(p, sep);
118 s = p + seplen;
119 }
120 *p = '\0';
121 return true;
122 }
123
124 __dead static void
125 usage(void)
126 {
127 const char *progname = getprogname();
128
129 fprintf(stderr,
130 "Usage:\t%s start | stop | flush | show | stats\n",
131 progname);
132 fprintf(stderr,
133 "\t%s validate | reload [<rule-file>]\n",
134 progname);
135 fprintf(stderr,
136 "\t%s rule \"rule-name\" { add | rem } <rule-syntax>\n",
137 progname);
138 fprintf(stderr,
139 "\t%s rule \"rule-name\" rem-id <rule-id>\n",
140 progname);
141 fprintf(stderr,
142 "\t%s rule \"rule-name\" { list | flush }\n",
143 progname);
144 fprintf(stderr,
145 "\t%s table <tid> { add | rem | test } <address/mask>\n",
146 progname);
147 fprintf(stderr,
148 "\t%s table <tid> { list | flush }\n",
149 progname);
150 fprintf(stderr,
151 "\t%s save | load\n",
152 progname);
153 fprintf(stderr,
154 "\t%s list [-46hNnw] [-i <ifname>]\n",
155 progname);
156 fprintf(stderr,
157 "\t%s debug [<rule-file>] [<raw-output>]\n",
158 progname);
159 exit(EXIT_FAILURE);
160 }
161
162 static int
163 npfctl_print_stats(int fd)
164 {
165 static const struct stats_s {
166 /* Note: -1 indicates a new section. */
167 int index;
168 const char * name;
169 } stats[] = {
170 { -1, "Packets passed" },
171 { NPF_STAT_PASS_DEFAULT, "default pass" },
172 { NPF_STAT_PASS_RULESET, "ruleset pass" },
173 { NPF_STAT_PASS_CONN, "state pass" },
174
175 { -1, "Packets blocked" },
176 { NPF_STAT_BLOCK_DEFAULT, "default block" },
177 { NPF_STAT_BLOCK_RULESET, "ruleset block" },
178
179 { -1, "State and NAT entries" },
180 { NPF_STAT_CONN_CREATE, "state allocations"},
181 { NPF_STAT_CONN_DESTROY, "state destructions"},
182 { NPF_STAT_NAT_CREATE, "NAT entry allocations" },
183 { NPF_STAT_NAT_DESTROY, "NAT entry destructions"},
184
185 { -1, "Network buffers" },
186 { NPF_STAT_NBUF_NONCONTIG, "non-contiguous cases" },
187 { NPF_STAT_NBUF_CONTIG_FAIL, "contig alloc failures" },
188
189 { -1, "Invalid packet state cases" },
190 { NPF_STAT_INVALID_STATE, "cases in total" },
191 { NPF_STAT_INVALID_STATE_TCP1, "TCP case I" },
192 { NPF_STAT_INVALID_STATE_TCP2, "TCP case II" },
193 { NPF_STAT_INVALID_STATE_TCP3, "TCP case III" },
194
195 { -1, "Packet race cases" },
196 { NPF_STAT_RACE_NAT, "NAT association race" },
197 { NPF_STAT_RACE_CONN, "duplicate state race" },
198
199 { -1, "Fragmentation" },
200 { NPF_STAT_FRAGMENTS, "fragments" },
201 { NPF_STAT_REASSEMBLY, "reassembled" },
202 { NPF_STAT_REASSFAIL, "failed reassembly" },
203
204 { -1, "Other" },
205 { NPF_STAT_ERROR, "unexpected errors" },
206 };
207 uint64_t *st = ecalloc(1, NPF_STATS_SIZE);
208
209 if (ioctl(fd, IOC_NPF_STATS, &st) != 0) {
210 err(EXIT_FAILURE, "ioctl(IOC_NPF_STATS)");
211 }
212
213 for (unsigned i = 0; i < __arraycount(stats); i++) {
214 const char *sname = stats[i].name;
215 int sidx = stats[i].index;
216
217 if (sidx == -1) {
218 printf("%s:\n", sname);
219 } else {
220 printf("\t%"PRIu64" %s\n", st[sidx], sname);
221 }
222 }
223
224 free(st);
225 return 0;
226 }
227
228 void
229 npfctl_print_error(const npf_error_t *ne)
230 {
231 const char *srcfile = ne->source_file;
232
233 if (srcfile) {
234 warnx("source %s line %d", srcfile, ne->source_line);
235 }
236 if (ne->id) {
237 warnx("object: %" PRIi64, ne->id);
238 }
239 }
240
241 char *
242 npfctl_print_addrmask(int alen, const char *fmt, const npf_addr_t *addr,
243 npf_netmask_t mask)
244 {
245 const unsigned buflen = 256;
246 char *buf = ecalloc(1, buflen);
247 struct sockaddr_storage ss;
248
249 memset(&ss, 0, sizeof(ss));
250
251 switch (alen) {
252 case 4: {
253 struct sockaddr_in *sin = (void *)&ss;
254 sin->sin_family = AF_INET;
255 memcpy(&sin->sin_addr, addr, sizeof(sin->sin_addr));
256 break;
257 }
258 case 16: {
259 struct sockaddr_in6 *sin6 = (void *)&ss;
260 sin6->sin6_family = AF_INET6;
261 memcpy(&sin6->sin6_addr, addr, sizeof(sin6->sin6_addr));
262 break;
263 }
264 default:
265 assert(false);
266 }
267 sockaddr_snprintf(buf, buflen, fmt, (const void *)&ss);
268 if (mask && mask != NPF_NO_NETMASK) {
269 const unsigned len = strlen(buf);
270 snprintf(&buf[len], buflen - len, "/%u", mask);
271 }
272 return buf;
273 }
274
275 __dead static void
276 npfctl_table(int fd, int argc, char **argv)
277 {
278 static const struct tblops_s {
279 const char * cmd;
280 int action;
281 } tblops[] = {
282 { "add", NPF_CMD_TABLE_ADD },
283 { "rem", NPF_CMD_TABLE_REMOVE },
284 { "del", NPF_CMD_TABLE_REMOVE },
285 { "test", NPF_CMD_TABLE_LOOKUP },
286 { "list", NPF_CMD_TABLE_LIST },
287 { "flush", NPF_CMD_TABLE_FLUSH },
288 { NULL, 0 }
289 };
290 npf_ioctl_table_t nct;
291 fam_addr_mask_t fam;
292 size_t buflen = 512;
293 char *cmd, *arg;
294 int n, alen;
295
296 /* Default action is list. */
297 memset(&nct, 0, sizeof(npf_ioctl_table_t));
298 nct.nct_name = argv[0];
299 cmd = argv[1];
300
301 for (n = 0; tblops[n].cmd != NULL; n++) {
302 if (strcmp(cmd, tblops[n].cmd) != 0) {
303 continue;
304 }
305 nct.nct_cmd = tblops[n].action;
306 break;
307 }
308 if (tblops[n].cmd == NULL) {
309 errx(EXIT_FAILURE, "invalid command '%s'", cmd);
310 }
311
312 switch (nct.nct_cmd) {
313 case NPF_CMD_TABLE_LIST:
314 case NPF_CMD_TABLE_FLUSH:
315 arg = NULL;
316 break;
317 default:
318 if (argc < 3) {
319 usage();
320 }
321 arg = argv[2];
322 }
323
324 again:
325 switch (nct.nct_cmd) {
326 case NPF_CMD_TABLE_LIST:
327 nct.nct_data.buf.buf = ecalloc(1, buflen);
328 nct.nct_data.buf.len = buflen;
329 break;
330 case NPF_CMD_TABLE_FLUSH:
331 break;
332 default:
333 if (!npfctl_parse_cidr(arg, &fam, &alen)) {
334 errx(EXIT_FAILURE, "invalid CIDR '%s'", arg);
335 }
336 nct.nct_data.ent.alen = alen;
337 memcpy(&nct.nct_data.ent.addr, &fam.fam_addr, alen);
338 nct.nct_data.ent.mask = fam.fam_mask;
339 }
340
341 if (ioctl(fd, IOC_NPF_TABLE, &nct) != -1) {
342 errno = 0;
343 }
344 switch (errno) {
345 case 0:
346 break;
347 case EEXIST:
348 errx(EXIT_FAILURE, "entry already exists or is conflicting");
349 case ENOENT:
350 errx(EXIT_FAILURE, "not found");
351 case EINVAL:
352 errx(EXIT_FAILURE, "invalid address, mask or table ID");
353 case ENOMEM:
354 if (nct.nct_cmd == NPF_CMD_TABLE_LIST) {
355 /* XXX */
356 free(nct.nct_data.buf.buf);
357 buflen <<= 1;
358 goto again;
359 }
360 /* FALLTHROUGH */
361 default:
362 err(EXIT_FAILURE, "ioctl(IOC_NPF_TABLE)");
363 }
364
365 if (nct.nct_cmd == NPF_CMD_TABLE_LIST) {
366 npf_ioctl_ent_t *ent = nct.nct_data.buf.buf;
367 char *buf;
368
369 while (nct.nct_data.buf.len--) {
370 if (!ent->alen)
371 break;
372 buf = npfctl_print_addrmask(ent->alen, "%a",
373 &ent->addr, ent->mask);
374 puts(buf);
375 ent++;
376 }
377 free(nct.nct_data.buf.buf);
378 } else {
379 printf("%s: %s\n", getprogname(),
380 nct.nct_cmd == NPF_CMD_TABLE_LOOKUP ?
381 "match" : "success");
382 }
383 exit(EXIT_SUCCESS);
384 }
385
386 static nl_rule_t *
387 npfctl_parse_rule(int argc, char **argv)
388 {
389 char rule_string[1024];
390 nl_rule_t *rl;
391
392 /* Get the rule string and parse it. */
393 if (!join(rule_string, sizeof(rule_string), argc, argv, " ")) {
394 errx(EXIT_FAILURE, "command too long");
395 }
396 npfctl_parse_string(rule_string);
397 if ((rl = npfctl_rule_ref()) == NULL) {
398 errx(EXIT_FAILURE, "could not parse the rule");
399 }
400 return rl;
401 }
402
403 #ifdef __NetBSD__
404 static unsigned char *
405 SHA1(const unsigned char *d, size_t l, unsigned char *md)
406 {
407 SHA1_CTX c;
408
409 SHA1Init(&c);
410 SHA1Update(&c, d, l);
411 SHA1Final(md, &c);
412 return md;
413 }
414 #endif
415
416 static void
417 npfctl_generate_key(nl_rule_t *rl, void *key)
418 {
419 void *meta;
420 size_t len;
421
422 if ((meta = npf_rule_export(rl, &len)) == NULL) {
423 errx(EXIT_FAILURE, "error generating rule key");
424 }
425 __CTASSERT(NPF_RULE_MAXKEYLEN >= SHA_DIGEST_LENGTH);
426 memset(key, 0, NPF_RULE_MAXKEYLEN);
427 SHA1(meta, len, key);
428 free(meta);
429 }
430
431 __dead static void
432 npfctl_rule(int fd, int argc, char **argv)
433 {
434 static const struct ruleops_s {
435 const char * cmd;
436 int action;
437 bool extra_arg;
438 } ruleops[] = {
439 { "add", NPF_CMD_RULE_ADD, true },
440 { "rem", NPF_CMD_RULE_REMKEY, true },
441 { "del", NPF_CMD_RULE_REMKEY, true },
442 { "rem-id", NPF_CMD_RULE_REMOVE, true },
443 { "list", NPF_CMD_RULE_LIST, false },
444 { "flush", NPF_CMD_RULE_FLUSH, false },
445 { NULL, 0, 0 }
446 };
447 uint8_t key[NPF_RULE_MAXKEYLEN];
448 const char *ruleset_name = argv[0];
449 const char *cmd = argv[1];
450 int error, action = 0;
451 uint64_t rule_id;
452 bool extra_arg;
453 nl_rule_t *rl;
454
455 for (int n = 0; ruleops[n].cmd != NULL; n++) {
456 if (strcmp(cmd, ruleops[n].cmd) == 0) {
457 action = ruleops[n].action;
458 extra_arg = ruleops[n].extra_arg;
459 break;
460 }
461 }
462 argc -= 2;
463 argv += 2;
464
465 if (!action || (extra_arg && argc == 0)) {
466 usage();
467 }
468
469 switch (action) {
470 case NPF_CMD_RULE_ADD:
471 rl = npfctl_parse_rule(argc, argv);
472 npfctl_generate_key(rl, key);
473 npf_rule_setkey(rl, key, sizeof(key));
474 error = npf_ruleset_add(fd, ruleset_name, rl, &rule_id);
475 break;
476 case NPF_CMD_RULE_REMKEY:
477 rl = npfctl_parse_rule(argc, argv);
478 npfctl_generate_key(rl, key);
479 error = npf_ruleset_remkey(fd, ruleset_name, key, sizeof(key));
480 break;
481 case NPF_CMD_RULE_REMOVE:
482 rule_id = strtoull(argv[0], NULL, 16);
483 error = npf_ruleset_remove(fd, ruleset_name, rule_id);
484 break;
485 case NPF_CMD_RULE_LIST:
486 error = npfctl_ruleset_show(fd, ruleset_name);
487 break;
488 case NPF_CMD_RULE_FLUSH:
489 error = npf_ruleset_flush(fd, ruleset_name);
490 break;
491 default:
492 abort();
493 }
494
495 switch (error) {
496 case 0:
497 /* Success. */
498 break;
499 case ESRCH:
500 errx(EXIT_FAILURE, "ruleset \"%s\" not found", ruleset_name);
501 case ENOENT:
502 errx(EXIT_FAILURE, "rule was not found");
503 default:
504 errx(EXIT_FAILURE, "rule operation: %s", strerror(error));
505 }
506 if (action == NPF_CMD_RULE_ADD) {
507 printf("OK %" PRIx64 "\n", rule_id);
508 }
509 exit(EXIT_SUCCESS);
510 }
511
512 static bool bpfjit = true;
513
514 void
515 npfctl_bpfjit(bool onoff)
516 {
517 bpfjit = onoff;
518 }
519
520 static void
521 npfctl_preload_bpfjit(void)
522 {
523 #ifdef __NetBSD__
524 modctl_load_t args = {
525 .ml_filename = "bpfjit",
526 .ml_flags = MODCTL_NO_PROP,
527 .ml_props = NULL,
528 .ml_propslen = 0
529 };
530
531 if (!bpfjit)
532 return;
533
534 if (modctl(MODCTL_LOAD, &args) != 0 && errno != EEXIST) {
535 static const char *p = "; performance will be degraded";
536 if (errno == ENOENT)
537 warnx("the bpfjit module seems to be missing%s", p);
538 else
539 warn("error loading the bpfjit module%s", p);
540 warnx("To disable this warning `set bpf.jit off' in "
541 "/etc/npf.conf");
542 }
543 #endif
544 }
545
546 static int
547 npfctl_load(int fd)
548 {
549 nl_config_t *ncf;
550 npf_error_t errinfo;
551 struct stat sb;
552 size_t blen;
553 void *blob;
554 int error;
555
556 /*
557 * The file may change while reading - we are not handling this,
558 * leaving this responsibility for the caller.
559 */
560 if (stat(NPF_DB_PATH, &sb) == -1) {
561 err(EXIT_FAILURE, "stat");
562 }
563 if ((blen = sb.st_size) == 0) {
564 err(EXIT_FAILURE, "saved configuration file is empty");
565 }
566 if ((blob = mmap(NULL, blen, PROT_READ,
567 MAP_FILE | MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
568 err(EXIT_FAILURE, "mmap");
569 }
570 ncf = npf_config_import(blob, blen);
571 munmap(blob, blen);
572 if (ncf == NULL) {
573 return errno;
574 }
575
576 /*
577 * Configuration imported - submit it now.
578 **/
579 errno = error = npf_config_submit(ncf, fd, &errinfo);
580 if (error) {
581 npfctl_print_error(&errinfo);
582 }
583 npf_config_destroy(ncf);
584 return error;
585 }
586
587 struct npf_conn_filter {
588 uint16_t alen;
589 const char *ifname;
590 bool nat;
591 bool wide;
592 bool name;
593 int width;
594 FILE *fp;
595 };
596
597 static int
598 npfctl_conn_print(unsigned alen, const npf_addr_t *a, const in_port_t *p,
599 const char *ifname, void *v)
600 {
601 struct npf_conn_filter *fil = v;
602 FILE *fp = fil->fp;
603 char *src, *dst;
604
605 if (fil->ifname && strcmp(ifname, fil->ifname) != 0)
606 return 0;
607 if (fil->alen && alen != fil->alen)
608 return 0;
609 if (fil->nat && !p[2])
610 return 0;
611
612 int w = fil->width;
613 const char *fmt = fil->name ? "%A" :
614 (alen == sizeof(struct in_addr) ? "%a" : "[%a]");
615 src = npfctl_print_addrmask(alen, fmt, &a[0], NPF_NO_NETMASK);
616 dst = npfctl_print_addrmask(alen, fmt, &a[1], NPF_NO_NETMASK);
617 if (fil->wide)
618 fprintf(fp, "%s:%d %s:%d", src, p[0], dst, p[1]);
619 else
620 fprintf(fp, "%*.*s:%-5d %*.*s:%-5d", w, w, src, p[0],
621 w, w, dst, p[1]);
622 free(src);
623 free(dst);
624 if (!p[2]) {
625 fputc('\n', fp);
626 return 1;
627 }
628 fprintf(fp, " via %s:%d\n", ifname, p[2]);
629 return 1;
630 }
631
632
633 static int
634 npfctl_conn_list(int fd, int argc, char **argv)
635 {
636 struct npf_conn_filter f;
637 int c;
638 int header = true;
639 memset(&f, 0, sizeof(f));
640
641 argc--;
642 argv++;
643
644 while ((c = getopt(argc, argv, "46hi:nNw")) != -1) {
645 switch (c) {
646 case '4':
647 f.alen = sizeof(struct in_addr);
648 break;
649 case '6':
650 f.alen = sizeof(struct in6_addr);
651 break;
652 case 'h':
653 header = false;
654 break;
655 case 'i':
656 f.ifname = optarg;
657 break;
658 case 'n':
659 f.nat = true;
660 break;
661 case 'N':
662 f.name = true;
663 break;
664 case 'w':
665 f.wide = true;
666 break;
667 default:
668 fprintf(stderr,
669 "Usage: %s list [-46hnNw] [-i <ifname>]\n",
670 getprogname());
671 exit(EXIT_FAILURE);
672 }
673 }
674 f.width = f.alen == sizeof(struct in_addr) ? 25 : 41;
675 int w = f.width + 6;
676 f.fp = stdout;
677 if (header)
678 fprintf(f.fp, "%*.*s %*.*s\n",
679 w, w, "From address:port ", w, w, "To address:port ");
680
681 npf_conn_list(fd, npfctl_conn_print, &f);
682 return 0;
683 }
684
685 static int
686 npfctl_open_dev(const char *path)
687 {
688 int fd, kernver;
689
690 fd = open(path, O_RDONLY);
691 if (fd == -1) {
692 err(EXIT_FAILURE, "cannot open '%s'", path);
693 }
694 if (ioctl(fd, IOC_NPF_VERSION, &kernver) == -1) {
695 err(EXIT_FAILURE, "ioctl(IOC_NPF_VERSION)");
696 }
697 if (kernver != NPF_VERSION) {
698 errx(EXIT_FAILURE,
699 "incompatible NPF interface version (%d, kernel %d)\n"
700 "Hint: update %s?", NPF_VERSION, kernver,
701 kernver > NPF_VERSION ? "userland" : "kernel");
702 }
703 return fd;
704 }
705
706 static void
707 npfctl(int action, int argc, char **argv)
708 {
709 int fd, boolval, ret = 0;
710 const char *fun = "";
711 nl_config_t *ncf;
712
713 switch (action) {
714 case NPFCTL_VALIDATE:
715 case NPFCTL_DEBUG:
716 fd = 0;
717 break;
718 default:
719 fd = npfctl_open_dev(NPF_DEV_PATH);
720 }
721
722 switch (action) {
723 case NPFCTL_START:
724 boolval = true;
725 ret = ioctl(fd, IOC_NPF_SWITCH, &boolval);
726 fun = "ioctl(IOC_NPF_SWITCH)";
727 break;
728 case NPFCTL_STOP:
729 boolval = false;
730 ret = ioctl(fd, IOC_NPF_SWITCH, &boolval);
731 fun = "ioctl(IOC_NPF_SWITCH)";
732 break;
733 case NPFCTL_RELOAD:
734 npfctl_config_init(false);
735 npfctl_parse_file(argc < 3 ? NPF_CONF_PATH : argv[2]);
736 npfctl_preload_bpfjit();
737 errno = ret = npfctl_config_send(fd);
738 fun = "npfctl_config_send";
739 break;
740 case NPFCTL_SHOWCONF:
741 ret = npfctl_config_show(fd);
742 fun = "npfctl_config_show";
743 break;
744 case NPFCTL_FLUSH:
745 ret = npf_config_flush(fd);
746 fun = "npf_config_flush";
747 break;
748 case NPFCTL_TABLE:
749 if ((argc -= 2) < 2) {
750 usage();
751 }
752 argv += 2;
753 npfctl_table(fd, argc, argv);
754 break;
755 case NPFCTL_RULE:
756 if ((argc -= 2) < 2) {
757 usage();
758 }
759 argv += 2;
760 npfctl_rule(fd, argc, argv);
761 break;
762 case NPFCTL_LOAD:
763 npfctl_preload_bpfjit();
764 ret = npfctl_load(fd);
765 fun = "npfctl_config_load";
766 break;
767 case NPFCTL_SAVE:
768 ncf = npf_config_retrieve(fd);
769 if (ncf) {
770 npfctl_config_save(ncf, NPF_DB_PATH);
771 npf_config_destroy(ncf);
772 } else {
773 ret = errno;
774 }
775 fun = "npfctl_config_save";
776 break;
777 case NPFCTL_STATS:
778 ret = npfctl_print_stats(fd);
779 fun = "npfctl_print_stats";
780 break;
781 case NPFCTL_CONN_LIST:
782 ret = npfctl_conn_list(fd, argc, argv);
783 fun = "npfctl_conn_list";
784 break;
785 case NPFCTL_VALIDATE:
786 npfctl_config_init(false);
787 npfctl_parse_file(argc > 2 ? argv[2] : NPF_CONF_PATH);
788 ret = npfctl_config_show(0);
789 fun = "npfctl_config_show";
790 break;
791 case NPFCTL_DEBUG:
792 npfctl_config_init(true);
793 npfctl_parse_file(argc > 2 ? argv[2] : NPF_CONF_PATH);
794 npfctl_config_debug(argc > 3 ? argv[3] : "/tmp/npf.nvlist");
795 break;
796 }
797 if (ret) {
798 err(EXIT_FAILURE, "%s", fun);
799 }
800 if (fd) {
801 close(fd);
802 }
803 }
804
805 int
806 main(int argc, char **argv)
807 {
808 char *cmd;
809
810 if (argc < 2) {
811 usage();
812 }
813 npfctl_show_init();
814 cmd = argv[1];
815
816 /* Find and call the subroutine. */
817 for (int n = 0; operations[n].cmd != NULL; n++) {
818 const char *opcmd = operations[n].cmd;
819 if (strncmp(cmd, opcmd, strlen(opcmd)) != 0)
820 continue;
821 npfctl(operations[n].action, argc, argv);
822 return EXIT_SUCCESS;
823 }
824 usage();
825 }
826