MFC r1.6 r1.15 (HEAD)
[dragonfly.git] / contrib / ipfilter / ipf.c
blobcf8528046897cc27fc49ebafe63527043d146893
1 /*
2 * Copyright (C) 1993-2001 by Darren Reed.
4 * See the IPFILTER.LICENCE file for details on licencing.
5 */
6 #ifdef __FreeBSD__
7 # ifndef __FreeBSD_cc_version
8 # include <osreldate.h>
9 # else
10 # if __FreeBSD_cc_version < 430000
11 # include <osreldate.h>
12 # endif
13 # endif
14 #endif
15 #if defined(__sgi) && (IRIX > 602)
16 # include <sys/ptimers.h>
17 #endif
18 #include <stdio.h>
19 #include <unistd.h>
20 #include <string.h>
21 #include <fcntl.h>
22 #include <errno.h>
23 #if !defined(__SVR4) && !defined(__GNUC__)
24 #include <strings.h>
25 #endif
26 #include <sys/types.h>
27 #include <sys/param.h>
28 #include <sys/file.h>
29 #include <stdlib.h>
30 #include <stddef.h>
31 #include <sys/socket.h>
32 #include <sys/ioctl.h>
33 #include <netinet/in.h>
34 #include <netinet/in_systm.h>
35 #include <sys/time.h>
36 #include <net/if.h>
37 #if __FreeBSD_version >= 300000
38 # include <net/if_var.h>
39 #endif
40 #include <netinet/ip.h>
41 #include <netdb.h>
42 #include <arpa/nameser.h>
43 #include <resolv.h>
44 #include "ip_compat.h"
45 #include "ip_fil.h"
46 #include "ip_nat.h"
47 #include "ip_state.h"
48 #include "ipf.h"
49 #include "ipl.h"
51 #if !defined(lint)
52 static const char sccsid[] = "@(#)ipf.c 1.23 6/5/96 (C) 1993-2000 Darren Reed";
53 static const char rcsid[] = "@(#)$Id: ipf.c,v 2.10.2.23 2003/06/27 14:39:13 darrenr Exp $";
54 #endif
56 #if SOLARIS
57 static void blockunknown __P((void));
58 #endif
59 #if !defined(__SVR4) && defined(__GNUC__)
60 extern char *index __P((const char *, int));
61 #endif
63 extern char *optarg;
64 extern int optind;
66 void frsync __P((void));
67 void zerostats __P((void));
68 int main __P((int, char *[]));
70 int opts = 0;
71 int use_inet6 = 0;
73 static int fd = -1;
75 static void procfile __P((char *, char *)), flushfilter __P((char *));
76 static int set_state __P((u_int));
77 static void showstats __P((friostat_t *));
78 static void packetlogon __P((char *)), swapactive __P((void));
79 static int opendevice __P((char *));
80 static void closedevice __P((void));
81 static char *getline __P((char *, size_t, FILE *, int *));
82 static char *ipfname = IPL_NAME;
83 static void usage __P((char *));
84 static int showversion __P((void));
85 static int get_flags __P((int *));
88 #if SOLARIS
89 # define OPTS "6AdDEf:F:Il:noPrsUvVyzZ"
90 #else
91 # define OPTS "6AdDEf:F:Il:noPrsvVyzZ"
92 #endif
94 static void usage(name)
95 char *name;
97 fprintf(stderr, "usage: %s [-%s] %s %s %s\n", name, OPTS,
98 "[-l block|pass|nomatch]", "[-F i|o|a|s|S]", "[-f filename]");
99 exit(1);
103 int main(argc,argv)
104 int argc;
105 char *argv[];
107 int c;
109 if (argc < 2)
110 usage(argv[0]);
112 while ((c = getopt(argc, argv, OPTS)) != -1) {
113 switch (c)
115 case '6' :
116 use_inet6 = 1;
117 break;
118 case 'A' :
119 opts &= ~OPT_INACTIVE;
120 break;
121 case 'E' :
122 if (set_state((u_int)1))
123 exit(1);
124 break;
125 case 'D' :
126 if (set_state((u_int)0))
127 exit(1);
128 break;
129 case 'd' :
130 opts |= OPT_DEBUG;
131 break;
132 case 'f' :
133 procfile(argv[0], optarg);
134 break;
135 case 'F' :
136 flushfilter(optarg);
137 break;
138 case 'I' :
139 opts |= OPT_INACTIVE;
140 break;
141 case 'l' :
142 packetlogon(optarg);
143 break;
144 case 'n' :
145 opts |= OPT_DONOTHING;
146 break;
147 case 'o' :
148 break;
149 case 'P' :
150 ipfname = IPL_AUTH;
151 break;
152 case 'r' :
153 opts |= OPT_REMOVE;
154 break;
155 case 's' :
156 swapactive();
157 break;
158 #if SOLARIS
159 case 'U' :
160 blockunknown();
161 break;
162 #endif
163 case 'v' :
164 opts += OPT_VERBOSE;
165 break;
166 case 'V' :
167 if (showversion())
168 exit(1);
169 break;
170 case 'y' :
171 frsync();
172 break;
173 case 'z' :
174 opts |= OPT_ZERORULEST;
175 break;
176 case 'Z' :
177 zerostats();
178 break;
179 case '?' :
180 default :
181 usage(argv[0]);
182 break;
186 if (optind < 2)
187 usage(argv[0]);
189 if (fd != -1)
190 (void) close(fd);
192 exit(0);
193 /* NOTREACHED */
197 static int opendevice(ipfdev)
198 char *ipfdev;
200 if (opts & OPT_DONOTHING)
201 return 0;
203 if (!ipfdev)
204 ipfdev = ipfname;
207 * shouldn't we really be testing for fd < 0 here and below?
210 if (fd != -1)
211 return 0;
213 if ((fd = open(ipfdev, O_RDWR)) == -1) {
214 if ((fd = open(ipfdev, O_RDONLY)) == -1) {
215 perror("open device");
216 if (errno == ENODEV)
217 fprintf(stderr, "IPFilter enabled?\n");
218 return -1;
222 return 0;
226 static void closedevice()
228 if (fd != -1)
229 close(fd);
230 fd = -1;
235 * Return codes:
236 * 0 Success
237 * !0 Failure (and an error message has already been printed)
239 static int get_flags(i)
240 int *i;
243 if (opts & OPT_DONOTHING)
244 return 0;
246 if (opendevice(ipfname) < 0)
247 return -1;
249 if (ioctl(fd, SIOCGETFF, i) == -1) {
250 perror("SIOCGETFF");
251 return -1;
253 return 0;
257 static int set_state(enable)
258 u_int enable;
260 if (opts & OPT_DONOTHING)
261 return 0;
263 if (opendevice(ipfname))
264 return -1;
266 if (ioctl(fd, SIOCFRENB, &enable) == -1) {
267 if (errno == EBUSY)
268 /* Not really an error */
269 fprintf(stderr,
270 "IP Filter: already initialized\n");
271 else {
272 perror("SIOCFRENB");
273 return -1;
276 return 0;
279 static void procfile(name, file)
280 char *name, *file;
282 FILE *fp;
283 char line[513], *s;
284 struct frentry *fr;
285 u_int add, del;
286 int linenum = 0;
287 int parsestatus;
289 if (opendevice(ipfname) == -1)
290 exit(1);
292 if (opts & OPT_INACTIVE) {
293 add = SIOCADIFR;
294 del = SIOCRMIFR;
295 } else {
296 add = SIOCADAFR;
297 del = SIOCRMAFR;
299 if (opts & OPT_DEBUG)
300 printf("add %x del %x\n", add, del);
302 initparse();
304 if (!strcmp(file, "-"))
305 fp = stdin;
306 else if (!(fp = fopen(file, "r"))) {
307 fprintf(stderr, "%s: fopen(%s) failed: %s\n", name, file,
308 STRERROR(errno));
309 exit(1);
312 while (getline(line, sizeof(line), fp, &linenum)) {
314 * treat CR as EOL. LF is converted to NUL by getline().
316 if ((s = index(line, '\r')))
317 *s = '\0';
319 * # is comment marker, everything after is a ignored
321 if ((s = index(line, '#')))
322 *s = '\0';
324 if (!*line)
325 continue;
327 if (opts & OPT_VERBOSE)
328 (void)fprintf(stderr, "[%s]\n", line);
330 parsestatus = 1;
331 fr = parse(line, linenum, &parsestatus);
332 (void)fflush(stdout);
334 if (parsestatus != 0) {
335 fprintf(stderr, "%s: %s: %s error (%d), quitting\n",
336 name, file,
337 ((parsestatus < 0)? "parse": "internal"),
338 parsestatus);
339 exit(1);
342 if (fr) {
343 if (opts & OPT_ZERORULEST)
344 add = SIOCZRLST;
345 else if (opts & OPT_INACTIVE)
346 add = (u_int)fr->fr_hits ? SIOCINIFR :
347 SIOCADIFR;
348 else
349 add = (u_int)fr->fr_hits ? SIOCINAFR :
350 SIOCADAFR;
351 if (fr->fr_hits)
352 fr->fr_hits--;
353 if (fr && (opts & OPT_VERBOSE))
354 printfr(fr);
355 if (fr && (opts & OPT_OUTQUE))
356 fr->fr_flags |= FR_OUTQUE;
358 if (opts & OPT_DEBUG)
359 binprint(fr);
361 if ((opts & OPT_ZERORULEST) &&
362 !(opts & OPT_DONOTHING)) {
363 if (ioctl(fd, add, &fr) == -1) {
364 fprintf(stderr, "%d:", linenum);
365 perror("ioctl(SIOCZRLST)");
366 exit(1);
367 } else {
368 #ifdef USE_QUAD_T
369 printf("hits %qd bytes %qd ",
370 (long long)fr->fr_hits,
371 (long long)fr->fr_bytes);
372 #else
373 printf("hits %ld bytes %ld ",
374 fr->fr_hits, fr->fr_bytes);
375 #endif
376 printfr(fr);
378 } else if ((opts & OPT_REMOVE) &&
379 !(opts & OPT_DONOTHING)) {
380 if (ioctl(fd, del, &fr) == -1) {
381 fprintf(stderr, "%d:", linenum);
382 perror("ioctl(delete rule)");
383 exit(1);
385 } else if (!(opts & OPT_DONOTHING)) {
386 if (ioctl(fd, add, &fr) == -1) {
387 fprintf(stderr, "%d:", linenum);
388 perror("ioctl(add/insert rule)");
389 exit(1);
394 if (ferror(fp) || !feof(fp)) {
395 fprintf(stderr, "%s: %s: file error or line too long\n",
396 name, file);
397 exit(1);
399 (void)fclose(fp);
403 * Similar to fgets(3) but can handle '\\' and NL is converted to NUL.
404 * Returns NULL if error occurred, EOF encounterd or input line is too long.
406 static char *getline(str, size, file, linenum)
407 register char *str;
408 size_t size;
409 FILE *file;
410 int *linenum;
412 char *p;
413 int s, len;
415 do {
416 for (p = str, s = size;; p += (len - 1), s -= (len - 1)) {
418 * if an error occurred, EOF was encounterd, or there
419 * was no room to put NUL, return NULL.
421 if (fgets(p, s, file) == NULL)
422 return (NULL);
423 len = strlen(p);
424 if (p[len - 1] != '\n') {
425 p[len] = '\0';
426 break;
428 (*linenum)++;
429 p[len - 1] = '\0';
430 if (len < 2 || p[len - 2] != '\\')
431 break;
432 else
434 * Convert '\\' to a space so words don't
435 * run together
437 p[len - 2] = ' ';
439 } while (*str == '\0');
440 return (str);
444 static void packetlogon(opt)
445 char *opt;
447 int flag;
449 if (get_flags(&flag))
450 exit(1);
452 if (flag != 0) {
453 if ((opts & (OPT_DONOTHING|OPT_VERBOSE)) == OPT_VERBOSE)
454 printf("log flag is currently %#x\n", flag);
457 flag &= ~(FF_LOGPASS|FF_LOGNOMATCH|FF_LOGBLOCK);
459 if (index(opt, 'p')) {
460 flag |= FF_LOGPASS;
461 if (opts & OPT_VERBOSE)
462 printf("set log flag: pass\n");
464 if (index(opt, 'm') && (*opt == 'n' || *opt == 'N')) {
465 flag |= FF_LOGNOMATCH;
466 if (opts & OPT_VERBOSE)
467 printf("set log flag: nomatch\n");
469 if (index(opt, 'b') || index(opt, 'd')) {
470 flag |= FF_LOGBLOCK;
471 if (opts & OPT_VERBOSE)
472 printf("set log flag: block\n");
475 if (opendevice(ipfname) == -1) {
476 exit(1);
479 if (!(opts & OPT_DONOTHING)) {
480 if (ioctl(fd, SIOCSETFF, &flag) != 0) {
481 perror("ioctl(SIOCSETFF)");
482 exit(1);
486 if ((opts & (OPT_DONOTHING|OPT_VERBOSE)) == OPT_VERBOSE) {
488 * Even though the ioctls above succeeded, it
489 * is possible that a calling script/program
490 * relies on the following verbose mode string.
491 * Thus, we still take an error exit if get_flags
492 * fails here.
494 if (get_flags(&flag))
495 exit(1);
496 printf("log flag is now %#x\n", flag);
501 static void flushfilter(arg)
502 char *arg;
504 int fl = 0, rem;
506 if (!arg || !*arg) {
507 fprintf(stderr, "-F: no filter specified\n");
508 exit(1);
511 if (!strcmp(arg, "s") || !strcmp(arg, "S")) {
512 if (*arg == 'S')
513 fl = 0;
514 else
515 fl = 1;
516 rem = fl;
518 closedevice();
520 if (opendevice(IPL_STATE) == -1) {
521 exit(1);
524 if (!(opts & OPT_DONOTHING)) {
525 if (use_inet6) {
526 if (ioctl(fd, SIOCIPFL6, &fl) == -1) {
527 perror("ioctl(SIOCIPFL6)");
528 exit(1);
530 } else {
531 if (ioctl(fd, SIOCIPFFL, &fl) == -1) {
532 perror("ioctl(SIOCIPFFL)");
533 exit(1);
537 if ((opts & (OPT_DONOTHING|OPT_VERBOSE)) == OPT_VERBOSE) {
538 printf("remove flags %s (%d)\n", arg, rem);
539 printf("removed %d filter rules\n", fl);
541 closedevice();
542 return;
544 if (strchr(arg, 'i') || strchr(arg, 'I'))
545 fl = FR_INQUE;
546 if (strchr(arg, 'o') || strchr(arg, 'O'))
547 fl = FR_OUTQUE;
548 if (strchr(arg, 'a') || strchr(arg, 'A'))
549 fl = FR_OUTQUE|FR_INQUE;
550 fl |= (opts & FR_INACTIVE);
551 rem = fl;
553 if (opendevice(ipfname) == -1) {
554 exit(1);
557 if (!(opts & OPT_DONOTHING)) {
558 if (use_inet6) {
559 if (ioctl(fd, SIOCIPFL6, &fl) == -1) {
560 perror("ioctl(SIOCIPFL6)");
561 exit(1);
563 } else {
564 if (ioctl(fd, SIOCIPFFL, &fl) == -1) {
565 perror("ioctl(SIOCIPFFL)");
566 exit(1);
570 if ((opts & (OPT_DONOTHING|OPT_VERBOSE)) == OPT_VERBOSE) {
571 printf("remove flags %s%s (%d)\n", (rem & FR_INQUE) ? "I" : "",
572 (rem & FR_OUTQUE) ? "O" : "", rem);
573 printf("removed %d filter rules\n", fl);
575 return;
579 static void swapactive()
581 int in = 2;
583 if (opendevice(ipfname) == -1) {
584 exit(1);
588 if (!(opts & OPT_DONOTHING)) {
589 if (ioctl(fd, SIOCSWAPA, &in) == -1) {
590 perror("ioctl(SIOCSWAPA)");
591 exit(1);
594 printf("Set %d now inactive\n", in);
598 void frsync()
600 int frsyn = 0;
602 if (opendevice(ipfname) == -1)
603 exit(1);
605 if (!(opts & OPT_DONOTHING)) {
606 if (ioctl(fd, SIOCFRSYN, &frsyn) == -1) {
607 perror("SIOCFRSYN");
608 exit(1);
611 printf("filter sync'd\n");
615 void zerostats()
617 friostat_t fio;
618 friostat_t *fiop = &fio;
620 if (opendevice(ipfname) == -1)
621 exit(1);
623 if (!(opts & OPT_DONOTHING)) {
624 if (ioctl(fd, SIOCFRZST, &fiop) == -1) {
625 perror("ioctl(SIOCFRZST)");
626 exit(-1);
628 showstats(fiop);
635 * Read the kernel stats for packets blocked and passed
637 static void showstats(fp)
638 friostat_t *fp;
640 #if SOLARIS
641 printf("dropped packets:\tin %lu\tout %lu\n",
642 fp->f_st[0].fr_drop, fp->f_st[1].fr_drop);
643 printf("non-ip packets:\t\tin %lu\tout %lu\n",
644 fp->f_st[0].fr_notip, fp->f_st[1].fr_notip);
645 printf(" bad packets:\t\tin %lu\tout %lu\n",
646 fp->f_st[0].fr_bad, fp->f_st[1].fr_bad);
647 #endif
648 printf(" input packets:\t\tblocked %lu passed %lu nomatch %lu",
649 fp->f_st[0].fr_block, fp->f_st[0].fr_pass,
650 fp->f_st[0].fr_nom);
651 printf(" counted %lu\n", fp->f_st[0].fr_acct);
652 printf("output packets:\t\tblocked %lu passed %lu nomatch %lu",
653 fp->f_st[1].fr_block, fp->f_st[1].fr_pass,
654 fp->f_st[1].fr_nom);
655 printf(" counted %lu\n", fp->f_st[0].fr_acct);
656 printf(" input packets logged:\tblocked %lu passed %lu\n",
657 fp->f_st[0].fr_bpkl, fp->f_st[0].fr_ppkl);
658 printf("output packets logged:\tblocked %lu passed %lu\n",
659 fp->f_st[1].fr_bpkl, fp->f_st[1].fr_ppkl);
660 printf(" packets logged:\tinput %lu-%lu output %lu-%lu\n",
661 fp->f_st[0].fr_pkl, fp->f_st[0].fr_skip,
662 fp->f_st[1].fr_pkl, fp->f_st[1].fr_skip);
666 #if SOLARIS
667 static void blockunknown()
669 int flag;
671 if (opendevice(ipfname) == -1)
672 exit(1);
674 if (get_flags(&flag))
675 exit(1);
677 if ((opts & (OPT_DONOTHING|OPT_VERBOSE)) == OPT_VERBOSE)
678 printf("log flag is currently %#x\n", flag);
680 flag ^= FF_BLOCKNONIP;
682 if (opendevice(ipfname) == -1)
683 exit(1);
685 if (!(opts & OPT_DONOTHING)) {
686 if (ioctl(fd, SIOCSETFF, &flag))
687 perror("ioctl(SIOCSETFF)");
690 if ((opts & (OPT_DONOTHING|OPT_VERBOSE)) == OPT_VERBOSE) {
691 if (ioctl(fd, SIOCGETFF, &flag))
692 perror("ioctl(SIOCGETFF)");
694 printf("log flag is now %#x\n", flag);
697 #endif
701 * nonzero return value means caller should exit with error
703 static int showversion()
705 struct friostat fio;
706 struct friostat *fiop=&fio;
707 int flags, vfd;
708 char *s;
710 printf("ipf: %s (%d)\n", IPL_VERSION, (int)sizeof(frentry_t));
712 if ((vfd = open(ipfname, O_RDONLY)) == -1) {
713 perror("open device");
714 return 1;
717 if (ioctl(vfd, SIOCGETFS, &fiop)) {
718 perror("ioctl(SIOCGETFS)");
719 close(vfd);
720 return 1;
722 close(vfd);
724 printf("Kernel: %-*.*s\n", (int)sizeof(fio.f_version),
725 (int)sizeof(fio.f_version), fio.f_version);
726 printf("Running: %s\n", fio.f_running ? "yes" : "no");
728 if (get_flags(&flags)) {
729 return 1;
731 printf("Log Flags: %#x = ", flags);
732 s = "";
733 if (flags & FF_LOGPASS) {
734 printf("pass");
735 s = ", ";
737 if (flags & FF_LOGBLOCK) {
738 printf("%sblock", s);
739 s = ", ";
741 if (flags & FF_LOGNOMATCH) {
742 printf("%snomatch", s);
743 s = ", ";
745 if (flags & FF_BLOCKNONIP) {
746 printf("%snonip", s);
747 s = ", ";
749 if (!*s)
750 printf("none set");
751 putchar('\n');
753 printf("Default: ");
754 if (fio.f_defpass & FR_PASS)
755 s = "pass";
756 else if (fio.f_defpass & FR_BLOCK)
757 s = "block";
758 else
759 s = "nomatch -> block";
760 printf("%s all, Logging: %savailable\n", s, fio.f_logging ? "" : "un");
761 printf("Active list: %d\n", fio.f_active);
763 return 0;