1145519Sdarrenr/*	$FreeBSD$	*/
2145510Sdarrenr
3145510Sdarrenr/*
4255332Scy * Copyright (C) 2012 by Darren Reed.
5145510Sdarrenr *
6145510Sdarrenr * See the IPFILTER.LICENCE file for details on licencing.
7145510Sdarrenr */
8145510Sdarrenr#ifdef	__FreeBSD__
9145510Sdarrenr# ifndef __FreeBSD_cc_version
10145510Sdarrenr#  include <osreldate.h>
11145510Sdarrenr# else
12145510Sdarrenr#  if __FreeBSD_cc_version < 430000
13145510Sdarrenr#   include <osreldate.h>
14145510Sdarrenr#  endif
15145510Sdarrenr# endif
16145510Sdarrenr#endif
17145510Sdarrenr#include <stdio.h>
18145510Sdarrenr#include <unistd.h>
19145510Sdarrenr#include <string.h>
20145510Sdarrenr#include <fcntl.h>
21145510Sdarrenr#include <errno.h>
22145510Sdarrenr#if !defined(__SVR4) && !defined(__GNUC__)
23145510Sdarrenr#include <strings.h>
24145510Sdarrenr#endif
25145510Sdarrenr#include <sys/types.h>
26145510Sdarrenr#include <sys/param.h>
27145510Sdarrenr#include <sys/file.h>
28145510Sdarrenr#include <stdlib.h>
29145510Sdarrenr#include <stddef.h>
30145510Sdarrenr#include <sys/socket.h>
31145510Sdarrenr#include <sys/ioctl.h>
32145510Sdarrenr#include <netinet/in.h>
33145510Sdarrenr#include <netinet/in_systm.h>
34145510Sdarrenr#include <sys/time.h>
35145510Sdarrenr#include <net/if.h>
36145510Sdarrenr#if __FreeBSD_version >= 300000
37145510Sdarrenr# include <net/if_var.h>
38145510Sdarrenr#endif
39145510Sdarrenr#include <netinet/ip.h>
40145510Sdarrenr#include <netdb.h>
41145510Sdarrenr#include <arpa/nameser.h>
42145510Sdarrenr#include <resolv.h>
43145510Sdarrenr#include "ipf.h"
44145554Sdarrenr#include "netinet/ipl.h"
45145510Sdarrenr
46145510Sdarrenr#if !defined(lint)
47255332Scystatic const char rcsid[] = "@(#)$Id$";
48145510Sdarrenr#endif
49145510Sdarrenr
50145510Sdarrenr#ifndef	IPF_SAVEDIR
51145510Sdarrenr# define	IPF_SAVEDIR	"/var/db/ipf"
52145510Sdarrenr#endif
53145510Sdarrenr#ifndef IPF_NATFILE
54145510Sdarrenr# define	IPF_NATFILE	"ipnat.ipf"
55145510Sdarrenr#endif
56145510Sdarrenr#ifndef IPF_STATEFILE
57145510Sdarrenr# define	IPF_STATEFILE	"ipstate.ipf"
58145510Sdarrenr#endif
59145510Sdarrenr
60145510Sdarrenr#if !defined(__SVR4) && defined(__GNUC__)
61145510Sdarrenrextern	char	*index __P((const char *, int));
62145510Sdarrenr#endif
63145510Sdarrenr
64145510Sdarrenrextern	char	*optarg;
65145510Sdarrenrextern	int	optind;
66145510Sdarrenr
67145510Sdarrenrint	main __P((int, char *[]));
68145510Sdarrenrvoid	usage __P((void));
69145510Sdarrenrint	changestateif __P((char *, char *));
70145510Sdarrenrint	changenatif __P((char *, char *));
71145510Sdarrenrint	readstate __P((int, char *));
72145510Sdarrenrint	readnat __P((int, char *));
73145510Sdarrenrint	writestate __P((int, char *));
74145510Sdarrenrint	opendevice __P((char *));
75145510Sdarrenrvoid	closedevice __P((int));
76145510Sdarrenrint	setlock __P((int, int));
77145510Sdarrenrint	writeall __P((char *));
78145510Sdarrenrint	readall __P((char *));
79145510Sdarrenrint	writenat __P((int, char *));
80145510Sdarrenr
81145510Sdarrenrint	opts = 0;
82145510Sdarrenrchar	*progname;
83145510Sdarrenr
84145510Sdarrenr
85145510Sdarrenrvoid usage()
86145510Sdarrenr{
87145510Sdarrenr	fprintf(stderr, "usage: %s [-nv] -l\n", progname);
88145510Sdarrenr	fprintf(stderr, "usage: %s [-nv] -u\n", progname);
89145510Sdarrenr	fprintf(stderr, "usage: %s [-nv] [-d <dir>] -R\n", progname);
90145510Sdarrenr	fprintf(stderr, "usage: %s [-nv] [-d <dir>] -W\n", progname);
91145510Sdarrenr	fprintf(stderr, "usage: %s [-nNSv] [-f <file>] -r\n", progname);
92145510Sdarrenr	fprintf(stderr, "usage: %s [-nNSv] [-f <file>] -w\n", progname);
93145510Sdarrenr	fprintf(stderr, "usage: %s [-nNSv] -f <filename> -i <if1>,<if2>\n",
94145510Sdarrenr		progname);
95145510Sdarrenr	exit(1);
96145510Sdarrenr}
97145510Sdarrenr
98145510Sdarrenr
99145510Sdarrenr/*
100145510Sdarrenr * Change interface names in state information saved out to disk.
101145510Sdarrenr */
102145510Sdarrenrint changestateif(ifs, fname)
103255332Scy	char *ifs, *fname;
104145510Sdarrenr{
105145510Sdarrenr	int fd, olen, nlen, rw;
106145510Sdarrenr	ipstate_save_t ips;
107145510Sdarrenr	off_t pos;
108145510Sdarrenr	char *s;
109145510Sdarrenr
110145510Sdarrenr	s = strchr(ifs, ',');
111145510Sdarrenr	if (!s)
112145510Sdarrenr		usage();
113145510Sdarrenr	*s++ = '\0';
114145510Sdarrenr	nlen = strlen(s);
115145510Sdarrenr	olen = strlen(ifs);
116145510Sdarrenr	if (nlen >= sizeof(ips.ips_is.is_ifname) ||
117145510Sdarrenr	    olen >= sizeof(ips.ips_is.is_ifname))
118145510Sdarrenr		usage();
119145510Sdarrenr
120145510Sdarrenr	fd = open(fname, O_RDWR);
121145510Sdarrenr	if (fd == -1) {
122145510Sdarrenr		perror("open");
123145510Sdarrenr		exit(1);
124145510Sdarrenr	}
125145510Sdarrenr
126145510Sdarrenr	for (pos = 0; read(fd, &ips, sizeof(ips)) == sizeof(ips); ) {
127145510Sdarrenr		rw = 0;
128145510Sdarrenr		if (!strncmp(ips.ips_is.is_ifname[0], ifs, olen + 1)) {
129145510Sdarrenr			strcpy(ips.ips_is.is_ifname[0], s);
130145510Sdarrenr			rw = 1;
131145510Sdarrenr		}
132145510Sdarrenr		if (!strncmp(ips.ips_is.is_ifname[1], ifs, olen + 1)) {
133145510Sdarrenr			strcpy(ips.ips_is.is_ifname[1], s);
134145510Sdarrenr			rw = 1;
135145510Sdarrenr		}
136170268Sdarrenr		if (!strncmp(ips.ips_is.is_ifname[2], ifs, olen + 1)) {
137170268Sdarrenr			strcpy(ips.ips_is.is_ifname[2], s);
138170268Sdarrenr			rw = 1;
139170268Sdarrenr		}
140170268Sdarrenr		if (!strncmp(ips.ips_is.is_ifname[3], ifs, olen + 1)) {
141170268Sdarrenr			strcpy(ips.ips_is.is_ifname[3], s);
142170268Sdarrenr			rw = 1;
143170268Sdarrenr		}
144145510Sdarrenr		if (rw == 1) {
145145510Sdarrenr			if (lseek(fd, pos, SEEK_SET) != pos) {
146145510Sdarrenr				perror("lseek");
147145510Sdarrenr				exit(1);
148145510Sdarrenr			}
149145510Sdarrenr			if (write(fd, &ips, sizeof(ips)) != sizeof(ips)) {
150145510Sdarrenr				perror("write");
151145510Sdarrenr				exit(1);
152145510Sdarrenr			}
153145510Sdarrenr		}
154145510Sdarrenr		pos = lseek(fd, 0, SEEK_CUR);
155145510Sdarrenr	}
156145510Sdarrenr	close(fd);
157145510Sdarrenr
158145510Sdarrenr	return 0;
159145510Sdarrenr}
160145510Sdarrenr
161145510Sdarrenr
162145510Sdarrenr/*
163145510Sdarrenr * Change interface names in NAT information saved out to disk.
164145510Sdarrenr */
165145510Sdarrenrint changenatif(ifs, fname)
166255332Scy	char *ifs, *fname;
167145510Sdarrenr{
168145510Sdarrenr	int fd, olen, nlen, rw;
169145510Sdarrenr	nat_save_t ipn;
170145510Sdarrenr	nat_t *nat;
171145510Sdarrenr	off_t pos;
172145510Sdarrenr	char *s;
173145510Sdarrenr
174145510Sdarrenr	s = strchr(ifs, ',');
175145510Sdarrenr	if (!s)
176145510Sdarrenr		usage();
177145510Sdarrenr	*s++ = '\0';
178145510Sdarrenr	nlen = strlen(s);
179145510Sdarrenr	olen = strlen(ifs);
180145510Sdarrenr	nat = &ipn.ipn_nat;
181145510Sdarrenr	if (nlen >= sizeof(nat->nat_ifnames[0]) ||
182145510Sdarrenr	    olen >= sizeof(nat->nat_ifnames[0]))
183145510Sdarrenr		usage();
184145510Sdarrenr
185145510Sdarrenr	fd = open(fname, O_RDWR);
186145510Sdarrenr	if (fd == -1) {
187145510Sdarrenr		perror("open");
188145510Sdarrenr		exit(1);
189145510Sdarrenr	}
190145510Sdarrenr
191145510Sdarrenr	for (pos = 0; read(fd, &ipn, sizeof(ipn)) == sizeof(ipn); ) {
192145510Sdarrenr		rw = 0;
193145510Sdarrenr		if (!strncmp(nat->nat_ifnames[0], ifs, olen + 1)) {
194145510Sdarrenr			strcpy(nat->nat_ifnames[0], s);
195145510Sdarrenr			rw = 1;
196145510Sdarrenr		}
197145510Sdarrenr		if (!strncmp(nat->nat_ifnames[1], ifs, olen + 1)) {
198145510Sdarrenr			strcpy(nat->nat_ifnames[1], s);
199145510Sdarrenr			rw = 1;
200145510Sdarrenr		}
201145510Sdarrenr		if (rw == 1) {
202145510Sdarrenr			if (lseek(fd, pos, SEEK_SET) != pos) {
203145510Sdarrenr				perror("lseek");
204145510Sdarrenr				exit(1);
205145510Sdarrenr			}
206145510Sdarrenr			if (write(fd, &ipn, sizeof(ipn)) != sizeof(ipn)) {
207145510Sdarrenr				perror("write");
208145510Sdarrenr				exit(1);
209145510Sdarrenr			}
210145510Sdarrenr		}
211145510Sdarrenr		pos = lseek(fd, 0, SEEK_CUR);
212145510Sdarrenr	}
213145510Sdarrenr	close(fd);
214145510Sdarrenr
215145510Sdarrenr	return 0;
216145510Sdarrenr}
217145510Sdarrenr
218145510Sdarrenr
219145510Sdarrenrint main(argc,argv)
220255332Scy	int argc;
221255332Scy	char *argv[];
222145510Sdarrenr{
223145510Sdarrenr	int c, lock = -1, devfd = -1, err = 0, rw = -1, ns = -1, set = 0;
224145510Sdarrenr	char *dirname = NULL, *filename = NULL, *ifs = NULL;
225145510Sdarrenr
226145510Sdarrenr	progname = argv[0];
227170268Sdarrenr	while ((c = getopt(argc, argv, "d:f:i:lNnSRruvWw")) != -1)
228145510Sdarrenr		switch (c)
229145510Sdarrenr		{
230145510Sdarrenr		case 'd' :
231145510Sdarrenr			if ((set == 0) && !dirname && !filename)
232145510Sdarrenr				dirname = optarg;
233145510Sdarrenr			else
234145510Sdarrenr				usage();
235145510Sdarrenr			break;
236145510Sdarrenr		case 'f' :
237145510Sdarrenr			if ((set != 0) && !dirname && !filename)
238145510Sdarrenr				filename = optarg;
239145510Sdarrenr			else
240145510Sdarrenr				usage();
241145510Sdarrenr			break;
242145510Sdarrenr		case 'i' :
243145510Sdarrenr			ifs = optarg;
244145510Sdarrenr			set = 1;
245145510Sdarrenr			break;
246145510Sdarrenr		case 'l' :
247145510Sdarrenr			if (filename || dirname || set)
248145510Sdarrenr				usage();
249145510Sdarrenr			lock = 1;
250145510Sdarrenr			set = 1;
251145510Sdarrenr			break;
252145510Sdarrenr		case 'n' :
253145510Sdarrenr			opts |= OPT_DONOTHING;
254145510Sdarrenr			break;
255145510Sdarrenr		case 'N' :
256145510Sdarrenr			if ((ns >= 0) || dirname || (rw != -1) || set)
257145510Sdarrenr				usage();
258145510Sdarrenr			ns = 0;
259145510Sdarrenr			set = 1;
260145510Sdarrenr			break;
261145510Sdarrenr		case 'r' :
262145510Sdarrenr			if (dirname || (rw != -1) || (ns == -1))
263145510Sdarrenr				usage();
264145510Sdarrenr			rw = 0;
265145510Sdarrenr			set = 1;
266145510Sdarrenr			break;
267145510Sdarrenr		case 'R' :
268145510Sdarrenr			rw = 2;
269145510Sdarrenr			set = 1;
270145510Sdarrenr			break;
271145510Sdarrenr		case 'S' :
272145510Sdarrenr			if ((ns >= 0) || dirname || (rw != -1) || set)
273145510Sdarrenr				usage();
274145510Sdarrenr			ns = 1;
275145510Sdarrenr			set = 1;
276145510Sdarrenr			break;
277145510Sdarrenr		case 'u' :
278145510Sdarrenr			if (filename || dirname || set)
279145510Sdarrenr				usage();
280145510Sdarrenr			lock = 0;
281145510Sdarrenr			set = 1;
282145510Sdarrenr			break;
283145510Sdarrenr		case 'v' :
284145510Sdarrenr			opts |= OPT_VERBOSE;
285145510Sdarrenr			break;
286145510Sdarrenr		case 'w' :
287145510Sdarrenr			if (dirname || (rw != -1) || (ns == -1))
288145510Sdarrenr				usage();
289145510Sdarrenr			rw = 1;
290145510Sdarrenr			set = 1;
291145510Sdarrenr			break;
292145510Sdarrenr		case 'W' :
293145510Sdarrenr			rw = 3;
294145510Sdarrenr			set = 1;
295145510Sdarrenr			break;
296145510Sdarrenr		case '?' :
297145510Sdarrenr		default :
298145510Sdarrenr			usage();
299145510Sdarrenr		}
300145510Sdarrenr
301145510Sdarrenr	if (ifs) {
302145510Sdarrenr		if (!filename || ns < 0)
303145510Sdarrenr			usage();
304145510Sdarrenr		if (ns == 0)
305145510Sdarrenr			return changenatif(ifs, filename);
306145510Sdarrenr		else
307145510Sdarrenr			return changestateif(ifs, filename);
308145510Sdarrenr	}
309145510Sdarrenr
310145510Sdarrenr	if ((ns >= 0) || (lock >= 0)) {
311145510Sdarrenr		if (lock >= 0)
312145510Sdarrenr			devfd = opendevice(NULL);
313145510Sdarrenr		else if (ns >= 0) {
314145510Sdarrenr			if (ns == 1)
315145510Sdarrenr				devfd = opendevice(IPSTATE_NAME);
316145510Sdarrenr			else if (ns == 0)
317145510Sdarrenr				devfd = opendevice(IPNAT_NAME);
318145510Sdarrenr		}
319145510Sdarrenr		if (devfd == -1)
320145510Sdarrenr			exit(1);
321145510Sdarrenr	}
322145510Sdarrenr
323145510Sdarrenr	if (lock >= 0)
324145510Sdarrenr		err = setlock(devfd, lock);
325145510Sdarrenr	else if (rw >= 0) {
326145510Sdarrenr		if (rw & 1) {	/* WRITE */
327145510Sdarrenr			if (rw & 2)
328145510Sdarrenr				err = writeall(dirname);
329145510Sdarrenr			else {
330145510Sdarrenr				if (ns == 0)
331145510Sdarrenr					err = writenat(devfd, filename);
332145510Sdarrenr				else if (ns == 1)
333145510Sdarrenr					err = writestate(devfd, filename);
334145510Sdarrenr			}
335145510Sdarrenr		} else {
336145510Sdarrenr			if (rw & 2)
337145510Sdarrenr				err = readall(dirname);
338145510Sdarrenr			else {
339145510Sdarrenr				if (ns == 0)
340145510Sdarrenr					err = readnat(devfd, filename);
341145510Sdarrenr				else if (ns == 1)
342145510Sdarrenr					err = readstate(devfd, filename);
343145510Sdarrenr			}
344145510Sdarrenr		}
345145510Sdarrenr	}
346145510Sdarrenr	return err;
347145510Sdarrenr}
348145510Sdarrenr
349145510Sdarrenr
350145510Sdarrenrint opendevice(ipfdev)
351255332Scy	char *ipfdev;
352145510Sdarrenr{
353145510Sdarrenr	int fd = -1;
354145510Sdarrenr
355145510Sdarrenr	if (opts & OPT_DONOTHING)
356145510Sdarrenr		return -2;
357145510Sdarrenr
358145510Sdarrenr	if (!ipfdev)
359145510Sdarrenr		ipfdev = IPL_NAME;
360145510Sdarrenr
361145510Sdarrenr	if ((fd = open(ipfdev, O_RDWR)) == -1)
362145510Sdarrenr		if ((fd = open(ipfdev, O_RDONLY)) == -1)
363145510Sdarrenr			perror("open device");
364145510Sdarrenr	return fd;
365145510Sdarrenr}
366145510Sdarrenr
367145510Sdarrenr
368145510Sdarrenrvoid closedevice(fd)
369255332Scy	int fd;
370145510Sdarrenr{
371145510Sdarrenr	close(fd);
372145510Sdarrenr}
373145510Sdarrenr
374145510Sdarrenr
375145510Sdarrenrint setlock(fd, lock)
376255332Scy	int fd, lock;
377145510Sdarrenr{
378145510Sdarrenr	if (opts & OPT_VERBOSE)
379145510Sdarrenr		printf("Turn lock %s\n", lock ? "on" : "off");
380145510Sdarrenr	if (!(opts & OPT_DONOTHING)) {
381145510Sdarrenr		if (ioctl(fd, SIOCSTLCK, &lock) == -1) {
382145510Sdarrenr			perror("SIOCSTLCK");
383145510Sdarrenr			return 1;
384145510Sdarrenr		}
385145510Sdarrenr		if (opts & OPT_VERBOSE)
386145510Sdarrenr			printf("Lock now %s\n", lock ? "on" : "off");
387145510Sdarrenr	}
388145510Sdarrenr	return 0;
389145510Sdarrenr}
390145510Sdarrenr
391145510Sdarrenr
392145510Sdarrenrint writestate(fd, file)
393255332Scy	int fd;
394255332Scy	char *file;
395145510Sdarrenr{
396145510Sdarrenr	ipstate_save_t ips, *ipsp;
397145510Sdarrenr	ipfobj_t obj;
398145510Sdarrenr	int wfd = -1;
399145510Sdarrenr
400145510Sdarrenr	if (!file)
401145510Sdarrenr		file = IPF_STATEFILE;
402145510Sdarrenr
403145510Sdarrenr	wfd = open(file, O_WRONLY|O_TRUNC|O_CREAT, 0600);
404145510Sdarrenr	if (wfd == -1) {
405145510Sdarrenr		fprintf(stderr, "%s ", file);
406145510Sdarrenr		perror("state:open");
407145510Sdarrenr		return 1;
408145510Sdarrenr	}
409145510Sdarrenr
410145510Sdarrenr	ipsp = &ips;
411145510Sdarrenr	bzero((char *)&obj, sizeof(obj));
412145510Sdarrenr	bzero((char *)ipsp, sizeof(ips));
413145510Sdarrenr
414145510Sdarrenr	obj.ipfo_rev = IPFILTER_VERSION;
415145510Sdarrenr	obj.ipfo_size = sizeof(*ipsp);
416145510Sdarrenr	obj.ipfo_type = IPFOBJ_STATESAVE;
417145510Sdarrenr	obj.ipfo_ptr = ipsp;
418145510Sdarrenr
419145510Sdarrenr	do {
420145510Sdarrenr
421145510Sdarrenr		if (opts & OPT_VERBOSE)
422145510Sdarrenr			printf("Getting state from addr %p\n", ips.ips_next);
423145510Sdarrenr		if (ioctl(fd, SIOCSTGET, &obj)) {
424145510Sdarrenr			if (errno == ENOENT)
425145510Sdarrenr				break;
426145510Sdarrenr			perror("state:SIOCSTGET");
427145510Sdarrenr			close(wfd);
428145510Sdarrenr			return 1;
429145510Sdarrenr		}
430145510Sdarrenr		if (opts & OPT_VERBOSE)
431145510Sdarrenr			printf("Got state next %p\n", ips.ips_next);
432145510Sdarrenr		if (write(wfd, ipsp, sizeof(ips)) != sizeof(ips)) {
433145510Sdarrenr			perror("state:write");
434145510Sdarrenr			close(wfd);
435145510Sdarrenr			return 1;
436145510Sdarrenr		}
437145510Sdarrenr	} while (ips.ips_next != NULL);
438145510Sdarrenr	close(wfd);
439145510Sdarrenr
440145510Sdarrenr	return 0;
441145510Sdarrenr}
442145510Sdarrenr
443145510Sdarrenr
444145510Sdarrenrint readstate(fd, file)
445255332Scy	int fd;
446255332Scy	char *file;
447145510Sdarrenr{
448145510Sdarrenr	ipstate_save_t ips, *is, *ipshead = NULL, *is1, *ipstail = NULL;
449145510Sdarrenr	int sfd = -1, i;
450145510Sdarrenr	ipfobj_t obj;
451145510Sdarrenr
452145510Sdarrenr	if (!file)
453145510Sdarrenr		file = IPF_STATEFILE;
454145510Sdarrenr
455145510Sdarrenr	sfd = open(file, O_RDONLY, 0600);
456145510Sdarrenr	if (sfd == -1) {
457145510Sdarrenr		fprintf(stderr, "%s ", file);
458145510Sdarrenr		perror("open");
459145510Sdarrenr		return 1;
460145510Sdarrenr	}
461145510Sdarrenr
462145510Sdarrenr	bzero((char *)&ips, sizeof(ips));
463145510Sdarrenr
464145510Sdarrenr	/*
465145510Sdarrenr	 * 1. Read all state information in.
466145510Sdarrenr	 */
467145510Sdarrenr	do {
468145510Sdarrenr		i = read(sfd, &ips, sizeof(ips));
469145510Sdarrenr		if (i == -1) {
470145510Sdarrenr			perror("read");
471161357Sguido			goto freeipshead;
472145510Sdarrenr		}
473145510Sdarrenr		if (i == 0)
474145510Sdarrenr			break;
475145510Sdarrenr		if (i != sizeof(ips)) {
476145510Sdarrenr			fprintf(stderr, "state:incomplete read: %d != %d\n",
477145510Sdarrenr				i, (int)sizeof(ips));
478161357Sguido			goto freeipshead;
479145510Sdarrenr		}
480145510Sdarrenr		is = (ipstate_save_t *)malloc(sizeof(*is));
481161357Sguido		if (is == NULL) {
482145510Sdarrenr			fprintf(stderr, "malloc failed\n");
483161357Sguido			goto freeipshead;
484145510Sdarrenr		}
485145510Sdarrenr
486145510Sdarrenr		bcopy((char *)&ips, (char *)is, sizeof(ips));
487145510Sdarrenr
488145510Sdarrenr		/*
489145510Sdarrenr		 * Check to see if this is the first state entry that will
490145510Sdarrenr		 * reference a particular rule and if so, flag it as such
491145510Sdarrenr		 * else just adjust the rule pointer to become a pointer to
492145510Sdarrenr		 * the other.  We do this so we have a means later for tracking
493145510Sdarrenr		 * who is referencing us when we get back the real pointer
494145510Sdarrenr		 * in is_rule after doing the ioctl.
495145510Sdarrenr		 */
496145510Sdarrenr		for (is1 = ipshead; is1 != NULL; is1 = is1->ips_next)
497145510Sdarrenr			if (is1->ips_rule == is->ips_rule)
498145510Sdarrenr				break;
499145510Sdarrenr		if (is1 == NULL)
500145510Sdarrenr			is->ips_is.is_flags |= SI_NEWFR;
501145510Sdarrenr		else
502145510Sdarrenr			is->ips_rule = (void *)&is1->ips_rule;
503145510Sdarrenr
504145510Sdarrenr		/*
505145510Sdarrenr		 * Use a tail-queue type list (add things to the end)..
506145510Sdarrenr		 */
507145510Sdarrenr		is->ips_next = NULL;
508145510Sdarrenr		if (!ipshead)
509145510Sdarrenr			ipshead = is;
510145510Sdarrenr		if (ipstail)
511145510Sdarrenr			ipstail->ips_next = is;
512145510Sdarrenr		ipstail = is;
513145510Sdarrenr	} while (1);
514145510Sdarrenr
515145510Sdarrenr	close(sfd);
516145510Sdarrenr
517145510Sdarrenr	obj.ipfo_rev = IPFILTER_VERSION;
518145510Sdarrenr	obj.ipfo_size = sizeof(*is);
519145510Sdarrenr	obj.ipfo_type = IPFOBJ_STATESAVE;
520145510Sdarrenr
521161357Sguido	while ((is = ipshead) != NULL) {
522145510Sdarrenr		if (opts & OPT_VERBOSE)
523145510Sdarrenr			printf("Loading new state table entry\n");
524145510Sdarrenr		if (is->ips_is.is_flags & SI_NEWFR) {
525145510Sdarrenr			if (opts & OPT_VERBOSE)
526145510Sdarrenr				printf("Loading new filter rule\n");
527145510Sdarrenr		}
528145510Sdarrenr
529145510Sdarrenr		obj.ipfo_ptr = is;
530145510Sdarrenr		if (!(opts & OPT_DONOTHING))
531145510Sdarrenr			if (ioctl(fd, SIOCSTPUT, &obj)) {
532145510Sdarrenr				perror("SIOCSTPUT");
533161357Sguido				goto freeipshead;
534145510Sdarrenr			}
535145510Sdarrenr
536145510Sdarrenr		if (is->ips_is.is_flags & SI_NEWFR) {
537145510Sdarrenr			if (opts & OPT_VERBOSE)
538145510Sdarrenr				printf("Real rule addr %p\n", is->ips_rule);
539145510Sdarrenr			for (is1 = is->ips_next; is1; is1 = is1->ips_next)
540145510Sdarrenr				if (is1->ips_rule == (frentry_t *)&is->ips_rule)
541145510Sdarrenr					is1->ips_rule = is->ips_rule;
542145510Sdarrenr		}
543161357Sguido
544161357Sguido		ipshead = is->ips_next;
545161357Sguido		free(is);
546145510Sdarrenr	}
547145510Sdarrenr
548145510Sdarrenr	return 0;
549161357Sguido
550161357Sguidofreeipshead:
551161357Sguido	while ((is = ipshead) != NULL) {
552161357Sguido		ipshead = is->ips_next;
553161357Sguido		free(is);
554161357Sguido	}
555161357Sguido	if (sfd != -1)
556161357Sguido		close(sfd);
557161357Sguido	return 1;
558145510Sdarrenr}
559145510Sdarrenr
560145510Sdarrenr
561145510Sdarrenrint readnat(fd, file)
562255332Scy	int fd;
563255332Scy	char *file;
564145510Sdarrenr{
565145510Sdarrenr	nat_save_t ipn, *in, *ipnhead = NULL, *in1, *ipntail = NULL;
566145510Sdarrenr	ipfobj_t obj;
567145510Sdarrenr	int nfd, i;
568145510Sdarrenr	nat_t *nat;
569145510Sdarrenr	char *s;
570145510Sdarrenr	int n;
571145510Sdarrenr
572145510Sdarrenr	nfd = -1;
573145510Sdarrenr	in = NULL;
574145510Sdarrenr	ipnhead = NULL;
575145510Sdarrenr	ipntail = NULL;
576145510Sdarrenr
577145510Sdarrenr	if (!file)
578145510Sdarrenr		file = IPF_NATFILE;
579145510Sdarrenr
580145510Sdarrenr	nfd = open(file, O_RDONLY);
581145510Sdarrenr	if (nfd == -1) {
582145510Sdarrenr		fprintf(stderr, "%s ", file);
583145510Sdarrenr		perror("nat:open");
584145510Sdarrenr		return 1;
585145510Sdarrenr	}
586145510Sdarrenr
587145510Sdarrenr	bzero((char *)&ipn, sizeof(ipn));
588145510Sdarrenr
589145510Sdarrenr	/*
590145510Sdarrenr	 * 1. Read all state information in.
591145510Sdarrenr	 */
592145510Sdarrenr	do {
593145510Sdarrenr		i = read(nfd, &ipn, sizeof(ipn));
594145510Sdarrenr		if (i == -1) {
595145510Sdarrenr			perror("read");
596161357Sguido			goto freenathead;
597145510Sdarrenr		}
598145510Sdarrenr		if (i == 0)
599145510Sdarrenr			break;
600145510Sdarrenr		if (i != sizeof(ipn)) {
601145510Sdarrenr			fprintf(stderr, "nat:incomplete read: %d != %d\n",
602145510Sdarrenr				i, (int)sizeof(ipn));
603161357Sguido			goto freenathead;
604145510Sdarrenr		}
605145510Sdarrenr
606145510Sdarrenr		in = (nat_save_t *)malloc(ipn.ipn_dsize);
607161357Sguido		if (in == NULL) {
608161357Sguido			fprintf(stderr, "nat:cannot malloc nat save atruct\n");
609161357Sguido			goto freenathead;
610161357Sguido		}
611145510Sdarrenr
612145510Sdarrenr		if (ipn.ipn_dsize > sizeof(ipn)) {
613145510Sdarrenr			n = ipn.ipn_dsize - sizeof(ipn);
614145510Sdarrenr			if (n > 0) {
615145510Sdarrenr				s = in->ipn_data + sizeof(in->ipn_data);
616145510Sdarrenr 				i = read(nfd, s, n);
617145510Sdarrenr				if (i == 0)
618145510Sdarrenr					break;
619145510Sdarrenr				if (i != n) {
620145510Sdarrenr					fprintf(stderr,
621145510Sdarrenr					    "nat:incomplete read: %d != %d\n",
622145510Sdarrenr					    i, n);
623161357Sguido					goto freenathead;
624145510Sdarrenr				}
625145510Sdarrenr			}
626145510Sdarrenr		}
627145510Sdarrenr		bcopy((char *)&ipn, (char *)in, sizeof(ipn));
628145510Sdarrenr
629145510Sdarrenr		/*
630145510Sdarrenr		 * Check to see if this is the first NAT entry that will
631145510Sdarrenr		 * reference a particular rule and if so, flag it as such
632145510Sdarrenr		 * else just adjust the rule pointer to become a pointer to
633145510Sdarrenr		 * the other.  We do this so we have a means later for tracking
634145510Sdarrenr		 * who is referencing us when we get back the real pointer
635145510Sdarrenr		 * in is_rule after doing the ioctl.
636145510Sdarrenr		 */
637145510Sdarrenr		nat = &in->ipn_nat;
638145510Sdarrenr		if (nat->nat_fr != NULL) {
639145510Sdarrenr			for (in1 = ipnhead; in1 != NULL; in1 = in1->ipn_next)
640145510Sdarrenr				if (in1->ipn_rule == nat->nat_fr)
641145510Sdarrenr					break;
642145510Sdarrenr			if (in1 == NULL)
643145510Sdarrenr				nat->nat_flags |= SI_NEWFR;
644145510Sdarrenr			else
645145510Sdarrenr				nat->nat_fr = &in1->ipn_fr;
646145510Sdarrenr		}
647145510Sdarrenr
648145510Sdarrenr		/*
649145510Sdarrenr		 * Use a tail-queue type list (add things to the end)..
650145510Sdarrenr		 */
651145510Sdarrenr		in->ipn_next = NULL;
652145510Sdarrenr		if (!ipnhead)
653145510Sdarrenr			ipnhead = in;
654145510Sdarrenr		if (ipntail)
655145510Sdarrenr			ipntail->ipn_next = in;
656145510Sdarrenr		ipntail = in;
657145510Sdarrenr	} while (1);
658145510Sdarrenr
659145510Sdarrenr	close(nfd);
660145510Sdarrenr	nfd = -1;
661145510Sdarrenr
662145510Sdarrenr	obj.ipfo_rev = IPFILTER_VERSION;
663145510Sdarrenr	obj.ipfo_type = IPFOBJ_NATSAVE;
664145510Sdarrenr
665161357Sguido	while ((in = ipnhead) != NULL) {
666145510Sdarrenr		if (opts & OPT_VERBOSE)
667145510Sdarrenr			printf("Loading new NAT table entry\n");
668145510Sdarrenr		nat = &in->ipn_nat;
669145510Sdarrenr		if (nat->nat_flags & SI_NEWFR) {
670145510Sdarrenr			if (opts & OPT_VERBOSE)
671145510Sdarrenr				printf("Loading new filter rule\n");
672145510Sdarrenr		}
673145510Sdarrenr
674145510Sdarrenr		obj.ipfo_ptr = in;
675145510Sdarrenr		obj.ipfo_size = in->ipn_dsize;
676145510Sdarrenr		if (!(opts & OPT_DONOTHING))
677145510Sdarrenr			if (ioctl(fd, SIOCSTPUT, &obj)) {
678145510Sdarrenr				fprintf(stderr, "in=%p:", in);
679145510Sdarrenr				perror("SIOCSTPUT");
680145510Sdarrenr				return 1;
681145510Sdarrenr			}
682145510Sdarrenr
683145510Sdarrenr		if (nat->nat_flags & SI_NEWFR) {
684145510Sdarrenr			if (opts & OPT_VERBOSE)
685145510Sdarrenr				printf("Real rule addr %p\n", nat->nat_fr);
686145510Sdarrenr			for (in1 = in->ipn_next; in1; in1 = in1->ipn_next)
687145510Sdarrenr				if (in1->ipn_rule == &in->ipn_fr)
688145510Sdarrenr					in1->ipn_rule = nat->nat_fr;
689145510Sdarrenr		}
690161357Sguido
691161357Sguido		ipnhead = in->ipn_next;
692161357Sguido		free(in);
693145510Sdarrenr	}
694145510Sdarrenr
695145510Sdarrenr	return 0;
696161357Sguido
697161357Sguidofreenathead:
698161357Sguido	while ((in = ipnhead) != NULL) {
699161357Sguido		ipnhead = in->ipn_next;
700161357Sguido		free(in);
701161357Sguido	}
702161357Sguido	if (nfd != -1)
703161357Sguido		close(nfd);
704161357Sguido	return 1;
705145510Sdarrenr}
706145510Sdarrenr
707145510Sdarrenr
708145510Sdarrenrint writenat(fd, file)
709255332Scy	int fd;
710255332Scy	char *file;
711145510Sdarrenr{
712145510Sdarrenr	nat_save_t *ipnp = NULL, *next = NULL;
713145510Sdarrenr	ipfobj_t obj;
714145510Sdarrenr	int nfd = -1;
715145510Sdarrenr	natget_t ng;
716145510Sdarrenr
717145510Sdarrenr	if (!file)
718145510Sdarrenr		file = IPF_NATFILE;
719145510Sdarrenr
720145510Sdarrenr	nfd = open(file, O_WRONLY|O_TRUNC|O_CREAT, 0600);
721145510Sdarrenr	if (nfd == -1) {
722145510Sdarrenr		fprintf(stderr, "%s ", file);
723145510Sdarrenr		perror("nat:open");
724145510Sdarrenr		return 1;
725145510Sdarrenr	}
726145510Sdarrenr
727145510Sdarrenr	obj.ipfo_rev = IPFILTER_VERSION;
728145510Sdarrenr	obj.ipfo_type = IPFOBJ_NATSAVE;
729145510Sdarrenr
730145510Sdarrenr	do {
731145510Sdarrenr		if (opts & OPT_VERBOSE)
732145510Sdarrenr			printf("Getting nat from addr %p\n", ipnp);
733145510Sdarrenr		ng.ng_ptr = next;
734145510Sdarrenr		ng.ng_sz = 0;
735145510Sdarrenr		if (ioctl(fd, SIOCSTGSZ, &ng)) {
736145510Sdarrenr			perror("nat:SIOCSTGSZ");
737145510Sdarrenr			close(nfd);
738145510Sdarrenr			if (ipnp != NULL)
739145510Sdarrenr				free(ipnp);
740145510Sdarrenr			return 1;
741145510Sdarrenr		}
742145510Sdarrenr
743145510Sdarrenr		if (opts & OPT_VERBOSE)
744145510Sdarrenr			printf("NAT size %d from %p\n", ng.ng_sz, ng.ng_ptr);
745145510Sdarrenr
746145510Sdarrenr		if (ng.ng_sz == 0)
747145510Sdarrenr			break;
748145510Sdarrenr
749145510Sdarrenr		if (!ipnp)
750145510Sdarrenr			ipnp = malloc(ng.ng_sz);
751145510Sdarrenr		else
752145510Sdarrenr			ipnp = realloc((char *)ipnp, ng.ng_sz);
753145510Sdarrenr		if (!ipnp) {
754145510Sdarrenr			fprintf(stderr,
755145510Sdarrenr				"malloc for %d bytes failed\n", ng.ng_sz);
756145510Sdarrenr			break;
757145510Sdarrenr		}
758145510Sdarrenr
759145510Sdarrenr		bzero((char *)ipnp, ng.ng_sz);
760145510Sdarrenr		obj.ipfo_size = ng.ng_sz;
761145510Sdarrenr		obj.ipfo_ptr = ipnp;
762145510Sdarrenr		ipnp->ipn_dsize = ng.ng_sz;
763145510Sdarrenr		ipnp->ipn_next = next;
764145510Sdarrenr		if (ioctl(fd, SIOCSTGET, &obj)) {
765145510Sdarrenr			if (errno == ENOENT)
766145510Sdarrenr				break;
767145510Sdarrenr			perror("nat:SIOCSTGET");
768145510Sdarrenr			close(nfd);
769145510Sdarrenr			free(ipnp);
770145510Sdarrenr			return 1;
771145510Sdarrenr		}
772145510Sdarrenr
773145510Sdarrenr		if (opts & OPT_VERBOSE)
774145510Sdarrenr			printf("Got nat next %p ipn_dsize %d ng_sz %d\n",
775145510Sdarrenr				ipnp->ipn_next, ipnp->ipn_dsize, ng.ng_sz);
776145510Sdarrenr		if (write(nfd, ipnp, ipnp->ipn_dsize) != ipnp->ipn_dsize) {
777145510Sdarrenr			perror("nat:write");
778145510Sdarrenr			close(nfd);
779145510Sdarrenr			free(ipnp);
780145510Sdarrenr			return 1;
781145510Sdarrenr		}
782145510Sdarrenr		next = ipnp->ipn_next;
783145510Sdarrenr	} while (ipnp && next);
784145510Sdarrenr	if (ipnp != NULL)
785145510Sdarrenr		free(ipnp);
786145510Sdarrenr	close(nfd);
787145510Sdarrenr
788145510Sdarrenr	return 0;
789145510Sdarrenr}
790145510Sdarrenr
791145510Sdarrenr
792145510Sdarrenrint writeall(dirname)
793255332Scy	char *dirname;
794145510Sdarrenr{
795145510Sdarrenr	int fd, devfd;
796145510Sdarrenr
797145510Sdarrenr	if (!dirname)
798145510Sdarrenr		dirname = IPF_SAVEDIR;
799145510Sdarrenr
800145510Sdarrenr	if (chdir(dirname)) {
801145510Sdarrenr		fprintf(stderr, "IPF_SAVEDIR=%s: ", dirname);
802145510Sdarrenr		perror("chdir(IPF_SAVEDIR)");
803145510Sdarrenr		return 1;
804145510Sdarrenr	}
805145510Sdarrenr
806145510Sdarrenr	fd = opendevice(NULL);
807145510Sdarrenr	if (fd == -1)
808145510Sdarrenr		return 1;
809145510Sdarrenr	if (setlock(fd, 1)) {
810145510Sdarrenr		close(fd);
811145510Sdarrenr		return 1;
812145510Sdarrenr	}
813145510Sdarrenr
814145510Sdarrenr	devfd = opendevice(IPSTATE_NAME);
815145510Sdarrenr	if (devfd == -1)
816145510Sdarrenr		goto bad;
817145510Sdarrenr	if (writestate(devfd, NULL))
818145510Sdarrenr		goto bad;
819145510Sdarrenr	close(devfd);
820145510Sdarrenr
821145510Sdarrenr	devfd = opendevice(IPNAT_NAME);
822145510Sdarrenr	if (devfd == -1)
823145510Sdarrenr		goto bad;
824145510Sdarrenr	if (writenat(devfd, NULL))
825145510Sdarrenr		goto bad;
826145510Sdarrenr	close(devfd);
827145510Sdarrenr
828145510Sdarrenr	if (setlock(fd, 0)) {
829145510Sdarrenr		close(fd);
830145510Sdarrenr		return 1;
831145510Sdarrenr	}
832145510Sdarrenr
833145510Sdarrenr	close(fd);
834145510Sdarrenr	return 0;
835145510Sdarrenr
836145510Sdarrenrbad:
837145510Sdarrenr	setlock(fd, 0);
838145510Sdarrenr	close(fd);
839145510Sdarrenr	return 1;
840145510Sdarrenr}
841145510Sdarrenr
842145510Sdarrenr
843145510Sdarrenrint readall(dirname)
844255332Scy	char *dirname;
845145510Sdarrenr{
846145510Sdarrenr	int fd, devfd;
847145510Sdarrenr
848145510Sdarrenr	if (!dirname)
849145510Sdarrenr		dirname = IPF_SAVEDIR;
850145510Sdarrenr
851145510Sdarrenr	if (chdir(dirname)) {
852145510Sdarrenr		perror("chdir(IPF_SAVEDIR)");
853145510Sdarrenr		return 1;
854145510Sdarrenr	}
855145510Sdarrenr
856145510Sdarrenr	fd = opendevice(NULL);
857145510Sdarrenr	if (fd == -1)
858145510Sdarrenr		return 1;
859145510Sdarrenr	if (setlock(fd, 1)) {
860145510Sdarrenr		close(fd);
861145510Sdarrenr		return 1;
862145510Sdarrenr	}
863145510Sdarrenr
864145510Sdarrenr	devfd = opendevice(IPSTATE_NAME);
865145510Sdarrenr	if (devfd == -1)
866145510Sdarrenr		return 1;
867145510Sdarrenr	if (readstate(devfd, NULL))
868145510Sdarrenr		return 1;
869145510Sdarrenr	close(devfd);
870145510Sdarrenr
871145510Sdarrenr	devfd = opendevice(IPNAT_NAME);
872145510Sdarrenr	if (devfd == -1)
873145510Sdarrenr		return 1;
874145510Sdarrenr	if (readnat(devfd, NULL))
875145510Sdarrenr		return 1;
876145510Sdarrenr	close(devfd);
877145510Sdarrenr
878145510Sdarrenr	if (setlock(fd, 0)) {
879145510Sdarrenr		close(fd);
880145510Sdarrenr		return 1;
881145510Sdarrenr	}
882145510Sdarrenr
883145510Sdarrenr	return 0;
884145510Sdarrenr}
885