xref: /openbsd-src/sbin/pfctl/pfctl.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: pfctl.c,v 1.32 2001/08/11 12:05:00 dhartmei Exp $ */
2 
3 /*
4  * Copyright (c) 2001, Daniel Hartmeier
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  *
11  *    - Redistributions of source code must retain the above copyright
12  *      notice, this list of conditions and the following disclaimer.
13  *    - Redistributions in binary form must reproduce the above
14  *      copyright notice, this list of conditions and the following
15  *      disclaimer in the documentation and/or other materials provided
16  *      with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  *
31  */
32 
33 #include <sys/types.h>
34 #include <sys/ioctl.h>
35 #include <sys/socket.h>
36 #include <net/if.h>
37 #include <netinet/in.h>
38 #include <net/pfvar.h>
39 
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <fcntl.h>
45 #include <errno.h>
46 #include <err.h>
47 
48 #include "pfctl_parser.h"
49 
50 #define PF_OPT_DISABLE		0x0001
51 #define PF_OPT_ENABLE		0x0002
52 #define PF_OPT_VERBOSE		0x0004
53 #define PF_OPT_NOACTION		0x0008
54 #define PF_OPT_QUIET		0x0010
55 
56 void	 usage(void);
57 int	 pfctl_enable(int, int);
58 int	 pfctl_disable(int, int);
59 int	 pfctl_clear_stats(int, int);
60 int	 pfctl_clear_rules(int, int);
61 int	 pfctl_clear_nat(int, int);
62 int	 pfctl_clear_states(int, int);
63 int	 pfctl_show_rules(int);
64 int	 pfctl_show_nat(int);
65 int	 pfctl_show_states(int, u_int8_t);
66 int	 pfctl_show_status(int);
67 int	 pfctl_rules(int, char *, int);
68 int	 pfctl_nat(int, char *, int);
69 int	 pfctl_log(int, char *, int);
70 int	 pfctl_debug(int, u_int32_t, int);
71 
72 int	 opts = 0;
73 char	*clearopt;
74 char	*logopt;
75 char	*natopt;
76 char	*rulesopt;
77 char	*showopt;
78 char	*debugopt;
79 
80 char	*infile;
81 
82 void
83 usage()
84 {
85 	extern char *__progname;
86 
87 	fprintf(stderr, "usage: %s [-dehnqv] [-F set] [-l interface] ",
88 	    __progname);
89 	fprintf(stderr, "[-N file] [-R file] [-s set] [-x level]\n");
90 	exit(1);
91 }
92 
93 int
94 pfctl_enable(int dev, int opts)
95 {
96 	if (ioctl(dev, DIOCSTART)) {
97 		if (errno == EEXIST)
98 			errx(1, "pf already enabled");
99 		else
100 			err(1, "DIOCSTART");
101 	}
102 	if ((opts & PF_OPT_QUIET) == 0)
103 		printf("pf enabled\n");
104 	return (0);
105 }
106 
107 int
108 pfctl_disable(int dev, int opts)
109 {
110 	if (ioctl(dev, DIOCSTOP)) {
111 		if (errno == ENOENT)
112 			errx(1, "pf not enabled");
113 		else
114 			err(1, "DIOCSTOP");
115 	}
116 	if ((opts & PF_OPT_QUIET) == 0)
117 		printf("pf disabled\n");
118 	return (0);
119 }
120 
121 int
122 pfctl_clear_stats(int dev, int opts)
123 {
124 	if (ioctl(dev, DIOCCLRSTATUS))
125 		err(1, "DIOCCLRSTATUS");
126 	if ((opts & PF_OPT_QUIET) == 0)
127 		printf("pf: statistics cleared\n");
128 	return (0);
129 }
130 
131 int
132 pfctl_clear_rules(int dev, int opts)
133 {
134 	struct pfioc_rule pr;
135 
136 	if (ioctl(dev, DIOCBEGINRULES, &pr.ticket))
137 		err(1, "DIOCBEGINRULES");
138 	else if (ioctl(dev, DIOCCOMMITRULES, &pr.ticket))
139 		err(1, "DIOCCOMMITRULES");
140 	if ((opts & PF_OPT_QUIET) == 0)
141 		printf("rules cleared\n");
142 	return (0);
143 }
144 
145 int
146 pfctl_clear_nat(int dev, int opts)
147 {
148 	struct pfioc_nat pn;
149 	struct pfioc_rdr pr;
150 
151 	if (ioctl(dev, DIOCBEGINNATS, &pn.ticket))
152 		err(1, "DIOCBEGINNATS");
153 	else if (ioctl(dev, DIOCCOMMITNATS, &pn.ticket))
154 		err(1, "DIOCCOMMITNATS");
155 	else if (ioctl(dev, DIOCBEGINRDRS, &pr.ticket))
156 		err(1, "DIOCBEGINRDRS");
157 	else if (ioctl(dev, DIOCCOMMITRDRS, &pr.ticket))
158 		err(1, "DIOCCOMMITRDRS");
159 	if ((opts & PF_OPT_QUIET) == 0)
160 		printf("nat cleared\n");
161 	return (0);
162 }
163 
164 int
165 pfctl_clear_states(int dev, int opts)
166 {
167 	if (ioctl(dev, DIOCCLRSTATES))
168 		err(1, "DIOCCLRSTATES");
169 	if ((opts & PF_OPT_QUIET) == 0)
170 		printf("states cleared\n");
171 	return (0);
172 }
173 
174 int
175 pfctl_show_rules(int dev)
176 {
177 	struct pfioc_rule pr;
178 	u_int32_t nr, mnr;
179 
180 	if (ioctl(dev, DIOCGETRULES, &pr))
181 		err(1, "DIOCGETRULES");
182 	mnr = pr.nr;
183 	for (nr = 0; nr < mnr; ++nr) {
184 		pr.nr = nr;
185 		if (ioctl(dev, DIOCGETRULE, &pr))
186 			err(1, "DIOCGETRULE");
187 		print_rule(&pr.rule);
188 	}
189 	return (0);
190 }
191 
192 int
193 pfctl_show_nat(int dev)
194 {
195 	struct pfioc_nat pn;
196 	struct pfioc_rdr pr;
197 	u_int32_t mnr, nr;
198 
199 	if (ioctl(dev, DIOCGETNATS, &pn))
200 		err(1, "DIOCGETNATS");
201 	mnr = pn.nr;
202 	for (nr = 0; nr < mnr; ++nr) {
203 		pn.nr = nr;
204 		if (ioctl(dev, DIOCGETNAT, &pn))
205 			err(1, "DIOCGETNAT");
206 		print_nat(&pn.nat);
207 	}
208 	if (ioctl(dev, DIOCGETRDRS, &pr))
209 		err(1, "DIOCGETRDRS");
210 	mnr = pr.nr;
211 	for (nr = 0; nr < mnr; ++nr) {
212 		pr.nr = nr;
213 		if (ioctl(dev, DIOCGETRDR, &pr))
214 			err(1, "DIOCGETRDR");
215 		print_rdr(&pr.rdr);
216 	}
217 	return (0);
218 }
219 
220 int
221 pfctl_show_states(int dev, u_int8_t proto)
222 {
223 	struct pfioc_state ps;
224 
225 	ps.nr = 0;
226 	while (!ioctl(dev, DIOCGETSTATE, &ps)) {
227 		if (!proto || (ps.state.proto == proto))
228 			print_state(&ps.state);
229 		ps.nr++;
230 	}
231 	return (0);
232 }
233 
234 int
235 pfctl_show_status(int dev)
236 {
237 	struct pf_status status;
238 
239 	if (ioctl(dev, DIOCGETSTATUS, &status))
240 		err(1, "DIOCGETSTATUS");
241 	print_status(&status);
242 	return (0);
243 }
244 
245 /* callbacks for rule/nat/rdr */
246 
247 int
248 pfctl_add_rule(struct pfctl *pf, struct pf_rule *r)
249 {
250 	memcpy(&pf->prule->rule, r, sizeof(pf->prule->rule));
251 	if ((pf->opts & PF_OPT_NOACTION) == 0) {
252 		if (ioctl(pf->dev, DIOCADDRULE, pf->prule))
253 			err(1, "DIOCADDRULE");
254 	}
255 	if (pf->opts & PF_OPT_VERBOSE)
256 		print_rule(&pf->prule->rule);
257 	return 0;
258 }
259 
260 int
261 pfctl_add_nat(struct pfctl *pf, struct pf_nat *n)
262 {
263 	memcpy(&pf->pnat->nat, n, sizeof(pf->pnat->nat));
264 	if ((pf->opts & PF_OPT_NOACTION) == 0) {
265 		if (ioctl(pf->dev, DIOCADDNAT, pf->pnat))
266 			err(1, "DIOCADDNAT");
267 	}
268 	if (pf->opts & PF_OPT_VERBOSE)
269 		print_nat(&pf->pnat->nat);
270 	return 0;
271 }
272 
273 int
274 pfctl_add_rdr(struct pfctl *pf, struct pf_rdr *r)
275 {
276 	memcpy(&pf->prdr->rdr, r, sizeof(pf->prdr->rdr));
277 	if ((pf->opts & PF_OPT_NOACTION) == 0) {
278 		if (ioctl(pf->dev, DIOCADDRDR, pf->prdr))
279 			err(1, "DIOCADDRDR");
280 	}
281 	if (pf->opts & PF_OPT_VERBOSE)
282 		print_rdr(&pf->prdr->rdr);
283 	return 0;
284 }
285 
286 int
287 pfctl_rules(int dev, char *filename, int opts)
288 {
289 	FILE *fin;
290 	struct pfioc_rule	pr;
291 	struct pfctl		pf;
292 
293 	if (strcmp(filename, "-") == 0) {
294 		infile = "stdin";
295 		fin = stdin;
296 	} else {
297 		fin = fopen(filename, "r");
298 		infile = filename;
299 	}
300 	if (fin == NULL)
301 		return (1);
302 	if ((opts & PF_OPT_NOACTION) == 0) {
303 		if (ioctl(dev, DIOCBEGINRULES, &pr.ticket))
304 			err(1, "DIOCBEGINRULES");
305 	}
306 	/* fill in callback data */
307 	pf.dev = dev;
308 	pf.opts = opts;
309 	pf.prule = &pr;
310 	if (parse_rules(fin, &pf) < 0)
311 		errx(1, "syntax error in rule file: pf rules not loaded");
312 	if ((opts & PF_OPT_NOACTION) == 0) {
313 		if (ioctl(dev, DIOCCOMMITRULES, &pr.ticket))
314 			err(1, "DIOCCOMMITRULES");
315 #if 0
316 		if ((opts & PF_OPT_QUIET) == 0)
317 			printf("%u rules loaded\n", n);
318 #endif
319 	}
320 	if (fin != stdin)
321 		fclose(fin);
322 	return (0);
323 }
324 
325 int
326 pfctl_nat(int dev, char *filename, int opts)
327 {
328 	FILE *fin;
329 	struct pfioc_nat	pn;
330 	struct pfioc_rdr	pr;
331 	struct pfctl		pf;
332 
333 	if (strcmp(filename, "-") == 0) {
334 		fin = stdin;
335 		infile = "stdin";
336 	} else {
337 		fin = fopen(filename, "r");
338 		infile = filename;
339 	}
340 	if (fin == NULL)
341 		return (1);
342 
343 	if ((opts & PF_OPT_NOACTION) == 0) {
344 		if (ioctl(dev, DIOCBEGINNATS, &pn.ticket))
345 			err(1, "DIOCBEGINNATS");
346 
347 		if (ioctl(dev, DIOCBEGINRDRS, &pr.ticket))
348 			err(1, "DIOCBEGINRDRS");
349 	}
350 	/* fill in callback data */
351 	pf.dev = dev;
352 	pf.opts = opts;
353 	pf.pnat = &pn;
354 	pf.prdr = &pr;
355 	if (parse_nat(fin, &pf) < 0)
356 		errx(1, "syntax error in file: nat rules not loaded");
357 	if ((opts & PF_OPT_NOACTION) == 0) {
358 		if (ioctl(dev, DIOCCOMMITNATS, &pn.ticket))
359 			err(1, "DIOCCOMMITNATS");
360 		if (ioctl(dev, DIOCCOMMITRDRS, &pr.ticket))
361 			err(1, "DIOCCOMMITRDRS");
362 #if 0
363 		if ((opts & PF_OPT_QUIET) == 0) {
364 			printf("%u nat entries loaded\n", n);
365 			printf("%u rdr entries loaded\n", r);
366 		}
367 #endif
368 	}
369 	if (fin != stdin)
370 		fclose(fin);
371 	return (0);
372 }
373 
374 int
375 pfctl_log(int dev, char *ifname, int opts)
376 {
377 	struct pfioc_if pi;
378 
379 	strncpy(pi.ifname, ifname, 16);
380 	if (ioctl(dev, DIOCSETSTATUSIF, &pi))
381 		err(1, "DIOCSETSTATUSIF");
382 	if ((opts & PF_OPT_QUIET) == 0)
383 		printf("now logging %s\n", pi.ifname);
384 	return (0);
385 }
386 
387 int
388 pfctl_debug(int dev, u_int32_t level, int opts)
389 {
390 	if (ioctl(dev, DIOCSETDEBUG, &level))
391 		err(1, "DIOCSETDEBUG");
392 	if ((opts & PF_OPT_QUIET) == 0) {
393 		printf("debug level set to '");
394 		switch (level) {
395 			case PF_DEBUG_NONE:
396 				printf("none");
397 				break;
398 			case PF_DEBUG_URGENT:
399 				printf("urgent");
400 				break;
401 			case PF_DEBUG_MISC:
402 				printf("misc");
403 				break;
404 			default:
405 				printf("<invalid>");
406 				break;
407 		}
408 		printf("'\n");
409 	}
410 	return (0);
411 }
412 
413 int
414 main(int argc, char *argv[])
415 {
416 	extern char *optarg;
417 	extern int optind;
418 	int error = 0;
419 	int dev = -1;
420 	int ch;
421 
422 	if (argc < 2)
423 		usage();
424 
425 	while ((ch = getopt(argc, argv, "deqF:hl:nN:R:s:vx:")) != -1) {
426 		switch (ch) {
427 		case 'd':
428 			opts |= PF_OPT_DISABLE;
429 			break;
430 		case 'e':
431 			opts |= PF_OPT_ENABLE;
432 			break;
433 		case 'q':
434 			opts |= PF_OPT_QUIET;
435 			break;
436 		case 'F':
437 			clearopt = optarg;
438 			break;
439 		case 'l':
440 			logopt = optarg;
441 			break;
442 		case 'n':
443 			opts |= PF_OPT_NOACTION;
444 			break;
445 		case 'N':
446 			natopt = optarg;
447 			break;
448 		case 'R':
449 			rulesopt = optarg;
450 			break;
451 		case 's':
452 			showopt = optarg;
453 			break;
454 		case 'v':
455 			opts |= PF_OPT_VERBOSE;
456 			break;
457 		case 'x':
458 			debugopt = optarg;
459 			break;
460 		case 'h':
461 		default:
462 			usage();
463 			/* NOTREACHED */
464 		}
465 	}
466 
467 	if (argc != optind) {
468 		warnx("unknown command line argument: %s ...", argv[optind]);
469 		usage();
470 		/* NOTREACHED */
471 	}
472 
473 	if ((opts & PF_OPT_NOACTION) == 0) {
474 		dev = open("/dev/pf", O_RDWR);
475 		if (dev == -1)
476 			err(1, "open(\"/dev/pf\")");
477 	} else {
478 		/* turn off options */
479 		opts &= ~ (PF_OPT_DISABLE | PF_OPT_ENABLE);
480 		clearopt = logopt = showopt = debugopt = NULL;
481 	}
482 
483 	if (opts & PF_OPT_DISABLE)
484 		if (pfctl_disable(dev, opts))
485 			error = 1;
486 
487 	if (clearopt != NULL) {
488 
489 		switch (*clearopt) {
490 		case 'r':
491 			pfctl_clear_rules(dev, opts);
492 			break;
493 		case 'n':
494 			pfctl_clear_nat(dev, opts);
495 			break;
496 		case 's':
497 			pfctl_clear_states(dev, opts);
498 			break;
499 		case 'i':
500 			pfctl_clear_stats(dev, opts);
501 			break;
502 		case 'a':
503 			pfctl_clear_rules(dev, opts);
504 			pfctl_clear_nat(dev, opts);
505 			pfctl_clear_states(dev, opts);
506 			pfctl_clear_stats(dev, opts);
507 			break;
508 		default:
509 			warnx("Unknown flush modifier '%s'", clearopt);
510 			error = 1;
511 		}
512 	}
513 
514 	if (rulesopt != NULL)
515 		if (pfctl_rules(dev, rulesopt, opts))
516 			error = 1;
517 
518 	if (natopt != NULL)
519 		if (pfctl_nat(dev, natopt, opts))
520 			error = 1;
521 
522 	if (showopt != NULL) {
523 		switch (*showopt) {
524 		case 'r':
525 			pfctl_show_rules(dev);
526 			break;
527 		case 'n':
528 			pfctl_show_nat(dev);
529 			break;
530 		case 's':
531 			pfctl_show_states(dev, 0);
532 			break;
533 		case 'i':
534 			pfctl_show_status(dev);
535 			break;
536 		case 'a':
537 			pfctl_show_rules(dev);
538 			pfctl_show_nat(dev);
539 			pfctl_show_states(dev, 0);
540 			pfctl_show_status(dev);
541 			break;
542 		default:
543 			warnx("Unknown show modifier '%s'", showopt);
544 			error = 1;
545 		}
546 	}
547 
548 	if (logopt != NULL)
549 		if (pfctl_log(dev, logopt, opts))
550 			error = 1;
551 
552 	if (opts & PF_OPT_ENABLE)
553 		if (pfctl_enable(dev, opts))
554 			error = 1;
555 
556 	if (debugopt != NULL) {
557 		switch (*debugopt) {
558 		case 'n':
559 			pfctl_debug(dev, PF_DEBUG_NONE, opts);
560 			break;
561 		case 'u':
562 			pfctl_debug(dev, PF_DEBUG_URGENT, opts);
563 			break;
564 		case 'm':
565 			pfctl_debug(dev, PF_DEBUG_MISC, opts);
566 			break;
567 		default:
568 			warnx("Unknown debug level '%s'", debugopt);
569 			error = 1;
570 		}
571 	}
572 
573 	close(dev);
574 
575 	exit(error);
576 }
577