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