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