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