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