Home | History | Annotate | Line # | Download | only in pfs
      1 /* $NetBSD: pfs.c,v 1.2 2015/06/16 23:04:14 christos 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.2 2015/06/16 23:04:14 christos 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", 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", 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", 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", 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