1167140Sbms/* $NetBSD: npfctl.c,v 1.10.2.15 2013/02/18 18:26:14 riz Exp $ */ 2167140Sbms 3167140Sbms/*- 4167140Sbms * Copyright (c) 2009-2013 The NetBSD Foundation, Inc. 5167140Sbms * All rights reserved. 6167140Sbms * 7167140Sbms * This material is based upon work partially supported by The 8167140Sbms * NetBSD Foundation under a contract with Mindaugas Rasiukevicius. 9167140Sbms * 10167140Sbms * Redistribution and use in source and binary forms, with or without 11167140Sbms * modification, are permitted provided that the following conditions 12167140Sbms * are met: 13167140Sbms * 1. Redistributions of source code must retain the above copyright 14167140Sbms * notice, this list of conditions and the following disclaimer. 15167140Sbms * 2. Redistributions in binary form must reproduce the above copyright 16167140Sbms * notice, this list of conditions and the following disclaimer in the 17167140Sbms * documentation and/or other materials provided with the distribution. 18167140Sbms * 19167140Sbms * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20167140Sbms * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21167140Sbms * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22167140Sbms * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23167140Sbms * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24167140Sbms * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25167140Sbms * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26167140Sbms * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27167140Sbms * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28167140Sbms * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29167140Sbms * POSSIBILITY OF SUCH DAMAGE. 30167140Sbms */ 31167140Sbms 32167140Sbms#include <sys/cdefs.h> 33167140Sbms__RCSID("$NetBSD: npfctl.c,v 1.10.2.15 2013/02/18 18:26:14 riz Exp $"); 34167140Sbms 35167140Sbms#include <sys/ioctl.h> 36167140Sbms#include <sys/stat.h> 37167140Sbms#include <sys/types.h> 38167140Sbms 39167340Sbms#include <stdio.h> 40167340Sbms#include <stdlib.h> 41167140Sbms#include <string.h> 42167140Sbms#include <err.h> 43167140Sbms#include <fcntl.h> 44167140Sbms#include <unistd.h> 45167140Sbms#include <errno.h> 46167140Sbms 47167140Sbms#include <openssl/sha.h> 48167140Sbms 49167140Sbms#include "npfctl.h" 50167140Sbms 51167140Sbmsextern void npf_yyparse_string(const char *); 52167140Sbms 53167140Sbmsenum { 54167140Sbms NPFCTL_START, 55167140Sbms NPFCTL_STOP, 56167140Sbms NPFCTL_RELOAD, 57167140Sbms NPFCTL_SHOWCONF, 58167140Sbms NPFCTL_FLUSH, 59167340Sbms NPFCTL_VALIDATE, 60167340Sbms NPFCTL_TABLE, 61167340Sbms NPFCTL_RULE, 62167340Sbms NPFCTL_STATS, 63167512Sbms NPFCTL_SESSIONS_SAVE, 64167512Sbms NPFCTL_SESSIONS_LOAD, 65167512Sbms}; 66167512Sbms 67167140Sbmsstatic const struct operations_s { 68167140Sbms const char * cmd; 69167149Sbms int action; 70167140Sbms} operations[] = { 71167340Sbms /* Start, stop, reload */ 72167340Sbms { "start", NPFCTL_START }, 73167340Sbms { "stop", NPFCTL_STOP }, 74167340Sbms { "reload", NPFCTL_RELOAD }, 75167140Sbms { "show", NPFCTL_SHOWCONF, }, 76167140Sbms { "flush", NPFCTL_FLUSH }, 77167140Sbms { "valid", NPFCTL_VALIDATE }, 78167140Sbms /* Table */ 79167140Sbms { "table", NPFCTL_TABLE }, 80167140Sbms /* Rule */ 81167341Sbms { "rule", NPFCTL_RULE }, 82167341Sbms /* Stats */ 83167140Sbms { "stats", NPFCTL_STATS }, 84167341Sbms /* Sessions */ 85167512Sbms { "sess-save", NPFCTL_SESSIONS_SAVE }, 86167140Sbms { "sess-load", NPFCTL_SESSIONS_LOAD }, 87167140Sbms /* --- */ 88167341Sbms { NULL, 0 } 89167341Sbms}; 90167140Sbms 91167140Sbmsstatic bool 92167341Sbmsjoin(char *buf, size_t buflen, int count, char **args) 93167340Sbms{ 94167341Sbms char *s = buf, *p = NULL; 95167340Sbms 96167512Sbms for (int i = 0; i < count; i++) { 97167140Sbms size_t len; 98167140Sbms 99167140Sbms p = stpncpy(s, args[i], buflen); 100167149Sbms len = p - s + 1; 101167149Sbms if (len >= buflen) { 102167140Sbms return false; 103167140Sbms } 104167140Sbms buflen -= len; 105167140Sbms *p = ' '; 106167140Sbms s = p + 1; 107167140Sbms } 108167140Sbms *p = '\0'; 109167140Sbms return true; 110167140Sbms} 111167140Sbms 112167140Sbms__dead static void 113167140Sbmsusage(void) 114167341Sbms{ 115167340Sbms const char *progname = getprogname(); 116167140Sbms 117167140Sbms fprintf(stderr, 118167140Sbms "usage:\t%s [ start | stop | reload | flush | show | stats ]\n", 119167340Sbms progname); 120167341Sbms fprintf(stderr, 121167140Sbms "\t%s rule \"rule-name\" { add | rem } <rule-syntax>\n", 122167140Sbms progname); 123167140Sbms fprintf(stderr, 124167140Sbms "\t%s rule \"rule-name\" rem-id <rule-id>\n", 125167140Sbms progname); 126167140Sbms fprintf(stderr, 127167140Sbms "\t%s rule \"rule-name\" { list | flush }\n", 128167512Sbms progname); 129167140Sbms fprintf(stderr, 130167140Sbms "\t%s table <tid> { add | rem | test } <address/mask>\n", 131167140Sbms progname); 132167140Sbms fprintf(stderr, 133167140Sbms "\t%s table <tid> { list | flush }\n", 134167140Sbms progname); 135167140Sbms fprintf(stderr, 136167149Sbms "\t%s ( sess-save | sess-load )\n", 137167140Sbms progname); 138167140Sbms exit(EXIT_FAILURE); 139167140Sbms} 140167140Sbms 141167140Sbmsstatic int 142167140Sbmsnpfctl_print_stats(int fd) 143167512Sbms{ 144167140Sbms static const struct stats_s { 145167340Sbms /* Note: -1 indicates a new section. */ 146167140Sbms int index; 147167341Sbms const char * name; 148167140Sbms } stats[] = { 149167140Sbms { -1, "Packets passed" }, 150167149Sbms { NPF_STAT_PASS_DEFAULT, "default pass" }, 151167140Sbms { NPF_STAT_PASS_RULESET, "ruleset pass" }, 152167140Sbms { NPF_STAT_PASS_SESSION, "session pass" }, 153167140Sbms 154167140Sbms { -1, "Packets blocked" }, 155167140Sbms { NPF_STAT_BLOCK_DEFAULT, "default block" }, 156167512Sbms { NPF_STAT_BLOCK_RULESET, "ruleset block" }, 157167140Sbms 158167140Sbms { -1, "Session and NAT entries" }, 159167140Sbms { NPF_STAT_SESSION_CREATE, "session allocations" }, 160167140Sbms { NPF_STAT_SESSION_DESTROY, "session destructions" }, 161167341Sbms { NPF_STAT_NAT_CREATE, "NAT entry allocations" }, 162167341Sbms { NPF_STAT_NAT_DESTROY, "NAT entry destructions"}, 163167341Sbms 164167140Sbms { -1, "Network buffers" }, 165167140Sbms { NPF_STAT_NBUF_NONCONTIG, "non-contiguous cases" }, 166167140Sbms { NPF_STAT_NBUF_CONTIG_FAIL, "contig alloc failures" }, 167167140Sbms 168167140Sbms { -1, "Invalid packet state cases" }, 169167140Sbms { NPF_STAT_INVALID_STATE, "cases in total" }, 170167140Sbms { NPF_STAT_INVALID_STATE_TCP1, "TCP case I" }, 171167140Sbms { NPF_STAT_INVALID_STATE_TCP2, "TCP case II" }, 172167140Sbms { NPF_STAT_INVALID_STATE_TCP3, "TCP case III" }, 173167340Sbms 174167340Sbms { -1, "Packet race cases" }, 175167340Sbms { NPF_STAT_RACE_NAT, "NAT association race" }, 176167140Sbms { NPF_STAT_RACE_SESSION, "duplicate session race"}, 177167140Sbms 178167140Sbms { -1, "Fragmentation" }, 179167140Sbms { NPF_STAT_FRAGMENTS, "fragments" }, 180167140Sbms { NPF_STAT_REASSEMBLY, "reassembled" }, 181167140Sbms { NPF_STAT_REASSFAIL, "failed reassembly" }, 182167512Sbms 183167512Sbms { -1, "Other" }, 184167512Sbms { NPF_STAT_ERROR, "unexpected errors" }, 185167140Sbms }; 186167140Sbms uint64_t *st = ecalloc(1, NPF_STATS_SIZE); 187167140Sbms 188167140Sbms if (ioctl(fd, IOC_NPF_STATS, &st) != 0) { 189167140Sbms err(EXIT_FAILURE, "ioctl(IOC_NPF_STATS)"); 190167140Sbms } 191167149Sbms 192167149Sbms for (unsigned i = 0; i < __arraycount(stats); i++) { 193167149Sbms const char *sname = stats[i].name; 194167140Sbms int sidx = stats[i].index; 195167140Sbms 196167140Sbms if (sidx == -1) { 197167140Sbms printf("%s:\n", sname); 198167140Sbms } else { 199167140Sbms printf("\t%"PRIu64" %s\n", st[sidx], sname); 200167140Sbms } 201167140Sbms } 202167140Sbms 203167140Sbms free(st); 204167140Sbms return 0; 205167140Sbms} 206167340Sbms 207167340Sbmsvoid 208167340Sbmsnpfctl_print_error(const nl_error_t *ne) 209167512Sbms{ 210167512Sbms static const char *ncode_errors[] = { 211167512Sbms [-NPF_ERR_OPCODE] = "invalid instruction", 212167512Sbms [-NPF_ERR_JUMP] = "invalid jump", 213167512Sbms [-NPF_ERR_REG] = "invalid register", 214167512Sbms [-NPF_ERR_INVAL] = "invalid argument value", 215167512Sbms [-NPF_ERR_RANGE] = "processing out of range" 216167140Sbms }; 217167140Sbms const int nc_err = ne->ne_ncode_error; 218167140Sbms const char *srcfile = ne->ne_source_file; 219167140Sbms 220167140Sbms if (srcfile) { 221167140Sbms warnx("source %s line %d", srcfile, ne->ne_source_line); 222167140Sbms } 223167140Sbms if (nc_err) { 224167140Sbms warnx("n-code error (%d): %s at offset 0x%x", 225167140Sbms nc_err, ncode_errors[-nc_err], ne->ne_ncode_errat); 226167140Sbms } 227167140Sbms if (ne->ne_id) { 228167140Sbms warnx("object: %d", ne->ne_id); 229167140Sbms } 230167140Sbms} 231167140Sbms 232167140Sbmschar * 233167140Sbmsnpfctl_print_addrmask(int alen, npf_addr_t *addr, npf_netmask_t mask) 234167140Sbms{ 235167140Sbms struct sockaddr_storage ss; 236167140Sbms char *buf = ecalloc(1, 64); 237167140Sbms int len; 238167140Sbms 239167140Sbms switch (alen) { 240167140Sbms case 4: { 241167140Sbms struct sockaddr_in *sin = (void *)&ss; 242167140Sbms sin->sin_len = sizeof(*sin); 243167140Sbms sin->sin_family = AF_INET; 244167140Sbms sin->sin_port = 0; 245167149Sbms memcpy(&sin->sin_addr, addr, sizeof(sin->sin_addr)); 246167149Sbms break; 247167149Sbms } 248167149Sbms case 16: { 249167149Sbms struct sockaddr_in6 *sin6 = (void *)&ss; 250167149Sbms sin6->sin6_len = sizeof(*sin6); 251167149Sbms sin6->sin6_family = AF_INET6; 252167149Sbms sin6->sin6_port = 0; 253167149Sbms sin6->sin6_scope_id = 0; 254167140Sbms memcpy(&sin6->sin6_addr, addr, sizeof(sin6->sin6_addr)); 255167140Sbms break; 256167140Sbms } 257167140Sbms default: 258167140Sbms assert(false); 259167140Sbms } 260167140Sbms len = sockaddr_snprintf(buf, 64, "%a", (struct sockaddr *)&ss); 261167140Sbms if (mask) { 262167140Sbms snprintf(&buf[len], 64 - len, "/%u", mask); 263167140Sbms } 264167140Sbms return buf; 265167140Sbms} 266167140Sbms 267167341Sbms__dead static void 268167341Sbmsnpfctl_table(int fd, int argc, char **argv) 269167341Sbms{ 270167341Sbms static const struct tblops_s { 271167341Sbms const char * cmd; 272167341Sbms int action; 273167341Sbms } tblops[] = { 274167341Sbms { "add", NPF_CMD_TABLE_ADD }, 275167341Sbms { "rem", NPF_CMD_TABLE_REMOVE }, 276167140Sbms { "del", NPF_CMD_TABLE_REMOVE }, 277167140Sbms { "test", NPF_CMD_TABLE_LOOKUP }, 278167140Sbms { "list", NPF_CMD_TABLE_LIST }, 279167140Sbms { NULL, 0 } 280167140Sbms }; 281167140Sbms npf_ioctl_table_t nct; 282167140Sbms fam_addr_mask_t fam; 283167140Sbms size_t buflen = 512; 284167140Sbms char *cmd, *arg = NULL; /* XXX gcc */ 285167140Sbms int n, alen; 286167140Sbms 287167140Sbms /* Default action is list. */ 288167140Sbms memset(&nct, 0, sizeof(npf_ioctl_table_t)); 289167140Sbms nct.nct_tid = atoi(argv[0]); 290167140Sbms cmd = argv[1]; 291167140Sbms 292167140Sbms for (n = 0; tblops[n].cmd != NULL; n++) { 293167140Sbms if (strcmp(cmd, tblops[n].cmd) != 0) { 294167140Sbms continue; 295167140Sbms } 296167140Sbms nct.nct_cmd = tblops[n].action; 297167140Sbms break; 298167140Sbms } 299167140Sbms if (tblops[n].cmd == NULL) { 300167140Sbms errx(EXIT_FAILURE, "invalid command '%s'", cmd); 301167140Sbms } 302167140Sbms if (nct.nct_cmd != NPF_CMD_TABLE_LIST) { 303167140Sbms if (argc < 3) { 304167140Sbms usage(); 305167340Sbms } 306167340Sbms arg = argv[2]; 307167340Sbms } 308167340Sbmsagain: 309167340Sbms if (nct.nct_cmd == NPF_CMD_TABLE_LIST) { 310167340Sbms nct.nct_data.buf.buf = ecalloc(1, buflen); 311167140Sbms nct.nct_data.buf.len = buflen; 312167340Sbms } else { 313167140Sbms if (!npfctl_parse_cidr(arg, &fam, &alen)) { 314167140Sbms errx(EXIT_FAILURE, "invalid CIDR '%s'", arg); 315167140Sbms } 316167140Sbms nct.nct_data.ent.alen = alen; 317167140Sbms memcpy(&nct.nct_data.ent.addr, &fam.fam_addr, alen); 318167140Sbms nct.nct_data.ent.mask = fam.fam_mask; 319167140Sbms } 320167140Sbms 321167340Sbms if (ioctl(fd, IOC_NPF_TABLE, &nct) != -1) { 322167340Sbms errno = 0; 323167340Sbms } 324167340Sbms switch (errno) { 325167340Sbms case 0: 326167340Sbms break; 327167340Sbms case EEXIST: 328167340Sbms errx(EXIT_FAILURE, "entry already exists or is conflicting"); 329167340Sbms case ENOENT: 330167340Sbms errx(EXIT_FAILURE, "no matching entry was not found"); 331167340Sbms case EINVAL: 332167340Sbms errx(EXIT_FAILURE, "invalid address, mask or table ID"); 333167340Sbms case ENOMEM: 334167340Sbms if (nct.nct_cmd == NPF_CMD_TABLE_LIST) { 335167340Sbms /* XXX */ 336167340Sbms free(nct.nct_data.buf.buf); 337167340Sbms buflen <<= 1; 338167340Sbms goto again; 339167340Sbms } 340167340Sbms /* FALLTHROUGH */ 341167340Sbms default: 342167340Sbms err(EXIT_FAILURE, "ioctl(IOC_NPF_TABLE)"); 343167340Sbms } 344167340Sbms 345167340Sbms if (nct.nct_cmd == NPF_CMD_TABLE_LIST) { 346167340Sbms npf_ioctl_ent_t *ent = nct.nct_data.buf.buf; 347167340Sbms char *buf; 348167340Sbms 349167340Sbms while (nct.nct_data.buf.len--) { 350167340Sbms if (!ent->alen) 351167340Sbms break; 352167340Sbms buf = npfctl_print_addrmask(ent->alen, 353167340Sbms &ent->addr, ent->mask); 354167340Sbms puts(buf); 355167340Sbms ent++; 356167140Sbms } 357167140Sbms free(nct.nct_data.buf.buf); 358167140Sbms } else { 359167140Sbms printf("%s: %s\n", getprogname(), 360167140Sbms nct.nct_cmd == NPF_CMD_TABLE_LOOKUP ? 361167140Sbms "matching entry found" : "success"); 362167140Sbms } 363167140Sbms exit(EXIT_SUCCESS); 364167140Sbms} 365167140Sbms 366167140Sbmsstatic nl_rule_t * 367npfctl_parse_rule(int argc, char **argv) 368{ 369 char rule_string[1024]; 370 nl_rule_t *rl; 371 372 /* Get the rule string and parse it. */ 373 if (!join(rule_string, sizeof(rule_string), argc, argv)) { 374 errx(EXIT_FAILURE, "command too long"); 375 } 376 npfctl_parse_string(rule_string); 377 if ((rl = npfctl_rule_ref()) == NULL) { 378 errx(EXIT_FAILURE, "could not parse the rule"); 379 } 380 return rl; 381} 382 383static void 384npfctl_generate_key(nl_rule_t *rl, void *key) 385{ 386 void *meta; 387 size_t len; 388 389 if ((meta = npf_rule_export(rl, &len)) == NULL) { 390 errx(EXIT_FAILURE, "error generating rule key"); 391 } 392 __CTASSERT(NPF_RULE_MAXKEYLEN >= SHA_DIGEST_LENGTH); 393 memset(key, 0, NPF_RULE_MAXKEYLEN); 394 SHA1(meta, len, key); 395 free(meta); 396} 397 398__dead static void 399npfctl_rule(int fd, int argc, char **argv) 400{ 401 static const struct ruleops_s { 402 const char * cmd; 403 int action; 404 } ruleops[] = { 405 { "add", NPF_CMD_RULE_ADD }, 406 { "rem", NPF_CMD_RULE_REMKEY }, 407 { "del", NPF_CMD_RULE_REMKEY }, 408 { "rem-id", NPF_CMD_RULE_REMOVE }, 409 { "list", NPF_CMD_RULE_LIST }, 410 { "flush", NPF_CMD_RULE_FLUSH }, 411 { NULL, 0 } 412 }; 413 uint8_t key[NPF_RULE_MAXKEYLEN]; 414 const char *ruleset_name = argv[0]; 415 const char *cmd = argv[1]; 416 int error, action = 0; 417 uint64_t rule_id; 418 nl_rule_t *rl; 419 420 for (int n = 0; ruleops[n].cmd != NULL; n++) { 421 if (strcmp(cmd, ruleops[n].cmd) == 0) { 422 action = ruleops[n].action; 423 break; 424 } 425 } 426 427 bool narg = action == NPF_CMD_RULE_LIST || action == NPF_CMD_RULE_FLUSH; 428 if (!action || (argc < 3 && !narg)) { 429 usage(); 430 } 431 argc -= 2; 432 argv += 2; 433 434 switch (action) { 435 case NPF_CMD_RULE_ADD: 436 rl = npfctl_parse_rule(argc, argv); 437 npfctl_generate_key(rl, key); 438 npf_rule_setkey(rl, key, sizeof(key)); 439 error = npf_ruleset_add(fd, ruleset_name, rl, &rule_id); 440 break; 441 case NPF_CMD_RULE_REMKEY: 442 rl = npfctl_parse_rule(argc, argv); 443 npfctl_generate_key(rl, key); 444 error = npf_ruleset_remkey(fd, ruleset_name, key, sizeof(key)); 445 break; 446 case NPF_CMD_RULE_REMOVE: 447 rule_id = strtoull(argv[0], NULL, 16); 448 error = npf_ruleset_remove(fd, ruleset_name, rule_id); 449 break; 450 case NPF_CMD_RULE_LIST: 451 error = npfctl_ruleset_show(fd, ruleset_name); 452 break; 453 case NPF_CMD_RULE_FLUSH: 454 error = npf_ruleset_flush(fd, ruleset_name); 455 break; 456 default: 457 assert(false); 458 } 459 460 switch (error) { 461 case 0: 462 /* Success. */ 463 break; 464 case ESRCH: 465 errx(EXIT_FAILURE, "ruleset \"%s\" not found", ruleset_name); 466 case ENOENT: 467 errx(EXIT_FAILURE, "rule was not found"); 468 default: 469 errx(EXIT_FAILURE, "rule operation: %s", strerror(error)); 470 } 471 if (action == NPF_CMD_RULE_ADD) { 472 printf("OK %" PRIx64 "\n", rule_id); 473 } 474 exit(EXIT_SUCCESS); 475} 476 477static void 478npfctl(int action, int argc, char **argv) 479{ 480 int fd, ver, boolval, ret = 0; 481 482 fd = open(NPF_DEV_PATH, O_RDONLY); 483 if (fd == -1) { 484 err(EXIT_FAILURE, "cannot open '%s'", NPF_DEV_PATH); 485 } 486 if (ioctl(fd, IOC_NPF_VERSION, &ver) == -1) { 487 err(EXIT_FAILURE, "ioctl(IOC_NPF_VERSION)"); 488 } 489 if (ver != NPF_VERSION) { 490 errx(EXIT_FAILURE, 491 "incompatible NPF interface version (%d, kernel %d)\n" 492 "Hint: update userland?", NPF_VERSION, ver); 493 } 494 495 const char *fun = ""; 496 switch (action) { 497 case NPFCTL_START: 498 boolval = true; 499 ret = ioctl(fd, IOC_NPF_SWITCH, &boolval); 500 fun = "ioctl(IOC_NPF_SWITCH)"; 501 break; 502 case NPFCTL_STOP: 503 boolval = false; 504 ret = ioctl(fd, IOC_NPF_SWITCH, &boolval); 505 fun = "ioctl(IOC_NPF_SWITCH)"; 506 break; 507 case NPFCTL_RELOAD: 508 npfctl_config_init(false); 509 npfctl_parse_file(argc < 3 ? NPF_CONF_PATH : argv[2]); 510 errno = ret = npfctl_config_send(fd, NULL); 511 fun = "npfctl_config_send"; 512 break; 513 case NPFCTL_SHOWCONF: 514 ret = npfctl_config_show(fd); 515 fun = "npfctl_config_show"; 516 break; 517 case NPFCTL_FLUSH: 518 ret = npf_config_flush(fd); 519 fun = "npf_config_flush"; 520 break; 521 case NPFCTL_VALIDATE: 522 npfctl_config_init(false); 523 npfctl_parse_file(argc < 3 ? NPF_CONF_PATH : argv[2]); 524 ret = npfctl_config_show(0); 525 fun = "npfctl_config_show"; 526 break; 527 case NPFCTL_TABLE: 528 if ((argc -= 2) < 2) { 529 usage(); 530 } 531 argv += 2; 532 npfctl_table(fd, argc, argv); 533 break; 534 case NPFCTL_RULE: 535 if ((argc -= 2) < 2) { 536 usage(); 537 } 538 argv += 2; 539 npfctl_rule(fd, argc, argv); 540 break; 541 case NPFCTL_STATS: 542 ret = npfctl_print_stats(fd); 543 fun = "npfctl_print_stats"; 544 break; 545 case NPFCTL_SESSIONS_SAVE: 546 if (npf_sessions_recv(fd, NPF_SESSDB_PATH) != 0) { 547 errx(EXIT_FAILURE, "could not save sessions to '%s'", 548 NPF_SESSDB_PATH); 549 } 550 break; 551 case NPFCTL_SESSIONS_LOAD: 552 if (npf_sessions_send(fd, NPF_SESSDB_PATH) != 0) { 553 errx(EXIT_FAILURE, "no sessions loaded from '%s'", 554 NPF_SESSDB_PATH); 555 } 556 break; 557 } 558 if (ret) { 559 err(EXIT_FAILURE, "%s", fun); 560 } 561 close(fd); 562} 563 564int 565main(int argc, char **argv) 566{ 567 char *cmd; 568 569 if (argc < 2) { 570 usage(); 571 } 572 cmd = argv[1]; 573 574 if (strcmp(cmd, "debug") == 0) { 575 const char *cfg = argc > 2 ? argv[2] : "/etc/npf.conf"; 576 const char *out = argc > 3 ? argv[3] : "/tmp/npf.plist"; 577 578 npfctl_config_init(true); 579 npfctl_parse_file(cfg); 580 npfctl_config_send(0, out); 581 return EXIT_SUCCESS; 582 } 583 584 /* Find and call the subroutine. */ 585 for (int n = 0; operations[n].cmd != NULL; n++) { 586 const char *opcmd = operations[n].cmd; 587 if (strncmp(cmd, opcmd, strlen(opcmd)) != 0) 588 continue; 589 npfctl(operations[n].action, argc, argv); 590 return EXIT_SUCCESS; 591 } 592 usage(); 593} 594