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.64 2020/05/30 14:16:56 rmind Exp $"); 32 33 #include <sys/types.h> 34 #include <sys/stat.h> 35 #include <sys/socket.h> 36 #include <sys/mman.h> 37 #include <sys/un.h> 38 #ifdef __NetBSD__ 39 #include <sys/module.h> 40 #endif 41 42 #include <stdio.h> 43 #include <string.h> 44 #include <stdlib.h> 45 #include <unistd.h> 46 #include <fcntl.h> 47 #include <errno.h> 48 #include <err.h> 49 50 #include "npfctl.h" 51 52 enum { 53 NPFCTL_START, 54 NPFCTL_STOP, 55 NPFCTL_RELOAD, 56 NPFCTL_SHOWCONF, 57 NPFCTL_FLUSH, 58 NPFCTL_VALIDATE, 59 NPFCTL_TABLE, 60 NPFCTL_RULE, 61 NPFCTL_STATS, 62 NPFCTL_SAVE, 63 NPFCTL_LOAD, 64 NPFCTL_DEBUG, 65 NPFCTL_CONN_LIST, 66 }; 67 68 bool 69 join(char *buf, size_t buflen, int count, char **args, const char *sep) 70 { 71 const unsigned seplen = strlen(sep); 72 char *s = buf, *p = NULL; 73 74 for (int i = 0; i < count; i++) { 75 size_t len; 76 77 p = stpncpy(s, args[i], buflen); 78 len = p - s + seplen; 79 if (len >= buflen) { 80 return false; 81 } 82 buflen -= len; 83 strcpy(p, sep); 84 s = p + seplen; 85 } 86 *p = '\0'; 87 return true; 88 } 89 90 __dead void 91 usage(void) 92 { 93 const char *progname = getprogname(); 94 95 fprintf(stderr, 96 "Usage:\t%s start | stop | flush | show | stats\n", 97 progname); 98 fprintf(stderr, 99 "\t%s validate | reload [<rule-file>]\n", 100 progname); 101 fprintf(stderr, 102 "\t%s rule \"rule-name\" { add | rem } <rule-syntax>\n", 103 progname); 104 fprintf(stderr, 105 "\t%s rule \"rule-name\" rem-id <rule-id>\n", 106 progname); 107 fprintf(stderr, 108 "\t%s rule \"rule-name\" { list | flush }\n", 109 progname); 110 fprintf(stderr, 111 "\t%s table \"table-name\" { add | rem | test } <address/mask>\n", 112 progname); 113 fprintf(stderr, 114 "\t%s table \"table-name\" { list | flush }\n", 115 progname); 116 fprintf(stderr, 117 "\t%s table \"table-name\" replace [-n \"name\"]" 118 " [-t <type>] <table-file>\n", 119 progname); 120 fprintf(stderr, 121 "\t%s save | load\n", 122 progname); 123 fprintf(stderr, 124 "\t%s list [-46hNnw] [-i <ifname>]\n", 125 progname); 126 fprintf(stderr, 127 "\t%s debug { -a | -b <binary-config> | -c <config> } " 128 "[ -o <outfile> ]\n", 129 progname); 130 exit(EXIT_FAILURE); 131 } 132 133 static int 134 npfctl_print_stats(int fd) 135 { 136 static const struct stats_s { 137 /* Note: -1 indicates a new section. */ 138 int index; 139 const char * name; 140 } stats[] = { 141 { -1, "Packets passed" }, 142 { NPF_STAT_PASS_DEFAULT, "default pass" }, 143 { NPF_STAT_PASS_RULESET, "ruleset pass" }, 144 { NPF_STAT_PASS_CONN, "state pass" }, 145 146 { -1, "Packets blocked" }, 147 { NPF_STAT_BLOCK_DEFAULT, "default block" }, 148 { NPF_STAT_BLOCK_RULESET, "ruleset block" }, 149 150 { -1, "State and NAT entries" }, 151 { NPF_STAT_CONN_CREATE, "state allocations"}, 152 { NPF_STAT_CONN_DESTROY, "state destructions"}, 153 { NPF_STAT_NAT_CREATE, "NAT entry allocations" }, 154 { NPF_STAT_NAT_DESTROY, "NAT entry destructions"}, 155 156 { -1, "Network buffers" }, 157 { NPF_STAT_NBUF_NONCONTIG, "non-contiguous cases" }, 158 { NPF_STAT_NBUF_CONTIG_FAIL, "contig alloc failures" }, 159 160 { -1, "Invalid packet state cases" }, 161 { NPF_STAT_INVALID_STATE, "cases in total" }, 162 { NPF_STAT_INVALID_STATE_TCP1, "TCP case I" }, 163 { NPF_STAT_INVALID_STATE_TCP2, "TCP case II" }, 164 { NPF_STAT_INVALID_STATE_TCP3, "TCP case III" }, 165 166 { -1, "Packet race cases" }, 167 { NPF_STAT_RACE_NAT, "NAT association race" }, 168 { NPF_STAT_RACE_CONN, "duplicate state race" }, 169 170 { -1, "Fragmentation" }, 171 { NPF_STAT_FRAGMENTS, "fragments" }, 172 { NPF_STAT_REASSEMBLY, "reassembled" }, 173 { NPF_STAT_REASSFAIL, "failed reassembly" }, 174 175 { -1, "Other" }, 176 { NPF_STAT_ERROR, "unexpected errors" }, 177 }; 178 uint64_t *st = ecalloc(1, NPF_STATS_SIZE); 179 180 if (ioctl(fd, IOC_NPF_STATS, &st) != 0) { 181 err(EXIT_FAILURE, "ioctl(IOC_NPF_STATS)"); 182 } 183 184 for (unsigned i = 0; i < __arraycount(stats); i++) { 185 const char *sname = stats[i].name; 186 int sidx = stats[i].index; 187 188 if (sidx == -1) { 189 printf("%s:\n", sname); 190 } else { 191 printf("\t%"PRIu64" %s\n", st[sidx], sname); 192 } 193 } 194 195 free(st); 196 return 0; 197 } 198 199 void 200 npfctl_print_error(const npf_error_t *ne) 201 { 202 const char *srcfile = ne->source_file; 203 204 if (ne->error_msg) { 205 errx(EXIT_FAILURE, "%s", ne->error_msg); 206 } 207 if (srcfile) { 208 warnx("source %s line %d", srcfile, ne->source_line); 209 } 210 if (ne->id) { 211 warnx("object: %" PRIi64, ne->id); 212 } 213 } 214 215 char * 216 npfctl_print_addrmask(int alen, const char *fmt, const npf_addr_t *addr, 217 npf_netmask_t mask) 218 { 219 const unsigned buflen = 256; 220 char *buf = ecalloc(1, buflen); 221 struct sockaddr_storage ss; 222 223 memset(&ss, 0, sizeof(ss)); 224 225 switch (alen) { 226 case 4: { 227 struct sockaddr_in *sin = (void *)&ss; 228 sin->sin_family = AF_INET; 229 memcpy(&sin->sin_addr, addr, sizeof(sin->sin_addr)); 230 break; 231 } 232 case 16: { 233 struct sockaddr_in6 *sin6 = (void *)&ss; 234 sin6->sin6_family = AF_INET6; 235 memcpy(&sin6->sin6_addr, addr, sizeof(sin6->sin6_addr)); 236 break; 237 } 238 default: 239 abort(); 240 } 241 sockaddr_snprintf(buf, buflen, fmt, (const void *)&ss); 242 if (mask && mask != NPF_NO_NETMASK) { 243 const unsigned len = strlen(buf); 244 snprintf(&buf[len], buflen - len, "/%u", mask); 245 } 246 return buf; 247 } 248 249 bool 250 npfctl_addr_iszero(const npf_addr_t *addr) 251 { 252 static const npf_addr_t zero; /* must be static */ 253 return memcmp(addr, &zero, sizeof(npf_addr_t)) == 0; 254 } 255 256 static bool bpfjit = true; 257 258 void 259 npfctl_bpfjit(bool onoff) 260 { 261 bpfjit = onoff; 262 } 263 264 static void 265 npfctl_preload_bpfjit(void) 266 { 267 #ifdef __NetBSD__ 268 modctl_load_t args = { 269 .ml_filename = "bpfjit", 270 .ml_flags = MODCTL_NO_PROP, 271 .ml_props = NULL, 272 .ml_propslen = 0 273 }; 274 275 if (!bpfjit) 276 return; 277 278 if (modctl(MODCTL_LOAD, &args) != 0 && errno != EEXIST) { 279 static const char *p = "; performance will be degraded"; 280 if (errno == ENOENT) 281 warnx("the bpfjit module seems to be missing%s", p); 282 else 283 warn("error loading the bpfjit module%s", p); 284 warnx("To disable this warning `set bpf.jit off' in " 285 "/etc/npf.conf"); 286 } 287 #endif 288 } 289 290 static nl_config_t * 291 npfctl_import(const char *path) 292 { 293 nl_config_t *ncf; 294 struct stat sb; 295 size_t blen; 296 void *blob; 297 int fd; 298 299 /* 300 * The file may change while reading - we are not handling this, 301 * just leaving this responsibility for the caller. 302 */ 303 if ((fd = open(path, O_RDONLY)) == -1) { 304 err(EXIT_FAILURE, "could not open `%s'", path); 305 } 306 if (fstat(fd, &sb) == -1) { 307 err(EXIT_FAILURE, "stat"); 308 } 309 if ((blen = sb.st_size) == 0) { 310 err(EXIT_FAILURE, "the binary configuration file is empty"); 311 } 312 blob = mmap(NULL, blen, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); 313 if (blob == MAP_FAILED) { 314 err(EXIT_FAILURE, "mmap"); 315 } 316 ncf = npf_config_import(blob, blen); 317 munmap(blob, blen); 318 return ncf; 319 } 320 321 static int 322 npfctl_load(int fd) 323 { 324 nl_config_t *ncf; 325 npf_error_t errinfo; 326 327 /* 328 * Import the configuration, submit it and destroy. 329 */ 330 ncf = npfctl_import(NPF_DB_PATH); 331 if (ncf == NULL) { 332 err(EXIT_FAILURE, "npf_config_import"); 333 } 334 if ((errno = npf_config_submit(ncf, fd, &errinfo)) != 0) { 335 npfctl_print_error(&errinfo); 336 } 337 npf_config_destroy(ncf); 338 return errno; 339 } 340 341 static int 342 npfctl_open_dev(const char *path) 343 { 344 struct stat st; 345 int fd; 346 347 if (lstat(path, &st) == -1) { 348 err(EXIT_FAILURE, "fstat"); 349 } 350 if ((st.st_mode & S_IFMT) == S_IFSOCK) { 351 struct sockaddr_un addr; 352 353 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { 354 err(EXIT_FAILURE, "socket"); 355 } 356 memset(&addr, 0, sizeof(addr)); 357 addr.sun_family = AF_UNIX; 358 strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); 359 360 if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { 361 err(EXIT_FAILURE, "connect"); 362 } 363 } else { 364 if ((fd = open(path, O_RDONLY)) == -1) { 365 err(EXIT_FAILURE, "cannot open '%s'", path); 366 } 367 } 368 return fd; 369 } 370 371 static void 372 npfctl_debug(int argc, char **argv) 373 { 374 const char *conf = NULL, *bconf = NULL, *outfile = NULL; 375 bool use_active = false; 376 nl_config_t *ncf = NULL; 377 int fd, c, optcount; 378 379 argc--; 380 argv++; 381 382 npfctl_config_init(true); 383 while ((c = getopt(argc, argv, "ab:c:o:")) != -1) { 384 switch (c) { 385 case 'a': 386 use_active = true; 387 break; 388 case 'b': 389 bconf = optarg; 390 break; 391 case 'c': 392 conf = optarg; 393 break; 394 case 'o': 395 outfile = optarg; 396 break; 397 default: 398 usage(); 399 } 400 } 401 402 /* 403 * Options -a, -b and -c are mutually exclusive, so allow only one. 404 * If no options were specified, then set the defaults. 405 */ 406 optcount = (int)!!use_active + (int)!!conf + (int)!!bconf; 407 if (optcount != 1) { 408 if (optcount > 1) { 409 usage(); 410 } 411 conf = NPF_CONF_PATH; 412 outfile = outfile ? outfile : "npf.nvlist"; 413 } 414 415 if (use_active) { 416 puts("Loading the active configuration"); 417 fd = npfctl_open_dev(NPF_DEV_PATH); 418 if ((ncf = npf_config_retrieve(fd)) == NULL) { 419 err(EXIT_FAILURE, "npf_config_retrieve"); 420 } 421 } 422 423 if (conf) { 424 printf("Loading %s\n", conf); 425 npfctl_parse_file(conf); 426 npfctl_config_build(); 427 ncf = npfctl_config_ref(); 428 } 429 430 if (bconf) { 431 printf("Importing %s\n", bconf); 432 ncf = npfctl_import(bconf); 433 } 434 435 printf("Configuration:\n\n"); 436 _npf_config_dump(ncf, STDOUT_FILENO); 437 if (outfile) { 438 printf("\nSaving binary to %s\n", outfile); 439 npfctl_config_save(ncf, outfile); 440 } 441 npf_config_destroy(ncf); 442 } 443 444 static void 445 npfctl(int action, int argc, char **argv) 446 { 447 int fd, boolval, ret = 0; 448 const char *fun = ""; 449 nl_config_t *ncf; 450 451 switch (action) { 452 case NPFCTL_VALIDATE: 453 case NPFCTL_DEBUG: 454 fd = 0; 455 break; 456 default: 457 fd = npfctl_open_dev(NPF_DEV_PATH); 458 } 459 460 switch (action) { 461 case NPFCTL_START: 462 boolval = true; 463 ret = ioctl(fd, IOC_NPF_SWITCH, &boolval); 464 fun = "ioctl(IOC_NPF_SWITCH)"; 465 break; 466 case NPFCTL_STOP: 467 boolval = false; 468 ret = ioctl(fd, IOC_NPF_SWITCH, &boolval); 469 fun = "ioctl(IOC_NPF_SWITCH)"; 470 break; 471 case NPFCTL_RELOAD: 472 npfctl_config_init(false); 473 npfctl_parse_file(argc < 3 ? NPF_CONF_PATH : argv[2]); 474 npfctl_preload_bpfjit(); 475 errno = ret = npfctl_config_send(fd); 476 fun = "npfctl_config_send"; 477 break; 478 case NPFCTL_SHOWCONF: 479 ret = npfctl_config_show(fd); 480 fun = "npfctl_config_show"; 481 break; 482 case NPFCTL_FLUSH: 483 ret = npf_config_flush(fd); 484 fun = "npf_config_flush"; 485 break; 486 case NPFCTL_TABLE: 487 if ((argc -= 2) < 2) { 488 usage(); 489 } 490 argv += 2; 491 if (strcmp(argv[1], "replace") == 0) { 492 npfctl_table_replace(fd, argc, argv); 493 } else { 494 npfctl_table(fd, argc, argv); 495 } 496 break; 497 case NPFCTL_RULE: 498 if ((argc -= 2) < 2) { 499 usage(); 500 } 501 argv += 2; 502 npfctl_rule(fd, argc, argv); 503 break; 504 case NPFCTL_LOAD: 505 npfctl_preload_bpfjit(); 506 ret = npfctl_load(fd); 507 fun = "npfctl_config_load"; 508 break; 509 case NPFCTL_SAVE: 510 ncf = npf_config_retrieve(fd); 511 if (ncf) { 512 npfctl_config_save(ncf, 513 argc > 2 ? argv[2] : NPF_DB_PATH); 514 npf_config_destroy(ncf); 515 } else { 516 ret = errno; 517 } 518 fun = "npfctl_config_save"; 519 break; 520 case NPFCTL_STATS: 521 ret = npfctl_print_stats(fd); 522 fun = "npfctl_print_stats"; 523 break; 524 case NPFCTL_CONN_LIST: 525 ret = npfctl_conn_list(fd, argc, argv); 526 fun = "npfctl_conn_list"; 527 break; 528 case NPFCTL_VALIDATE: 529 npfctl_config_init(false); 530 npfctl_parse_file(argc > 2 ? argv[2] : NPF_CONF_PATH); 531 ret = npfctl_config_show(0); 532 fun = "npfctl_config_show"; 533 break; 534 case NPFCTL_DEBUG: 535 npfctl_debug(argc, argv); 536 break; 537 } 538 if (ret) { 539 err(EXIT_FAILURE, "%s", fun); 540 } 541 if (fd) { 542 close(fd); 543 } 544 } 545 546 int 547 main(int argc, char **argv) 548 { 549 static const struct operations_s { 550 const char * cmd; 551 int action; 552 } operations[] = { 553 /* Start, stop, reload */ 554 { "start", NPFCTL_START }, 555 { "stop", NPFCTL_STOP }, 556 { "reload", NPFCTL_RELOAD }, 557 { "show", NPFCTL_SHOWCONF, }, 558 { "flush", NPFCTL_FLUSH }, 559 /* Table */ 560 { "table", NPFCTL_TABLE }, 561 /* Rule */ 562 { "rule", NPFCTL_RULE }, 563 /* Stats */ 564 { "stats", NPFCTL_STATS }, 565 /* Full state save/load */ 566 { "save", NPFCTL_SAVE }, 567 { "load", NPFCTL_LOAD }, 568 { "list", NPFCTL_CONN_LIST }, 569 /* Misc. */ 570 { "valid", NPFCTL_VALIDATE }, 571 { "debug", NPFCTL_DEBUG }, 572 /* --- */ 573 { NULL, 0 } 574 }; 575 char *cmd; 576 577 if (argc < 2) { 578 usage(); 579 } 580 cmd = argv[1]; 581 582 /* Find and call the subroutine. */ 583 for (int n = 0; operations[n].cmd != NULL; n++) { 584 const char *opcmd = operations[n].cmd; 585 586 if (strncmp(cmd, opcmd, strlen(opcmd)) != 0) { 587 continue; 588 } 589 npfctl(operations[n].action, argc, argv); 590 return EXIT_SUCCESS; 591 } 592 usage(); 593 } 594