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.
13 # ifndef __FreeBSD_cc_version
14 # include <osreldate.h>
16 # if __FreeBSD_cc_version < 430000
17 # include <osreldate.h>
26 #if !defined(__SVR4) && !defined(__GNUC__)
29 #include <sys/types.h>
30 #include <sys/param.h>
34 #include <sys/socket.h>
35 #include <sys/ioctl.h>
36 #include <netinet/in.h>
37 #include <netinet/in_systm.h>
40 #if __FreeBSD_version >= 300000
41 # include <net/if_var.h>
43 #include <netinet/ip.h>
45 #include <arpa/nameser.h>
48 #include "netinet/ipl.h"
52 static const char rcsid
[] = "@(#)Id: ipfs.c,v 1.12 2003/12/01 01:56:53 darrenr Exp";
56 # define IPF_SAVEDIR "/var/db/ipf"
59 # define IPF_NATFILE "ipnat.ipf"
62 # define IPF_STATEFILE "ipstate.ipf"
65 #if !defined(__SVR4) && defined(__GNUC__)
66 extern char *index
__P((const char *, int));
72 int main
__P((int, char *[]));
73 void usage
__P((void));
74 int changestateif
__P((char *, char *));
75 int changenatif
__P((char *, char *));
76 int readstate
__P((int, char *));
77 int readnat
__P((int, char *));
78 int writestate
__P((int, char *));
79 int opendevice
__P((char *));
80 void closedevice
__P((int));
81 int setlock
__P((int, int));
82 int writeall
__P((char *));
83 int readall
__P((char *));
84 int writenat
__P((int, char *));
92 const char *zoneopt
= "[-G|-z zonename] ";
93 fprintf(stderr
, "usage: %s %s[-nv] -l\n", progname
, zoneopt
);
94 fprintf(stderr
, "usage: %s %s[-nv] -u\n", progname
, zoneopt
);
95 fprintf(stderr
, "usage: %s %s[-nv] [-d <dir>] -R\n", progname
, zoneopt
);
96 fprintf(stderr
, "usage: %s %s[-nv] [-d <dir>] -W\n", progname
, zoneopt
);
97 fprintf(stderr
, "usage: %s %s[-nv] [-N|-S] [-f <file>] -r\n", progname
,
99 fprintf(stderr
, "usage: %s %s[-nv] [-N|-S] [-f <file>] -w\n", progname
,
101 fprintf(stderr
, "usage: %s %s[-nv] [-N|-S] -f <file> -i <if1>,<if2>\n",
108 * Change interface names in state information saved out to disk.
110 int changestateif(ifs
, fname
)
113 int fd
, olen
, nlen
, rw
;
118 s
= strchr(ifs
, ',');
124 if (nlen
>= sizeof(ips
.ips_is
.is_ifname
) ||
125 olen
>= sizeof(ips
.ips_is
.is_ifname
))
128 fd
= open(fname
, O_RDWR
);
134 for (pos
= 0; read(fd
, &ips
, sizeof(ips
)) == sizeof(ips
); ) {
136 if (!strncmp(ips
.ips_is
.is_ifname
[0], ifs
, olen
+ 1)) {
137 strcpy(ips
.ips_is
.is_ifname
[0], s
);
140 if (!strncmp(ips
.ips_is
.is_ifname
[1], ifs
, olen
+ 1)) {
141 strcpy(ips
.ips_is
.is_ifname
[1], s
);
145 if (lseek(fd
, pos
, SEEK_SET
) != pos
) {
149 if (write(fd
, &ips
, sizeof(ips
)) != sizeof(ips
)) {
154 pos
= lseek(fd
, 0, SEEK_CUR
);
163 * Change interface names in NAT information saved out to disk.
165 int changenatif(ifs
, fname
)
168 int fd
, olen
, nlen
, rw
;
174 s
= strchr(ifs
, ',');
181 if (nlen
>= sizeof(nat
->nat_ifnames
[0]) ||
182 olen
>= sizeof(nat
->nat_ifnames
[0]))
185 fd
= open(fname
, O_RDWR
);
191 for (pos
= 0; read(fd
, &ipn
, sizeof(ipn
)) == sizeof(ipn
); ) {
193 if (!strncmp(nat
->nat_ifnames
[0], ifs
, olen
+ 1)) {
194 strcpy(nat
->nat_ifnames
[0], s
);
197 if (!strncmp(nat
->nat_ifnames
[1], ifs
, olen
+ 1)) {
198 strcpy(nat
->nat_ifnames
[1], s
);
202 if (lseek(fd
, pos
, SEEK_SET
) != pos
) {
206 if (write(fd
, &ipn
, sizeof(ipn
)) != sizeof(ipn
)) {
211 pos
= lseek(fd
, 0, SEEK_CUR
);
223 int c
, lock
= -1, devfd
= -1, err
= 0, rw
= -1, ns
= -1, set
= 0;
224 char *dirname
= NULL
, *filename
= NULL
, *ifs
= NULL
;
227 while ((c
= getopt(argc
, argv
, "d:f:G:lNnSRruvWwz:")) != -1)
231 if ((set
== 0) && !dirname
&& !filename
)
237 if ((set
== 0) && !dirname
&& !filename
)
243 setzonename_global(optarg
);
250 if (filename
|| dirname
|| set
)
256 opts
|= OPT_DONOTHING
;
259 if ((ns
>= 0) || dirname
|| (rw
!= -1) || set
)
265 if (dirname
|| (rw
!= -1) || (ns
== -1))
275 if ((ns
>= 0) || dirname
|| (rw
!= -1) || set
)
281 if (filename
|| dirname
|| set
)
290 if (dirname
|| (rw
!= -1) || (ns
== -1))
308 if (!filename
|| ns
< 0)
311 return changenatif(ifs
, filename
);
313 return changestateif(ifs
, filename
);
316 if ((ns
>= 0) || (lock
>= 0)) {
318 devfd
= opendevice(NULL
);
321 devfd
= opendevice(IPSTATE_NAME
);
323 devfd
= opendevice(IPNAT_NAME
);
330 err
= setlock(devfd
, lock
);
332 if (rw
& 1) { /* WRITE */
334 err
= writeall(dirname
);
337 err
= writenat(devfd
, filename
);
339 err
= writestate(devfd
, filename
);
343 err
= readall(dirname
);
346 err
= readnat(devfd
, filename
);
348 err
= readstate(devfd
, filename
);
356 int opendevice(ipfdev
)
361 if (opts
& OPT_DONOTHING
)
367 if ((fd
= open(ipfdev
, O_RDWR
)) == -1)
368 if ((fd
= open(ipfdev
, O_RDONLY
)) == -1)
369 perror("open device");
371 if (setzone(fd
) != 0) {
387 int setlock(fd
, lock
)
390 if (opts
& OPT_VERBOSE
)
391 printf("Turn lock %s\n", lock
? "on" : "off");
392 if (!(opts
& OPT_DONOTHING
)) {
393 if (ioctl(fd
, SIOCSTLCK
, &lock
) == -1) {
397 if (opts
& OPT_VERBOSE
)
398 printf("Lock now %s\n", lock
? "on" : "off");
404 int writestate(fd
, file
)
408 ipstate_save_t ips
, *ipsp
;
413 file
= IPF_STATEFILE
;
415 wfd
= open(file
, O_WRONLY
|O_TRUNC
|O_CREAT
, 0600);
417 fprintf(stderr
, "%s ", file
);
418 perror("state:open");
423 bzero((char *)&obj
, sizeof(obj
));
424 bzero((char *)ipsp
, sizeof(ips
));
426 obj
.ipfo_rev
= IPFILTER_VERSION
;
427 obj
.ipfo_size
= sizeof(*ipsp
);
428 obj
.ipfo_type
= IPFOBJ_STATESAVE
;
433 if (opts
& OPT_VERBOSE
)
434 printf("Getting state from addr %p\n", ips
.ips_next
);
435 if (ioctl(fd
, SIOCSTGET
, &obj
)) {
438 perror("state:SIOCSTGET");
442 if (opts
& OPT_VERBOSE
)
443 printf("Got state next %p\n", ips
.ips_next
);
444 if (write(wfd
, ipsp
, sizeof(ips
)) != sizeof(ips
)) {
445 perror("state:write");
449 } while (ips
.ips_next
!= NULL
);
456 int readstate(fd
, file
)
460 ipstate_save_t ips
, *is
, *ipshead
= NULL
, *is1
, *ipstail
= NULL
;
465 file
= IPF_STATEFILE
;
467 sfd
= open(file
, O_RDONLY
, 0600);
469 fprintf(stderr
, "%s ", file
);
474 bzero((char *)&ips
, sizeof(ips
));
477 * 1. Read all state information in.
480 i
= read(sfd
, &ips
, sizeof(ips
));
488 if (i
!= sizeof(ips
)) {
489 fprintf(stderr
, "state:incomplete read: %d != %d\n",
490 i
, (int)sizeof(ips
));
494 is
= (ipstate_save_t
*)malloc(sizeof(*is
));
496 fprintf(stderr
, "malloc failed\n");
500 bcopy((char *)&ips
, (char *)is
, sizeof(ips
));
503 * Check to see if this is the first state entry that will
504 * reference a particular rule and if so, flag it as such
505 * else just adjust the rule pointer to become a pointer to
506 * the other. We do this so we have a means later for tracking
507 * who is referencing us when we get back the real pointer
508 * in is_rule after doing the ioctl.
510 for (is1
= ipshead
; is1
!= NULL
; is1
= is1
->ips_next
)
511 if (is1
->ips_rule
== is
->ips_rule
)
514 is
->ips_is
.is_flags
|= SI_NEWFR
;
516 is
->ips_rule
= (void *)&is1
->ips_rule
;
519 * Use a tail-queue type list (add things to the end)..
525 ipstail
->ips_next
= is
;
531 obj
.ipfo_rev
= IPFILTER_VERSION
;
532 obj
.ipfo_size
= sizeof(*is
);
533 obj
.ipfo_type
= IPFOBJ_STATESAVE
;
535 for (is
= ipshead
; is
; is
= is
->ips_next
) {
536 if (opts
& OPT_VERBOSE
)
537 printf("Loading new state table entry\n");
538 if (is
->ips_is
.is_flags
& SI_NEWFR
) {
539 if (opts
& OPT_VERBOSE
)
540 printf("Loading new filter rule\n");
544 if (!(opts
& OPT_DONOTHING
))
545 if (ioctl(fd
, SIOCSTPUT
, &obj
)) {
550 if (is
->ips_is
.is_flags
& SI_NEWFR
) {
551 if (opts
& OPT_VERBOSE
)
552 printf("Real rule addr %p\n", is
->ips_rule
);
553 for (is1
= is
->ips_next
; is1
; is1
= is1
->ips_next
)
554 if (is1
->ips_rule
== (frentry_t
*)&is
->ips_rule
)
555 is1
->ips_rule
= is
->ips_rule
;
563 int readnat(fd
, file
)
567 nat_save_t ipn
, *in
, *ipnhead
= NULL
, *in1
, *ipntail
= NULL
;
582 nfd
= open(file
, O_RDONLY
);
584 fprintf(stderr
, "%s ", file
);
589 bzero((char *)&ipn
, sizeof(ipn
));
592 * 1. Read all state information in.
595 i
= read(nfd
, &ipn
, sizeof(ipn
));
603 if (i
!= sizeof(ipn
)) {
604 fprintf(stderr
, "nat:incomplete read: %d != %d\n",
605 i
, (int)sizeof(ipn
));
610 in
= (nat_save_t
*)malloc(ipn
.ipn_dsize
);
614 if (ipn
.ipn_dsize
> sizeof(ipn
)) {
615 n
= ipn
.ipn_dsize
- sizeof(ipn
);
617 s
= in
->ipn_data
+ sizeof(in
->ipn_data
);
623 "nat:incomplete read: %d != %d\n",
630 bcopy((char *)&ipn
, (char *)in
, sizeof(ipn
));
633 * Check to see if this is the first NAT entry that will
634 * reference a particular rule and if so, flag it as such
635 * else just adjust the rule pointer to become a pointer to
636 * the other. We do this so we have a means later for tracking
637 * who is referencing us when we get back the real pointer
638 * in is_rule after doing the ioctl.
641 if (nat
->nat_fr
!= NULL
) {
642 for (in1
= ipnhead
; in1
!= NULL
; in1
= in1
->ipn_next
)
643 if (in1
->ipn_rule
== nat
->nat_fr
)
646 nat
->nat_flags
|= SI_NEWFR
;
648 nat
->nat_fr
= &in1
->ipn_fr
;
652 * Use a tail-queue type list (add things to the end)..
658 ipntail
->ipn_next
= in
;
665 obj
.ipfo_rev
= IPFILTER_VERSION
;
666 obj
.ipfo_type
= IPFOBJ_NATSAVE
;
668 for (in
= ipnhead
; in
; in
= in
->ipn_next
) {
669 if (opts
& OPT_VERBOSE
)
670 printf("Loading new NAT table entry\n");
672 if (nat
->nat_flags
& SI_NEWFR
) {
673 if (opts
& OPT_VERBOSE
)
674 printf("Loading new filter rule\n");
678 obj
.ipfo_size
= in
->ipn_dsize
;
679 if (!(opts
& OPT_DONOTHING
))
680 if (ioctl(fd
, SIOCSTPUT
, &obj
)) {
681 fprintf(stderr
, "in=%p:", in
);
686 if (nat
->nat_flags
& SI_NEWFR
) {
687 if (opts
& OPT_VERBOSE
)
688 printf("Real rule addr %p\n", nat
->nat_fr
);
689 for (in1
= in
->ipn_next
; in1
; in1
= in1
->ipn_next
)
690 if (in1
->ipn_rule
== &in
->ipn_fr
)
691 in1
->ipn_rule
= nat
->nat_fr
;
699 int writenat(fd
, file
)
703 nat_save_t
*ipnp
= NULL
, *next
= NULL
;
711 nfd
= open(file
, O_WRONLY
|O_TRUNC
|O_CREAT
, 0600);
713 fprintf(stderr
, "%s ", file
);
718 obj
.ipfo_rev
= IPFILTER_VERSION
;
719 obj
.ipfo_type
= IPFOBJ_NATSAVE
;
722 if (opts
& OPT_VERBOSE
)
723 printf("Getting nat from addr %p\n", ipnp
);
726 if (ioctl(fd
, SIOCSTGSZ
, &ng
)) {
727 perror("nat:SIOCSTGSZ");
734 if (opts
& OPT_VERBOSE
)
735 printf("NAT size %d from %p\n", ng
.ng_sz
, ng
.ng_ptr
);
741 ipnp
= malloc(ng
.ng_sz
);
743 ipnp
= realloc((char *)ipnp
, ng
.ng_sz
);
746 "malloc for %d bytes failed\n", ng
.ng_sz
);
750 bzero((char *)ipnp
, ng
.ng_sz
);
751 obj
.ipfo_size
= ng
.ng_sz
;
753 ipnp
->ipn_dsize
= ng
.ng_sz
;
754 ipnp
->ipn_next
= next
;
755 if (ioctl(fd
, SIOCSTGET
, &obj
)) {
758 perror("nat:SIOCSTGET");
764 if (opts
& OPT_VERBOSE
)
765 printf("Got nat next %p ipn_dsize %d ng_sz %d\n",
766 ipnp
->ipn_next
, ipnp
->ipn_dsize
, ng
.ng_sz
);
767 if (write(nfd
, ipnp
, ipnp
->ipn_dsize
) != ipnp
->ipn_dsize
) {
773 next
= ipnp
->ipn_next
;
774 } while (ipnp
&& next
);
783 int writeall(dirname
)
789 dirname
= IPF_SAVEDIR
;
791 if (chdir(dirname
)) {
792 fprintf(stderr
, "IPF_SAVEDIR=%s: ", dirname
);
793 perror("chdir(IPF_SAVEDIR)");
797 fd
= opendevice(NULL
);
800 if (setlock(fd
, 1)) {
805 devfd
= opendevice(IPSTATE_NAME
);
808 if (writestate(devfd
, NULL
))
812 devfd
= opendevice(IPNAT_NAME
);
815 if (writenat(devfd
, NULL
))
819 if (setlock(fd
, 0)) {
840 dirname
= IPF_SAVEDIR
;
842 if (chdir(dirname
)) {
843 perror("chdir(IPF_SAVEDIR)");
847 fd
= opendevice(NULL
);
850 if (setlock(fd
, 1)) {
855 devfd
= opendevice(IPSTATE_NAME
);
858 if (readstate(devfd
, NULL
))
862 devfd
= opendevice(IPNAT_NAME
);
865 if (readnat(devfd
, NULL
))
869 if (setlock(fd
, 0)) {