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