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 } 600 bcopy((char *)ipnp, (char *)in, sizeof(ipn)); 601 602 /* 603 * Check to see if this is the first state entry that will 604 * reference a particular rule and if so, flag it as such 605 * else just adjust the rule pointer to become a pointer to 606 * the other. We do this so we have a means later for tracking 607 * who is referencing us when we get back the real pointer 608 * in is_rule after doing the ioctl. 609 */ 610 nat = &in->ipn_nat; 611 if (nat->nat_fr != NULL) { 612 for (in1 = ipnhead; in1 != NULL; in1 = in1->ipn_next) 613 if (in1->ipn_rule == nat->nat_fr) 614 break; 615 if (in1 == NULL) 616 nat->nat_flags |= SI_NEWFR; 617 else 618 nat->nat_fr = &in1->ipn_fr; 619 } 620 621 /* 622 * Use a tail-queue type list (add things to the end).. 623 */ 624 in->ipn_next = NULL; 625 if (!ipnhead) 626 ipnhead = in; 627 if (ipntail) 628 ipntail->ipn_next = in; 629 ipntail = in; 630 } while (1); 631 632 close(nfd); 633 634 for (in = ipnhead; in; in = in->ipn_next) { 635 if (opts & OPT_VERBOSE) 636 printf("Loading new NAT table entry\n"); 637 nat = &in->ipn_nat; 638 if (nat->nat_flags & SI_NEWFR) { 639 if (opts & OPT_VERBOSE) 640 printf("Loading new filter rule\n"); 641 } 642 if (!(opts & OPT_DONOTHING)) 643 if (ioctl(fd, SIOCSTPUT, &in)) { 644 perror("SIOCSTPUT"); 645 return 1; 646 } 647 648 if (nat->nat_flags & SI_NEWFR) { 649 if (opts & OPT_VERBOSE) 650 printf("Real rule addr %p\n", nat->nat_fr); 651 for (in1 = in->ipn_next; in1; in1 = in1->ipn_next) 652 if (in1->ipn_rule == &in->ipn_fr) 653 in1->ipn_rule = nat->nat_fr; 654 } 655 } 656 657 return 0; 658 } 659 660 661 int writenat(fd, file) 662 int fd; 663 char *file; 664 { 665 nat_save_t *ipnp = NULL, *next = NULL; 666 int nfd = -1; 667 natget_t ng; 668 669 if (!file) 670 file = IPF_NATFILE; 671 672 nfd = open(file, O_WRONLY|O_TRUNC|O_CREAT, 0600); 673 if (nfd == -1) { 674 fprintf(stderr, "%s ", file); 675 perror("nat:open"); 676 return 1; 677 } 678 679 680 do { 681 if (opts & OPT_VERBOSE) 682 printf("Getting nat from addr %p\n", ipnp); 683 ng.ng_ptr = next; 684 ng.ng_sz = 0; 685 if (ioctl(fd, SIOCSTGSZ, &ng)) { 686 perror("nat:SIOCSTGSZ"); 687 close(nfd); 688 if (ipnp != NULL) 689 free(ipnp); 690 return 1; 691 } 692 693 if (opts & OPT_VERBOSE) 694 printf("NAT size %d from %p\n", ng.ng_sz, ng.ng_ptr); 695 696 if (ng.ng_sz == 0) 697 break; 698 699 if (!ipnp) 700 ipnp = malloc(ng.ng_sz); 701 else 702 ipnp = realloc((char *)ipnp, ng.ng_sz); 703 if (!ipnp) { 704 fprintf(stderr, 705 "malloc for %d bytes failed\n", ng.ng_sz); 706 break; 707 } 708 709 bzero((char *)ipnp, ng.ng_sz); 710 ipnp->ipn_next = next; 711 if (ioctl(fd, SIOCSTGET, &ipnp)) { 712 if (errno == ENOENT) 713 break; 714 perror("nat:SIOCSTGET"); 715 close(nfd); 716 free(ipnp); 717 return 1; 718 } 719 720 if (opts & OPT_VERBOSE) 721 printf("Got nat next %p\n", ipnp->ipn_next); 722 if (write(nfd, ipnp, ng.ng_sz) != ng.ng_sz) { 723 perror("nat:write"); 724 close(nfd); 725 free(ipnp); 726 return 1; 727 } 728 next = ipnp->ipn_next; 729 } while (ipnp && next); 730 if (ipnp != NULL) 731 free(ipnp); 732 close(nfd); 733 734 return 0; 735 } 736 737 738 int writeall(dirname) 739 char *dirname; 740 { 741 int fd, devfd; 742 743 if (!dirname) 744 dirname = IPF_SAVEDIR; 745 746 if (chdir(dirname)) { 747 perror("chdir(IPF_SAVEDIR)"); 748 return 1; 749 } 750 751 fd = opendevice(NULL); 752 if (fd == -1) 753 return 1; 754 if (setlock(fd, 1)) { 755 close(fd); 756 return 1; 757 } 758 759 devfd = opendevice(IPSTATE_NAME); 760 if (devfd == -1) 761 goto bad; 762 if (writestate(devfd, NULL)) 763 goto bad; 764 close(devfd); 765 766 devfd = opendevice(IPNAT_NAME); 767 if (devfd == -1) 768 goto bad; 769 if (writenat(devfd, NULL)) 770 goto bad; 771 close(devfd); 772 773 if (setlock(fd, 0)) { 774 close(fd); 775 return 1; 776 } 777 778 close(fd); 779 return 0; 780 781 bad: 782 setlock(fd, 0); 783 close(fd); 784 return 1; 785 } 786 787 788 int readall(dirname) 789 char *dirname; 790 { 791 int fd, devfd; 792 793 if (!dirname) 794 dirname = IPF_SAVEDIR; 795 796 if (chdir(dirname)) { 797 perror("chdir(IPF_SAVEDIR)"); 798 return 1; 799 } 800 801 fd = opendevice(NULL); 802 if (fd == -1) 803 return 1; 804 if (setlock(fd, 1)) { 805 close(fd); 806 return 1; 807 } 808 809 devfd = opendevice(IPSTATE_NAME); 810 if (devfd == -1) 811 return 1; 812 if (readstate(devfd, NULL)) 813 return 1; 814 close(devfd); 815 816 devfd = opendevice(IPNAT_NAME); 817 if (devfd == -1) 818 return 1; 819 if (readnat(devfd, NULL)) 820 return 1; 821 close(devfd); 822 823 if (setlock(fd, 0)) { 824 close(fd); 825 return 1; 826 } 827 828 return 0; 829 } 830