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