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