add UNLEASHED_OBJ to unleashed.mk
[unleashed/tickless.git] / usr / src / cmd / ipf / tools / ipfs.c
blob63d2fc1711d2bef3653e99ce567c4a0ce9244b65
1 /*
2 * Copyright (C) 1999-2001, 2003 by Darren Reed.
4 * See the IPFILTER.LICENCE file for details on licencing.
6 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
7 * Use is subject to license terms.
9 * Copyright (c) 2014, Joyent, Inc. All rights reserved.
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 #include "netinet/ipl.h"
49 #include "ipfzone.h"
51 static const char rcsid[] = "@(#)Id: ipfs.c,v 1.12 2003/12/01 01:56:53 darrenr Exp";
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
63 #if !defined(__SVR4) && defined(__GNUC__)
64 extern char *index __P((const char *, int));
65 #endif
67 extern char *optarg;
68 extern int optind;
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 *));
84 int opts = 0;
85 char *progname;
88 void usage()
90 const char *zoneopt = "[-G|-z zonename] ";
91 fprintf(stderr, "usage: %s %s[-nv] -l\n", progname, zoneopt);
92 fprintf(stderr, "usage: %s %s[-nv] -u\n", progname, zoneopt);
93 fprintf(stderr, "usage: %s %s[-nv] [-d <dir>] -R\n", progname, zoneopt);
94 fprintf(stderr, "usage: %s %s[-nv] [-d <dir>] -W\n", progname, zoneopt);
95 fprintf(stderr, "usage: %s %s[-nv] [-N|-S] [-f <file>] -r\n", progname,
96 zoneopt);
97 fprintf(stderr, "usage: %s %s[-nv] [-N|-S] [-f <file>] -w\n", progname,
98 zoneopt);
99 fprintf(stderr, "usage: %s %s[-nv] [-N|-S] -f <file> -i <if1>,<if2>\n",
100 progname, zoneopt);
101 exit(1);
106 * Change interface names in state information saved out to disk.
108 int changestateif(ifs, fname)
109 char *ifs, *fname;
111 int fd, olen, nlen, rw;
112 ipstate_save_t ips;
113 off_t pos;
114 char *s;
116 s = strchr(ifs, ',');
117 if (!s)
118 usage();
119 *s++ = '\0';
120 nlen = strlen(s);
121 olen = strlen(ifs);
122 if (nlen >= sizeof(ips.ips_is.is_ifname) ||
123 olen >= sizeof(ips.ips_is.is_ifname))
124 usage();
126 fd = open(fname, O_RDWR);
127 if (fd == -1) {
128 perror("open");
129 exit(1);
132 for (pos = 0; read(fd, &ips, sizeof(ips)) == sizeof(ips); ) {
133 rw = 0;
134 if (!strncmp(ips.ips_is.is_ifname[0], ifs, olen + 1)) {
135 strcpy(ips.ips_is.is_ifname[0], s);
136 rw = 1;
138 if (!strncmp(ips.ips_is.is_ifname[1], ifs, olen + 1)) {
139 strcpy(ips.ips_is.is_ifname[1], s);
140 rw = 1;
142 if (rw == 1) {
143 if (lseek(fd, pos, SEEK_SET) != pos) {
144 perror("lseek");
145 exit(1);
147 if (write(fd, &ips, sizeof(ips)) != sizeof(ips)) {
148 perror("write");
149 exit(1);
152 pos = lseek(fd, 0, SEEK_CUR);
154 close(fd);
156 return 0;
161 * Change interface names in NAT information saved out to disk.
163 int changenatif(ifs, fname)
164 char *ifs, *fname;
166 int fd, olen, nlen, rw;
167 nat_save_t ipn;
168 nat_t *nat;
169 off_t pos;
170 char *s;
172 s = strchr(ifs, ',');
173 if (!s)
174 usage();
175 *s++ = '\0';
176 nlen = strlen(s);
177 olen = strlen(ifs);
178 nat = &ipn.ipn_nat;
179 if (nlen >= sizeof(nat->nat_ifnames[0]) ||
180 olen >= sizeof(nat->nat_ifnames[0]))
181 usage();
183 fd = open(fname, O_RDWR);
184 if (fd == -1) {
185 perror("open");
186 exit(1);
189 for (pos = 0; read(fd, &ipn, sizeof(ipn)) == sizeof(ipn); ) {
190 rw = 0;
191 if (!strncmp(nat->nat_ifnames[0], ifs, olen + 1)) {
192 strcpy(nat->nat_ifnames[0], s);
193 rw = 1;
195 if (!strncmp(nat->nat_ifnames[1], ifs, olen + 1)) {
196 strcpy(nat->nat_ifnames[1], s);
197 rw = 1;
199 if (rw == 1) {
200 if (lseek(fd, pos, SEEK_SET) != pos) {
201 perror("lseek");
202 exit(1);
204 if (write(fd, &ipn, sizeof(ipn)) != sizeof(ipn)) {
205 perror("write");
206 exit(1);
209 pos = lseek(fd, 0, SEEK_CUR);
211 close(fd);
213 return 0;
217 int main(argc,argv)
218 int argc;
219 char *argv[];
221 int c, lock = -1, devfd = -1, err = 0, rw = -1, ns = -1, set = 0;
222 char *dirname = NULL, *filename = NULL, *ifs = NULL;
224 progname = argv[0];
225 while ((c = getopt(argc, argv, "d:f:G:lNnSRruvWwz:")) != -1)
226 switch (c)
228 case 'd' :
229 if ((set == 0) && !dirname && !filename)
230 dirname = optarg;
231 else
232 usage();
233 break;
234 case 'f' :
235 if ((set == 0) && !dirname && !filename)
236 filename = optarg;
237 else
238 usage();
239 break;
240 case 'G' :
241 setzonename_global(optarg);
242 break;
243 case 'i' :
244 ifs = optarg;
245 set = 1;
246 break;
247 case 'l' :
248 if (filename || dirname || set)
249 usage();
250 lock = 1;
251 set = 1;
252 break;
253 case 'n' :
254 opts |= OPT_DONOTHING;
255 break;
256 case 'N' :
257 if ((ns >= 0) || dirname || (rw != -1) || set)
258 usage();
259 ns = 0;
260 set = 1;
261 break;
262 case 'r' :
263 if (dirname || (rw != -1) || (ns == -1))
264 usage();
265 rw = 0;
266 set = 1;
267 break;
268 case 'R' :
269 rw = 2;
270 set = 1;
271 break;
272 case 'S' :
273 if ((ns >= 0) || dirname || (rw != -1) || set)
274 usage();
275 ns = 1;
276 set = 1;
277 break;
278 case 'u' :
279 if (filename || dirname || set)
280 usage();
281 lock = 0;
282 set = 1;
283 break;
284 case 'v' :
285 opts |= OPT_VERBOSE;
286 break;
287 case 'w' :
288 if (dirname || (rw != -1) || (ns == -1))
289 usage();
290 rw = 1;
291 set = 1;
292 break;
293 case 'W' :
294 rw = 3;
295 set = 1;
296 break;
297 case 'z' :
298 setzonename(optarg);
299 break;
300 case '?' :
301 default :
302 usage();
305 if (ifs) {
306 if (!filename || ns < 0)
307 usage();
308 if (ns == 0)
309 return changenatif(ifs, filename);
310 else
311 return changestateif(ifs, filename);
314 if ((ns >= 0) || (lock >= 0)) {
315 if (lock >= 0)
316 devfd = opendevice(NULL);
317 else if (ns >= 0) {
318 if (ns == 1)
319 devfd = opendevice(IPSTATE_NAME);
320 else if (ns == 0)
321 devfd = opendevice(IPNAT_NAME);
323 if (devfd == -1)
324 exit(1);
327 if (lock >= 0)
328 err = setlock(devfd, lock);
329 else if (rw >= 0) {
330 if (rw & 1) { /* WRITE */
331 if (rw & 2)
332 err = writeall(dirname);
333 else {
334 if (ns == 0)
335 err = writenat(devfd, filename);
336 else if (ns == 1)
337 err = writestate(devfd, filename);
339 } else {
340 if (rw & 2)
341 err = readall(dirname);
342 else {
343 if (ns == 0)
344 err = readnat(devfd, filename);
345 else if (ns == 1)
346 err = readstate(devfd, filename);
350 return err;
354 int opendevice(ipfdev)
355 char *ipfdev;
357 int fd = -1;
359 if (opts & OPT_DONOTHING)
360 return -2;
362 if (!ipfdev)
363 ipfdev = IPL_NAME;
365 if ((fd = open(ipfdev, O_RDWR)) == -1)
366 if ((fd = open(ipfdev, O_RDONLY)) == -1)
367 perror("open device");
369 if (setzone(fd) != 0) {
370 close(fd);
371 fd = -1;
374 return fd;
378 void closedevice(fd)
379 int fd;
381 close(fd);
385 int setlock(fd, lock)
386 int fd, lock;
388 if (opts & OPT_VERBOSE)
389 printf("Turn lock %s\n", lock ? "on" : "off");
390 if (!(opts & OPT_DONOTHING)) {
391 if (ioctl(fd, SIOCSTLCK, &lock) == -1) {
392 perror("SIOCSTLCK");
393 return 1;
395 if (opts & OPT_VERBOSE)
396 printf("Lock now %s\n", lock ? "on" : "off");
398 return 0;
402 int writestate(fd, file)
403 int fd;
404 char *file;
406 ipstate_save_t ips, *ipsp;
407 ipfobj_t obj;
408 int wfd = -1;
410 if (!file)
411 file = IPF_STATEFILE;
413 wfd = open(file, O_WRONLY|O_TRUNC|O_CREAT, 0600);
414 if (wfd == -1) {
415 fprintf(stderr, "%s ", file);
416 perror("state:open");
417 return 1;
420 ipsp = &ips;
421 bzero((char *)&obj, sizeof(obj));
422 bzero((char *)ipsp, sizeof(ips));
424 obj.ipfo_rev = IPFILTER_VERSION;
425 obj.ipfo_size = sizeof(*ipsp);
426 obj.ipfo_type = IPFOBJ_STATESAVE;
427 obj.ipfo_ptr = ipsp;
429 do {
431 if (opts & OPT_VERBOSE)
432 printf("Getting state from addr %p\n", ips.ips_next);
433 if (ioctl(fd, SIOCSTGET, &obj)) {
434 if (errno == ENOENT)
435 break;
436 perror("state:SIOCSTGET");
437 close(wfd);
438 return 1;
440 if (opts & OPT_VERBOSE)
441 printf("Got state next %p\n", ips.ips_next);
442 if (write(wfd, ipsp, sizeof(ips)) != sizeof(ips)) {
443 perror("state:write");
444 close(wfd);
445 return 1;
447 } while (ips.ips_next != NULL);
448 close(wfd);
450 return 0;
454 int readstate(fd, file)
455 int fd;
456 char *file;
458 ipstate_save_t ips, *is, *ipshead = NULL, *is1, *ipstail = NULL;
459 int sfd = -1, i;
460 ipfobj_t obj;
462 if (!file)
463 file = IPF_STATEFILE;
465 sfd = open(file, O_RDONLY, 0600);
466 if (sfd == -1) {
467 fprintf(stderr, "%s ", file);
468 perror("open");
469 return 1;
472 bzero((char *)&ips, sizeof(ips));
475 * 1. Read all state information in.
477 do {
478 i = read(sfd, &ips, sizeof(ips));
479 if (i == -1) {
480 perror("read");
481 close(sfd);
482 return 1;
484 if (i == 0)
485 break;
486 if (i != sizeof(ips)) {
487 fprintf(stderr, "state:incomplete read: %d != %d\n",
488 i, (int)sizeof(ips));
489 close(sfd);
490 return 1;
492 is = (ipstate_save_t *)malloc(sizeof(*is));
493 if(!is) {
494 fprintf(stderr, "malloc failed\n");
495 return 1;
498 bcopy((char *)&ips, (char *)is, sizeof(ips));
501 * Check to see if this is the first state entry that will
502 * reference a particular rule and if so, flag it as such
503 * else just adjust the rule pointer to become a pointer to
504 * the other. We do this so we have a means later for tracking
505 * who is referencing us when we get back the real pointer
506 * in is_rule after doing the ioctl.
508 for (is1 = ipshead; is1 != NULL; is1 = is1->ips_next)
509 if (is1->ips_rule == is->ips_rule)
510 break;
511 if (is1 == NULL)
512 is->ips_is.is_flags |= SI_NEWFR;
513 else
514 is->ips_rule = (void *)&is1->ips_rule;
517 * Use a tail-queue type list (add things to the end)..
519 is->ips_next = NULL;
520 if (!ipshead)
521 ipshead = is;
522 if (ipstail)
523 ipstail->ips_next = is;
524 ipstail = is;
525 } while (1);
527 close(sfd);
529 obj.ipfo_rev = IPFILTER_VERSION;
530 obj.ipfo_size = sizeof(*is);
531 obj.ipfo_type = IPFOBJ_STATESAVE;
533 for (is = ipshead; is; is = is->ips_next) {
534 if (opts & OPT_VERBOSE)
535 printf("Loading new state table entry\n");
536 if (is->ips_is.is_flags & SI_NEWFR) {
537 if (opts & OPT_VERBOSE)
538 printf("Loading new filter rule\n");
541 obj.ipfo_ptr = is;
542 if (!(opts & OPT_DONOTHING))
543 if (ioctl(fd, SIOCSTPUT, &obj)) {
544 perror("SIOCSTPUT");
545 return 1;
548 if (is->ips_is.is_flags & SI_NEWFR) {
549 if (opts & OPT_VERBOSE)
550 printf("Real rule addr %p\n", is->ips_rule);
551 for (is1 = is->ips_next; is1; is1 = is1->ips_next)
552 if (is1->ips_rule == (frentry_t *)&is->ips_rule)
553 is1->ips_rule = is->ips_rule;
557 return 0;
561 int readnat(fd, file)
562 int fd;
563 char *file;
565 nat_save_t ipn, *in, *ipnhead = NULL, *in1, *ipntail = NULL;
566 ipfobj_t obj;
567 int nfd, i;
568 nat_t *nat;
569 char *s;
570 int n;
572 nfd = -1;
573 in = NULL;
574 ipnhead = NULL;
575 ipntail = NULL;
577 if (!file)
578 file = IPF_NATFILE;
580 nfd = open(file, O_RDONLY);
581 if (nfd == -1) {
582 fprintf(stderr, "%s ", file);
583 perror("nat:open");
584 return 1;
587 bzero((char *)&ipn, sizeof(ipn));
590 * 1. Read all state information in.
592 do {
593 i = read(nfd, &ipn, sizeof(ipn));
594 if (i == -1) {
595 perror("read");
596 close(nfd);
597 return 1;
599 if (i == 0)
600 break;
601 if (i != sizeof(ipn)) {
602 fprintf(stderr, "nat:incomplete read: %d != %d\n",
603 i, (int)sizeof(ipn));
604 close(nfd);
605 return 1;
608 in = (nat_save_t *)malloc(ipn.ipn_dsize);
609 if (!in)
610 break;
612 if (ipn.ipn_dsize > sizeof(ipn)) {
613 n = ipn.ipn_dsize - sizeof(ipn);
614 if (n > 0) {
615 s = in->ipn_data + sizeof(in->ipn_data);
616 i = read(nfd, s, n);
617 if (i == 0)
618 break;
619 if (i != n) {
620 fprintf(stderr,
621 "nat:incomplete read: %d != %d\n",
622 i, n);
623 close(nfd);
624 return 1;
628 bcopy((char *)&ipn, (char *)in, sizeof(ipn));
631 * Check to see if this is the first NAT entry that will
632 * reference a particular rule and if so, flag it as such
633 * else just adjust the rule pointer to become a pointer to
634 * the other. We do this so we have a means later for tracking
635 * who is referencing us when we get back the real pointer
636 * in is_rule after doing the ioctl.
638 nat = &in->ipn_nat;
639 if (nat->nat_fr != NULL) {
640 for (in1 = ipnhead; in1 != NULL; in1 = in1->ipn_next)
641 if (in1->ipn_rule == nat->nat_fr)
642 break;
643 if (in1 == NULL)
644 nat->nat_flags |= SI_NEWFR;
645 else
646 nat->nat_fr = &in1->ipn_fr;
650 * Use a tail-queue type list (add things to the end)..
652 in->ipn_next = NULL;
653 if (!ipnhead)
654 ipnhead = in;
655 if (ipntail)
656 ipntail->ipn_next = in;
657 ipntail = in;
658 } while (1);
660 close(nfd);
661 nfd = -1;
663 obj.ipfo_rev = IPFILTER_VERSION;
664 obj.ipfo_type = IPFOBJ_NATSAVE;
666 for (in = ipnhead; in; in = in->ipn_next) {
667 if (opts & OPT_VERBOSE)
668 printf("Loading new NAT table entry\n");
669 nat = &in->ipn_nat;
670 if (nat->nat_flags & SI_NEWFR) {
671 if (opts & OPT_VERBOSE)
672 printf("Loading new filter rule\n");
675 obj.ipfo_ptr = in;
676 obj.ipfo_size = in->ipn_dsize;
677 if (!(opts & OPT_DONOTHING))
678 if (ioctl(fd, SIOCSTPUT, &obj)) {
679 fprintf(stderr, "in=%p:", in);
680 perror("SIOCSTPUT");
681 return 1;
684 if (nat->nat_flags & SI_NEWFR) {
685 if (opts & OPT_VERBOSE)
686 printf("Real rule addr %p\n", nat->nat_fr);
687 for (in1 = in->ipn_next; in1; in1 = in1->ipn_next)
688 if (in1->ipn_rule == &in->ipn_fr)
689 in1->ipn_rule = nat->nat_fr;
693 return 0;
697 int writenat(fd, file)
698 int fd;
699 char *file;
701 nat_save_t *ipnp = NULL, *next = NULL;
702 ipfobj_t obj;
703 int nfd = -1;
704 natget_t ng;
706 if (!file)
707 file = IPF_NATFILE;
709 nfd = open(file, O_WRONLY|O_TRUNC|O_CREAT, 0600);
710 if (nfd == -1) {
711 fprintf(stderr, "%s ", file);
712 perror("nat:open");
713 return 1;
716 obj.ipfo_rev = IPFILTER_VERSION;
717 obj.ipfo_type = IPFOBJ_NATSAVE;
719 do {
720 if (opts & OPT_VERBOSE)
721 printf("Getting nat from addr %p\n", ipnp);
722 ng.ng_ptr = next;
723 ng.ng_sz = 0;
724 if (ioctl(fd, SIOCSTGSZ, &ng)) {
725 perror("nat:SIOCSTGSZ");
726 close(nfd);
727 free(ipnp);
728 return 1;
731 if (opts & OPT_VERBOSE)
732 printf("NAT size %d from %p\n", ng.ng_sz, ng.ng_ptr);
734 if (ng.ng_sz == 0)
735 break;
737 if (!ipnp)
738 ipnp = malloc(ng.ng_sz);
739 else
740 ipnp = realloc((char *)ipnp, ng.ng_sz);
741 if (!ipnp) {
742 fprintf(stderr,
743 "malloc for %d bytes failed\n", ng.ng_sz);
744 break;
747 bzero((char *)ipnp, ng.ng_sz);
748 obj.ipfo_size = ng.ng_sz;
749 obj.ipfo_ptr = ipnp;
750 ipnp->ipn_dsize = ng.ng_sz;
751 ipnp->ipn_next = next;
752 if (ioctl(fd, SIOCSTGET, &obj)) {
753 if (errno == ENOENT)
754 break;
755 perror("nat:SIOCSTGET");
756 close(nfd);
757 free(ipnp);
758 return 1;
761 if (opts & OPT_VERBOSE)
762 printf("Got nat next %p ipn_dsize %d ng_sz %d\n",
763 ipnp->ipn_next, ipnp->ipn_dsize, ng.ng_sz);
764 if (write(nfd, ipnp, ipnp->ipn_dsize) != ipnp->ipn_dsize) {
765 perror("nat:write");
766 close(nfd);
767 free(ipnp);
768 return 1;
770 next = ipnp->ipn_next;
771 } while (ipnp && next);
772 free(ipnp);
773 close(nfd);
775 return 0;
779 int writeall(dirname)
780 char *dirname;
782 int fd, devfd;
784 if (!dirname)
785 dirname = IPF_SAVEDIR;
787 if (chdir(dirname)) {
788 fprintf(stderr, "IPF_SAVEDIR=%s: ", dirname);
789 perror("chdir(IPF_SAVEDIR)");
790 return 1;
793 fd = opendevice(NULL);
794 if (fd == -1)
795 return 1;
796 if (setlock(fd, 1)) {
797 close(fd);
798 return 1;
801 devfd = opendevice(IPSTATE_NAME);
802 if (devfd == -1)
803 goto bad;
804 if (writestate(devfd, NULL))
805 goto bad;
806 close(devfd);
808 devfd = opendevice(IPNAT_NAME);
809 if (devfd == -1)
810 goto bad;
811 if (writenat(devfd, NULL))
812 goto bad;
813 close(devfd);
815 if (setlock(fd, 0)) {
816 close(fd);
817 return 1;
820 close(fd);
821 return 0;
823 bad:
824 setlock(fd, 0);
825 close(fd);
826 return 1;
830 int readall(dirname)
831 char *dirname;
833 int fd, devfd;
835 if (!dirname)
836 dirname = IPF_SAVEDIR;
838 if (chdir(dirname)) {
839 perror("chdir(IPF_SAVEDIR)");
840 return 1;
843 fd = opendevice(NULL);
844 if (fd == -1)
845 return 1;
846 if (setlock(fd, 1)) {
847 close(fd);
848 return 1;
851 devfd = opendevice(IPSTATE_NAME);
852 if (devfd == -1)
853 return 1;
854 if (readstate(devfd, NULL))
855 return 1;
856 close(devfd);
858 devfd = opendevice(IPNAT_NAME);
859 if (devfd == -1)
860 return 1;
861 if (readnat(devfd, NULL))
862 return 1;
863 close(devfd);
865 if (setlock(fd, 0)) {
866 close(fd);
867 return 1;
870 return 0;