Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / ipfilter / ipf.c
1 /*
2  * Copyright (C) 1993-2001 by Darren Reed.
3  *
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"
50
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.19 2002/12/06 11:41:13 darrenr Exp $";
54 #endif
55
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
62
63 extern  char    *optarg;
64
65 void    frsync __P((void));
66 void    zerostats __P((void));
67 int     main __P((int, char *[]));
68
69 int     opts = 0;
70 int     use_inet6 = 0;
71
72 static  int     fd = -1;
73
74 static  void    procfile __P((char *, char *)), flushfilter __P((char *));
75 static  void    set_state __P((u_int)), showstats __P((friostat_t *));
76 static  void    packetlogon __P((char *)), swapactive __P((void));
77 static  int     opendevice __P((char *));
78 static  void    closedevice __P((void));
79 static  char    *getline __P((char *, size_t, FILE *, int *));
80 static  char    *ipfname = IPL_NAME;
81 static  void    usage __P((void));
82 static  int     showversion __P((void));
83 static  int     get_flags __P((void));
84
85
86 #if SOLARIS
87 # define        OPTS    "6AdDEf:F:Il:noPrsUvVyzZ"
88 #else
89 # define        OPTS    "6AdDEf:F:Il:noPrsvVyzZ"
90 #endif
91
92 static void usage()
93 {
94         fprintf(stderr, "usage: ipf [-%s] %s %s %s\n", OPTS,
95                 "[-l block|pass|nomatch]", "[-F i|o|a|s|S]", "[-f filename]");
96         exit(1);
97 }
98
99
100 int main(argc,argv)
101 int argc;
102 char *argv[];
103 {
104         int c;
105
106         while ((c = getopt(argc, argv, OPTS)) != -1) {
107                 switch (c)
108                 {
109                 case '6' :
110                         use_inet6 = 1;
111                         break;
112                 case 'A' :
113                         opts &= ~OPT_INACTIVE;
114                         break;
115                 case 'E' :
116                         set_state((u_int)1);
117                         break;
118                 case 'D' :
119                         set_state((u_int)0);
120                         break;
121                 case 'd' :
122                         opts |= OPT_DEBUG;
123                         break;
124                 case 'f' :
125                         procfile(argv[0], optarg);
126                         break;
127                 case 'F' :
128                         flushfilter(optarg);
129                         break;
130                 case 'I' :
131                         opts |= OPT_INACTIVE;
132                         break;
133                 case 'l' :
134                         packetlogon(optarg);
135                         break;
136                 case 'n' :
137                         opts |= OPT_DONOTHING;
138                         break;
139                 case 'o' :
140                         break;
141                 case 'P' :
142                         ipfname = IPL_AUTH;
143                         break;
144                 case 'r' :
145                         opts |= OPT_REMOVE;
146                         break;
147                 case 's' :
148                         swapactive();
149                         break;
150 #if SOLARIS
151                 case 'U' :
152                         blockunknown();
153                         break;
154 #endif
155                 case 'v' :
156                         opts += OPT_VERBOSE;
157                         break;
158                 case 'V' :
159                         if (showversion())
160                                 exit(1);
161                         break;
162                 case 'y' :
163                         frsync();
164                         break;
165                 case 'z' :
166                         opts |= OPT_ZERORULEST;
167                         break;
168                 case 'Z' :
169                         zerostats();
170                         break;
171                 default :
172                         usage();
173                         break;
174                 }
175         }
176
177         if (fd != -1)
178                 (void) close(fd);
179
180         exit(0);
181         /* NOTREACHED */
182 }
183
184
185 static int opendevice(ipfdev)
186 char *ipfdev;
187 {
188         if (opts & OPT_DONOTHING)
189                 return -2;
190
191         if (!ipfdev)
192                 ipfdev = ipfname;
193
194         if (!(opts & OPT_DONOTHING) && fd == -1)
195                 if ((fd = open(ipfdev, O_RDWR)) == -1)
196                         if ((fd = open(ipfdev, O_RDONLY)) == -1) {
197                                 perror("open device");
198                                 if (errno == ENODEV)
199                                         fprintf(stderr, "IPFilter enabled?\n");
200                         }
201         return fd;
202 }
203
204
205 static void closedevice()
206 {
207         close(fd);
208         fd = -1;
209 }
210
211
212 static  int     get_flags()
213 {
214         int i;
215
216         if ((opendevice(ipfname) != -2) && (ioctl(fd, SIOCGETFF, &i) == -1)) {
217                 perror("SIOCGETFF");
218                 return 0;
219         }
220         return i;
221 }
222
223
224 static  void    set_state(enable)
225 u_int   enable;
226 {
227         if (opendevice(ipfname) != -2)
228                 if (ioctl(fd, SIOCFRENB, &enable) == -1) {
229                         if (errno == EBUSY)
230                                 fprintf(stderr,
231                                         "IP Filter: already initialized\n");
232                         else
233                                 perror("SIOCFRENB");
234                 }
235         return;
236 }
237
238 static  void    procfile(name, file)
239 char    *name, *file;
240 {
241         FILE    *fp;
242         char    line[513], *s;
243         struct  frentry *fr;
244         u_int   add, del;
245         int     linenum = 0;
246
247         (void) opendevice(ipfname);
248
249         if (opts & OPT_INACTIVE) {
250                 add = SIOCADIFR;
251                 del = SIOCRMIFR;
252         } else {
253                 add = SIOCADAFR;
254                 del = SIOCRMAFR;
255         }
256         if (opts & OPT_DEBUG)
257                 printf("add %x del %x\n", add, del);
258
259         initparse();
260
261         if (!strcmp(file, "-"))
262                 fp = stdin;
263         else if (!(fp = fopen(file, "r"))) {
264                 fprintf(stderr, "%s: fopen(%s) failed: %s\n", name, file,
265                         STRERROR(errno));
266                 exit(1);
267         }
268
269         while (getline(line, sizeof(line), fp, &linenum)) {
270                 /*
271                  * treat CR as EOL.  LF is converted to NUL by getline().
272                  */
273                 if ((s = index(line, '\r')))
274                         *s = '\0';
275                 /*
276                  * # is comment marker, everything after is a ignored
277                  */
278                 if ((s = index(line, '#')))
279                         *s = '\0';
280
281                 if (!*line)
282                         continue;
283
284                 if (opts & OPT_VERBOSE)
285                         (void)fprintf(stderr, "[%s]\n", line);
286
287                 fr = parse(line, linenum);
288                 (void)fflush(stdout);
289
290                 if (fr) {
291                         if (opts & OPT_ZERORULEST)
292                                 add = SIOCZRLST;
293                         else if (opts & OPT_INACTIVE)
294                                 add = (u_int)fr->fr_hits ? SIOCINIFR :
295                                                            SIOCADIFR;
296                         else
297                                 add = (u_int)fr->fr_hits ? SIOCINAFR :
298                                                            SIOCADAFR;
299                         if (fr->fr_hits)
300                                 fr->fr_hits--;
301                         if (fr && (opts & OPT_VERBOSE))
302                                 printfr(fr);
303                         if (fr && (opts & OPT_OUTQUE))
304                                 fr->fr_flags |= FR_OUTQUE;
305
306                         if (opts & OPT_DEBUG)
307                                 binprint(fr);
308
309                         if ((opts & OPT_ZERORULEST) &&
310                             !(opts & OPT_DONOTHING)) {
311                                 if (ioctl(fd, add, &fr) == -1) {
312                                         fprintf(stderr, "%d:", linenum);
313                                         perror("ioctl(SIOCZRLST)");
314                                 } else {
315 #ifdef  USE_QUAD_T
316                                         printf("hits %qd bytes %qd ",
317                                                 (long long)fr->fr_hits,
318                                                 (long long)fr->fr_bytes);
319 #else
320                                         printf("hits %ld bytes %ld ",
321                                                 fr->fr_hits, fr->fr_bytes);
322 #endif
323                                         printfr(fr);
324                                 }
325                         } else if ((opts & OPT_REMOVE) &&
326                                    !(opts & OPT_DONOTHING)) {
327                                 if (ioctl(fd, del, &fr) == -1) {
328                                         fprintf(stderr, "%d:", linenum);
329                                         perror("ioctl(delete rule)");
330                                 }
331                         } else if (!(opts & OPT_DONOTHING)) {
332                                 if (ioctl(fd, add, &fr) == -1) {
333                                         fprintf(stderr, "%d:", linenum);
334                                         perror("ioctl(add/insert rule)");
335                                 }
336                         }
337                 }
338         }
339         if (ferror(fp) || !feof(fp)) {
340                 fprintf(stderr, "%s: %s: file error or line too long\n",
341                     name, file);
342                 exit(1);
343         }
344         (void)fclose(fp);
345 }
346
347 /*
348  * Similar to fgets(3) but can handle '\\' and NL is converted to NUL.
349  * Returns NULL if error occured, EOF encounterd or input line is too long.
350  */
351 static char *getline(str, size, file, linenum)
352 register char   *str;
353 size_t  size;
354 FILE    *file;
355 int     *linenum;
356 {
357         char *p;
358         int s, len;
359
360         do {
361                 for (p = str, s = size;; p += (len - 1), s -= (len - 1)) {
362                         /*
363                          * if an error occured, EOF was encounterd, or there
364                          * was no room to put NUL, return NULL.
365                          */
366                         if (fgets(p, s, file) == NULL)
367                                 return (NULL);
368                         len = strlen(p);
369                         if (p[len - 1] != '\n') {
370                                 p[len] = '\0';
371                                 break;
372                         }
373                         (*linenum)++;
374                         p[len - 1] = '\0';
375                         if (len < 2 || p[len - 2] != '\\')
376                                 break;
377                         else
378                                 /*
379                                  * Convert '\\' to a space so words don't
380                                  * run together
381                                  */
382                                 p[len - 2] = ' ';
383                 }
384         } while (*str == '\0');
385         return (str);
386 }
387
388
389 static void packetlogon(opt)
390 char    *opt;
391 {
392         int     flag;
393
394         flag = get_flags();
395         if (flag != 0) {
396                 if ((opts & (OPT_DONOTHING|OPT_VERBOSE)) == OPT_VERBOSE)
397                         printf("log flag is currently %#x\n", flag);
398         }
399
400         flag &= ~(FF_LOGPASS|FF_LOGNOMATCH|FF_LOGBLOCK);
401
402         if (index(opt, 'p')) {
403                 flag |= FF_LOGPASS;
404                 if (opts & OPT_VERBOSE)
405                         printf("set log flag: pass\n");
406         }
407         if (index(opt, 'm') && (*opt == 'n' || *opt == 'N')) {
408                 flag |= FF_LOGNOMATCH;
409                 if (opts & OPT_VERBOSE)
410                         printf("set log flag: nomatch\n");
411         }
412         if (index(opt, 'b') || index(opt, 'd')) {
413                 flag |= FF_LOGBLOCK;
414                 if (opts & OPT_VERBOSE)
415                         printf("set log flag: block\n");
416         }
417
418         if (opendevice(ipfname) != -2 && (ioctl(fd, SIOCSETFF, &flag) != 0))
419                 perror("ioctl(SIOCSETFF)");
420
421         if ((opts & (OPT_DONOTHING|OPT_VERBOSE)) == OPT_VERBOSE) {
422                 flag = get_flags();
423                 printf("log flag is now %#x\n", flag);
424         }
425 }
426
427
428 static  void    flushfilter(arg)
429 char    *arg;
430 {
431         int     fl = 0, rem;
432
433         if (!arg || !*arg)
434                 return;
435         if (!strcmp(arg, "s") || !strcmp(arg, "S")) {
436                 if (*arg == 'S')
437                         fl = 0;
438                 else
439                         fl = 1;
440                 rem = fl;
441
442                 closedevice();
443                 if (opendevice(IPL_STATE) != -2) {
444                         if (use_inet6) {
445                                 if (ioctl(fd, SIOCIPFL6, &fl) == -1)
446                                         perror("ioctl(SIOCIPFL6)");
447                         } else {
448                                 if (ioctl(fd, SIOCIPFFL, &fl) == -1)
449                                         perror("ioctl(SIOCIPFFL)");
450                         }
451                 }
452                 if ((opts & (OPT_DONOTHING|OPT_VERBOSE)) == OPT_VERBOSE) {
453                         printf("remove flags %s (%d)\n", arg, rem);
454                         printf("removed %d filter rules\n", fl);
455                 }
456                 closedevice();
457                 return;
458         }
459         if (strchr(arg, 'i') || strchr(arg, 'I'))
460                 fl = FR_INQUE;
461         if (strchr(arg, 'o') || strchr(arg, 'O'))
462                 fl = FR_OUTQUE;
463         if (strchr(arg, 'a') || strchr(arg, 'A'))
464                 fl = FR_OUTQUE|FR_INQUE;
465         fl |= (opts & FR_INACTIVE);
466         rem = fl;
467
468         if (opendevice(ipfname) != -2) {
469                 if (use_inet6) {
470                         if (ioctl(fd, SIOCIPFL6, &fl) == -1)
471                                 perror("ioctl(SIOCIPFL6)");
472                 } else {
473                         if (ioctl(fd, SIOCIPFFL, &fl) == -1)
474                                 perror("ioctl(SIOCIPFFL)");
475                 }
476         }
477         if ((opts & (OPT_DONOTHING|OPT_VERBOSE)) == OPT_VERBOSE) {
478                 printf("remove flags %s%s (%d)\n", (rem & FR_INQUE) ? "I" : "",
479                         (rem & FR_OUTQUE) ? "O" : "", rem);
480                 printf("removed %d filter rules\n", fl);
481         }
482         return;
483 }
484
485
486 static void swapactive()
487 {
488         int in = 2;
489
490         if (opendevice(ipfname) != -2 && ioctl(fd, SIOCSWAPA, &in) == -1)
491                 perror("ioctl(SIOCSWAPA)");
492         else
493                 printf("Set %d now inactive\n", in);
494 }
495
496
497 void frsync()
498 {
499         int frsyn = 0;
500
501         if (opendevice(ipfname) != -2 && ioctl(fd, SIOCFRSYN, &frsyn) == -1)
502                 perror("SIOCFRSYN");
503         else
504                 printf("filter sync'd\n");
505 }
506
507
508 void zerostats()
509 {
510         friostat_t      fio;
511         friostat_t      *fiop = &fio;
512
513         if (opendevice(ipfname) != -2) {
514                 if (ioctl(fd, SIOCFRZST, &fiop) == -1) {
515                         perror("ioctl(SIOCFRZST)");
516                         exit(-1);
517                 }
518                 showstats(fiop);
519         }
520
521 }
522
523
524 /*
525  * read the kernel stats for packets blocked and passed
526  */
527 static void showstats(fp)
528 friostat_t      *fp;
529 {
530 #if SOLARIS
531         printf("dropped packets:\tin %lu\tout %lu\n",
532                         fp->f_st[0].fr_drop, fp->f_st[1].fr_drop);
533         printf("non-ip packets:\t\tin %lu\tout %lu\n",
534                         fp->f_st[0].fr_notip, fp->f_st[1].fr_notip);
535         printf("   bad packets:\t\tin %lu\tout %lu\n",
536                         fp->f_st[0].fr_bad, fp->f_st[1].fr_bad);
537 #endif
538         printf(" input packets:\t\tblocked %lu passed %lu nomatch %lu",
539                         fp->f_st[0].fr_block, fp->f_st[0].fr_pass,
540                         fp->f_st[0].fr_nom);
541         printf(" counted %lu\n", fp->f_st[0].fr_acct);
542         printf("output packets:\t\tblocked %lu passed %lu nomatch %lu",
543                         fp->f_st[1].fr_block, fp->f_st[1].fr_pass,
544                         fp->f_st[1].fr_nom);
545         printf(" counted %lu\n", fp->f_st[0].fr_acct);
546         printf(" input packets logged:\tblocked %lu passed %lu\n",
547                         fp->f_st[0].fr_bpkl, fp->f_st[0].fr_ppkl);
548         printf("output packets logged:\tblocked %lu passed %lu\n",
549                         fp->f_st[1].fr_bpkl, fp->f_st[1].fr_ppkl);
550         printf(" packets logged:\tinput %lu-%lu output %lu-%lu\n",
551                         fp->f_st[0].fr_pkl, fp->f_st[0].fr_skip,
552                         fp->f_st[1].fr_pkl, fp->f_st[1].fr_skip);
553 }
554
555
556 #if SOLARIS
557 static void blockunknown()
558 {
559         u_32_t  flag;
560
561         if (opendevice(ipfname) == -1)
562                 return;
563
564         flag = get_flags();
565         if ((opts & (OPT_DONOTHING|OPT_VERBOSE)) == OPT_VERBOSE)
566                 printf("log flag is currently %#x\n", flag);
567
568         flag ^= FF_BLOCKNONIP;
569
570         if (opendevice(ipfname) != -2 && ioctl(fd, SIOCSETFF, &flag))
571                 perror("ioctl(SIOCSETFF)");
572
573         if ((opts & (OPT_DONOTHING|OPT_VERBOSE)) == OPT_VERBOSE) {
574                 if (ioctl(fd, SIOCGETFF, &flag))
575                         perror("ioctl(SIOCGETFF)");
576
577                 printf("log flag is now %#x\n", flag);
578         }
579 }
580 #endif
581
582
583 static int showversion()
584 {
585         struct friostat fio;
586         struct friostat *fiop=&fio;
587         u_32_t flags;
588         char *s;
589         int vfd;
590
591         printf("ipf: %s (%d)\n", IPL_VERSION, (int)sizeof(frentry_t));
592
593         if ((vfd = open(ipfname, O_RDONLY)) == -1) {
594                 perror("open device");
595                 return 1;
596         }
597
598         if (ioctl(vfd, SIOCGETFS, &fiop)) {
599                 perror("ioctl(SIOCGETFS)");
600                 close(vfd);
601                 return 1;
602         }
603         close(vfd);
604         flags = get_flags();
605
606         printf("Kernel: %-*.*s\n", (int)sizeof(fio.f_version),
607                 (int)sizeof(fio.f_version), fio.f_version);
608         printf("Running: %s\n", fio.f_running ? "yes" : "no");
609         printf("Log Flags: %#x = ", flags);
610         s = "";
611         if (flags & FF_LOGPASS) {
612                 printf("pass");
613                 s = ", ";
614         }
615         if (flags & FF_LOGBLOCK) {
616                 printf("%sblock", s);
617                 s = ", ";
618         }
619         if (flags & FF_LOGNOMATCH) {
620                 printf("%snomatch", s);
621                 s = ", ";
622         }
623         if (flags & FF_BLOCKNONIP) {
624                 printf("%snonip", s);
625                 s = ", ";
626         }
627         if (!*s)
628                 printf("none set");
629         putchar('\n');
630
631         printf("Default: ");
632         if (fio.f_defpass & FR_PASS)
633                 s = "pass";
634         else if (fio.f_defpass & FR_BLOCK)
635                 s = "block";
636         else
637                 s = "nomatch -> block";
638         printf("%s all, Logging: %savailable\n", s, fio.f_logging ? "" : "un");
639         printf("Active list: %d\n", fio.f_active);
640
641         return 0;
642 }