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