pfctl.c revision 126353
1/* $OpenBSD: pfctl.c,v 1.188 2003/08/29 21:47:36 cedric Exp $ */ 2 3/* 4 * Copyright (c) 2001 Daniel Hartmeier 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * - Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * - Redistributions in binary form must reproduce the above 14 * copyright notice, this list of conditions and the following 15 * disclaimer in the documentation and/or other materials provided 16 * with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 21 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 22 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 28 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 * 31 */ 32 33#include <sys/types.h> 34#include <sys/ioctl.h> 35#include <sys/socket.h> 36 37#include <net/if.h> 38#include <netinet/in.h> 39#include <net/pfvar.h> 40#include <arpa/inet.h> 41#include <altq/altq.h> 42 43#include <err.h> 44#include <errno.h> 45#include <fcntl.h> 46#include <limits.h> 47#include <netdb.h> 48#include <stdio.h> 49#include <stdlib.h> 50#include <string.h> 51#include <unistd.h> 52 53#include "pfctl_parser.h" 54#include "pfctl.h" 55 56void usage(void); 57int pfctl_enable(int, int); 58int pfctl_disable(int, int); 59int pfctl_clear_stats(int, int); 60int pfctl_clear_rules(int, int, char *, char *); 61int pfctl_clear_nat(int, int, char *, char *); 62int pfctl_clear_altq(int, int); 63int pfctl_clear_states(int, int); 64int pfctl_kill_states(int, int); 65int pfctl_get_pool(int, struct pf_pool *, u_int32_t, u_int32_t, int, 66 char *, char *); 67void pfctl_print_rule_counters(struct pf_rule *, int); 68int pfctl_show_rules(int, int, int, char *, char *); 69int pfctl_show_nat(int, int, char *, char *); 70int pfctl_show_states(int, u_int8_t, int); 71int pfctl_show_status(int); 72int pfctl_show_timeouts(int); 73int pfctl_show_limits(int); 74int pfctl_debug(int, u_int32_t, int); 75int pfctl_clear_rule_counters(int, int); 76int pfctl_test_altqsupport(int, int); 77int pfctl_show_anchors(int, int, char *); 78const char *pfctl_lookup_option(char *, const char **); 79 80const char *clearopt; 81char *rulesopt; 82const char *showopt; 83const char *debugopt; 84char *anchoropt; 85char *tableopt; 86const char *tblcmdopt; 87int state_killers; 88char *state_kill[2]; 89int loadopt; 90int altqsupport; 91 92int dev = -1; 93 94const char *infile; 95 96static const struct { 97 const char *name; 98 int index; 99} pf_limits[] = { 100 { "states", PF_LIMIT_STATES }, 101 { "frags", PF_LIMIT_FRAGS }, 102 { NULL, 0 } 103}; 104 105struct pf_hint { 106 const char *name; 107 int timeout; 108}; 109static const struct pf_hint pf_hint_normal[] = { 110 { "tcp.first", 2 * 60 }, 111 { "tcp.opening", 30 }, 112 { "tcp.established", 24 * 60 * 60 }, 113 { "tcp.closing", 15 * 60 }, 114 { "tcp.finwait", 45 }, 115 { "tcp.closed", 90 }, 116 { NULL, 0 } 117}; 118static const struct pf_hint pf_hint_satellite[] = { 119 { "tcp.first", 3 * 60 }, 120 { "tcp.opening", 30 + 5 }, 121 { "tcp.established", 24 * 60 * 60 }, 122 { "tcp.closing", 15 * 60 + 5 }, 123 { "tcp.finwait", 45 + 5 }, 124 { "tcp.closed", 90 + 5 }, 125 { NULL, 0 } 126}; 127static const struct pf_hint pf_hint_conservative[] = { 128 { "tcp.first", 60 * 60 }, 129 { "tcp.opening", 15 * 60 }, 130 { "tcp.established", 5 * 24 * 60 * 60 }, 131 { "tcp.closing", 60 * 60 }, 132 { "tcp.finwait", 10 * 60 }, 133 { "tcp.closed", 3 * 60 }, 134 { NULL, 0 } 135}; 136static const struct pf_hint pf_hint_aggressive[] = { 137 { "tcp.first", 30 }, 138 { "tcp.opening", 5 }, 139 { "tcp.established", 5 * 60 * 60 }, 140 { "tcp.closing", 60 }, 141 { "tcp.finwait", 30 }, 142 { "tcp.closed", 30 }, 143 { NULL, 0 } 144}; 145 146static const struct { 147 const char *name; 148 const struct pf_hint *hint; 149} pf_hints[] = { 150 { "normal", pf_hint_normal }, 151 { "satellite", pf_hint_satellite }, 152 { "high-latency", pf_hint_satellite }, 153 { "conservative", pf_hint_conservative }, 154 { "aggressive", pf_hint_aggressive }, 155 { NULL, NULL } 156}; 157 158static const char *clearopt_list[] = { 159 "nat", "queue", "rules", "state", "info", "Tables", "osfp", "all", NULL 160}; 161 162static const char *showopt_list[] = { 163 "nat", "queue", "rules", "Anchors", "state", "info", "labels", 164 "timeouts", "memory", "Tables", "osfp", "all", NULL 165}; 166 167static const char *tblcmdopt_list[] = { 168 "kill", "flush", "add", "delete", "load", "replace", "show", 169 "test", "zero", NULL 170}; 171 172static const char *debugopt_list[] = { 173 "none", "urgent", "misc", "loud", NULL 174}; 175 176 177void 178usage(void) 179{ 180 extern char *__progname; 181 182 fprintf(stderr, "usage: %s [-AdeghnNqrROvz] ", __progname); 183 fprintf(stderr, "[-a anchor[:ruleset]] [-D macro=value]\n"); 184 fprintf(stderr, " "); 185 fprintf(stderr, "[-f file] [-F modifier] [-k host] [-s modifier]\n"); 186 fprintf(stderr, " "); 187 fprintf(stderr, "[-t table] [-T command [address ...]] [-x level]\n"); 188 exit(1); 189} 190 191int 192pfctl_enable(int dev, int opts) 193{ 194 if (ioctl(dev, DIOCSTART)) { 195 if (errno == EEXIST) 196 errx(1, "pf already enabled"); 197 else 198 err(1, "DIOCSTART"); 199 } 200 if ((opts & PF_OPT_QUIET) == 0) 201 fprintf(stderr, "pf enabled\n"); 202 203 if (altqsupport && ioctl(dev, DIOCSTARTALTQ)) 204 if (errno != EEXIST) 205 err(1, "DIOCSTARTALTQ"); 206 207 return (0); 208} 209 210int 211pfctl_disable(int dev, int opts) 212{ 213 if (ioctl(dev, DIOCSTOP)) { 214 if (errno == ENOENT) 215 errx(1, "pf not enabled"); 216 else 217 err(1, "DIOCSTOP"); 218 } 219 if ((opts & PF_OPT_QUIET) == 0) 220 fprintf(stderr, "pf disabled\n"); 221 222 if (altqsupport && ioctl(dev, DIOCSTOPALTQ)) 223 if (errno != ENOENT) 224 err(1, "DIOCSTOPALTQ"); 225 226 return (0); 227} 228 229int 230pfctl_clear_stats(int dev, int opts) 231{ 232 if (ioctl(dev, DIOCCLRSTATUS)) 233 err(1, "DIOCCLRSTATUS"); 234 if ((opts & PF_OPT_QUIET) == 0) 235 fprintf(stderr, "pf: statistics cleared\n"); 236 return (0); 237} 238 239int 240pfctl_clear_rules(int dev, int opts, char *anchorname, char *rulesetname) 241{ 242 struct pfioc_rule pr; 243 244 if (*anchorname && !*rulesetname) { 245 struct pfioc_ruleset pr; 246 int mnr, nr, r; 247 248 memset(&pr, 0, sizeof(pr)); 249 memcpy(pr.anchor, anchorname, sizeof(pr.anchor)); 250 if (ioctl(dev, DIOCGETRULESETS, &pr)) { 251 if (errno == EINVAL) 252 fprintf(stderr, "No rulesets in anchor '%s'.\n", 253 anchorname); 254 else 255 err(1, "DIOCGETRULESETS"); 256 return (-1); 257 } 258 mnr = pr.nr; 259 for (nr = mnr - 1; nr >= 0; --nr) { 260 pr.nr = nr; 261 if (ioctl(dev, DIOCGETRULESET, &pr)) 262 err(1, "DIOCGETRULESET"); 263 r = pfctl_clear_rules(dev, opts | PF_OPT_QUIET, 264 anchorname, pr.name); 265 if (r) 266 return (r); 267 } 268 if ((opts & PF_OPT_QUIET) == 0) 269 fprintf(stderr, "rules cleared\n"); 270 return (0); 271 } 272 memset(&pr, 0, sizeof(pr)); 273 memcpy(pr.anchor, anchorname, sizeof(pr.anchor)); 274 memcpy(pr.ruleset, rulesetname, sizeof(pr.ruleset)); 275 pr.rule.action = PF_SCRUB; 276 if (ioctl(dev, DIOCBEGINRULES, &pr)) 277 err(1, "DIOCBEGINRULES"); 278 else if (ioctl(dev, DIOCCOMMITRULES, &pr)) 279 err(1, "DIOCCOMMITRULES"); 280 pr.rule.action = PF_PASS; 281 if (ioctl(dev, DIOCBEGINRULES, &pr)) 282 err(1, "DIOCBEGINRULES"); 283 else if (ioctl(dev, DIOCCOMMITRULES, &pr)) 284 err(1, "DIOCCOMMITRULES"); 285 if ((opts & PF_OPT_QUIET) == 0) 286 fprintf(stderr, "rules cleared\n"); 287 return (0); 288} 289 290int 291pfctl_clear_nat(int dev, int opts, char *anchorname, char *rulesetname) 292{ 293 struct pfioc_rule pr; 294 295 if (*anchorname && !*rulesetname) { 296 struct pfioc_ruleset pr; 297 int mnr, nr, r; 298 299 memset(&pr, 0, sizeof(pr)); 300 memcpy(pr.anchor, anchorname, sizeof(pr.anchor)); 301 if (ioctl(dev, DIOCGETRULESETS, &pr)) { 302 if (errno == EINVAL) 303 fprintf(stderr, "No rulesets in anchor '%s'.\n", 304 anchorname); 305 else 306 err(1, "DIOCGETRULESETS"); 307 return (-1); 308 } 309 mnr = pr.nr; 310 for (nr = mnr - 1; nr >= 0; --nr) { 311 pr.nr = nr; 312 if (ioctl(dev, DIOCGETRULESET, &pr)) 313 err(1, "DIOCGETRULESET"); 314 r = pfctl_clear_nat(dev, opts | PF_OPT_QUIET, 315 anchorname, pr.name); 316 if (r) 317 return (r); 318 } 319 if ((opts & PF_OPT_QUIET) == 0) 320 fprintf(stderr, "nat cleared\n"); 321 return (0); 322 } 323 memset(&pr, 0, sizeof(pr)); 324 memcpy(pr.anchor, anchorname, sizeof(pr.anchor)); 325 memcpy(pr.ruleset, rulesetname, sizeof(pr.ruleset)); 326 pr.rule.action = PF_NAT; 327 if (ioctl(dev, DIOCBEGINRULES, &pr)) 328 err(1, "DIOCBEGINRULES"); 329 else if (ioctl(dev, DIOCCOMMITRULES, &pr)) 330 err(1, "DIOCCOMMITRULES"); 331 pr.rule.action = PF_BINAT; 332 if (ioctl(dev, DIOCBEGINRULES, &pr)) 333 err(1, "DIOCBEGINRULES"); 334 else if (ioctl(dev, DIOCCOMMITRULES, &pr)) 335 err(1, "DIOCCOMMITRULES"); 336 pr.rule.action = PF_RDR; 337 if (ioctl(dev, DIOCBEGINRULES, &pr)) 338 err(1, "DIOCBEGINRULES"); 339 else if (ioctl(dev, DIOCCOMMITRULES, &pr)) 340 err(1, "DIOCCOMMITRULES"); 341 if ((opts & PF_OPT_QUIET) == 0) 342 fprintf(stderr, "nat cleared\n"); 343 return (0); 344} 345 346int 347pfctl_clear_altq(int dev, int opts) 348{ 349 struct pfioc_altq pa; 350 351 if (!altqsupport) 352 return (-1); 353 memset(&pa, 0, sizeof(pa)); 354 if (ioctl(dev, DIOCBEGINALTQS, &pa.ticket)) 355 err(1, "DIOCBEGINALTQS"); 356 else if (ioctl(dev, DIOCCOMMITALTQS, &pa.ticket)) 357 err(1, "DIOCCOMMITALTQS"); 358 if ((opts & PF_OPT_QUIET) == 0) 359 fprintf(stderr, "altq cleared\n"); 360 return (0); 361} 362 363int 364pfctl_clear_states(int dev, int opts) 365{ 366 if (ioctl(dev, DIOCCLRSTATES)) 367 err(1, "DIOCCLRSTATES"); 368 if ((opts & PF_OPT_QUIET) == 0) 369 fprintf(stderr, "states cleared\n"); 370 return (0); 371} 372 373int 374pfctl_kill_states(int dev, int opts) 375{ 376 struct pfioc_state_kill psk; 377 struct addrinfo *res[2], *resp[2]; 378 struct sockaddr last_src, last_dst; 379 int killed, sources, dests; 380 int ret_ga; 381 382 killed = sources = dests = 0; 383 384 memset(&psk, 0, sizeof(psk)); 385 memset(&psk.psk_src.addr.v.a.mask, 0xff, 386 sizeof(psk.psk_src.addr.v.a.mask)); 387 memset(&last_src, 0xff, sizeof(last_src)); 388 memset(&last_dst, 0xff, sizeof(last_dst)); 389 390 if ((ret_ga = getaddrinfo(state_kill[0], NULL, NULL, &res[0]))) { 391 errx(1, "getaddrinfo: %s", gai_strerror(ret_ga)); 392 /* NOTREACHED */ 393 } 394 for (resp[0] = res[0]; resp[0]; resp[0] = resp[0]->ai_next) { 395 if (resp[0]->ai_addr == NULL) 396 continue; 397 /* We get lots of duplicates. Catch the easy ones */ 398 if (memcmp(&last_src, resp[0]->ai_addr, sizeof(last_src)) == 0) 399 continue; 400 last_src = *(struct sockaddr *)resp[0]->ai_addr; 401 402 psk.psk_af = resp[0]->ai_family; 403 sources++; 404 405 if (psk.psk_af == AF_INET) 406 psk.psk_src.addr.v.a.addr.v4 = 407 ((struct sockaddr_in *)resp[0]->ai_addr)->sin_addr; 408 else if (psk.psk_af == AF_INET6) 409 psk.psk_src.addr.v.a.addr.v6 = 410 ((struct sockaddr_in6 *)resp[0]->ai_addr)-> 411 sin6_addr; 412 else 413 errx(1, "Unknown address family %d", psk.psk_af); 414 415 if (state_killers > 1) { 416 dests = 0; 417 memset(&psk.psk_dst.addr.v.a.mask, 0xff, 418 sizeof(psk.psk_dst.addr.v.a.mask)); 419 memset(&last_dst, 0xff, sizeof(last_dst)); 420 if ((ret_ga = getaddrinfo(state_kill[1], NULL, NULL, 421 &res[1]))) { 422 errx(1, "getaddrinfo: %s", gai_strerror(ret_ga)); 423 /* NOTREACHED */ 424 } 425 for (resp[1] = res[1]; resp[1]; 426 resp[1] = resp[1]->ai_next) { 427 if (resp[1]->ai_addr == NULL) 428 continue; 429 if (psk.psk_af != resp[1]->ai_family) 430 continue; 431 432 if (memcmp(&last_dst, resp[1]->ai_addr, 433 sizeof(last_dst)) == 0) 434 continue; 435 last_dst = *(struct sockaddr *)resp[1]->ai_addr; 436 437 dests++; 438 439 if (psk.psk_af == AF_INET) 440 psk.psk_dst.addr.v.a.addr.v4 = 441 ((struct sockaddr_in *)resp[1]-> 442 ai_addr)->sin_addr; 443 else if (psk.psk_af == AF_INET6) 444 psk.psk_dst.addr.v.a.addr.v6 = 445 ((struct sockaddr_in6 *)resp[1]-> 446 ai_addr)->sin6_addr; 447 else 448 errx(1, "Unknown address family %d", 449 psk.psk_af); 450 451 if (ioctl(dev, DIOCKILLSTATES, &psk)) 452 err(1, "DIOCKILLSTATES"); 453 killed += psk.psk_af; 454 /* fixup psk.psk_af */ 455 psk.psk_af = resp[1]->ai_family; 456 } 457 freeaddrinfo(res[1]); 458 } else { 459 if (ioctl(dev, DIOCKILLSTATES, &psk)) 460 err(1, "DIOCKILLSTATES"); 461 killed += psk.psk_af; 462 /* fixup psk.psk_af */ 463 psk.psk_af = res[0]->ai_family; 464 } 465 } 466 467 freeaddrinfo(res[0]); 468 469 if ((opts & PF_OPT_QUIET) == 0) 470 fprintf(stderr, "killed %d states from %d sources and %d " 471 "destinations\n", killed, sources, dests); 472 return (0); 473} 474 475int 476pfctl_get_pool(int dev, struct pf_pool *pool, u_int32_t nr, 477 u_int32_t ticket, int r_action, char *anchorname, char *rulesetname) 478{ 479 struct pfioc_pooladdr pp; 480 struct pf_pooladdr *pa; 481 u_int32_t pnr, mpnr; 482 483 memset(&pp, 0, sizeof(pp)); 484 memcpy(pp.anchor, anchorname, sizeof(pp.anchor)); 485 memcpy(pp.ruleset, rulesetname, sizeof(pp.ruleset)); 486 pp.r_action = r_action; 487 pp.r_num = nr; 488 pp.ticket = ticket; 489 if (ioctl(dev, DIOCGETADDRS, &pp)) { 490 warn("DIOCGETADDRS"); 491 return (-1); 492 } 493 mpnr = pp.nr; 494 TAILQ_INIT(&pool->list); 495 for (pnr = 0; pnr < mpnr; ++pnr) { 496 pp.nr = pnr; 497 if (ioctl(dev, DIOCGETADDR, &pp)) { 498 warn("DIOCGETADDR"); 499 return (-1); 500 } 501 pa = calloc(1, sizeof(struct pf_pooladdr)); 502 if (pa == NULL) 503 err(1, "calloc"); 504 bcopy(&pp.addr, pa, sizeof(struct pf_pooladdr)); 505 TAILQ_INSERT_TAIL(&pool->list, pa, entries); 506 } 507 508 return (0); 509} 510 511void 512pfctl_clear_pool(struct pf_pool *pool) 513{ 514 struct pf_pooladdr *pa; 515 516 while ((pa = TAILQ_FIRST(&pool->list)) != NULL) { 517 TAILQ_REMOVE(&pool->list, pa, entries); 518 free(pa); 519 } 520} 521 522void 523pfctl_print_rule_counters(struct pf_rule *rule, int opts) 524{ 525 if (opts & PF_OPT_DEBUG) { 526 const char *t[PF_SKIP_COUNT] = { "i", "d", "f", 527 "p", "sa", "sp", "da", "dp" }; 528 int i; 529 530 printf(" [ Skip steps: "); 531 for (i = 0; i < PF_SKIP_COUNT; ++i) { 532 if (rule->skip[i].nr == rule->nr + 1) 533 continue; 534 printf("%s=", t[i]); 535 if (rule->skip[i].nr == -1) 536 printf("end "); 537 else 538 printf("%u ", rule->skip[i].nr); 539 } 540 printf("]\n"); 541 542 printf(" [ queue: qname=%s qid=%u pqname=%s pqid=%u ]\n", 543 rule->qname, rule->qid, rule->pqname, rule->pqid); 544 } 545 if (opts & PF_OPT_VERBOSE) 546 printf(" [ Evaluations: %-8llu Packets: %-8llu " 547 "Bytes: %-10llu States: %-6u]\n", 548 rule->evaluations, rule->packets, 549 rule->bytes, rule->states); 550} 551 552int 553pfctl_show_rules(int dev, int opts, int format, char *anchorname, 554 char *rulesetname) 555{ 556 struct pfioc_rule pr; 557 u_int32_t nr, mnr; 558 int rule_numbers = opts & (PF_OPT_VERBOSE2 | PF_OPT_DEBUG); 559 560 if (*anchorname && !*rulesetname) { 561 struct pfioc_ruleset pr; 562 int r; 563 564 memset(&pr, 0, sizeof(pr)); 565 memcpy(pr.anchor, anchorname, sizeof(pr.anchor)); 566 if (ioctl(dev, DIOCGETRULESETS, &pr)) { 567 if (errno == EINVAL) 568 fprintf(stderr, "No rulesets in anchor '%s'.\n", 569 anchorname); 570 else 571 err(1, "DIOCGETRULESETS"); 572 return (-1); 573 } 574 mnr = pr.nr; 575 for (nr = 0; nr < mnr; ++nr) { 576 pr.nr = nr; 577 if (ioctl(dev, DIOCGETRULESET, &pr)) 578 err(1, "DIOCGETRULESET"); 579 r = pfctl_show_rules(dev, opts, format, anchorname, 580 pr.name); 581 if (r) 582 return (r); 583 } 584 return (0); 585 } 586 587 memset(&pr, 0, sizeof(pr)); 588 memcpy(pr.anchor, anchorname, sizeof(pr.anchor)); 589 memcpy(pr.ruleset, rulesetname, sizeof(pr.ruleset)); 590 pr.rule.action = PF_SCRUB; 591 if (ioctl(dev, DIOCGETRULES, &pr)) { 592 warn("DIOCGETRULES"); 593 return (-1); 594 } 595 mnr = pr.nr; 596 for (nr = 0; nr < mnr; ++nr) { 597 pr.nr = nr; 598 if (ioctl(dev, DIOCGETRULE, &pr)) { 599 warn("DIOCGETRULE"); 600 return (-1); 601 } 602 603 if (pfctl_get_pool(dev, &pr.rule.rpool, 604 nr, pr.ticket, PF_SCRUB, anchorname, rulesetname) != 0) 605 return (-1); 606 607 switch (format) { 608 case 1: 609 if (pr.rule.label[0]) { 610 printf("%s ", pr.rule.label); 611 printf("%llu %llu %llu\n", 612 pr.rule.evaluations, pr.rule.packets, 613 pr.rule.bytes); 614 } 615 break; 616 default: 617 print_rule(&pr.rule, rule_numbers); 618 pfctl_print_rule_counters(&pr.rule, opts); 619 } 620 pfctl_clear_pool(&pr.rule.rpool); 621 } 622 pr.rule.action = PF_PASS; 623 if (ioctl(dev, DIOCGETRULES, &pr)) { 624 warn("DIOCGETRULES"); 625 return (-1); 626 } 627 mnr = pr.nr; 628 for (nr = 0; nr < mnr; ++nr) { 629 pr.nr = nr; 630 if (ioctl(dev, DIOCGETRULE, &pr)) { 631 warn("DIOCGETRULE"); 632 return (-1); 633 } 634 635 if (pfctl_get_pool(dev, &pr.rule.rpool, 636 nr, pr.ticket, PF_PASS, anchorname, rulesetname) != 0) 637 return (-1); 638 639 switch (format) { 640 case 1: 641 if (pr.rule.label[0]) { 642 printf("%s ", pr.rule.label); 643 printf("%llu %llu %llu\n", 644 pr.rule.evaluations, pr.rule.packets, 645 pr.rule.bytes); 646 } 647 break; 648 default: 649 print_rule(&pr.rule, rule_numbers); 650 pfctl_print_rule_counters(&pr.rule, opts); 651 } 652 pfctl_clear_pool(&pr.rule.rpool); 653 } 654 return (0); 655} 656 657int 658pfctl_show_nat(int dev, int opts, char *anchorname, char *rulesetname) 659{ 660 struct pfioc_rule pr; 661 u_int32_t mnr, nr; 662 static int nattype[3] = { PF_NAT, PF_RDR, PF_BINAT }; 663 int i; 664 665 if (*anchorname && !*rulesetname) { 666 struct pfioc_ruleset pr; 667 int r; 668 669 memset(&pr, 0, sizeof(pr)); 670 memcpy(pr.anchor, anchorname, sizeof(pr.anchor)); 671 if (ioctl(dev, DIOCGETRULESETS, &pr)) { 672 if (errno == EINVAL) 673 fprintf(stderr, "No rulesets in anchor '%s'.\n", 674 anchorname); 675 else 676 err(1, "DIOCGETRULESETS"); 677 return (-1); 678 } 679 mnr = pr.nr; 680 for (nr = 0; nr < mnr; ++nr) { 681 pr.nr = nr; 682 if (ioctl(dev, DIOCGETRULESET, &pr)) 683 err(1, "DIOCGETRULESET"); 684 r = pfctl_show_nat(dev, opts, anchorname, pr.name); 685 if (r) 686 return (r); 687 } 688 return (0); 689 } 690 691 memset(&pr, 0, sizeof(pr)); 692 memcpy(pr.anchor, anchorname, sizeof(pr.anchor)); 693 memcpy(pr.ruleset, rulesetname, sizeof(pr.ruleset)); 694 for (i = 0; i < 3; i++) { 695 pr.rule.action = nattype[i]; 696 if (ioctl(dev, DIOCGETRULES, &pr)) { 697 warn("DIOCGETRULES"); 698 return (-1); 699 } 700 mnr = pr.nr; 701 for (nr = 0; nr < mnr; ++nr) { 702 pr.nr = nr; 703 if (ioctl(dev, DIOCGETRULE, &pr)) { 704 warn("DIOCGETRULE"); 705 return (-1); 706 } 707 if (pfctl_get_pool(dev, &pr.rule.rpool, nr, 708 pr.ticket, nattype[i], anchorname, 709 rulesetname) != 0) 710 return (-1); 711 print_rule(&pr.rule, opts & PF_OPT_VERBOSE2); 712 pfctl_print_rule_counters(&pr.rule, opts); 713 pfctl_clear_pool(&pr.rule.rpool); 714 } 715 } 716 return (0); 717} 718 719int 720pfctl_show_states(int dev, u_int8_t proto, int opts) 721{ 722 struct pfioc_states ps; 723 struct pf_state *p; 724 char *inbuf = NULL; 725 unsigned len = 0; 726 int i; 727 728 memset(&ps, 0, sizeof(ps)); 729 for (;;) { 730 ps.ps_len = len; 731 if (len) { 732 ps.ps_buf = inbuf = realloc(inbuf, len); 733 if (inbuf == NULL) 734 err(1, "realloc"); 735 } 736 if (ioctl(dev, DIOCGETSTATES, &ps) < 0) { 737 warn("DIOCGETSTATES"); 738 return (-1); 739 } 740 if (ps.ps_len + sizeof(struct pfioc_states) < len) 741 break; 742 if (len == 0 && ps.ps_len == 0) 743 return (0); 744 if (len == 0 && ps.ps_len != 0) 745 len = ps.ps_len; 746 if (ps.ps_len == 0) 747 return (0); /* no states */ 748 len *= 2; 749 } 750 p = ps.ps_states; 751 for (i = 0; i < ps.ps_len; i += sizeof(*p)) { 752 if (!proto || (p->proto == proto)) 753 print_state(p, opts); 754 p++; 755 } 756 return (0); 757} 758 759int 760pfctl_show_status(int dev) 761{ 762 struct pf_status status; 763 764 if (ioctl(dev, DIOCGETSTATUS, &status)) { 765 warn("DIOCGETSTATUS"); 766 return (-1); 767 } 768 print_status(&status); 769 return (0); 770} 771 772int 773pfctl_show_timeouts(int dev) 774{ 775 struct pfioc_tm pt; 776 int i; 777 778 memset(&pt, 0, sizeof(pt)); 779 for (i = 0; pf_timeouts[i].name; i++) { 780 pt.timeout = pf_timeouts[i].timeout; 781 if (ioctl(dev, DIOCGETTIMEOUT, &pt)) 782 err(1, "DIOCGETTIMEOUT"); 783 printf("%-20s %10d", pf_timeouts[i].name, pt.seconds); 784 if (i >= PFTM_ADAPTIVE_START && i <= PFTM_ADAPTIVE_END) 785 printf(" states"); 786 else 787 printf("s"); 788 printf("\n"); 789 } 790 return (0); 791 792} 793 794int 795pfctl_show_limits(int dev) 796{ 797 struct pfioc_limit pl; 798 int i; 799 800 memset(&pl, 0, sizeof(pl)); 801 for (i = 0; pf_limits[i].name; i++) { 802 pl.index = i; 803 if (ioctl(dev, DIOCGETLIMIT, &pl)) 804 err(1, "DIOCGETLIMIT"); 805 printf("%-10s ", pf_limits[i].name); 806 if (pl.limit == UINT_MAX) 807 printf("unlimited\n"); 808 else 809 printf("hard limit %6u\n", pl.limit); 810 } 811 return (0); 812} 813 814/* callbacks for rule/nat/rdr/addr */ 815int 816pfctl_add_pool(struct pfctl *pf, struct pf_pool *p, sa_family_t af) 817{ 818 struct pf_pooladdr *pa; 819 820 if ((pf->opts & PF_OPT_NOACTION) == 0) { 821 if (ioctl(pf->dev, DIOCBEGINADDRS, &pf->paddr)) 822 err(1, "DIOCBEGINADDRS"); 823 } 824 825 pf->paddr.af = af; 826 TAILQ_FOREACH(pa, &p->list, entries) { 827 memcpy(&pf->paddr.addr, pa, sizeof(struct pf_pooladdr)); 828 if ((pf->opts & PF_OPT_NOACTION) == 0) { 829 if (ioctl(pf->dev, DIOCADDADDR, &pf->paddr)) 830 err(1, "DIOCADDADDR"); 831 } 832 } 833 return (0); 834} 835 836int 837pfctl_add_rule(struct pfctl *pf, struct pf_rule *r) 838{ 839 u_int8_t rs_num; 840 841 switch (r->action) { 842 case PF_SCRUB: 843 if ((loadopt & PFCTL_FLAG_FILTER) == 0) 844 return (0); 845 rs_num = PF_RULESET_SCRUB; 846 break; 847 case PF_DROP: 848 case PF_PASS: 849 if ((loadopt & PFCTL_FLAG_FILTER) == 0) 850 return (0); 851 rs_num = PF_RULESET_FILTER; 852 break; 853 case PF_NAT: 854 case PF_NONAT: 855 if ((loadopt & PFCTL_FLAG_NAT) == 0) 856 return (0); 857 rs_num = PF_RULESET_NAT; 858 break; 859 case PF_RDR: 860 case PF_NORDR: 861 if ((loadopt & PFCTL_FLAG_NAT) == 0) 862 return (0); 863 rs_num = PF_RULESET_RDR; 864 break; 865 case PF_BINAT: 866 case PF_NOBINAT: 867 if ((loadopt & PFCTL_FLAG_NAT) == 0) 868 return (0); 869 rs_num = PF_RULESET_BINAT; 870 break; 871 default: 872 errx(1, "Invalid rule type"); 873 break; 874 } 875 876 if ((pf->opts & PF_OPT_NOACTION) == 0) { 877 if (pfctl_add_pool(pf, &r->rpool, r->af)) 878 return (1); 879 memcpy(&pf->prule[rs_num]->rule, r, 880 sizeof(pf->prule[rs_num]->rule)); 881 pf->prule[rs_num]->pool_ticket = pf->paddr.ticket; 882 if (ioctl(pf->dev, DIOCADDRULE, pf->prule[rs_num])) 883 err(1, "DIOCADDRULE"); 884 } 885 if (pf->opts & PF_OPT_VERBOSE) 886 print_rule(r, pf->opts & PF_OPT_VERBOSE2); 887 pfctl_clear_pool(&r->rpool); 888 return (0); 889} 890 891int 892pfctl_add_altq(struct pfctl *pf, struct pf_altq *a) 893{ 894 if (altqsupport && 895 (loadopt & PFCTL_FLAG_ALTQ) != 0) { 896 memcpy(&pf->paltq->altq, a, sizeof(struct pf_altq)); 897 if ((pf->opts & PF_OPT_NOACTION) == 0) { 898 if (ioctl(pf->dev, DIOCADDALTQ, pf->paltq)) { 899 if (errno == ENXIO) 900 errx(1, "qtype not configured"); 901 else if (errno == ENODEV) 902 errx(1, "%s: driver does not support " 903 "altq", a->ifname); 904 else 905 err(1, "DIOCADDALTQ"); 906 } 907 } 908 pfaltq_store(&pf->paltq->altq); 909 } 910 return (0); 911} 912 913int 914pfctl_rules(int dev, char *filename, int opts, char *anchorname, 915 char *rulesetname) 916{ 917#define ERR(x) do { warn(x); goto _error; } while(0) 918#define ERRX(x) do { warnx(x); goto _error; } while(0) 919 920 FILE *fin; 921 struct pfioc_rule pr[PF_RULESET_MAX]; 922 struct pfioc_altq pa; 923 struct pfctl pf; 924 struct pfr_table trs; 925 int i; 926 927 memset(&pa, 0, sizeof(pa)); 928 memset(&pf, 0, sizeof(pf)); 929 memset(&trs, 0, sizeof(trs)); 930 for (i = 0; i < PF_RULESET_MAX; i++) { 931 memset(&pr[i], 0, sizeof(pr[i])); 932 memcpy(pr[i].anchor, anchorname, sizeof(pr[i].anchor)); 933 memcpy(pr[i].ruleset, rulesetname, sizeof(pr[i].ruleset)); 934 } 935 if (strlcpy(trs.pfrt_anchor, anchorname, 936 sizeof(trs.pfrt_anchor)) >= sizeof(trs.pfrt_anchor) || 937 strlcpy(trs.pfrt_ruleset, rulesetname, 938 sizeof(trs.pfrt_ruleset)) >= sizeof(trs.pfrt_ruleset)) 939 ERRX("pfctl_rules: strlcpy"); 940 if (strcmp(filename, "-") == 0) { 941 fin = stdin; 942 infile = "stdin"; 943 } else { 944 if ((fin = fopen(filename, "r")) == NULL) { 945 warn("%s", filename); 946 return (1); 947 } 948 infile = filename; 949 } 950 if ((opts & PF_OPT_NOACTION) == 0) { 951 if ((loadopt & PFCTL_FLAG_NAT) != 0) { 952 pr[PF_RULESET_NAT].rule.action = PF_NAT; 953 if (ioctl(dev, DIOCBEGINRULES, &pr[PF_RULESET_NAT])) 954 ERR("DIOCBEGINRULES"); 955 pr[PF_RULESET_RDR].rule.action = PF_RDR; 956 if (ioctl(dev, DIOCBEGINRULES, &pr[PF_RULESET_RDR])) 957 ERR("DIOCBEGINRULES"); 958 pr[PF_RULESET_BINAT].rule.action = PF_BINAT; 959 if (ioctl(dev, DIOCBEGINRULES, &pr[PF_RULESET_BINAT])) 960 ERR("DIOCBEGINRULES"); 961 } 962 if (((altqsupport && (loadopt & PFCTL_FLAG_ALTQ) != 0)) && 963 ioctl(dev, DIOCBEGINALTQS, &pa.ticket)) { 964 ERR("DIOCBEGINALTQS"); 965 } 966 if ((loadopt & PFCTL_FLAG_FILTER) != 0) { 967 pr[PF_RULESET_SCRUB].rule.action = PF_SCRUB; 968 if (ioctl(dev, DIOCBEGINRULES, &pr[PF_RULESET_SCRUB])) 969 ERR("DIOCBEGINRULES"); 970 pr[PF_RULESET_FILTER].rule.action = PF_PASS; 971 if (ioctl(dev, DIOCBEGINRULES, &pr[PF_RULESET_FILTER])) 972 ERR("DIOCBEGINRULES"); 973 } 974 if (loadopt & PFCTL_FLAG_TABLE) { 975 if (pfr_ina_begin(&trs, &pf.tticket, NULL, 0) != 0) 976 ERR("begin table"); 977 } 978 } 979 /* fill in callback data */ 980 pf.dev = dev; 981 pf.opts = opts; 982 pf.loadopt = loadopt; 983 pf.paltq = &pa; 984 for (i = 0; i < PF_RULESET_MAX; i++) { 985 pf.prule[i] = &pr[i]; 986 } 987 pf.rule_nr = 0; 988 pf.anchor = anchorname; 989 pf.ruleset = rulesetname; 990 if (parse_rules(fin, &pf) < 0) { 991 if ((opts & PF_OPT_NOACTION) == 0) 992 ERRX("Syntax error in config file: " 993 "pf rules not loaded"); 994 else 995 goto _error; 996 } 997 if ((altqsupport && (loadopt & PFCTL_FLAG_ALTQ) != 0)) 998 if (check_commit_altq(dev, opts) != 0) 999 ERRX("errors in altq config"); 1000 if ((opts & PF_OPT_NOACTION) == 0) { 1001 if ((loadopt & PFCTL_FLAG_NAT) != 0) { 1002 pr[PF_RULESET_NAT].rule.action = PF_NAT; 1003 if (ioctl(dev, DIOCCOMMITRULES, &pr[PF_RULESET_NAT]) && 1004 (errno != EINVAL || pf.rule_nr)) 1005 ERR("DIOCCOMMITRULES NAT"); 1006 pr[PF_RULESET_RDR].rule.action = PF_RDR; 1007 if (ioctl(dev, DIOCCOMMITRULES, &pr[PF_RULESET_RDR]) && 1008 (errno != EINVAL || pf.rule_nr)) 1009 ERR("DIOCCOMMITRULES RDR"); 1010 pr[PF_RULESET_BINAT].rule.action = PF_BINAT; 1011 if (ioctl(dev, DIOCCOMMITRULES, &pr[PF_RULESET_BINAT]) && 1012 (errno != EINVAL || pf.rule_nr)) 1013 ERR("DIOCCOMMITRULES BINAT"); 1014 } 1015 if (((altqsupport && (loadopt & PFCTL_FLAG_ALTQ) != 0)) && 1016 ioctl(dev, DIOCCOMMITALTQS, &pa.ticket)) 1017 ERR("DIOCCOMMITALTQS"); 1018 if ((loadopt & PFCTL_FLAG_FILTER) != 0) { 1019 pr[PF_RULESET_SCRUB].rule.action = PF_SCRUB; 1020 if (ioctl(dev, DIOCCOMMITRULES, &pr[PF_RULESET_SCRUB]) && 1021 (errno != EINVAL || pf.rule_nr)) 1022 ERR("DIOCCOMMITRULES SCRUB"); 1023 pr[PF_RULESET_FILTER].rule.action = PF_PASS; 1024 if (ioctl(dev, DIOCCOMMITRULES, &pr[PF_RULESET_FILTER]) && 1025 (errno != EINVAL || pf.rule_nr)) 1026 ERR("DIOCCOMMITRULES FILTER"); 1027 } 1028 if (loadopt & PFCTL_FLAG_TABLE) { 1029 if (pfr_ina_commit(&trs, pf.tticket, NULL, NULL, 0)) 1030 ERR("commit table"); 1031 pf.tdirty = 0; 1032 } 1033 } 1034 if (fin != stdin) 1035 fclose(fin); 1036 1037 /* process "load anchor" directives */ 1038 if (!anchorname[0] && !rulesetname[0]) 1039 if (pfctl_load_anchors(dev, opts) == -1) 1040 ERRX("load anchors"); 1041 1042 return (0); 1043 1044_error: 1045 if (pf.tdirty) /* cleanup kernel leftover */ 1046 pfr_ina_begin(&trs, NULL, NULL, 0); 1047 exit(1); 1048 1049#undef ERR 1050#undef ERRX 1051} 1052 1053int 1054pfctl_set_limit(struct pfctl *pf, const char *opt, unsigned int limit) 1055{ 1056 struct pfioc_limit pl; 1057 int i; 1058 1059 if ((loadopt & PFCTL_FLAG_OPTION) == 0) 1060 return (0); 1061 1062 memset(&pl, 0, sizeof(pl)); 1063 for (i = 0; pf_limits[i].name; i++) { 1064 if (strcasecmp(opt, pf_limits[i].name) == 0) { 1065 pl.index = i; 1066 pl.limit = limit; 1067 if ((pf->opts & PF_OPT_NOACTION) == 0) { 1068 if (ioctl(pf->dev, DIOCSETLIMIT, &pl)) { 1069 if (errno == EBUSY) { 1070 warnx("Current pool " 1071 "size exceeds requested " 1072 "hard limit"); 1073 return (1); 1074 } else 1075 err(1, "DIOCSETLIMIT"); 1076 } 1077 } 1078 break; 1079 } 1080 } 1081 if (pf_limits[i].name == NULL) { 1082 warnx("Bad pool name."); 1083 return (1); 1084 } 1085 1086 if (pf->opts & PF_OPT_VERBOSE) 1087 printf("set limit %s %d\n", opt, limit); 1088 1089 return (0); 1090} 1091 1092int 1093pfctl_set_timeout(struct pfctl *pf, const char *opt, int seconds, int quiet) 1094{ 1095 struct pfioc_tm pt; 1096 int i; 1097 1098 if ((loadopt & PFCTL_FLAG_OPTION) == 0) 1099 return (0); 1100 1101 memset(&pt, 0, sizeof(pt)); 1102 for (i = 0; pf_timeouts[i].name; i++) { 1103 if (strcasecmp(opt, pf_timeouts[i].name) == 0) { 1104 pt.timeout = pf_timeouts[i].timeout; 1105 break; 1106 } 1107 } 1108 1109 if (pf_timeouts[i].name == NULL) { 1110 warnx("Bad timeout name."); 1111 return (1); 1112 } 1113 1114 pt.seconds = seconds; 1115 if ((pf->opts & PF_OPT_NOACTION) == 0) { 1116 if (ioctl(pf->dev, DIOCSETTIMEOUT, &pt)) 1117 err(1, "DIOCSETTIMEOUT"); 1118 } 1119 1120 if (pf->opts & PF_OPT_VERBOSE && ! quiet) 1121 printf("set timeout %s %d\n", opt, seconds); 1122 1123 return (0); 1124} 1125 1126int 1127pfctl_set_optimization(struct pfctl *pf, const char *opt) 1128{ 1129 const struct pf_hint *hint; 1130 int i, r; 1131 1132 if ((loadopt & PFCTL_FLAG_OPTION) == 0) 1133 return (0); 1134 1135 for (i = 0; pf_hints[i].name; i++) 1136 if (strcasecmp(opt, pf_hints[i].name) == 0) 1137 break; 1138 1139 hint = pf_hints[i].hint; 1140 if (hint == NULL) { 1141 warnx("Bad hint name."); 1142 return (1); 1143 } 1144 1145 for (i = 0; hint[i].name; i++) 1146 if ((r = pfctl_set_timeout(pf, hint[i].name, 1147 hint[i].timeout, 1))) 1148 return (r); 1149 1150 if (pf->opts & PF_OPT_VERBOSE) 1151 printf("set optimization %s\n", opt); 1152 1153 return (0); 1154} 1155 1156int 1157pfctl_set_logif(struct pfctl *pf, char *ifname) 1158{ 1159 struct pfioc_if pi; 1160 1161 if ((loadopt & PFCTL_FLAG_OPTION) == 0) 1162 return (0); 1163 1164 memset(&pi, 0, sizeof(pi)); 1165 if ((pf->opts & PF_OPT_NOACTION) == 0) { 1166 if (!strcmp(ifname, "none")) 1167 bzero(pi.ifname, sizeof(pi.ifname)); 1168 else { 1169 if (strlcpy(pi.ifname, ifname, 1170 sizeof(pi.ifname)) >= sizeof(pi.ifname)) 1171 errx(1, "pfctl_set_logif: strlcpy"); 1172 } 1173 if (ioctl(pf->dev, DIOCSETSTATUSIF, &pi)) 1174 err(1, "DIOCSETSTATUSIF"); 1175 } 1176 1177 if (pf->opts & PF_OPT_VERBOSE) 1178 printf("set loginterface %s\n", ifname); 1179 1180 return (0); 1181} 1182 1183int 1184pfctl_debug(int dev, u_int32_t level, int opts) 1185{ 1186 if (ioctl(dev, DIOCSETDEBUG, &level)) 1187 err(1, "DIOCSETDEBUG"); 1188 if ((opts & PF_OPT_QUIET) == 0) { 1189 fprintf(stderr, "debug level set to '"); 1190 switch (level) { 1191 case PF_DEBUG_NONE: 1192 fprintf(stderr, "none"); 1193 break; 1194 case PF_DEBUG_URGENT: 1195 fprintf(stderr, "urgent"); 1196 break; 1197 case PF_DEBUG_MISC: 1198 fprintf(stderr, "misc"); 1199 break; 1200 case PF_DEBUG_NOISY: 1201 fprintf(stderr, "loud"); 1202 break; 1203 default: 1204 fprintf(stderr, "<invalid>"); 1205 break; 1206 } 1207 fprintf(stderr, "'\n"); 1208 } 1209 return (0); 1210} 1211 1212int 1213pfctl_clear_rule_counters(int dev, int opts) 1214{ 1215 if (ioctl(dev, DIOCCLRRULECTRS)) 1216 err(1, "DIOCCLRRULECTRS"); 1217 if ((opts & PF_OPT_QUIET) == 0) 1218 fprintf(stderr, "pf: rule counters cleared\n"); 1219 return (0); 1220} 1221 1222int 1223pfctl_test_altqsupport(int dev, int opts) 1224{ 1225 struct pfioc_altq pa; 1226 1227 if (ioctl(dev, DIOCGETALTQS, &pa)) { 1228 if (errno == ENODEV) { 1229 if (!(opts & PF_OPT_QUIET)) 1230 fprintf(stderr, "No ALTQ support in kernel\n" 1231 "ALTQ related functions disabled\n"); 1232 return (0); 1233 } else 1234 err(1, "DIOCGETALTQS"); 1235 } 1236 return (1); 1237} 1238 1239int 1240pfctl_show_anchors(int dev, int opts, char *anchorname) 1241{ 1242 u_int32_t nr, mnr; 1243 1244 if (!*anchorname) { 1245 struct pfioc_anchor pa; 1246 1247 memset(&pa, 0, sizeof(pa)); 1248 if (ioctl(dev, DIOCGETANCHORS, &pa)) { 1249 warn("DIOCGETANCHORS"); 1250 return (-1); 1251 } 1252 mnr = pa.nr; 1253 if (!(opts & PF_OPT_QUIET)) 1254 printf("%u anchors:\n", mnr); 1255 for (nr = 0; nr < mnr; ++nr) { 1256 pa.nr = nr; 1257 if (ioctl(dev, DIOCGETANCHOR, &pa)) { 1258 warn("DIOCGETANCHOR"); 1259 return (-1); 1260 } 1261 printf(" %s\n", pa.name); 1262 } 1263 } else { 1264 struct pfioc_ruleset pr; 1265 1266 memset(&pr, 0, sizeof(pr)); 1267 memcpy(pr.anchor, anchorname, sizeof(pr.anchor)); 1268 if (ioctl(dev, DIOCGETRULESETS, &pr)) { 1269 if (errno == EINVAL) 1270 fprintf(stderr, "No rulesets in anchor '%s'.\n", 1271 anchorname); 1272 else 1273 err(1, "DIOCGETRULESETS"); 1274 return (-1); 1275 } 1276 mnr = pr.nr; 1277 if (!(opts & PF_OPT_QUIET)) 1278 printf("%u rulesets in anchor %s:\n", mnr, anchorname); 1279 for (nr = 0; nr < mnr; ++nr) { 1280 pr.nr = nr; 1281 if (ioctl(dev, DIOCGETRULESET, &pr)) 1282 err(1, "DIOCGETRULESET"); 1283 printf(" %s:%s\n", pr.anchor, pr.name); 1284 } 1285 } 1286 return (0); 1287} 1288 1289const char * 1290pfctl_lookup_option(char *cmd, const char **list) 1291{ 1292 if (cmd != NULL && *cmd) 1293 for (; *list; list++) 1294 if (!strncmp(cmd, *list, strlen(cmd))) 1295 return (*list); 1296 return (NULL); 1297} 1298 1299int 1300main(int argc, char *argv[]) 1301{ 1302 int error = 0; 1303 int ch; 1304 int mode = O_RDONLY; 1305 int opts = 0; 1306 char anchorname[PF_ANCHOR_NAME_SIZE]; 1307 char rulesetname[PF_RULESET_NAME_SIZE]; 1308 1309 if (argc < 2) 1310 usage(); 1311 1312 while ((ch = getopt(argc, argv, "a:AdD:eqf:F:ghk:nNOrRs:t:T:vx:z")) != 1313 -1) { 1314 switch (ch) { 1315 case 'a': 1316 anchoropt = optarg; 1317 break; 1318 case 'd': 1319 opts |= PF_OPT_DISABLE; 1320 mode = O_RDWR; 1321 break; 1322 case 'D': 1323 if (pfctl_cmdline_symset(optarg) < 0) 1324 warnx("could not parse macro definition %s", 1325 optarg); 1326 break; 1327 case 'e': 1328 opts |= PF_OPT_ENABLE; 1329 mode = O_RDWR; 1330 break; 1331 case 'q': 1332 opts |= PF_OPT_QUIET; 1333 break; 1334 case 'F': 1335 clearopt = pfctl_lookup_option(optarg, clearopt_list); 1336 if (clearopt == NULL) { 1337 warnx("Unknown flush modifier '%s'", optarg); 1338 usage(); 1339 } 1340 mode = O_RDWR; 1341 break; 1342 case 'k': 1343 if (state_killers >= 2) { 1344 warnx("can only specify -k twice"); 1345 usage(); 1346 /* NOTREACHED */ 1347 } 1348 state_kill[state_killers++] = optarg; 1349 mode = O_RDWR; 1350 break; 1351 case 'n': 1352 opts |= PF_OPT_NOACTION; 1353 break; 1354 case 'N': 1355 loadopt |= PFCTL_FLAG_NAT; 1356 break; 1357 case 'r': 1358 opts |= PF_OPT_USEDNS; 1359 break; 1360 case 'f': 1361 rulesopt = optarg; 1362 mode = O_RDWR; 1363 break; 1364 case 'g': 1365 opts |= PF_OPT_DEBUG; 1366 break; 1367 case 'A': 1368 loadopt |= PFCTL_FLAG_ALTQ; 1369 break; 1370 case 'R': 1371 loadopt |= PFCTL_FLAG_FILTER; 1372 break; 1373 case 'O': 1374 loadopt |= PFCTL_FLAG_OPTION; 1375 break; 1376 case 's': 1377 showopt = pfctl_lookup_option(optarg, showopt_list); 1378 if (showopt == NULL) { 1379 warnx("Unknown show modifier '%s'", optarg); 1380 usage(); 1381 } 1382 break; 1383 case 't': 1384 tableopt = optarg; 1385 break; 1386 case 'T': 1387 tblcmdopt = pfctl_lookup_option(optarg, tblcmdopt_list); 1388 if (tblcmdopt == NULL) { 1389 warnx("Unknown table command '%s'", optarg); 1390 usage(); 1391 } 1392 break; 1393 case 'v': 1394 if (opts & PF_OPT_VERBOSE) 1395 opts |= PF_OPT_VERBOSE2; 1396 opts |= PF_OPT_VERBOSE; 1397 break; 1398 case 'x': 1399 debugopt = pfctl_lookup_option(optarg, debugopt_list); 1400 if (debugopt == NULL) { 1401 warnx("Unknown debug level '%s'", optarg); 1402 usage(); 1403 } 1404 mode = O_RDWR; 1405 break; 1406 case 'z': 1407 opts |= PF_OPT_CLRRULECTRS; 1408 mode = O_RDWR; 1409 break; 1410 case 'h': 1411 /* FALLTHROUGH */ 1412 default: 1413 usage(); 1414 /* NOTREACHED */ 1415 } 1416 } 1417 1418 if (tblcmdopt != NULL) { 1419 argc -= optind; 1420 argv += optind; 1421 ch = *tblcmdopt; 1422 if (ch == 'l') { 1423 loadopt |= PFCTL_FLAG_TABLE; 1424 tblcmdopt = NULL; 1425 } else { 1426 mode = strchr("acdfkrz", ch) ? O_RDWR : O_RDONLY; 1427 if (opts & PF_OPT_NOACTION) { 1428 dev = open("/dev/pf", mode); 1429 if (dev >= 0) 1430 opts |= PF_OPT_DUMMYACTION; 1431 } 1432 } 1433 } else if (argc != optind) { 1434 warnx("unknown command line argument: %s ...", argv[optind]); 1435 usage(); 1436 /* NOTREACHED */ 1437 } 1438 if (loadopt == 0) 1439 loadopt = ~0; 1440 1441 memset(anchorname, 0, sizeof(anchorname)); 1442 memset(rulesetname, 0, sizeof(rulesetname)); 1443 if (anchoropt != NULL) { 1444 char *t; 1445 1446 if ((t = strchr(anchoropt, ':')) == NULL) { 1447 if (strlcpy(anchorname, anchoropt, 1448 sizeof(anchorname)) >= sizeof(anchorname)) 1449 errx(1, "anchor name '%s' too long", 1450 anchoropt); 1451 } else { 1452 char *p; 1453 1454 if ((p = strdup(anchoropt)) == NULL) 1455 err(1, "anchoropt: strdup"); 1456 t = strsep(&p, ":"); 1457 if (*t == '\0' || *p == '\0') 1458 errx(1, "anchor '%s' invalid", anchoropt); 1459 if (strlcpy(anchorname, t, sizeof(anchorname)) >= 1460 sizeof(anchorname)) 1461 errx(1, "anchor name '%s' too long", t); 1462 if (strlcpy(rulesetname, p, sizeof(rulesetname)) >= 1463 sizeof(rulesetname)) 1464 errx(1, "ruleset name '%s' too long", p); 1465 free(t); /* not p */ 1466 } 1467 loadopt &= PFCTL_FLAG_FILTER|PFCTL_FLAG_NAT|PFCTL_FLAG_TABLE; 1468 } 1469 1470 if ((opts & PF_OPT_NOACTION) == 0) { 1471 dev = open("/dev/pf", mode); 1472 if (dev == -1) 1473 err(1, "/dev/pf"); 1474 altqsupport = pfctl_test_altqsupport(dev, opts); 1475 } else { 1476 /* turn off options */ 1477 opts &= ~ (PF_OPT_DISABLE | PF_OPT_ENABLE); 1478 clearopt = showopt = debugopt = NULL; 1479 altqsupport = 1; 1480 } 1481 1482 if (opts & PF_OPT_DISABLE) 1483 if (pfctl_disable(dev, opts)) 1484 error = 1; 1485 1486 if (showopt != NULL) { 1487 switch (*showopt) { 1488 case 'A': 1489 pfctl_show_anchors(dev, opts, anchorname); 1490 break; 1491 case 'r': 1492 pfctl_load_fingerprints(dev, opts); 1493 pfctl_show_rules(dev, opts, 0, anchorname, 1494 rulesetname); 1495 break; 1496 case 'l': 1497 pfctl_load_fingerprints(dev, opts); 1498 pfctl_show_rules(dev, opts, 1, anchorname, 1499 rulesetname); 1500 break; 1501 case 'n': 1502 pfctl_load_fingerprints(dev, opts); 1503 pfctl_show_nat(dev, opts, anchorname, rulesetname); 1504 break; 1505 case 'q': 1506 pfctl_show_altq(dev, opts, opts & PF_OPT_VERBOSE2); 1507 break; 1508 case 's': 1509 pfctl_show_states(dev, 0, opts); 1510 break; 1511 case 'i': 1512 pfctl_show_status(dev); 1513 break; 1514 case 't': 1515 pfctl_show_timeouts(dev); 1516 break; 1517 case 'm': 1518 pfctl_show_limits(dev); 1519 break; 1520 case 'a': 1521 pfctl_load_fingerprints(dev, opts); 1522 1523 pfctl_show_rules(dev, opts, 0, anchorname, 1524 rulesetname); 1525 pfctl_show_nat(dev, opts, anchorname, rulesetname); 1526 pfctl_show_altq(dev, opts, 0); 1527 pfctl_show_states(dev, 0, opts); 1528 pfctl_show_status(dev); 1529 pfctl_show_rules(dev, opts, 1, anchorname, rulesetname); 1530 pfctl_show_timeouts(dev); 1531 pfctl_show_limits(dev); 1532 pfctl_show_tables(anchorname, rulesetname, opts); 1533 pfctl_show_fingerprints(opts); 1534 break; 1535 case 'T': 1536 pfctl_show_tables(anchorname, rulesetname, opts); 1537 break; 1538 case 'o': 1539 pfctl_load_fingerprints(dev, opts); 1540 pfctl_show_fingerprints(opts); 1541 break; 1542 } 1543 } 1544 1545 if (clearopt != NULL) { 1546 switch (*clearopt) { 1547 case 'r': 1548 pfctl_clear_rules(dev, opts, anchorname, rulesetname); 1549 break; 1550 case 'n': 1551 pfctl_clear_nat(dev, opts, anchorname, rulesetname); 1552 break; 1553 case 'q': 1554 pfctl_clear_altq(dev, opts); 1555 break; 1556 case 's': 1557 pfctl_clear_states(dev, opts); 1558 break; 1559 case 'i': 1560 pfctl_clear_stats(dev, opts); 1561 break; 1562 case 'a': 1563 pfctl_clear_rules(dev, opts, anchorname, rulesetname); 1564 pfctl_clear_nat(dev, opts, anchorname, rulesetname); 1565 pfctl_clear_altq(dev, opts); 1566 pfctl_clear_states(dev, opts); 1567 pfctl_clear_stats(dev, opts); 1568 pfctl_clear_tables(anchorname, rulesetname, opts); 1569 pfctl_clear_fingerprints(dev, opts); 1570 break; 1571 case 'o': 1572 pfctl_clear_fingerprints(dev, opts); 1573 break; 1574 case 'T': 1575 pfctl_clear_tables(anchorname, rulesetname, opts); 1576 break; 1577 } 1578 } 1579 if (state_killers) 1580 pfctl_kill_states(dev, opts); 1581 1582 if (tblcmdopt != NULL) { 1583 error = pfctl_command_tables(argc, argv, tableopt, 1584 tblcmdopt, rulesopt, anchorname, rulesetname, opts); 1585 rulesopt = NULL; 1586 } 1587 1588 if (rulesopt != NULL) 1589 if (pfctl_file_fingerprints(dev, opts, PF_OSFP_FILE)) 1590 error = 1; 1591 1592 if (rulesopt != NULL) { 1593 if (pfctl_rules(dev, rulesopt, opts, anchorname, rulesetname)) 1594 error = 1; 1595 else if (!(opts & PF_OPT_NOACTION) && 1596 (loadopt & PFCTL_FLAG_TABLE)) 1597 warn_namespace_collision(NULL); 1598 } 1599 1600 if (opts & PF_OPT_ENABLE) 1601 if (pfctl_enable(dev, opts)) 1602 error = 1; 1603 1604 if (debugopt != NULL) { 1605 switch (*debugopt) { 1606 case 'n': 1607 pfctl_debug(dev, PF_DEBUG_NONE, opts); 1608 break; 1609 case 'u': 1610 pfctl_debug(dev, PF_DEBUG_URGENT, opts); 1611 break; 1612 case 'm': 1613 pfctl_debug(dev, PF_DEBUG_MISC, opts); 1614 break; 1615 case 'l': 1616 pfctl_debug(dev, PF_DEBUG_NOISY, opts); 1617 break; 1618 } 1619 } 1620 1621 if (opts & PF_OPT_CLRRULECTRS) { 1622 if (pfctl_clear_rule_counters(dev, opts)) 1623 error = 1; 1624 } 1625 exit(error); 1626} 1627