xref: /netbsd/usr.sbin/pf/pfs/pfs.c (revision 6550d01e)
1 /* $NetBSD: pfs.c,v 1.1 2010/05/07 17:41:58 degroote Exp $ */
2 
3 /*-
4  * Copyright (c) 2010 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 
31 #ifndef lint
32 __RCSID("$NetBSD: pfs.c,v 1.1 2010/05/07 17:41:58 degroote Exp $");
33 #endif
34 
35 #include <sys/types.h>
36 #include <sys/ioctl.h>
37 #include <sys/socket.h>
38 #include <sys/stat.h>
39 
40 #include <net/if.h>
41 #include <netinet/in.h>
42 #define TCPSTATES
43 #include <netinet/tcp_fsm.h>
44 #include <net/pfvar.h>
45 #include <arpa/inet.h>
46 
47 #include <err.h>
48 #include <errno.h>
49 #include <fcntl.h>
50 #include <limits.h>
51 #include <netdb.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <stdbool.h>
56 #include <unistd.h>
57 
58 #include "parser.h"
59 
60 __dead static void usage(void);
61 static int setlock(int, int, int);
62 static int get_states(int, int, struct pfioc_states*);
63 static int dump_states_binary(int, int, const char*);
64 static int restore_states_binary(int, int, const char*);
65 static int dump_states_ascii(int, int, const char*);
66 static int restore_states_ascii(int, int, const char*);
67 static char* print_host(const struct pfsync_state_host *h, sa_family_t, char*, size_t);
68 static void print_peer(const struct pfsync_state_peer *peer, uint8_t, FILE*);
69 static int print_states(int, int, FILE*);
70 static void display_states(const struct pfioc_states*, int, FILE*);
71 static int test_ascii_dump(int, const char*, const char*);
72 
73 static char pf_device[] = "/dev/pf";
74 
75 __dead static void
76 usage(void)
77 {
78 	fprintf(stderr,
79 			"usage : %s [-v] [-u | -l | -w <filename> | -r <filename> |\n"
80 			"			[ -W <filename> | -R <filename> ]\n",
81 			getprogname());
82 	exit(EXIT_FAILURE);
83 }
84 
85 /*
86  * The state table must be locked before calling this function
87  * Return the number of state in case of success, -1 in case of failure
88  * ps::ps_buf must be freed by user after use (in case of success)
89  */
90 static int
91 get_states(int fd, int verbose __unused, struct pfioc_states* ps)
92 {
93 	memset(ps, 0, sizeof(*ps));
94 	ps->ps_len = 0;
95 	char* inbuf;
96 
97 	// ask the kernel how much memory we need to allocate
98 	if (ioctl(fd, DIOCGETSTATES, ps) == -1) {
99 		err(EXIT_FAILURE, "DIOCGETSTATES");
100 	}
101 
102 	/* no state */
103 	if (ps->ps_len == 0)
104 		return 0;
105 
106 	inbuf = malloc(ps->ps_len);
107 	if (inbuf == NULL)
108 		err(EXIT_FAILURE, NULL);
109 
110 	ps->ps_buf = inbuf;
111 
112 	// really retrieve the different states
113 	if (ioctl(fd, DIOCGETSTATES, ps) == -1) {
114 		free(ps->ps_buf);
115 		err(EXIT_FAILURE, "DIOCGETSTATES");
116 	}
117 
118 	return (ps->ps_len / sizeof(struct pfsync_state));
119 }
120 
121 static int
122 dump_states_binary(int fd, int verbose, const char* filename)
123 {
124 	int wfd;
125 	struct pfioc_states ps;
126 	struct pfsync_state *p = NULL;
127 	int nb_states;
128 	int i;
129 	int error = 0;
130 	int errno_saved = 0;
131 
132 	wfd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0600);
133 	if (wfd == -1)
134 		err(EXIT_FAILURE, "Cannot open %s", filename);
135 
136 	nb_states = get_states(fd, verbose, &ps);
137 	if (nb_states <= 0) {
138 		close(wfd);
139 		return nb_states;
140 	}
141 
142 	/*
143 	 * In the file, write the number of states, then store the different states
144 	 * When we will switch to text format, we probably don't care any more about the len
145 	 */
146 	if (write(wfd, &nb_states, sizeof(nb_states)) != sizeof(nb_states)) {
147 		error = EXIT_FAILURE;
148 		errno_saved = errno;
149 		goto done;
150 	}
151 
152 	p = ps.ps_states;
153 	for (i = 0; i < nb_states; i++) {
154 		if (write(wfd, &p[i], sizeof(*p)) != sizeof(*p)) {
155 			error = EXIT_FAILURE;
156 			errno_saved = errno;
157 			goto done;
158 		}
159 	}
160 
161 done:
162 	free(p);
163 	close(wfd);
164 	// close can't modify errno
165 	if (error) {
166 		errno = errno_saved;
167 		err(error, NULL);
168 	}
169 
170 	return 0;
171 }
172 
173 static int
174 restore_states_binary(int fd, int verbose __unused, const char* filename)
175 {
176 	int rfd;
177 	struct pfioc_states ps;
178 	struct pfsync_state *p;
179 	int nb_states;
180 	int errno_saved = 0;
181 	int i;
182 
183 	rfd = open(filename, O_RDONLY, 0600);
184 	if (rfd == -1)
185 		err(EXIT_FAILURE, "Cannot open %s", filename);
186 
187 	if (read(rfd, &nb_states, sizeof(nb_states)) != sizeof(nb_states)) {
188 		errno_saved = errno;
189 		close(rfd);
190 		errno = errno_saved;
191 		err(EXIT_FAILURE, NULL);
192 	}
193 
194 	ps.ps_len = nb_states * sizeof(struct pfsync_state);
195 	ps.ps_states = malloc(ps.ps_len);
196 	if (ps.ps_states == NULL) {
197 		errno_saved = errno;
198 		close(rfd);
199 		errno = errno_saved;
200 		err(EXIT_FAILURE, NULL);
201 	}
202 
203 	p = ps.ps_states;
204 
205 	for (i = 0; i < nb_states; i++) {
206 		if (read(rfd, &p[i], sizeof(*p)) != sizeof(*p)) {
207 			errno_saved = errno;
208 			close(rfd);
209 			free(ps.ps_states);
210 			errno = errno_saved;
211 			err(EXIT_FAILURE, NULL);
212 		}
213 	}
214 
215 	if (ioctl(fd, DIOCADDSTATES, &ps) == -1) {
216 		errno_saved = errno;
217 		close(rfd);
218 		free(ps.ps_states);
219 		errno = errno_saved;
220 		err(EXIT_FAILURE, "DIOCADDSTATES");
221 	}
222 
223 	free(ps.ps_states);
224 	close(rfd);
225 	return 0;
226 }
227 
228 static char*
229 print_host(const struct pfsync_state_host *h, sa_family_t af, char* buf,
230 		size_t size_buf)
231 {
232 	uint16_t port;
233 	char	buf_addr[48];
234 
235 	port = ntohs(h->port);
236 	if (inet_ntop(af, &(h->addr) , buf_addr, sizeof(buf_addr)) == NULL) {
237 		strcpy(buf_addr, "?");
238 	}
239 
240 	snprintf(buf, size_buf, "%s:[%d]", buf_addr, port);
241 	return buf;
242 }
243 
244 static void
245 print_peer(const struct pfsync_state_peer* peer, uint8_t proto, FILE* f)
246 {
247 	if (proto == IPPROTO_TCP) {
248 		if (peer->state < TCP_NSTATES)
249 			fprintf(f, "state %s", tcpstates[peer->state]);
250 
251 		if (peer->seqdiff != 0)
252 			fprintf(f, " seq [%" PRIu32 ":%" PRIu32 ",%" PRIu32"]",
253 					peer->seqlo, peer->seqhi, peer->seqdiff);
254 		else
255 			fprintf(f, " seq [%" PRIu32 ":%" PRIu32 "]",
256 					peer->seqlo, peer->seqhi);
257 
258 		if (peer->mss != 0)
259 			fprintf(f, " max_win %" PRIu16 " mss %" PRIu16 " wscale %" PRIu8,
260 					peer->max_win, peer->mss, peer->wscale);
261 		else
262 			fprintf(f, " max_win %" PRIu16 " wscale %" PRIu8, peer->max_win,
263 					peer->wscale);
264 
265 	} else {
266 		if (proto == IPPROTO_UDP) {
267 			const char *mystates[] = PFUDPS_NAMES;
268 			if (peer->state < PFUDPS_NSTATES)
269 				fprintf(f, "state %s", mystates[peer->state]);
270 		} else if (proto == IPPROTO_ICMP || proto == IPPROTO_ICMPV6) {
271 			fprintf(f, " state %" PRIu8, peer->state);
272 		} else {
273 			const char *mystates[] = PFOTHERS_NAMES;
274 			if (peer->state < PFOTHERS_NSTATES)
275 				fprintf(f, " state %s", mystates[peer->state]);
276 		}
277 	}
278 
279 	if (peer->scrub.scrub_flag == PFSYNC_SCRUB_FLAG_VALID) {
280 		fprintf(f, " scrub flags %" PRIu16 "ttl %" PRIu8 "mod %"PRIu32,
281 				peer->scrub.pfss_flags, peer->scrub.pfss_ttl, peer->scrub.pfss_ts_mod);
282 	} else {
283 		fprintf(f, " no-scrub");
284 	}
285 }
286 
287 static void
288 display_states(const struct pfioc_states *ps, int verbose __unused, FILE* f)
289 {
290 	struct pfsync_state *p = NULL;
291 	struct pfsync_state_peer *src, *dst;
292 	struct protoent *proto;
293 	int nb_states;
294 	int i;
295 	uint64_t id;
296 
297 	p = ps->ps_states;
298 	nb_states = ps->ps_len / sizeof(struct pfsync_state);
299 
300 	for (i = 0; i < nb_states; i++, p++) {
301 		fprintf(f, "state %s ", p->direction == PF_OUT ? "out" : "in");
302 		fprintf(f, "on %s ", p->ifname);
303 
304 		if ((proto = getprotobynumber(p->proto)) != NULL)
305 			fprintf(f, "proto %s ", proto->p_name);
306 		else
307 			fprintf(f, "proto %u ", p->proto);
308 
309 
310 		if (PF_ANEQ(&p->lan.addr, &p->gwy.addr, p->af) ||
311 				(p->lan.port != p->gwy.port)) {
312 
313 			char buf1[64], buf2[64], buf3[64];
314 			fprintf(f, "from %s to %s using %s",
315 					print_host(&p->lan, p->af, buf1, sizeof(buf1)),
316 					print_host(&p->ext, p->af, buf2, sizeof(buf2)),
317 					print_host(&p->gwy, p->af, buf3, sizeof(buf3)));
318 		} else {
319 			char buf1[64], buf2[64];
320 			fprintf(f, "from %s to %s",
321 					print_host(&p->lan, p->af, buf1, sizeof(buf1)),
322 					print_host(&p->ext, p->af, buf2, sizeof(buf2)));
323 		}
324 
325 		memcpy(&id, p->id, sizeof(p->id));
326 		fprintf(f, " id %" PRIu64 " cid %" PRIu32 " expire %" PRIu32 " timeout %" PRIu8,
327 				id , p->creatorid, p->expire, p->timeout);
328 
329 		if (p->direction == PF_OUT) {
330 			src = &p->src;
331 			dst = &p->dst;
332 		} else {
333 			src = &p->dst;
334 			dst = &p->src;
335 		}
336 
337 		fprintf(f, " src ");
338 		print_peer(src, p->proto, f);
339 		fprintf(f, " dst ");
340 		print_peer(dst, p->proto, f);
341 
342 		fprintf(f, "\n");
343 	}
344 }
345 
346 static int
347 print_states(int fd, int verbose, FILE* f)
348 {
349 	struct pfioc_states ps;
350 	int nb_states;
351 
352 	nb_states = get_states(fd, verbose, &ps);
353 	if (nb_states <= 0) {
354 		return nb_states;
355 	}
356 
357 	display_states(&ps, verbose, f);
358 
359 	free(ps.ps_states);
360 	return 0;
361 }
362 
363 static int
364 dump_states_ascii(int fd, int verbose, const char* filename)
365 {
366 	FILE *f;
367 
368 	if (strcmp(filename, "-") == 0) {
369 		f = stdout;
370 	} else {
371 		f = fopen(filename, "w");
372 		if (f == NULL)
373 			err(EXIT_FAILURE, "Can't open %s\n", filename);
374 	}
375 
376 	print_states(fd, verbose, f);
377 
378 	if (f != stdout)
379 		fclose(f);
380 
381 	return 0;
382 }
383 
384 static int
385 restore_states_ascii(int fd, int verbose __unused, const char* filename)
386 {
387 	FILE *f;
388 	struct pfioc_states ps;
389 	int errno_saved;
390 
391 	f = fopen(filename, "r");
392 	if (f == NULL)
393 		err(EXIT_FAILURE, "Can't open %s\n", filename);
394 
395 	parse(f, &ps);
396 
397 	if (ioctl(fd, DIOCADDSTATES, &ps) == -1) {
398 		errno_saved = errno;
399 		fclose(f);
400 		free(ps.ps_states);
401 		errno = errno_saved;
402 		err(EXIT_FAILURE, "DIOCADDSTATES");
403 	}
404 
405 	free(ps.ps_states);
406 	fclose(f);
407 	return 0;
408 }
409 
410 static int
411 setlock(int fd, int verbose, int lock)
412 {
413 	if (verbose)
414 		printf("Turning lock %s\n", lock ? "on" : "off");
415 
416 	if (ioctl(fd, DIOCSETLCK, &lock) == -1)
417 		err(EXIT_FAILURE, "DIOCSETLCK");
418 
419 	return 0;
420 }
421 
422 static int
423 test_ascii_dump(int verbose, const char* file1, const char *file2)
424 {
425 	FILE *f1, *f2;
426 	struct pfioc_states ps;
427 	int errno_saved;
428 
429 	f1 = fopen(file1, "r");
430 	if (f1 == NULL)
431 		err(EXIT_FAILURE, "Can't open %s\n", file1);
432 
433 
434 	f2 = fopen(file2, "w");
435 	if (f2 == NULL) {
436 		errno_saved = errno;
437 		fclose(f2);
438 		errno = errno_saved;
439 		err(EXIT_FAILURE, "Can't open %s\n", file2);
440 	}
441 
442 	parse(f1, &ps);
443 	display_states(&ps, verbose, f2);
444 
445 	free(ps.ps_states);
446 	fclose(f1);
447 	fclose(f2);
448 
449 	return 0;
450 }
451 
452 int main(int argc, char *argv[])
453 {
454 	setprogname(argv[0]);
455 
456 	int lock = 0;
457 	int set = 0;
458 	int dump = 0;
459 	int restore = 0;
460 	int verbose = 0;
461 	int test = 0;
462 	bool binary = false;
463 	char* filename = NULL;
464 	char* filename2 = NULL;
465 	int error = 0;
466 	int fd;
467 	int c;
468 
469 	while ((c = getopt(argc, argv, "ulvw:r:R:W:bt:o:")) != -1)
470 		switch (c) {
471 		case 'u' :
472 			lock = 0;
473 			set = 1;
474 			break;
475 
476 		case 'l' :
477 			lock = 1;
478 			set = 1;
479 			break;
480 
481 		case 'b':
482 			binary = true;
483 			break;
484 
485 		case 'r':
486 			restore = 1;
487 			filename = optarg;
488 			break;
489 
490 		case 'v':
491 			verbose=1;
492 			break;
493 
494 		case 'w':
495 			dump=1;
496 			filename=optarg;
497 			break;
498 
499 		case 'R':
500 			restore = 1;
501 			set = 1;
502 			filename = optarg;
503 			break;
504 
505 		case 'W':
506 			dump = 1;
507 			set = 1;
508 			filename = optarg;
509 			break;
510 
511 		case 't':
512 			test=1;
513 			filename = optarg;
514 			break;
515 
516 		case 'o':
517 			filename2 = optarg;
518 			break;
519 
520 		case '?' :
521 		default:
522 			usage();
523 		}
524 
525 	if (set == 0 && dump == 0 && restore == 0 && test == 0)
526 		usage();
527 
528 	if (dump == 1 && restore == 1)
529 		usage();
530 
531 	if (test == 1) {
532 		if (filename2 == NULL) {
533 			fprintf(stderr, "-o <file> is required when using -t\n");
534 			err(EXIT_FAILURE, NULL);
535 		}
536 		error = test_ascii_dump(verbose, filename, filename2);
537 	} else {
538 		fd = open(pf_device, O_RDWR);
539 		if (fd == -1)
540 			err(EXIT_FAILURE, "Cannot open %s", pf_device);
541 
542 		if (set != 0 && dump == 0 && restore == 0)
543 			error = setlock(fd, verbose, lock);
544 
545 		if (dump) {
546 			if (set)
547 				error = setlock(fd, verbose, 1);
548 
549 			if (binary)
550 				error = dump_states_binary(fd, verbose, filename);
551 			else
552 				error = dump_states_ascii(fd, verbose, filename);
553 
554 			if (set)
555 				error = setlock(fd, verbose, 0);
556 		}
557 
558 		if (restore) {
559 			if (set)
560 				error = setlock(fd, verbose, 1);
561 
562 			if (binary)
563 				error = restore_states_binary(fd, verbose, filename);
564 			else
565 				error = restore_states_ascii(fd, verbose, filename);
566 
567 			if (set)
568 				error = setlock(fd, verbose, 0);
569 		}
570 
571 		close(fd);
572 	}
573 
574 	return error;
575 }
576