npfctl.c revision 1.57 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.57 2019/01/19 21:19:32 rmind 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 case 'i':
655 f.ifname = optarg;
656 break;
657 case 'n':
658 f.nat = true;
659 break;
660 case 'N':
661 f.name = true;
662 break;
663 case 'w':
664 f.wide = true;
665 break;
666 default:
667 fprintf(stderr,
668 "Usage: %s list [-46hnNw] [-i <ifname>]\n",
669 getprogname());
670 exit(EXIT_FAILURE);
671 }
672 }
673 f.width = f.alen == sizeof(struct in_addr) ? 25 : 41;
674 int w = f.width + 6;
675 f.fp = stdout;
676 if (header)
677 fprintf(f.fp, "%*.*s %*.*s\n",
678 w, w, "From address:port ", w, w, "To address:port ");
679
680 npf_conn_list(fd, npfctl_conn_print, &f);
681 return 0;
682 }
683
684 static int
685 npfctl_open_dev(const char *path)
686 {
687 int fd, kernver;
688
689 fd = open(path, O_RDONLY);
690 if (fd == -1) {
691 err(EXIT_FAILURE, "cannot open '%s'", path);
692 }
693 if (ioctl(fd, IOC_NPF_VERSION, &kernver) == -1) {
694 err(EXIT_FAILURE, "ioctl(IOC_NPF_VERSION)");
695 }
696 if (kernver != NPF_VERSION) {
697 errx(EXIT_FAILURE,
698 "incompatible NPF interface version (%d, kernel %d)\n"
699 "Hint: update %s?", NPF_VERSION, kernver,
700 kernver > NPF_VERSION ? "userland" : "kernel");
701 }
702 return fd;
703 }
704
705 static void
706 npfctl(int action, int argc, char **argv)
707 {
708 int fd, boolval, ret = 0;
709 const char *fun = "";
710 nl_config_t *ncf;
711
712 switch (action) {
713 case NPFCTL_VALIDATE:
714 case NPFCTL_DEBUG:
715 fd = 0;
716 break;
717 default:
718 fd = npfctl_open_dev(NPF_DEV_PATH);
719 }
720
721 switch (action) {
722 case NPFCTL_START:
723 boolval = true;
724 ret = ioctl(fd, IOC_NPF_SWITCH, &boolval);
725 fun = "ioctl(IOC_NPF_SWITCH)";
726 break;
727 case NPFCTL_STOP:
728 boolval = false;
729 ret = ioctl(fd, IOC_NPF_SWITCH, &boolval);
730 fun = "ioctl(IOC_NPF_SWITCH)";
731 break;
732 case NPFCTL_RELOAD:
733 npfctl_config_init(false);
734 npfctl_parse_file(argc < 3 ? NPF_CONF_PATH : argv[2]);
735 npfctl_preload_bpfjit();
736 errno = ret = npfctl_config_send(fd);
737 fun = "npfctl_config_send";
738 break;
739 case NPFCTL_SHOWCONF:
740 ret = npfctl_config_show(fd);
741 fun = "npfctl_config_show";
742 break;
743 case NPFCTL_FLUSH:
744 ret = npf_config_flush(fd);
745 fun = "npf_config_flush";
746 break;
747 case NPFCTL_TABLE:
748 if ((argc -= 2) < 2) {
749 usage();
750 }
751 argv += 2;
752 npfctl_table(fd, argc, argv);
753 break;
754 case NPFCTL_RULE:
755 if ((argc -= 2) < 2) {
756 usage();
757 }
758 argv += 2;
759 npfctl_rule(fd, argc, argv);
760 break;
761 case NPFCTL_LOAD:
762 npfctl_preload_bpfjit();
763 ret = npfctl_load(fd);
764 fun = "npfctl_config_load";
765 break;
766 case NPFCTL_SAVE:
767 ncf = npf_config_retrieve(fd);
768 if (ncf) {
769 npfctl_config_save(ncf, NPF_DB_PATH);
770 npf_config_destroy(ncf);
771 } else {
772 ret = errno;
773 }
774 fun = "npfctl_config_save";
775 break;
776 case NPFCTL_STATS:
777 ret = npfctl_print_stats(fd);
778 fun = "npfctl_print_stats";
779 break;
780 case NPFCTL_CONN_LIST:
781 ret = npfctl_conn_list(fd, argc, argv);
782 fun = "npfctl_conn_list";
783 break;
784 case NPFCTL_VALIDATE:
785 npfctl_config_init(false);
786 npfctl_parse_file(argc > 2 ? argv[2] : NPF_CONF_PATH);
787 ret = npfctl_config_show(0);
788 fun = "npfctl_config_show";
789 break;
790 case NPFCTL_DEBUG:
791 npfctl_config_init(true);
792 npfctl_parse_file(argc > 2 ? argv[2] : NPF_CONF_PATH);
793 npfctl_config_debug(argc > 3 ? argv[3] : "/tmp/npf.nvlist");
794 break;
795 }
796 if (ret) {
797 err(EXIT_FAILURE, "%s", fun);
798 }
799 if (fd) {
800 close(fd);
801 }
802 }
803
804 int
805 main(int argc, char **argv)
806 {
807 char *cmd;
808
809 if (argc < 2) {
810 usage();
811 }
812 npfctl_show_init();
813 cmd = argv[1];
814
815 /* Find and call the subroutine. */
816 for (int n = 0; operations[n].cmd != NULL; n++) {
817 const char *opcmd = operations[n].cmd;
818 if (strncmp(cmd, opcmd, strlen(opcmd)) != 0)
819 continue;
820 npfctl(operations[n].action, argc, argv);
821 return EXIT_SUCCESS;
822 }
823 usage();
824 }
825