4 * Copyright (C) 2001-2006 by Darren Reed.
6 * See the IPFILTER.LICENCE file for details on licencing.
9 # ifndef __FreeBSD_cc_version
10 # include <osreldate.h>
12 # if __FreeBSD_cc_version < 430000
13 # include <osreldate.h>
22 #if !defined(__SVR4) && !defined(__GNUC__)
25 #include <sys/types.h>
26 #include <sys/param.h>
30 #include <sys/socket.h>
31 #include <sys/ioctl.h>
32 #include <netinet/in.h>
33 #include <netinet/in_systm.h>
36 #if __FreeBSD_version >= 300000
37 # include <net/if_var.h>
39 #include <netinet/ip.h>
41 #include <arpa/nameser.h>
44 #include "netinet/ipl.h"
47 static const char rcsid
[] = "@(#)Id: ipfs.c,v 1.12 2003/12/01 01:56:53 darrenr Exp";
51 # define IPF_SAVEDIR "/var/db/ipf"
54 # define IPF_NATFILE "ipnat.ipf"
57 # define IPF_STATEFILE "ipstate.ipf"
60 #if !defined(__SVR4) && defined(__GNUC__)
61 extern char *index
__P((const char *, int));
67 int main
__P((int, char *[]));
68 void usage
__P((void));
69 int changestateif
__P((char *, char *));
70 int changenatif
__P((char *, char *));
71 int readstate
__P((int, char *));
72 int readnat
__P((int, char *));
73 int writestate
__P((int, char *));
74 int opendevice
__P((char *));
75 void closedevice
__P((int));
76 int setlock
__P((int, int));
77 int writeall
__P((char *));
78 int readall
__P((char *));
79 int writenat
__P((int, char *));
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",
100 * Change interface names in state information saved out to disk.
102 int changestateif(ifs
, fname
)
105 int fd
, olen
, nlen
, rw
;
110 s
= strchr(ifs
, ',');
116 if (nlen
>= sizeof(ips
.ips_is
.is_ifname
) ||
117 olen
>= sizeof(ips
.ips_is
.is_ifname
))
120 fd
= open(fname
, O_RDWR
);
126 for (pos
= 0; read(fd
, &ips
, sizeof(ips
)) == sizeof(ips
); ) {
128 if (!strncmp(ips
.ips_is
.is_ifname
[0], ifs
, olen
+ 1)) {
129 strcpy(ips
.ips_is
.is_ifname
[0], s
);
132 if (!strncmp(ips
.ips_is
.is_ifname
[1], ifs
, olen
+ 1)) {
133 strcpy(ips
.ips_is
.is_ifname
[1], s
);
136 if (!strncmp(ips
.ips_is
.is_ifname
[2], ifs
, olen
+ 1)) {
137 strcpy(ips
.ips_is
.is_ifname
[2], s
);
140 if (!strncmp(ips
.ips_is
.is_ifname
[3], ifs
, olen
+ 1)) {
141 strcpy(ips
.ips_is
.is_ifname
[3], 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
);
201 if (!strncmp(nat
->nat_ifnames
[2], ifs
, olen
+ 1)) {
202 strcpy(nat
->nat_ifnames
[2], s
);
205 if (!strncmp(nat
->nat_ifnames
[3], ifs
, olen
+ 1)) {
206 strcpy(nat
->nat_ifnames
[3], s
);
210 if (lseek(fd
, pos
, SEEK_SET
) != pos
) {
214 if (write(fd
, &ipn
, sizeof(ipn
)) != sizeof(ipn
)) {
219 pos
= lseek(fd
, 0, SEEK_CUR
);
231 int c
, lock
= -1, devfd
= -1, err
= 0, rw
= -1, ns
= -1, set
= 0;
232 char *dirname
= NULL
, *filename
= NULL
, *ifs
= NULL
;
235 while ((c
= getopt(argc
, argv
, "d:f:i:lNnSRruvWw")) != -1)
239 if ((set
== 0) && !dirname
&& !filename
)
245 if ((set
!= 0) && !dirname
&& !filename
)
255 if (filename
|| dirname
|| set
)
261 opts
|= OPT_DONOTHING
;
264 if ((ns
>= 0) || dirname
|| (rw
!= -1) || set
)
270 if (dirname
|| (rw
!= -1) || (ns
== -1))
280 if ((ns
>= 0) || dirname
|| (rw
!= -1) || set
)
286 if (filename
|| dirname
|| set
)
295 if (dirname
|| (rw
!= -1) || (ns
== -1))
310 if (!filename
|| ns
< 0)
313 return changenatif(ifs
, filename
);
315 return changestateif(ifs
, filename
);
318 if ((ns
>= 0) || (lock
>= 0)) {
320 devfd
= opendevice(NULL
);
323 devfd
= opendevice(IPSTATE_NAME
);
325 devfd
= opendevice(IPNAT_NAME
);
332 err
= setlock(devfd
, lock
);
334 if (rw
& 1) { /* WRITE */
336 err
= writeall(dirname
);
339 err
= writenat(devfd
, filename
);
341 err
= writestate(devfd
, filename
);
345 err
= readall(dirname
);
348 err
= readnat(devfd
, filename
);
350 err
= readstate(devfd
, filename
);
358 int opendevice(ipfdev
)
363 if (opts
& OPT_DONOTHING
)
369 if ((fd
= open(ipfdev
, O_RDWR
)) == -1)
370 if ((fd
= open(ipfdev
, O_RDONLY
)) == -1)
371 perror("open device");
383 int setlock(fd
, lock
)
386 if (opts
& OPT_VERBOSE
)
387 printf("Turn lock %s\n", lock
? "on" : "off");
388 if (!(opts
& OPT_DONOTHING
)) {
389 if (ioctl(fd
, SIOCSTLCK
, &lock
) == -1) {
393 if (opts
& OPT_VERBOSE
)
394 printf("Lock now %s\n", lock
? "on" : "off");
400 int writestate(fd
, file
)
404 ipstate_save_t ips
, *ipsp
;
409 file
= IPF_STATEFILE
;
411 wfd
= open(file
, O_WRONLY
|O_TRUNC
|O_CREAT
, 0600);
413 fprintf(stderr
, "%s ", file
);
414 perror("state:open");
419 bzero((char *)&obj
, sizeof(obj
));
420 bzero((char *)ipsp
, sizeof(ips
));
422 obj
.ipfo_rev
= IPFILTER_VERSION
;
423 obj
.ipfo_size
= sizeof(*ipsp
);
424 obj
.ipfo_type
= IPFOBJ_STATESAVE
;
429 if (opts
& OPT_VERBOSE
)
430 printf("Getting state from addr %p\n", ips
.ips_next
);
431 if (ioctl(fd
, SIOCSTGET
, &obj
)) {
434 perror("state:SIOCSTGET");
438 if (opts
& OPT_VERBOSE
)
439 printf("Got state next %p\n", ips
.ips_next
);
440 if (write(wfd
, ipsp
, sizeof(ips
)) != sizeof(ips
)) {
441 perror("state:write");
445 } while (ips
.ips_next
!= NULL
);
452 int readstate(fd
, file
)
456 ipstate_save_t ips
, *is
, *ipshead
= NULL
, *is1
, *ipstail
= NULL
;
461 file
= IPF_STATEFILE
;
463 sfd
= open(file
, O_RDONLY
, 0600);
465 fprintf(stderr
, "%s ", file
);
470 bzero((char *)&ips
, sizeof(ips
));
473 * 1. Read all state information in.
476 i
= read(sfd
, &ips
, sizeof(ips
));
483 if (i
!= sizeof(ips
)) {
484 fprintf(stderr
, "state:incomplete read: %d != %d\n",
485 i
, (int)sizeof(ips
));
488 is
= (ipstate_save_t
*)malloc(sizeof(*is
));
490 fprintf(stderr
, "malloc failed\n");
494 bcopy((char *)&ips
, (char *)is
, sizeof(ips
));
497 * Check to see if this is the first state entry that will
498 * reference a particular rule and if so, flag it as such
499 * else just adjust the rule pointer to become a pointer to
500 * the other. We do this so we have a means later for tracking
501 * who is referencing us when we get back the real pointer
502 * in is_rule after doing the ioctl.
504 for (is1
= ipshead
; is1
!= NULL
; is1
= is1
->ips_next
)
505 if (is1
->ips_rule
== is
->ips_rule
)
508 is
->ips_is
.is_flags
|= SI_NEWFR
;
510 is
->ips_rule
= (void *)&is1
->ips_rule
;
513 * Use a tail-queue type list (add things to the end)..
519 ipstail
->ips_next
= is
;
525 obj
.ipfo_rev
= IPFILTER_VERSION
;
526 obj
.ipfo_size
= sizeof(*is
);
527 obj
.ipfo_type
= IPFOBJ_STATESAVE
;
529 while ((is
= ipshead
) != NULL
) {
530 if (opts
& OPT_VERBOSE
)
531 printf("Loading new state table entry\n");
532 if (is
->ips_is
.is_flags
& SI_NEWFR
) {
533 if (opts
& OPT_VERBOSE
)
534 printf("Loading new filter rule\n");
538 if (!(opts
& OPT_DONOTHING
))
539 if (ioctl(fd
, SIOCSTPUT
, &obj
)) {
544 if (is
->ips_is
.is_flags
& SI_NEWFR
) {
545 if (opts
& OPT_VERBOSE
)
546 printf("Real rule addr %p\n", is
->ips_rule
);
547 for (is1
= is
->ips_next
; is1
; is1
= is1
->ips_next
)
548 if (is1
->ips_rule
== (frentry_t
*)&is
->ips_rule
)
549 is1
->ips_rule
= is
->ips_rule
;
552 ipshead
= is
->ips_next
;
559 while ((is
= ipshead
) != NULL
) {
560 ipshead
= is
->ips_next
;
569 int readnat(fd
, file
)
573 nat_save_t ipn
, *in
, *ipnhead
= NULL
, *in1
, *ipntail
= NULL
;
588 nfd
= open(file
, O_RDONLY
);
590 fprintf(stderr
, "%s ", file
);
595 bzero((char *)&ipn
, sizeof(ipn
));
598 * 1. Read all state information in.
601 i
= read(nfd
, &ipn
, sizeof(ipn
));
608 if (i
!= sizeof(ipn
)) {
609 fprintf(stderr
, "nat:incomplete read: %d != %d\n",
610 i
, (int)sizeof(ipn
));
614 in
= (nat_save_t
*)malloc(ipn
.ipn_dsize
);
616 fprintf(stderr
, "nat:cannot malloc nat save atruct\n");
620 if (ipn
.ipn_dsize
> sizeof(ipn
)) {
621 n
= ipn
.ipn_dsize
- sizeof(ipn
);
623 s
= in
->ipn_data
+ sizeof(in
->ipn_data
);
629 "nat:incomplete read: %d != %d\n",
635 bcopy((char *)&ipn
, (char *)in
, sizeof(ipn
));
638 * Check to see if this is the first NAT entry that will
639 * reference a particular rule and if so, flag it as such
640 * else just adjust the rule pointer to become a pointer to
641 * the other. We do this so we have a means later for tracking
642 * who is referencing us when we get back the real pointer
643 * in is_rule after doing the ioctl.
646 if (nat
->nat_fr
!= NULL
) {
647 for (in1
= ipnhead
; in1
!= NULL
; in1
= in1
->ipn_next
)
648 if (in1
->ipn_rule
== nat
->nat_fr
)
651 nat
->nat_flags
|= SI_NEWFR
;
653 nat
->nat_fr
= &in1
->ipn_fr
;
657 * Use a tail-queue type list (add things to the end)..
663 ipntail
->ipn_next
= in
;
670 obj
.ipfo_rev
= IPFILTER_VERSION
;
671 obj
.ipfo_type
= IPFOBJ_NATSAVE
;
673 while ((in
= ipnhead
) != NULL
) {
674 if (opts
& OPT_VERBOSE
)
675 printf("Loading new NAT table entry\n");
677 if (nat
->nat_flags
& SI_NEWFR
) {
678 if (opts
& OPT_VERBOSE
)
679 printf("Loading new filter rule\n");
683 obj
.ipfo_size
= in
->ipn_dsize
;
684 if (!(opts
& OPT_DONOTHING
))
685 if (ioctl(fd
, SIOCSTPUT
, &obj
)) {
686 fprintf(stderr
, "in=%p:", in
);
691 if (nat
->nat_flags
& SI_NEWFR
) {
692 if (opts
& OPT_VERBOSE
)
693 printf("Real rule addr %p\n", nat
->nat_fr
);
694 for (in1
= in
->ipn_next
; in1
; in1
= in1
->ipn_next
)
695 if (in1
->ipn_rule
== &in
->ipn_fr
)
696 in1
->ipn_rule
= nat
->nat_fr
;
699 ipnhead
= in
->ipn_next
;
706 while ((in
= ipnhead
) != NULL
) {
707 ipnhead
= in
->ipn_next
;
716 int writenat(fd
, file
)
720 nat_save_t
*ipnp
= NULL
, *next
= NULL
;
728 nfd
= open(file
, O_WRONLY
|O_TRUNC
|O_CREAT
, 0600);
730 fprintf(stderr
, "%s ", file
);
735 obj
.ipfo_rev
= IPFILTER_VERSION
;
736 obj
.ipfo_type
= IPFOBJ_NATSAVE
;
739 if (opts
& OPT_VERBOSE
)
740 printf("Getting nat from addr %p\n", ipnp
);
743 if (ioctl(fd
, SIOCSTGSZ
, &ng
)) {
744 perror("nat:SIOCSTGSZ");
751 if (opts
& OPT_VERBOSE
)
752 printf("NAT size %d from %p\n", ng
.ng_sz
, ng
.ng_ptr
);
758 ipnp
= malloc(ng
.ng_sz
);
760 ipnp
= realloc((char *)ipnp
, ng
.ng_sz
);
763 "malloc for %d bytes failed\n", ng
.ng_sz
);
767 bzero((char *)ipnp
, ng
.ng_sz
);
768 obj
.ipfo_size
= ng
.ng_sz
;
770 ipnp
->ipn_dsize
= ng
.ng_sz
;
771 ipnp
->ipn_next
= next
;
772 if (ioctl(fd
, SIOCSTGET
, &obj
)) {
775 perror("nat:SIOCSTGET");
781 if (opts
& OPT_VERBOSE
)
782 printf("Got nat next %p ipn_dsize %d ng_sz %d\n",
783 ipnp
->ipn_next
, ipnp
->ipn_dsize
, ng
.ng_sz
);
784 if (write(nfd
, ipnp
, ipnp
->ipn_dsize
) != ipnp
->ipn_dsize
) {
790 next
= ipnp
->ipn_next
;
791 } while (ipnp
&& next
);
800 int writeall(dirname
)
806 dirname
= IPF_SAVEDIR
;
808 if (chdir(dirname
)) {
809 fprintf(stderr
, "IPF_SAVEDIR=%s: ", dirname
);
810 perror("chdir(IPF_SAVEDIR)");
814 fd
= opendevice(NULL
);
817 if (setlock(fd
, 1)) {
822 devfd
= opendevice(IPSTATE_NAME
);
825 if (writestate(devfd
, NULL
))
829 devfd
= opendevice(IPNAT_NAME
);
832 if (writenat(devfd
, NULL
))
836 if (setlock(fd
, 0)) {
857 dirname
= IPF_SAVEDIR
;
859 if (chdir(dirname
)) {
860 perror("chdir(IPF_SAVEDIR)");
864 fd
= opendevice(NULL
);
867 if (setlock(fd
, 1)) {
872 devfd
= opendevice(IPSTATE_NAME
);
875 if (readstate(devfd
, NULL
))
879 devfd
= opendevice(IPNAT_NAME
);
882 if (readnat(devfd
, NULL
))
886 if (setlock(fd
, 0)) {