ipfs.c revision 145510
1/* $NetBSD$ */ 2 3/* 4 * Copyright (C) 1999-2001, 2003 by Darren Reed. 5 * 6 * See the IPFILTER.LICENCE file for details on licencing. 7 */ 8#ifdef __FreeBSD__ 9# ifndef __FreeBSD_cc_version 10# include <osreldate.h> 11# else 12# if __FreeBSD_cc_version < 430000 13# include <osreldate.h> 14# endif 15# endif 16#endif 17#include <stdio.h> 18#include <unistd.h> 19#include <string.h> 20#include <fcntl.h> 21#include <errno.h> 22#if !defined(__SVR4) && !defined(__GNUC__) 23#include <strings.h> 24#endif 25#include <sys/types.h> 26#include <sys/param.h> 27#include <sys/file.h> 28#include <stdlib.h> 29#include <stddef.h> 30#include <sys/socket.h> 31#include <sys/ioctl.h> 32#include <netinet/in.h> 33#include <netinet/in_systm.h> 34#include <sys/time.h> 35#include <net/if.h> 36#if __FreeBSD_version >= 300000 37# include <net/if_var.h> 38#endif 39#include <netinet/ip.h> 40#include <netdb.h> 41#include <arpa/nameser.h> 42#include <resolv.h> 43#include "ipf.h" 44#include "ipl.h" 45 46#if !defined(lint) 47static const char rcsid[] = "@(#)Id: ipfs.c,v 1.12 2003/12/01 01:56:53 darrenr Exp"; 48#endif 49 50#ifndef IPF_SAVEDIR 51# define IPF_SAVEDIR "/var/db/ipf" 52#endif 53#ifndef IPF_NATFILE 54# define IPF_NATFILE "ipnat.ipf" 55#endif 56#ifndef IPF_STATEFILE 57# define IPF_STATEFILE "ipstate.ipf" 58#endif 59 60#if !defined(__SVR4) && defined(__GNUC__) 61extern char *index __P((const char *, int)); 62#endif 63 64extern char *optarg; 65extern int optind; 66 67int main __P((int, char *[])); 68void usage __P((void)); 69int changestateif __P((char *, char *)); 70int changenatif __P((char *, char *)); 71int readstate __P((int, char *)); 72int readnat __P((int, char *)); 73int writestate __P((int, char *)); 74int opendevice __P((char *)); 75void closedevice __P((int)); 76int setlock __P((int, int)); 77int writeall __P((char *)); 78int readall __P((char *)); 79int writenat __P((int, char *)); 80 81int opts = 0; 82char *progname; 83 84 85void usage() 86{ 87 fprintf(stderr, "usage: %s [-nv] -l\n", progname); 88 fprintf(stderr, "usage: %s [-nv] -u\n", progname); 89 fprintf(stderr, "usage: %s [-nv] [-d <dir>] -R\n", progname); 90 fprintf(stderr, "usage: %s [-nv] [-d <dir>] -W\n", progname); 91 fprintf(stderr, "usage: %s [-nNSv] [-f <file>] -r\n", progname); 92 fprintf(stderr, "usage: %s [-nNSv] [-f <file>] -w\n", progname); 93 fprintf(stderr, "usage: %s [-nNSv] -f <filename> -i <if1>,<if2>\n", 94 progname); 95 exit(1); 96} 97 98 99/* 100 * Change interface names in state information saved out to disk. 101 */ 102int changestateif(ifs, fname) 103char *ifs, *fname; 104{ 105 int fd, olen, nlen, rw; 106 ipstate_save_t ips; 107 off_t pos; 108 char *s; 109 110 s = strchr(ifs, ','); 111 if (!s) 112 usage(); 113 *s++ = '\0'; 114 nlen = strlen(s); 115 olen = strlen(ifs); 116 if (nlen >= sizeof(ips.ips_is.is_ifname) || 117 olen >= sizeof(ips.ips_is.is_ifname)) 118 usage(); 119 120 fd = open(fname, O_RDWR); 121 if (fd == -1) { 122 perror("open"); 123 exit(1); 124 } 125 126 for (pos = 0; read(fd, &ips, sizeof(ips)) == sizeof(ips); ) { 127 rw = 0; 128 if (!strncmp(ips.ips_is.is_ifname[0], ifs, olen + 1)) { 129 strcpy(ips.ips_is.is_ifname[0], s); 130 rw = 1; 131 } 132 if (!strncmp(ips.ips_is.is_ifname[1], ifs, olen + 1)) { 133 strcpy(ips.ips_is.is_ifname[1], s); 134 rw = 1; 135 } 136 if (rw == 1) { 137 if (lseek(fd, pos, SEEK_SET) != pos) { 138 perror("lseek"); 139 exit(1); 140 } 141 if (write(fd, &ips, sizeof(ips)) != sizeof(ips)) { 142 perror("write"); 143 exit(1); 144 } 145 } 146 pos = lseek(fd, 0, SEEK_CUR); 147 } 148 close(fd); 149 150 return 0; 151} 152 153 154/* 155 * Change interface names in NAT information saved out to disk. 156 */ 157int changenatif(ifs, fname) 158char *ifs, *fname; 159{ 160 int fd, olen, nlen, rw; 161 nat_save_t ipn; 162 nat_t *nat; 163 off_t pos; 164 char *s; 165 166 s = strchr(ifs, ','); 167 if (!s) 168 usage(); 169 *s++ = '\0'; 170 nlen = strlen(s); 171 olen = strlen(ifs); 172 nat = &ipn.ipn_nat; 173 if (nlen >= sizeof(nat->nat_ifnames[0]) || 174 olen >= sizeof(nat->nat_ifnames[0])) 175 usage(); 176 177 fd = open(fname, O_RDWR); 178 if (fd == -1) { 179 perror("open"); 180 exit(1); 181 } 182 183 for (pos = 0; read(fd, &ipn, sizeof(ipn)) == sizeof(ipn); ) { 184 rw = 0; 185 if (!strncmp(nat->nat_ifnames[0], ifs, olen + 1)) { 186 strcpy(nat->nat_ifnames[0], s); 187 rw = 1; 188 } 189 if (!strncmp(nat->nat_ifnames[1], ifs, olen + 1)) { 190 strcpy(nat->nat_ifnames[1], s); 191 rw = 1; 192 } 193 if (rw == 1) { 194 if (lseek(fd, pos, SEEK_SET) != pos) { 195 perror("lseek"); 196 exit(1); 197 } 198 if (write(fd, &ipn, sizeof(ipn)) != sizeof(ipn)) { 199 perror("write"); 200 exit(1); 201 } 202 } 203 pos = lseek(fd, 0, SEEK_CUR); 204 } 205 close(fd); 206 207 return 0; 208} 209 210 211int main(argc,argv) 212int argc; 213char *argv[]; 214{ 215 int c, lock = -1, devfd = -1, err = 0, rw = -1, ns = -1, set = 0; 216 char *dirname = NULL, *filename = NULL, *ifs = NULL; 217 218 progname = argv[0]; 219 while ((c = getopt(argc, argv, "d:f:lNnSRruvWw")) != -1) 220 switch (c) 221 { 222 case 'd' : 223 if ((set == 0) && !dirname && !filename) 224 dirname = optarg; 225 else 226 usage(); 227 break; 228 case 'f' : 229 if ((set != 0) && !dirname && !filename) 230 filename = optarg; 231 else 232 usage(); 233 break; 234 case 'i' : 235 ifs = optarg; 236 set = 1; 237 break; 238 case 'l' : 239 if (filename || dirname || set) 240 usage(); 241 lock = 1; 242 set = 1; 243 break; 244 case 'n' : 245 opts |= OPT_DONOTHING; 246 break; 247 case 'N' : 248 if ((ns >= 0) || dirname || (rw != -1) || set) 249 usage(); 250 ns = 0; 251 set = 1; 252 break; 253 case 'r' : 254 if (dirname || (rw != -1) || (ns == -1)) 255 usage(); 256 rw = 0; 257 set = 1; 258 break; 259 case 'R' : 260 rw = 2; 261 set = 1; 262 break; 263 case 'S' : 264 if ((ns >= 0) || dirname || (rw != -1) || set) 265 usage(); 266 ns = 1; 267 set = 1; 268 break; 269 case 'u' : 270 if (filename || dirname || set) 271 usage(); 272 lock = 0; 273 set = 1; 274 break; 275 case 'v' : 276 opts |= OPT_VERBOSE; 277 break; 278 case 'w' : 279 if (dirname || (rw != -1) || (ns == -1)) 280 usage(); 281 rw = 1; 282 set = 1; 283 break; 284 case 'W' : 285 rw = 3; 286 set = 1; 287 break; 288 case '?' : 289 default : 290 usage(); 291 } 292 293 if (ifs) { 294 if (!filename || ns < 0) 295 usage(); 296 if (ns == 0) 297 return changenatif(ifs, filename); 298 else 299 return changestateif(ifs, filename); 300 } 301 302 if ((ns >= 0) || (lock >= 0)) { 303 if (lock >= 0) 304 devfd = opendevice(NULL); 305 else if (ns >= 0) { 306 if (ns == 1) 307 devfd = opendevice(IPSTATE_NAME); 308 else if (ns == 0) 309 devfd = opendevice(IPNAT_NAME); 310 } 311 if (devfd == -1) 312 exit(1); 313 } 314 315 if (lock >= 0) 316 err = setlock(devfd, lock); 317 else if (rw >= 0) { 318 if (rw & 1) { /* WRITE */ 319 if (rw & 2) 320 err = writeall(dirname); 321 else { 322 if (ns == 0) 323 err = writenat(devfd, filename); 324 else if (ns == 1) 325 err = writestate(devfd, filename); 326 } 327 } else { 328 if (rw & 2) 329 err = readall(dirname); 330 else { 331 if (ns == 0) 332 err = readnat(devfd, filename); 333 else if (ns == 1) 334 err = readstate(devfd, filename); 335 } 336 } 337 } 338 return err; 339} 340 341 342int opendevice(ipfdev) 343char *ipfdev; 344{ 345 int fd = -1; 346 347 if (opts & OPT_DONOTHING) 348 return -2; 349 350 if (!ipfdev) 351 ipfdev = IPL_NAME; 352 353 if ((fd = open(ipfdev, O_RDWR)) == -1) 354 if ((fd = open(ipfdev, O_RDONLY)) == -1) 355 perror("open device"); 356 return fd; 357} 358 359 360void closedevice(fd) 361int fd; 362{ 363 close(fd); 364} 365 366 367int setlock(fd, lock) 368int fd, lock; 369{ 370 if (opts & OPT_VERBOSE) 371 printf("Turn lock %s\n", lock ? "on" : "off"); 372 if (!(opts & OPT_DONOTHING)) { 373 if (ioctl(fd, SIOCSTLCK, &lock) == -1) { 374 perror("SIOCSTLCK"); 375 return 1; 376 } 377 if (opts & OPT_VERBOSE) 378 printf("Lock now %s\n", lock ? "on" : "off"); 379 } 380 return 0; 381} 382 383 384int writestate(fd, file) 385int fd; 386char *file; 387{ 388 ipstate_save_t ips, *ipsp; 389 ipfobj_t obj; 390 int wfd = -1; 391 392 if (!file) 393 file = IPF_STATEFILE; 394 395 wfd = open(file, O_WRONLY|O_TRUNC|O_CREAT, 0600); 396 if (wfd == -1) { 397 fprintf(stderr, "%s ", file); 398 perror("state:open"); 399 return 1; 400 } 401 402 ipsp = &ips; 403 bzero((char *)&obj, sizeof(obj)); 404 bzero((char *)ipsp, sizeof(ips)); 405 406 obj.ipfo_rev = IPFILTER_VERSION; 407 obj.ipfo_size = sizeof(*ipsp); 408 obj.ipfo_type = IPFOBJ_STATESAVE; 409 obj.ipfo_ptr = ipsp; 410 411 do { 412 413 if (opts & OPT_VERBOSE) 414 printf("Getting state from addr %p\n", ips.ips_next); 415 if (ioctl(fd, SIOCSTGET, &obj)) { 416 if (errno == ENOENT) 417 break; 418 perror("state:SIOCSTGET"); 419 close(wfd); 420 return 1; 421 } 422 if (opts & OPT_VERBOSE) 423 printf("Got state next %p\n", ips.ips_next); 424 if (write(wfd, ipsp, sizeof(ips)) != sizeof(ips)) { 425 perror("state:write"); 426 close(wfd); 427 return 1; 428 } 429 } while (ips.ips_next != NULL); 430 close(wfd); 431 432 return 0; 433} 434 435 436int readstate(fd, file) 437int fd; 438char *file; 439{ 440 ipstate_save_t ips, *is, *ipshead = NULL, *is1, *ipstail = NULL; 441 int sfd = -1, i; 442 ipfobj_t obj; 443 444 if (!file) 445 file = IPF_STATEFILE; 446 447 sfd = open(file, O_RDONLY, 0600); 448 if (sfd == -1) { 449 fprintf(stderr, "%s ", file); 450 perror("open"); 451 return 1; 452 } 453 454 bzero((char *)&ips, sizeof(ips)); 455 456 /* 457 * 1. Read all state information in. 458 */ 459 do { 460 i = read(sfd, &ips, sizeof(ips)); 461 if (i == -1) { 462 perror("read"); 463 close(sfd); 464 return 1; 465 } 466 if (i == 0) 467 break; 468 if (i != sizeof(ips)) { 469 fprintf(stderr, "state:incomplete read: %d != %d\n", 470 i, (int)sizeof(ips)); 471 close(sfd); 472 return 1; 473 } 474 is = (ipstate_save_t *)malloc(sizeof(*is)); 475 if(!is) { 476 fprintf(stderr, "malloc failed\n"); 477 return 1; 478 } 479 480 bcopy((char *)&ips, (char *)is, sizeof(ips)); 481 482 /* 483 * Check to see if this is the first state entry that will 484 * reference a particular rule and if so, flag it as such 485 * else just adjust the rule pointer to become a pointer to 486 * the other. We do this so we have a means later for tracking 487 * who is referencing us when we get back the real pointer 488 * in is_rule after doing the ioctl. 489 */ 490 for (is1 = ipshead; is1 != NULL; is1 = is1->ips_next) 491 if (is1->ips_rule == is->ips_rule) 492 break; 493 if (is1 == NULL) 494 is->ips_is.is_flags |= SI_NEWFR; 495 else 496 is->ips_rule = (void *)&is1->ips_rule; 497 498 /* 499 * Use a tail-queue type list (add things to the end).. 500 */ 501 is->ips_next = NULL; 502 if (!ipshead) 503 ipshead = is; 504 if (ipstail) 505 ipstail->ips_next = is; 506 ipstail = is; 507 } while (1); 508 509 close(sfd); 510 511 obj.ipfo_rev = IPFILTER_VERSION; 512 obj.ipfo_size = sizeof(*is); 513 obj.ipfo_type = IPFOBJ_STATESAVE; 514 515 for (is = ipshead; is; is = is->ips_next) { 516 if (opts & OPT_VERBOSE) 517 printf("Loading new state table entry\n"); 518 if (is->ips_is.is_flags & SI_NEWFR) { 519 if (opts & OPT_VERBOSE) 520 printf("Loading new filter rule\n"); 521 } 522 523 obj.ipfo_ptr = is; 524 if (!(opts & OPT_DONOTHING)) 525 if (ioctl(fd, SIOCSTPUT, &obj)) { 526 perror("SIOCSTPUT"); 527 return 1; 528 } 529 530 if (is->ips_is.is_flags & SI_NEWFR) { 531 if (opts & OPT_VERBOSE) 532 printf("Real rule addr %p\n", is->ips_rule); 533 for (is1 = is->ips_next; is1; is1 = is1->ips_next) 534 if (is1->ips_rule == (frentry_t *)&is->ips_rule) 535 is1->ips_rule = is->ips_rule; 536 } 537 } 538 539 return 0; 540} 541 542 543int readnat(fd, file) 544int fd; 545char *file; 546{ 547 nat_save_t ipn, *in, *ipnhead = NULL, *in1, *ipntail = NULL; 548 ipfobj_t obj; 549 int nfd, i; 550 nat_t *nat; 551 char *s; 552 int n; 553 554 nfd = -1; 555 in = NULL; 556 ipnhead = NULL; 557 ipntail = NULL; 558 559 if (!file) 560 file = IPF_NATFILE; 561 562 nfd = open(file, O_RDONLY); 563 if (nfd == -1) { 564 fprintf(stderr, "%s ", file); 565 perror("nat:open"); 566 return 1; 567 } 568 569 bzero((char *)&ipn, sizeof(ipn)); 570 571 /* 572 * 1. Read all state information in. 573 */ 574 do { 575 i = read(nfd, &ipn, sizeof(ipn)); 576 if (i == -1) { 577 perror("read"); 578 close(nfd); 579 return 1; 580 } 581 if (i == 0) 582 break; 583 if (i != sizeof(ipn)) { 584 fprintf(stderr, "nat:incomplete read: %d != %d\n", 585 i, (int)sizeof(ipn)); 586 close(nfd); 587 return 1; 588 } 589 590 in = (nat_save_t *)malloc(ipn.ipn_dsize); 591 if (!in) 592 break; 593 594 if (ipn.ipn_dsize > sizeof(ipn)) { 595 n = ipn.ipn_dsize - sizeof(ipn); 596 if (n > 0) { 597 s = in->ipn_data + sizeof(in->ipn_data); 598 i = read(nfd, s, n); 599 if (i == 0) 600 break; 601 if (i != n) { 602 fprintf(stderr, 603 "nat:incomplete read: %d != %d\n", 604 i, n); 605 close(nfd); 606 return 1; 607 } 608 } 609 } 610 bcopy((char *)&ipn, (char *)in, sizeof(ipn)); 611 612 /* 613 * Check to see if this is the first NAT entry that will 614 * reference a particular rule and if so, flag it as such 615 * else just adjust the rule pointer to become a pointer to 616 * the other. We do this so we have a means later for tracking 617 * who is referencing us when we get back the real pointer 618 * in is_rule after doing the ioctl. 619 */ 620 nat = &in->ipn_nat; 621 if (nat->nat_fr != NULL) { 622 for (in1 = ipnhead; in1 != NULL; in1 = in1->ipn_next) 623 if (in1->ipn_rule == nat->nat_fr) 624 break; 625 if (in1 == NULL) 626 nat->nat_flags |= SI_NEWFR; 627 else 628 nat->nat_fr = &in1->ipn_fr; 629 } 630 631 /* 632 * Use a tail-queue type list (add things to the end).. 633 */ 634 in->ipn_next = NULL; 635 if (!ipnhead) 636 ipnhead = in; 637 if (ipntail) 638 ipntail->ipn_next = in; 639 ipntail = in; 640 } while (1); 641 642 close(nfd); 643 nfd = -1; 644 645 obj.ipfo_rev = IPFILTER_VERSION; 646 obj.ipfo_type = IPFOBJ_NATSAVE; 647 648 for (in = ipnhead; in; in = in->ipn_next) { 649 if (opts & OPT_VERBOSE) 650 printf("Loading new NAT table entry\n"); 651 nat = &in->ipn_nat; 652 if (nat->nat_flags & SI_NEWFR) { 653 if (opts & OPT_VERBOSE) 654 printf("Loading new filter rule\n"); 655 } 656 657 obj.ipfo_ptr = in; 658 obj.ipfo_size = in->ipn_dsize; 659 if (!(opts & OPT_DONOTHING)) 660 if (ioctl(fd, SIOCSTPUT, &obj)) { 661 fprintf(stderr, "in=%p:", in); 662 perror("SIOCSTPUT"); 663 return 1; 664 } 665 666 if (nat->nat_flags & SI_NEWFR) { 667 if (opts & OPT_VERBOSE) 668 printf("Real rule addr %p\n", nat->nat_fr); 669 for (in1 = in->ipn_next; in1; in1 = in1->ipn_next) 670 if (in1->ipn_rule == &in->ipn_fr) 671 in1->ipn_rule = nat->nat_fr; 672 } 673 } 674 675 return 0; 676} 677 678 679int writenat(fd, file) 680int fd; 681char *file; 682{ 683 nat_save_t *ipnp = NULL, *next = NULL; 684 ipfobj_t obj; 685 int nfd = -1; 686 natget_t ng; 687 688 if (!file) 689 file = IPF_NATFILE; 690 691 nfd = open(file, O_WRONLY|O_TRUNC|O_CREAT, 0600); 692 if (nfd == -1) { 693 fprintf(stderr, "%s ", file); 694 perror("nat:open"); 695 return 1; 696 } 697 698 obj.ipfo_rev = IPFILTER_VERSION; 699 obj.ipfo_type = IPFOBJ_NATSAVE; 700 701 do { 702 if (opts & OPT_VERBOSE) 703 printf("Getting nat from addr %p\n", ipnp); 704 ng.ng_ptr = next; 705 ng.ng_sz = 0; 706 if (ioctl(fd, SIOCSTGSZ, &ng)) { 707 perror("nat:SIOCSTGSZ"); 708 close(nfd); 709 if (ipnp != NULL) 710 free(ipnp); 711 return 1; 712 } 713 714 if (opts & OPT_VERBOSE) 715 printf("NAT size %d from %p\n", ng.ng_sz, ng.ng_ptr); 716 717 if (ng.ng_sz == 0) 718 break; 719 720 if (!ipnp) 721 ipnp = malloc(ng.ng_sz); 722 else 723 ipnp = realloc((char *)ipnp, ng.ng_sz); 724 if (!ipnp) { 725 fprintf(stderr, 726 "malloc for %d bytes failed\n", ng.ng_sz); 727 break; 728 } 729 730 bzero((char *)ipnp, ng.ng_sz); 731 obj.ipfo_size = ng.ng_sz; 732 obj.ipfo_ptr = ipnp; 733 ipnp->ipn_dsize = ng.ng_sz; 734 ipnp->ipn_next = next; 735 if (ioctl(fd, SIOCSTGET, &obj)) { 736 if (errno == ENOENT) 737 break; 738 perror("nat:SIOCSTGET"); 739 close(nfd); 740 free(ipnp); 741 return 1; 742 } 743 744 if (opts & OPT_VERBOSE) 745 printf("Got nat next %p ipn_dsize %d ng_sz %d\n", 746 ipnp->ipn_next, ipnp->ipn_dsize, ng.ng_sz); 747 if (write(nfd, ipnp, ipnp->ipn_dsize) != ipnp->ipn_dsize) { 748 perror("nat:write"); 749 close(nfd); 750 free(ipnp); 751 return 1; 752 } 753 next = ipnp->ipn_next; 754 } while (ipnp && next); 755 if (ipnp != NULL) 756 free(ipnp); 757 close(nfd); 758 759 return 0; 760} 761 762 763int writeall(dirname) 764char *dirname; 765{ 766 int fd, devfd; 767 768 if (!dirname) 769 dirname = IPF_SAVEDIR; 770 771 if (chdir(dirname)) { 772 fprintf(stderr, "IPF_SAVEDIR=%s: ", dirname); 773 perror("chdir(IPF_SAVEDIR)"); 774 return 1; 775 } 776 777 fd = opendevice(NULL); 778 if (fd == -1) 779 return 1; 780 if (setlock(fd, 1)) { 781 close(fd); 782 return 1; 783 } 784 785 devfd = opendevice(IPSTATE_NAME); 786 if (devfd == -1) 787 goto bad; 788 if (writestate(devfd, NULL)) 789 goto bad; 790 close(devfd); 791 792 devfd = opendevice(IPNAT_NAME); 793 if (devfd == -1) 794 goto bad; 795 if (writenat(devfd, NULL)) 796 goto bad; 797 close(devfd); 798 799 if (setlock(fd, 0)) { 800 close(fd); 801 return 1; 802 } 803 804 close(fd); 805 return 0; 806 807bad: 808 setlock(fd, 0); 809 close(fd); 810 return 1; 811} 812 813 814int readall(dirname) 815char *dirname; 816{ 817 int fd, devfd; 818 819 if (!dirname) 820 dirname = IPF_SAVEDIR; 821 822 if (chdir(dirname)) { 823 perror("chdir(IPF_SAVEDIR)"); 824 return 1; 825 } 826 827 fd = opendevice(NULL); 828 if (fd == -1) 829 return 1; 830 if (setlock(fd, 1)) { 831 close(fd); 832 return 1; 833 } 834 835 devfd = opendevice(IPSTATE_NAME); 836 if (devfd == -1) 837 return 1; 838 if (readstate(devfd, NULL)) 839 return 1; 840 close(devfd); 841 842 devfd = opendevice(IPNAT_NAME); 843 if (devfd == -1) 844 return 1; 845 if (readnat(devfd, NULL)) 846 return 1; 847 close(devfd); 848 849 if (setlock(fd, 0)) { 850 close(fd); 851 return 1; 852 } 853 854 return 0; 855} 856