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