1 /* $NetBSD: npfctl.c,v 1.53 2017/01/11 02:10:44 christos 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.53 2017/01/11 02:10:44 christos 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 %s?", NPF_VERSION, ver, 699 NPF_VERSION > ver ? "userland" : "kernel"); 700 } 701 return fd; 702 } 703 704 static void 705 npfctl(int action, int argc, char **argv) 706 { 707 int fd, boolval, ret = 0; 708 const char *fun = ""; 709 nl_config_t *ncf; 710 711 switch (action) { 712 case NPFCTL_VALIDATE: 713 case NPFCTL_DEBUG: 714 fd = 0; 715 break; 716 default: 717 fd = npfctl_open_dev(NPF_DEV_PATH); 718 } 719 720 switch (action) { 721 case NPFCTL_START: 722 boolval = true; 723 ret = ioctl(fd, IOC_NPF_SWITCH, &boolval); 724 fun = "ioctl(IOC_NPF_SWITCH)"; 725 break; 726 case NPFCTL_STOP: 727 boolval = false; 728 ret = ioctl(fd, IOC_NPF_SWITCH, &boolval); 729 fun = "ioctl(IOC_NPF_SWITCH)"; 730 break; 731 case NPFCTL_RELOAD: 732 npfctl_config_init(false); 733 npfctl_parse_file(argc < 3 ? NPF_CONF_PATH : argv[2]); 734 npfctl_preload_bpfjit(); 735 errno = ret = npfctl_config_send(fd, NULL); 736 fun = "npfctl_config_send"; 737 break; 738 case NPFCTL_SHOWCONF: 739 ret = npfctl_config_show(fd); 740 fun = "npfctl_config_show"; 741 break; 742 case NPFCTL_FLUSH: 743 ret = npf_config_flush(fd); 744 fun = "npf_config_flush"; 745 break; 746 case NPFCTL_TABLE: 747 if ((argc -= 2) < 2) { 748 usage(); 749 } 750 argv += 2; 751 npfctl_table(fd, argc, argv); 752 break; 753 case NPFCTL_RULE: 754 if ((argc -= 2) < 2) { 755 usage(); 756 } 757 argv += 2; 758 npfctl_rule(fd, argc, argv); 759 break; 760 case NPFCTL_LOAD: 761 npfctl_preload_bpfjit(); 762 ret = npfctl_load(fd); 763 fun = "npfctl_config_load"; 764 break; 765 case NPFCTL_SAVE: 766 ncf = npf_config_retrieve(fd); 767 if (ncf) { 768 npfctl_config_save(ncf, NPF_DB_PATH); 769 npf_config_destroy(ncf); 770 } else { 771 ret = errno; 772 } 773 fun = "npfctl_config_save"; 774 break; 775 case NPFCTL_STATS: 776 ret = npfctl_print_stats(fd); 777 fun = "npfctl_print_stats"; 778 break; 779 case NPFCTL_CONN_LIST: 780 ret = npfctl_conn_list(fd, argc, argv); 781 fun = "npfctl_conn_list"; 782 break; 783 case NPFCTL_VALIDATE: 784 npfctl_config_init(false); 785 npfctl_parse_file(argc > 2 ? argv[2] : NPF_CONF_PATH); 786 ret = npfctl_config_show(0); 787 fun = "npfctl_config_show"; 788 break; 789 case NPFCTL_DEBUG: 790 npfctl_config_init(true); 791 npfctl_parse_file(argc > 2 ? argv[2] : NPF_CONF_PATH); 792 npfctl_config_send(0, argc > 3 ? argv[3] : "/tmp/npf.plist"); 793 break; 794 } 795 if (ret) { 796 err(EXIT_FAILURE, "%s", fun); 797 } 798 if (fd) { 799 close(fd); 800 } 801 } 802 803 int 804 main(int argc, char **argv) 805 { 806 char *cmd; 807 808 if (argc < 2) { 809 usage(); 810 } 811 npfctl_show_init(); 812 cmd = argv[1]; 813 814 /* Find and call the subroutine. */ 815 for (int n = 0; operations[n].cmd != NULL; n++) { 816 const char *opcmd = operations[n].cmd; 817 if (strncmp(cmd, opcmd, strlen(opcmd)) != 0) 818 continue; 819 npfctl(operations[n].action, argc, argv); 820 return EXIT_SUCCESS; 821 } 822 usage(); 823 } 824