1 /* $OpenBSD: pfctl.c,v 1.32 2001/08/11 12:05:00 dhartmei 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 #include <net/if.h> 37 #include <netinet/in.h> 38 #include <net/pfvar.h> 39 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <unistd.h> 44 #include <fcntl.h> 45 #include <errno.h> 46 #include <err.h> 47 48 #include "pfctl_parser.h" 49 50 #define PF_OPT_DISABLE 0x0001 51 #define PF_OPT_ENABLE 0x0002 52 #define PF_OPT_VERBOSE 0x0004 53 #define PF_OPT_NOACTION 0x0008 54 #define PF_OPT_QUIET 0x0010 55 56 void usage(void); 57 int pfctl_enable(int, int); 58 int pfctl_disable(int, int); 59 int pfctl_clear_stats(int, int); 60 int pfctl_clear_rules(int, int); 61 int pfctl_clear_nat(int, int); 62 int pfctl_clear_states(int, int); 63 int pfctl_show_rules(int); 64 int pfctl_show_nat(int); 65 int pfctl_show_states(int, u_int8_t); 66 int pfctl_show_status(int); 67 int pfctl_rules(int, char *, int); 68 int pfctl_nat(int, char *, int); 69 int pfctl_log(int, char *, int); 70 int pfctl_debug(int, u_int32_t, int); 71 72 int opts = 0; 73 char *clearopt; 74 char *logopt; 75 char *natopt; 76 char *rulesopt; 77 char *showopt; 78 char *debugopt; 79 80 char *infile; 81 82 void 83 usage() 84 { 85 extern char *__progname; 86 87 fprintf(stderr, "usage: %s [-dehnqv] [-F set] [-l interface] ", 88 __progname); 89 fprintf(stderr, "[-N file] [-R file] [-s set] [-x level]\n"); 90 exit(1); 91 } 92 93 int 94 pfctl_enable(int dev, int opts) 95 { 96 if (ioctl(dev, DIOCSTART)) { 97 if (errno == EEXIST) 98 errx(1, "pf already enabled"); 99 else 100 err(1, "DIOCSTART"); 101 } 102 if ((opts & PF_OPT_QUIET) == 0) 103 printf("pf enabled\n"); 104 return (0); 105 } 106 107 int 108 pfctl_disable(int dev, int opts) 109 { 110 if (ioctl(dev, DIOCSTOP)) { 111 if (errno == ENOENT) 112 errx(1, "pf not enabled"); 113 else 114 err(1, "DIOCSTOP"); 115 } 116 if ((opts & PF_OPT_QUIET) == 0) 117 printf("pf disabled\n"); 118 return (0); 119 } 120 121 int 122 pfctl_clear_stats(int dev, int opts) 123 { 124 if (ioctl(dev, DIOCCLRSTATUS)) 125 err(1, "DIOCCLRSTATUS"); 126 if ((opts & PF_OPT_QUIET) == 0) 127 printf("pf: statistics cleared\n"); 128 return (0); 129 } 130 131 int 132 pfctl_clear_rules(int dev, int opts) 133 { 134 struct pfioc_rule pr; 135 136 if (ioctl(dev, DIOCBEGINRULES, &pr.ticket)) 137 err(1, "DIOCBEGINRULES"); 138 else if (ioctl(dev, DIOCCOMMITRULES, &pr.ticket)) 139 err(1, "DIOCCOMMITRULES"); 140 if ((opts & PF_OPT_QUIET) == 0) 141 printf("rules cleared\n"); 142 return (0); 143 } 144 145 int 146 pfctl_clear_nat(int dev, int opts) 147 { 148 struct pfioc_nat pn; 149 struct pfioc_rdr pr; 150 151 if (ioctl(dev, DIOCBEGINNATS, &pn.ticket)) 152 err(1, "DIOCBEGINNATS"); 153 else if (ioctl(dev, DIOCCOMMITNATS, &pn.ticket)) 154 err(1, "DIOCCOMMITNATS"); 155 else if (ioctl(dev, DIOCBEGINRDRS, &pr.ticket)) 156 err(1, "DIOCBEGINRDRS"); 157 else if (ioctl(dev, DIOCCOMMITRDRS, &pr.ticket)) 158 err(1, "DIOCCOMMITRDRS"); 159 if ((opts & PF_OPT_QUIET) == 0) 160 printf("nat cleared\n"); 161 return (0); 162 } 163 164 int 165 pfctl_clear_states(int dev, int opts) 166 { 167 if (ioctl(dev, DIOCCLRSTATES)) 168 err(1, "DIOCCLRSTATES"); 169 if ((opts & PF_OPT_QUIET) == 0) 170 printf("states cleared\n"); 171 return (0); 172 } 173 174 int 175 pfctl_show_rules(int dev) 176 { 177 struct pfioc_rule pr; 178 u_int32_t nr, mnr; 179 180 if (ioctl(dev, DIOCGETRULES, &pr)) 181 err(1, "DIOCGETRULES"); 182 mnr = pr.nr; 183 for (nr = 0; nr < mnr; ++nr) { 184 pr.nr = nr; 185 if (ioctl(dev, DIOCGETRULE, &pr)) 186 err(1, "DIOCGETRULE"); 187 print_rule(&pr.rule); 188 } 189 return (0); 190 } 191 192 int 193 pfctl_show_nat(int dev) 194 { 195 struct pfioc_nat pn; 196 struct pfioc_rdr pr; 197 u_int32_t mnr, nr; 198 199 if (ioctl(dev, DIOCGETNATS, &pn)) 200 err(1, "DIOCGETNATS"); 201 mnr = pn.nr; 202 for (nr = 0; nr < mnr; ++nr) { 203 pn.nr = nr; 204 if (ioctl(dev, DIOCGETNAT, &pn)) 205 err(1, "DIOCGETNAT"); 206 print_nat(&pn.nat); 207 } 208 if (ioctl(dev, DIOCGETRDRS, &pr)) 209 err(1, "DIOCGETRDRS"); 210 mnr = pr.nr; 211 for (nr = 0; nr < mnr; ++nr) { 212 pr.nr = nr; 213 if (ioctl(dev, DIOCGETRDR, &pr)) 214 err(1, "DIOCGETRDR"); 215 print_rdr(&pr.rdr); 216 } 217 return (0); 218 } 219 220 int 221 pfctl_show_states(int dev, u_int8_t proto) 222 { 223 struct pfioc_state ps; 224 225 ps.nr = 0; 226 while (!ioctl(dev, DIOCGETSTATE, &ps)) { 227 if (!proto || (ps.state.proto == proto)) 228 print_state(&ps.state); 229 ps.nr++; 230 } 231 return (0); 232 } 233 234 int 235 pfctl_show_status(int dev) 236 { 237 struct pf_status status; 238 239 if (ioctl(dev, DIOCGETSTATUS, &status)) 240 err(1, "DIOCGETSTATUS"); 241 print_status(&status); 242 return (0); 243 } 244 245 /* callbacks for rule/nat/rdr */ 246 247 int 248 pfctl_add_rule(struct pfctl *pf, struct pf_rule *r) 249 { 250 memcpy(&pf->prule->rule, r, sizeof(pf->prule->rule)); 251 if ((pf->opts & PF_OPT_NOACTION) == 0) { 252 if (ioctl(pf->dev, DIOCADDRULE, pf->prule)) 253 err(1, "DIOCADDRULE"); 254 } 255 if (pf->opts & PF_OPT_VERBOSE) 256 print_rule(&pf->prule->rule); 257 return 0; 258 } 259 260 int 261 pfctl_add_nat(struct pfctl *pf, struct pf_nat *n) 262 { 263 memcpy(&pf->pnat->nat, n, sizeof(pf->pnat->nat)); 264 if ((pf->opts & PF_OPT_NOACTION) == 0) { 265 if (ioctl(pf->dev, DIOCADDNAT, pf->pnat)) 266 err(1, "DIOCADDNAT"); 267 } 268 if (pf->opts & PF_OPT_VERBOSE) 269 print_nat(&pf->pnat->nat); 270 return 0; 271 } 272 273 int 274 pfctl_add_rdr(struct pfctl *pf, struct pf_rdr *r) 275 { 276 memcpy(&pf->prdr->rdr, r, sizeof(pf->prdr->rdr)); 277 if ((pf->opts & PF_OPT_NOACTION) == 0) { 278 if (ioctl(pf->dev, DIOCADDRDR, pf->prdr)) 279 err(1, "DIOCADDRDR"); 280 } 281 if (pf->opts & PF_OPT_VERBOSE) 282 print_rdr(&pf->prdr->rdr); 283 return 0; 284 } 285 286 int 287 pfctl_rules(int dev, char *filename, int opts) 288 { 289 FILE *fin; 290 struct pfioc_rule pr; 291 struct pfctl pf; 292 293 if (strcmp(filename, "-") == 0) { 294 infile = "stdin"; 295 fin = stdin; 296 } else { 297 fin = fopen(filename, "r"); 298 infile = filename; 299 } 300 if (fin == NULL) 301 return (1); 302 if ((opts & PF_OPT_NOACTION) == 0) { 303 if (ioctl(dev, DIOCBEGINRULES, &pr.ticket)) 304 err(1, "DIOCBEGINRULES"); 305 } 306 /* fill in callback data */ 307 pf.dev = dev; 308 pf.opts = opts; 309 pf.prule = ≺ 310 if (parse_rules(fin, &pf) < 0) 311 errx(1, "syntax error in rule file: pf rules not loaded"); 312 if ((opts & PF_OPT_NOACTION) == 0) { 313 if (ioctl(dev, DIOCCOMMITRULES, &pr.ticket)) 314 err(1, "DIOCCOMMITRULES"); 315 #if 0 316 if ((opts & PF_OPT_QUIET) == 0) 317 printf("%u rules loaded\n", n); 318 #endif 319 } 320 if (fin != stdin) 321 fclose(fin); 322 return (0); 323 } 324 325 int 326 pfctl_nat(int dev, char *filename, int opts) 327 { 328 FILE *fin; 329 struct pfioc_nat pn; 330 struct pfioc_rdr pr; 331 struct pfctl pf; 332 333 if (strcmp(filename, "-") == 0) { 334 fin = stdin; 335 infile = "stdin"; 336 } else { 337 fin = fopen(filename, "r"); 338 infile = filename; 339 } 340 if (fin == NULL) 341 return (1); 342 343 if ((opts & PF_OPT_NOACTION) == 0) { 344 if (ioctl(dev, DIOCBEGINNATS, &pn.ticket)) 345 err(1, "DIOCBEGINNATS"); 346 347 if (ioctl(dev, DIOCBEGINRDRS, &pr.ticket)) 348 err(1, "DIOCBEGINRDRS"); 349 } 350 /* fill in callback data */ 351 pf.dev = dev; 352 pf.opts = opts; 353 pf.pnat = &pn; 354 pf.prdr = ≺ 355 if (parse_nat(fin, &pf) < 0) 356 errx(1, "syntax error in file: nat rules not loaded"); 357 if ((opts & PF_OPT_NOACTION) == 0) { 358 if (ioctl(dev, DIOCCOMMITNATS, &pn.ticket)) 359 err(1, "DIOCCOMMITNATS"); 360 if (ioctl(dev, DIOCCOMMITRDRS, &pr.ticket)) 361 err(1, "DIOCCOMMITRDRS"); 362 #if 0 363 if ((opts & PF_OPT_QUIET) == 0) { 364 printf("%u nat entries loaded\n", n); 365 printf("%u rdr entries loaded\n", r); 366 } 367 #endif 368 } 369 if (fin != stdin) 370 fclose(fin); 371 return (0); 372 } 373 374 int 375 pfctl_log(int dev, char *ifname, int opts) 376 { 377 struct pfioc_if pi; 378 379 strncpy(pi.ifname, ifname, 16); 380 if (ioctl(dev, DIOCSETSTATUSIF, &pi)) 381 err(1, "DIOCSETSTATUSIF"); 382 if ((opts & PF_OPT_QUIET) == 0) 383 printf("now logging %s\n", pi.ifname); 384 return (0); 385 } 386 387 int 388 pfctl_debug(int dev, u_int32_t level, int opts) 389 { 390 if (ioctl(dev, DIOCSETDEBUG, &level)) 391 err(1, "DIOCSETDEBUG"); 392 if ((opts & PF_OPT_QUIET) == 0) { 393 printf("debug level set to '"); 394 switch (level) { 395 case PF_DEBUG_NONE: 396 printf("none"); 397 break; 398 case PF_DEBUG_URGENT: 399 printf("urgent"); 400 break; 401 case PF_DEBUG_MISC: 402 printf("misc"); 403 break; 404 default: 405 printf("<invalid>"); 406 break; 407 } 408 printf("'\n"); 409 } 410 return (0); 411 } 412 413 int 414 main(int argc, char *argv[]) 415 { 416 extern char *optarg; 417 extern int optind; 418 int error = 0; 419 int dev = -1; 420 int ch; 421 422 if (argc < 2) 423 usage(); 424 425 while ((ch = getopt(argc, argv, "deqF:hl:nN:R:s:vx:")) != -1) { 426 switch (ch) { 427 case 'd': 428 opts |= PF_OPT_DISABLE; 429 break; 430 case 'e': 431 opts |= PF_OPT_ENABLE; 432 break; 433 case 'q': 434 opts |= PF_OPT_QUIET; 435 break; 436 case 'F': 437 clearopt = optarg; 438 break; 439 case 'l': 440 logopt = optarg; 441 break; 442 case 'n': 443 opts |= PF_OPT_NOACTION; 444 break; 445 case 'N': 446 natopt = optarg; 447 break; 448 case 'R': 449 rulesopt = optarg; 450 break; 451 case 's': 452 showopt = optarg; 453 break; 454 case 'v': 455 opts |= PF_OPT_VERBOSE; 456 break; 457 case 'x': 458 debugopt = optarg; 459 break; 460 case 'h': 461 default: 462 usage(); 463 /* NOTREACHED */ 464 } 465 } 466 467 if (argc != optind) { 468 warnx("unknown command line argument: %s ...", argv[optind]); 469 usage(); 470 /* NOTREACHED */ 471 } 472 473 if ((opts & PF_OPT_NOACTION) == 0) { 474 dev = open("/dev/pf", O_RDWR); 475 if (dev == -1) 476 err(1, "open(\"/dev/pf\")"); 477 } else { 478 /* turn off options */ 479 opts &= ~ (PF_OPT_DISABLE | PF_OPT_ENABLE); 480 clearopt = logopt = showopt = debugopt = NULL; 481 } 482 483 if (opts & PF_OPT_DISABLE) 484 if (pfctl_disable(dev, opts)) 485 error = 1; 486 487 if (clearopt != NULL) { 488 489 switch (*clearopt) { 490 case 'r': 491 pfctl_clear_rules(dev, opts); 492 break; 493 case 'n': 494 pfctl_clear_nat(dev, opts); 495 break; 496 case 's': 497 pfctl_clear_states(dev, opts); 498 break; 499 case 'i': 500 pfctl_clear_stats(dev, opts); 501 break; 502 case 'a': 503 pfctl_clear_rules(dev, opts); 504 pfctl_clear_nat(dev, opts); 505 pfctl_clear_states(dev, opts); 506 pfctl_clear_stats(dev, opts); 507 break; 508 default: 509 warnx("Unknown flush modifier '%s'", clearopt); 510 error = 1; 511 } 512 } 513 514 if (rulesopt != NULL) 515 if (pfctl_rules(dev, rulesopt, opts)) 516 error = 1; 517 518 if (natopt != NULL) 519 if (pfctl_nat(dev, natopt, opts)) 520 error = 1; 521 522 if (showopt != NULL) { 523 switch (*showopt) { 524 case 'r': 525 pfctl_show_rules(dev); 526 break; 527 case 'n': 528 pfctl_show_nat(dev); 529 break; 530 case 's': 531 pfctl_show_states(dev, 0); 532 break; 533 case 'i': 534 pfctl_show_status(dev); 535 break; 536 case 'a': 537 pfctl_show_rules(dev); 538 pfctl_show_nat(dev); 539 pfctl_show_states(dev, 0); 540 pfctl_show_status(dev); 541 break; 542 default: 543 warnx("Unknown show modifier '%s'", showopt); 544 error = 1; 545 } 546 } 547 548 if (logopt != NULL) 549 if (pfctl_log(dev, logopt, opts)) 550 error = 1; 551 552 if (opts & PF_OPT_ENABLE) 553 if (pfctl_enable(dev, opts)) 554 error = 1; 555 556 if (debugopt != NULL) { 557 switch (*debugopt) { 558 case 'n': 559 pfctl_debug(dev, PF_DEBUG_NONE, opts); 560 break; 561 case 'u': 562 pfctl_debug(dev, PF_DEBUG_URGENT, opts); 563 break; 564 case 'm': 565 pfctl_debug(dev, PF_DEBUG_MISC, opts); 566 break; 567 default: 568 warnx("Unknown debug level '%s'", debugopt); 569 error = 1; 570 } 571 } 572 573 close(dev); 574 575 exit(error); 576 } 577