npfctl.c revision 1.59 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.59 2019/07/23 00:52:02 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 (ne->error_msg) {
234 warnx("%s", ne->error_msg);
235 }
236 if (srcfile) {
237 warnx("source %s line %d", srcfile, ne->source_line);
238 }
239 if (ne->id) {
240 warnx("object: %" PRIi64, ne->id);
241 }
242 }
243
244 char *
245 npfctl_print_addrmask(int alen, const char *fmt, const npf_addr_t *addr,
246 npf_netmask_t mask)
247 {
248 const unsigned buflen = 256;
249 char *buf = ecalloc(1, buflen);
250 struct sockaddr_storage ss;
251
252 memset(&ss, 0, sizeof(ss));
253
254 switch (alen) {
255 case 4: {
256 struct sockaddr_in *sin = (void *)&ss;
257 sin->sin_family = AF_INET;
258 memcpy(&sin->sin_addr, addr, sizeof(sin->sin_addr));
259 break;
260 }
261 case 16: {
262 struct sockaddr_in6 *sin6 = (void *)&ss;
263 sin6->sin6_family = AF_INET6;
264 memcpy(&sin6->sin6_addr, addr, sizeof(sin6->sin6_addr));
265 break;
266 }
267 default:
268 assert(false);
269 }
270 sockaddr_snprintf(buf, buflen, fmt, (const void *)&ss);
271 if (mask && mask != NPF_NO_NETMASK) {
272 const unsigned len = strlen(buf);
273 snprintf(&buf[len], buflen - len, "/%u", mask);
274 }
275 return buf;
276 }
277
278 __dead static void
279 npfctl_table(int fd, int argc, char **argv)
280 {
281 static const struct tblops_s {
282 const char * cmd;
283 int action;
284 } tblops[] = {
285 { "add", NPF_CMD_TABLE_ADD },
286 { "rem", NPF_CMD_TABLE_REMOVE },
287 { "del", NPF_CMD_TABLE_REMOVE },
288 { "test", NPF_CMD_TABLE_LOOKUP },
289 { "list", NPF_CMD_TABLE_LIST },
290 { "flush", NPF_CMD_TABLE_FLUSH },
291 { NULL, 0 }
292 };
293 npf_ioctl_table_t nct;
294 fam_addr_mask_t fam;
295 size_t buflen = 512;
296 char *cmd, *arg;
297 int n, alen;
298
299 /* Default action is list. */
300 memset(&nct, 0, sizeof(npf_ioctl_table_t));
301 nct.nct_name = argv[0];
302 cmd = argv[1];
303
304 for (n = 0; tblops[n].cmd != NULL; n++) {
305 if (strcmp(cmd, tblops[n].cmd) != 0) {
306 continue;
307 }
308 nct.nct_cmd = tblops[n].action;
309 break;
310 }
311 if (tblops[n].cmd == NULL) {
312 errx(EXIT_FAILURE, "invalid command '%s'", cmd);
313 }
314
315 switch (nct.nct_cmd) {
316 case NPF_CMD_TABLE_LIST:
317 case NPF_CMD_TABLE_FLUSH:
318 arg = NULL;
319 break;
320 default:
321 if (argc < 3) {
322 usage();
323 }
324 arg = argv[2];
325 }
326
327 again:
328 switch (nct.nct_cmd) {
329 case NPF_CMD_TABLE_LIST:
330 nct.nct_data.buf.buf = ecalloc(1, buflen);
331 nct.nct_data.buf.len = buflen;
332 break;
333 case NPF_CMD_TABLE_FLUSH:
334 break;
335 default:
336 if (!npfctl_parse_cidr(arg, &fam, &alen)) {
337 errx(EXIT_FAILURE, "invalid CIDR '%s'", arg);
338 }
339 nct.nct_data.ent.alen = alen;
340 memcpy(&nct.nct_data.ent.addr, &fam.fam_addr, alen);
341 nct.nct_data.ent.mask = fam.fam_mask;
342 }
343
344 if (ioctl(fd, IOC_NPF_TABLE, &nct) != -1) {
345 errno = 0;
346 }
347 switch (errno) {
348 case 0:
349 break;
350 case EEXIST:
351 errx(EXIT_FAILURE, "entry already exists or is conflicting");
352 case ENOENT:
353 errx(EXIT_FAILURE, "not found");
354 case EINVAL:
355 errx(EXIT_FAILURE, "invalid address, mask or table ID");
356 case ENOMEM:
357 if (nct.nct_cmd == NPF_CMD_TABLE_LIST) {
358 /* XXX */
359 free(nct.nct_data.buf.buf);
360 buflen <<= 1;
361 goto again;
362 }
363 /* FALLTHROUGH */
364 default:
365 err(EXIT_FAILURE, "ioctl(IOC_NPF_TABLE)");
366 }
367
368 if (nct.nct_cmd == NPF_CMD_TABLE_LIST) {
369 npf_ioctl_ent_t *ent = nct.nct_data.buf.buf;
370 char *buf;
371
372 while (nct.nct_data.buf.len--) {
373 if (!ent->alen)
374 break;
375 buf = npfctl_print_addrmask(ent->alen, "%a",
376 &ent->addr, ent->mask);
377 puts(buf);
378 ent++;
379 }
380 free(nct.nct_data.buf.buf);
381 } else {
382 printf("%s: %s\n", getprogname(),
383 nct.nct_cmd == NPF_CMD_TABLE_LOOKUP ?
384 "match" : "success");
385 }
386 exit(EXIT_SUCCESS);
387 }
388
389 static nl_rule_t *
390 npfctl_parse_rule(int argc, char **argv)
391 {
392 char rule_string[1024];
393 nl_rule_t *rl;
394
395 /* Get the rule string and parse it. */
396 if (!join(rule_string, sizeof(rule_string), argc, argv, " ")) {
397 errx(EXIT_FAILURE, "command too long");
398 }
399 npfctl_parse_string(rule_string);
400 if ((rl = npfctl_rule_ref()) == NULL) {
401 errx(EXIT_FAILURE, "could not parse the rule");
402 }
403 return rl;
404 }
405
406 #ifdef __NetBSD__
407 static unsigned char *
408 SHA1(const unsigned char *d, size_t l, unsigned char *md)
409 {
410 SHA1_CTX c;
411
412 SHA1Init(&c);
413 SHA1Update(&c, d, l);
414 SHA1Final(md, &c);
415 return md;
416 }
417 #endif
418
419 static void
420 npfctl_generate_key(nl_rule_t *rl, void *key)
421 {
422 void *meta;
423 size_t len;
424
425 if ((meta = npf_rule_export(rl, &len)) == NULL) {
426 errx(EXIT_FAILURE, "error generating rule key");
427 }
428 __CTASSERT(NPF_RULE_MAXKEYLEN >= SHA_DIGEST_LENGTH);
429 memset(key, 0, NPF_RULE_MAXKEYLEN);
430 SHA1(meta, len, key);
431 free(meta);
432 }
433
434 __dead static void
435 npfctl_rule(int fd, int argc, char **argv)
436 {
437 static const struct ruleops_s {
438 const char * cmd;
439 int action;
440 bool extra_arg;
441 } ruleops[] = {
442 { "add", NPF_CMD_RULE_ADD, true },
443 { "rem", NPF_CMD_RULE_REMKEY, true },
444 { "del", NPF_CMD_RULE_REMKEY, true },
445 { "rem-id", NPF_CMD_RULE_REMOVE, true },
446 { "list", NPF_CMD_RULE_LIST, false },
447 { "flush", NPF_CMD_RULE_FLUSH, false },
448 { NULL, 0, 0 }
449 };
450 uint8_t key[NPF_RULE_MAXKEYLEN];
451 const char *ruleset_name = argv[0];
452 const char *cmd = argv[1];
453 int error, action = 0;
454 uint64_t rule_id;
455 bool extra_arg;
456 nl_rule_t *rl;
457
458 for (int n = 0; ruleops[n].cmd != NULL; n++) {
459 if (strcmp(cmd, ruleops[n].cmd) == 0) {
460 action = ruleops[n].action;
461 extra_arg = ruleops[n].extra_arg;
462 break;
463 }
464 }
465 argc -= 2;
466 argv += 2;
467
468 if (!action || (extra_arg && argc == 0)) {
469 usage();
470 }
471
472 switch (action) {
473 case NPF_CMD_RULE_ADD:
474 rl = npfctl_parse_rule(argc, argv);
475 npfctl_generate_key(rl, key);
476 npf_rule_setkey(rl, key, sizeof(key));
477 error = npf_ruleset_add(fd, ruleset_name, rl, &rule_id);
478 break;
479 case NPF_CMD_RULE_REMKEY:
480 rl = npfctl_parse_rule(argc, argv);
481 npfctl_generate_key(rl, key);
482 error = npf_ruleset_remkey(fd, ruleset_name, key, sizeof(key));
483 break;
484 case NPF_CMD_RULE_REMOVE:
485 rule_id = strtoull(argv[0], NULL, 16);
486 error = npf_ruleset_remove(fd, ruleset_name, rule_id);
487 break;
488 case NPF_CMD_RULE_LIST:
489 error = npfctl_ruleset_show(fd, ruleset_name);
490 break;
491 case NPF_CMD_RULE_FLUSH:
492 error = npf_ruleset_flush(fd, ruleset_name);
493 break;
494 default:
495 abort();
496 }
497
498 switch (error) {
499 case 0:
500 /* Success. */
501 break;
502 case ESRCH:
503 errx(EXIT_FAILURE, "ruleset \"%s\" not found", ruleset_name);
504 case ENOENT:
505 errx(EXIT_FAILURE, "rule was not found");
506 default:
507 errx(EXIT_FAILURE, "rule operation: %s", strerror(error));
508 }
509 if (action == NPF_CMD_RULE_ADD) {
510 printf("OK %" PRIx64 "\n", rule_id);
511 }
512 exit(EXIT_SUCCESS);
513 }
514
515 static bool bpfjit = true;
516
517 void
518 npfctl_bpfjit(bool onoff)
519 {
520 bpfjit = onoff;
521 }
522
523 static void
524 npfctl_preload_bpfjit(void)
525 {
526 #ifdef __NetBSD__
527 modctl_load_t args = {
528 .ml_filename = "bpfjit",
529 .ml_flags = MODCTL_NO_PROP,
530 .ml_props = NULL,
531 .ml_propslen = 0
532 };
533
534 if (!bpfjit)
535 return;
536
537 if (modctl(MODCTL_LOAD, &args) != 0 && errno != EEXIST) {
538 static const char *p = "; performance will be degraded";
539 if (errno == ENOENT)
540 warnx("the bpfjit module seems to be missing%s", p);
541 else
542 warn("error loading the bpfjit module%s", p);
543 warnx("To disable this warning `set bpf.jit off' in "
544 "/etc/npf.conf");
545 }
546 #endif
547 }
548
549 static int
550 npfctl_load(int fd)
551 {
552 nl_config_t *ncf;
553 npf_error_t errinfo;
554 struct stat sb;
555 size_t blen;
556 void *blob;
557 int error;
558
559 /*
560 * The file may change while reading - we are not handling this,
561 * leaving this responsibility for the caller.
562 */
563 if (stat(NPF_DB_PATH, &sb) == -1) {
564 err(EXIT_FAILURE, "stat");
565 }
566 if ((blen = sb.st_size) == 0) {
567 err(EXIT_FAILURE, "saved configuration file is empty");
568 }
569 if ((blob = mmap(NULL, blen, PROT_READ,
570 MAP_FILE | MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
571 err(EXIT_FAILURE, "mmap");
572 }
573 ncf = npf_config_import(blob, blen);
574 munmap(blob, blen);
575 if (ncf == NULL) {
576 return errno;
577 }
578
579 /*
580 * Configuration imported - submit it now.
581 **/
582 errno = error = npf_config_submit(ncf, fd, &errinfo);
583 if (error) {
584 npfctl_print_error(&errinfo);
585 }
586 npf_config_destroy(ncf);
587 return error;
588 }
589
590 struct npf_conn_filter {
591 uint16_t alen;
592 const char * ifname;
593 bool nat;
594 bool wide;
595 bool name;
596 int width;
597 FILE * fp;
598 };
599
600 static int
601 npfctl_conn_print(unsigned alen, const npf_addr_t *a, const in_port_t *p,
602 const char *ifname, void *v)
603 {
604 struct npf_conn_filter *fil = v;
605 FILE *fp = fil->fp;
606 char *src, *dst;
607
608 if (fil->ifname && strcmp(ifname, fil->ifname) != 0)
609 return 0;
610 if (fil->alen && alen != fil->alen)
611 return 0;
612 if (fil->nat && !p[2])
613 return 0;
614
615 int w = fil->width;
616 const char *fmt = fil->name ? "%A" :
617 (alen == sizeof(struct in_addr) ? "%a" : "[%a]");
618 src = npfctl_print_addrmask(alen, fmt, &a[0], NPF_NO_NETMASK);
619 dst = npfctl_print_addrmask(alen, fmt, &a[1], NPF_NO_NETMASK);
620
621 if (fil->wide) {
622 fprintf(fp, "%s:%d %s:%d", src, p[0], dst, p[1]);
623 } else {
624 fprintf(fp, "%*.*s:%-5d %*.*s:%-5d", w, w, src, p[0],
625 w, w, dst, p[1]);
626 }
627 free(src);
628 free(dst);
629 if (!p[2]) {
630 fputc('\n', fp);
631 return 1;
632 }
633 fprintf(fp, " via %s:%d\n", ifname, p[2]);
634 return 1;
635 }
636
637 static int
638 npfctl_conn_list(int fd, int argc, char **argv)
639 {
640 struct npf_conn_filter f;
641 int c, w, header = true;
642
643 memset(&f, 0, sizeof(f));
644 argc--;
645 argv++;
646
647 while ((c = getopt(argc, argv, "46hi:nNw")) != -1) {
648 switch (c) {
649 case '4':
650 f.alen = sizeof(struct in_addr);
651 break;
652 case '6':
653 f.alen = sizeof(struct in6_addr);
654 break;
655 case 'h':
656 header = false;
657 break;
658 case 'i':
659 f.ifname = optarg;
660 break;
661 case 'n':
662 f.nat = true;
663 break;
664 case 'N':
665 f.name = true;
666 break;
667 case 'w':
668 f.wide = true;
669 break;
670 default:
671 fprintf(stderr,
672 "Usage: %s list [-46hnNw] [-i <ifname>]\n",
673 getprogname());
674 exit(EXIT_FAILURE);
675 }
676 }
677 f.width = f.alen == sizeof(struct in_addr) ? 25 : 41;
678 w = f.width + 6;
679 f.fp = stdout;
680
681 if (header) {
682 fprintf(f.fp, "%*.*s %*.*s\n",
683 w, w, "From address:port ", w, w, "To address:port ");
684 }
685 npf_conn_list(fd, npfctl_conn_print, &f);
686 return 0;
687 }
688
689 static int
690 npfctl_open_dev(const char *path)
691 {
692 int fd, kernver;
693
694 fd = open(path, O_RDONLY);
695 if (fd == -1) {
696 err(EXIT_FAILURE, "cannot open '%s'", path);
697 }
698 if (ioctl(fd, IOC_NPF_VERSION, &kernver) == -1) {
699 err(EXIT_FAILURE, "ioctl(IOC_NPF_VERSION)");
700 }
701 if (kernver != NPF_VERSION) {
702 errx(EXIT_FAILURE,
703 "incompatible NPF interface version (%d, kernel %d)\n"
704 "Hint: update %s?", NPF_VERSION, kernver,
705 kernver > NPF_VERSION ? "userland" : "kernel");
706 }
707 return fd;
708 }
709
710 static void
711 npfctl(int action, int argc, char **argv)
712 {
713 int fd, boolval, ret = 0;
714 const char *fun = "";
715 nl_config_t *ncf;
716
717 switch (action) {
718 case NPFCTL_VALIDATE:
719 case NPFCTL_DEBUG:
720 fd = 0;
721 break;
722 default:
723 fd = npfctl_open_dev(NPF_DEV_PATH);
724 }
725
726 switch (action) {
727 case NPFCTL_START:
728 boolval = true;
729 ret = ioctl(fd, IOC_NPF_SWITCH, &boolval);
730 fun = "ioctl(IOC_NPF_SWITCH)";
731 break;
732 case NPFCTL_STOP:
733 boolval = false;
734 ret = ioctl(fd, IOC_NPF_SWITCH, &boolval);
735 fun = "ioctl(IOC_NPF_SWITCH)";
736 break;
737 case NPFCTL_RELOAD:
738 npfctl_config_init(false);
739 npfctl_parse_file(argc < 3 ? NPF_CONF_PATH : argv[2]);
740 npfctl_preload_bpfjit();
741 errno = ret = npfctl_config_send(fd);
742 fun = "npfctl_config_send";
743 break;
744 case NPFCTL_SHOWCONF:
745 ret = npfctl_config_show(fd);
746 fun = "npfctl_config_show";
747 break;
748 case NPFCTL_FLUSH:
749 ret = npf_config_flush(fd);
750 fun = "npf_config_flush";
751 break;
752 case NPFCTL_TABLE:
753 if ((argc -= 2) < 2) {
754 usage();
755 }
756 argv += 2;
757 npfctl_table(fd, argc, argv);
758 break;
759 case NPFCTL_RULE:
760 if ((argc -= 2) < 2) {
761 usage();
762 }
763 argv += 2;
764 npfctl_rule(fd, argc, argv);
765 break;
766 case NPFCTL_LOAD:
767 npfctl_preload_bpfjit();
768 ret = npfctl_load(fd);
769 fun = "npfctl_config_load";
770 break;
771 case NPFCTL_SAVE:
772 ncf = npf_config_retrieve(fd);
773 if (ncf) {
774 npfctl_config_save(ncf, NPF_DB_PATH);
775 npf_config_destroy(ncf);
776 } else {
777 ret = errno;
778 }
779 fun = "npfctl_config_save";
780 break;
781 case NPFCTL_STATS:
782 ret = npfctl_print_stats(fd);
783 fun = "npfctl_print_stats";
784 break;
785 case NPFCTL_CONN_LIST:
786 ret = npfctl_conn_list(fd, argc, argv);
787 fun = "npfctl_conn_list";
788 break;
789 case NPFCTL_VALIDATE:
790 npfctl_config_init(false);
791 npfctl_parse_file(argc > 2 ? argv[2] : NPF_CONF_PATH);
792 ret = npfctl_config_show(0);
793 fun = "npfctl_config_show";
794 break;
795 case NPFCTL_DEBUG:
796 npfctl_config_init(true);
797 npfctl_parse_file(argc > 2 ? argv[2] : NPF_CONF_PATH);
798 npfctl_config_debug(argc > 3 ? argv[3] : "/tmp/npf.nvlist");
799 break;
800 }
801 if (ret) {
802 err(EXIT_FAILURE, "%s", fun);
803 }
804 if (fd) {
805 close(fd);
806 }
807 }
808
809 int
810 main(int argc, char **argv)
811 {
812 char *cmd;
813
814 if (argc < 2) {
815 usage();
816 }
817 npfctl_show_init();
818 cmd = argv[1];
819
820 /* Find and call the subroutine. */
821 for (int n = 0; operations[n].cmd != NULL; n++) {
822 const char *opcmd = operations[n].cmd;
823 if (strncmp(cmd, opcmd, strlen(opcmd)) != 0)
824 continue;
825 npfctl(operations[n].action, argc, argv);
826 return EXIT_SUCCESS;
827 }
828 usage();
829 }
830