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
usage(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
get_states(int fd,int verbose __unused,struct pfioc_states * ps)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
dump_states_binary(int fd,int verbose,const char * filename)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
restore_states_binary(int fd,int verbose __unused,const char * filename)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*
print_host(const struct pfsync_state_host * h,sa_family_t af,char * buf,size_t size_buf)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
print_peer(const struct pfsync_state_peer * peer,uint8_t proto,FILE * f)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
display_states(const struct pfioc_states * ps,int verbose __unused,FILE * f)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
print_states(int fd,int verbose,FILE * f)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
dump_states_ascii(int fd,int verbose,const char * filename)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
restore_states_ascii(int fd,int verbose __unused,const char * filename)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
setlock(int fd,int verbose,int lock)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
test_ascii_dump(int verbose,const char * file1,const char * file2)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
main(int argc,char * argv[])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