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