/* * Copyright (C) 1993-2001 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #ifdef __FreeBSD__ # ifndef __FreeBSD_cc_version # include # else # if __FreeBSD_cc_version < 430000 # include # endif # endif #endif #if defined(__sgi) && (IRIX > 602) # include #endif #include #include #include #include #include #if !defined(__SVR4) && !defined(__GNUC__) #include #endif #include #include #include #include #include #include #include #include #include #include #include #if __FreeBSD_version >= 300000 # include #endif #include #include #include #include #include "ip_compat.h" #include "ip_fil.h" #include "ip_nat.h" #include "ip_state.h" #include "ipf.h" #include "ipl.h" #if !defined(lint) static const char sccsid[] = "@(#)ipf.c 1.23 6/5/96 (C) 1993-2000 Darren Reed"; static const char rcsid[] = "@(#)$Id: ipf.c,v 2.10.2.23 2003/06/27 14:39:13 darrenr Exp $"; #endif #if SOLARIS static void blockunknown __P((void)); #endif #if !defined(__SVR4) && defined(__GNUC__) extern char *index __P((const char *, int)); #endif extern char *optarg; extern int optind; void frsync __P((void)); void zerostats __P((void)); int main __P((int, char *[])); int opts = 0; int use_inet6 = 0; static int fd = -1; static void procfile __P((char *, char *)), flushfilter __P((char *)); static int set_state __P((u_int)); static void showstats __P((friostat_t *)); static void packetlogon __P((char *)), swapactive __P((void)); static int opendevice __P((char *)); static void closedevice __P((void)); static char *getline __P((char *, size_t, FILE *, int *)); static char *ipfname = IPL_NAME; static void usage __P((char *)); static int showversion __P((void)); static int get_flags __P((int *)); #if SOLARIS # define OPTS "6AdDEf:F:Il:noPrsUvVyzZ" #else # define OPTS "6AdDEf:F:Il:noPrsvVyzZ" #endif static void usage(name) char *name; { fprintf(stderr, "usage: %s [-%s] %s %s %s\n", name, OPTS, "[-l block|pass|nomatch]", "[-F i|o|a|s|S]", "[-f filename]"); exit(1); } int main(argc,argv) int argc; char *argv[]; { int c; if (argc < 2) usage(argv[0]); while ((c = getopt(argc, argv, OPTS)) != -1) { switch (c) { case '6' : use_inet6 = 1; break; case 'A' : opts &= ~OPT_INACTIVE; break; case 'E' : if (set_state((u_int)1)) exit(1); break; case 'D' : if (set_state((u_int)0)) exit(1); break; case 'd' : opts |= OPT_DEBUG; break; case 'f' : procfile(argv[0], optarg); break; case 'F' : flushfilter(optarg); break; case 'I' : opts |= OPT_INACTIVE; break; case 'l' : packetlogon(optarg); break; case 'n' : opts |= OPT_DONOTHING; break; case 'o' : break; case 'P' : ipfname = IPL_AUTH; break; case 'r' : opts |= OPT_REMOVE; break; case 's' : swapactive(); break; #if SOLARIS case 'U' : blockunknown(); break; #endif case 'v' : opts += OPT_VERBOSE; break; case 'V' : if (showversion()) exit(1); break; case 'y' : frsync(); break; case 'z' : opts |= OPT_ZERORULEST; break; case 'Z' : zerostats(); break; case '?' : default : usage(argv[0]); break; } } if (optind < 2) usage(argv[0]); if (fd != -1) (void) close(fd); exit(0); /* NOTREACHED */ } static int opendevice(ipfdev) char *ipfdev; { if (opts & OPT_DONOTHING) return 0; if (!ipfdev) ipfdev = ipfname; /* * shouldn't we really be testing for fd < 0 here and below? */ if (fd != -1) return 0; if ((fd = open(ipfdev, O_RDWR)) == -1) { if ((fd = open(ipfdev, O_RDONLY)) == -1) { perror("open device"); if (errno == ENODEV) fprintf(stderr, "IPFilter enabled?\n"); return -1; } } return 0; } static void closedevice() { if (fd != -1) close(fd); fd = -1; } /* * Return codes: * 0 Success * !0 Failure (and an error message has already been printed) */ static int get_flags(i) int *i; { if (opts & OPT_DONOTHING) return 0; if (opendevice(ipfname) < 0) return -1; if (ioctl(fd, SIOCGETFF, i) == -1) { perror("SIOCGETFF"); return -1; } return 0; } static int set_state(enable) u_int enable; { if (opts & OPT_DONOTHING) return 0; if (opendevice(ipfname)) return -1; if (ioctl(fd, SIOCFRENB, &enable) == -1) { if (errno == EBUSY) /* Not really an error */ fprintf(stderr, "IP Filter: already initialized\n"); else { perror("SIOCFRENB"); return -1; } } return 0; } static void procfile(name, file) char *name, *file; { FILE *fp; char line[513], *s; struct frentry *fr; u_int add, del; int linenum = 0; int parsestatus; if (opendevice(ipfname) == -1) exit(1); if (opts & OPT_INACTIVE) { add = SIOCADIFR; del = SIOCRMIFR; } else { add = SIOCADAFR; del = SIOCRMAFR; } if (opts & OPT_DEBUG) printf("add %x del %x\n", add, del); initparse(); if (!strcmp(file, "-")) fp = stdin; else if (!(fp = fopen(file, "r"))) { fprintf(stderr, "%s: fopen(%s) failed: %s\n", name, file, STRERROR(errno)); exit(1); } while (getline(line, sizeof(line), fp, &linenum)) { /* * treat CR as EOL. LF is converted to NUL by getline(). */ if ((s = index(line, '\r'))) *s = '\0'; /* * # is comment marker, everything after is a ignored */ if ((s = index(line, '#'))) *s = '\0'; if (!*line) continue; if (opts & OPT_VERBOSE) (void)fprintf(stderr, "[%s]\n", line); parsestatus = 1; fr = parse(line, linenum, &parsestatus); (void)fflush(stdout); if (parsestatus != 0) { fprintf(stderr, "%s: %s: %s error (%d), quitting\n", name, file, ((parsestatus < 0)? "parse": "internal"), parsestatus); exit(1); } if (fr) { if (opts & OPT_ZERORULEST) add = SIOCZRLST; else if (opts & OPT_INACTIVE) add = (u_int)fr->fr_hits ? SIOCINIFR : SIOCADIFR; else add = (u_int)fr->fr_hits ? SIOCINAFR : SIOCADAFR; if (fr->fr_hits) fr->fr_hits--; if (fr && (opts & OPT_VERBOSE)) printfr(fr); if (fr && (opts & OPT_OUTQUE)) fr->fr_flags |= FR_OUTQUE; if (opts & OPT_DEBUG) binprint(fr); if ((opts & OPT_ZERORULEST) && !(opts & OPT_DONOTHING)) { if (ioctl(fd, add, &fr) == -1) { fprintf(stderr, "%d:", linenum); perror("ioctl(SIOCZRLST)"); exit(1); } else { #ifdef USE_QUAD_T printf("hits %qd bytes %qd ", (long long)fr->fr_hits, (long long)fr->fr_bytes); #else printf("hits %ld bytes %ld ", fr->fr_hits, fr->fr_bytes); #endif printfr(fr); } } else if ((opts & OPT_REMOVE) && !(opts & OPT_DONOTHING)) { if (ioctl(fd, del, &fr) == -1) { fprintf(stderr, "%d:", linenum); perror("ioctl(delete rule)"); exit(1); } } else if (!(opts & OPT_DONOTHING)) { if (ioctl(fd, add, &fr) == -1) { fprintf(stderr, "%d:", linenum); perror("ioctl(add/insert rule)"); exit(1); } } } } if (ferror(fp) || !feof(fp)) { fprintf(stderr, "%s: %s: file error or line too long\n", name, file); exit(1); } (void)fclose(fp); } /* * Similar to fgets(3) but can handle '\\' and NL is converted to NUL. * Returns NULL if error occurred, EOF encounterd or input line is too long. */ static char *getline(str, size, file, linenum) register char *str; size_t size; FILE *file; int *linenum; { char *p; int s, len; do { for (p = str, s = size;; p += (len - 1), s -= (len - 1)) { /* * if an error occurred, EOF was encounterd, or there * was no room to put NUL, return NULL. */ if (fgets(p, s, file) == NULL) return (NULL); len = strlen(p); if (p[len - 1] != '\n') { p[len] = '\0'; break; } (*linenum)++; p[len - 1] = '\0'; if (len < 2 || p[len - 2] != '\\') break; else /* * Convert '\\' to a space so words don't * run together */ p[len - 2] = ' '; } } while (*str == '\0'); return (str); } static void packetlogon(opt) char *opt; { int flag; if (get_flags(&flag)) exit(1); if (flag != 0) { if ((opts & (OPT_DONOTHING|OPT_VERBOSE)) == OPT_VERBOSE) printf("log flag is currently %#x\n", flag); } flag &= ~(FF_LOGPASS|FF_LOGNOMATCH|FF_LOGBLOCK); if (index(opt, 'p')) { flag |= FF_LOGPASS; if (opts & OPT_VERBOSE) printf("set log flag: pass\n"); } if (index(opt, 'm') && (*opt == 'n' || *opt == 'N')) { flag |= FF_LOGNOMATCH; if (opts & OPT_VERBOSE) printf("set log flag: nomatch\n"); } if (index(opt, 'b') || index(opt, 'd')) { flag |= FF_LOGBLOCK; if (opts & OPT_VERBOSE) printf("set log flag: block\n"); } if (opendevice(ipfname) == -1) { exit(1); } if (!(opts & OPT_DONOTHING)) { if (ioctl(fd, SIOCSETFF, &flag) != 0) { perror("ioctl(SIOCSETFF)"); exit(1); } } if ((opts & (OPT_DONOTHING|OPT_VERBOSE)) == OPT_VERBOSE) { /* * Even though the ioctls above succeeded, it * is possible that a calling script/program * relies on the following verbose mode string. * Thus, we still take an error exit if get_flags * fails here. */ if (get_flags(&flag)) exit(1); printf("log flag is now %#x\n", flag); } } static void flushfilter(arg) char *arg; { int fl = 0, rem; if (!arg || !*arg) { fprintf(stderr, "-F: no filter specified\n"); exit(1); } if (!strcmp(arg, "s") || !strcmp(arg, "S")) { if (*arg == 'S') fl = 0; else fl = 1; rem = fl; closedevice(); if (opendevice(IPL_STATE) == -1) { exit(1); } if (!(opts & OPT_DONOTHING)) { if (use_inet6) { if (ioctl(fd, SIOCIPFL6, &fl) == -1) { perror("ioctl(SIOCIPFL6)"); exit(1); } } else { if (ioctl(fd, SIOCIPFFL, &fl) == -1) { perror("ioctl(SIOCIPFFL)"); exit(1); } } } if ((opts & (OPT_DONOTHING|OPT_VERBOSE)) == OPT_VERBOSE) { printf("remove flags %s (%d)\n", arg, rem); printf("removed %d filter rules\n", fl); } closedevice(); return; } if (strchr(arg, 'i') || strchr(arg, 'I')) fl = FR_INQUE; if (strchr(arg, 'o') || strchr(arg, 'O')) fl = FR_OUTQUE; if (strchr(arg, 'a') || strchr(arg, 'A')) fl = FR_OUTQUE|FR_INQUE; fl |= (opts & FR_INACTIVE); rem = fl; if (opendevice(ipfname) == -1) { exit(1); } if (!(opts & OPT_DONOTHING)) { if (use_inet6) { if (ioctl(fd, SIOCIPFL6, &fl) == -1) { perror("ioctl(SIOCIPFL6)"); exit(1); } } else { if (ioctl(fd, SIOCIPFFL, &fl) == -1) { perror("ioctl(SIOCIPFFL)"); exit(1); } } } if ((opts & (OPT_DONOTHING|OPT_VERBOSE)) == OPT_VERBOSE) { printf("remove flags %s%s (%d)\n", (rem & FR_INQUE) ? "I" : "", (rem & FR_OUTQUE) ? "O" : "", rem); printf("removed %d filter rules\n", fl); } return; } static void swapactive() { int in = 2; if (opendevice(ipfname) == -1) { exit(1); } if (!(opts & OPT_DONOTHING)) { if (ioctl(fd, SIOCSWAPA, &in) == -1) { perror("ioctl(SIOCSWAPA)"); exit(1); } } printf("Set %d now inactive\n", in); } void frsync() { int frsyn = 0; if (opendevice(ipfname) == -1) exit(1); if (!(opts & OPT_DONOTHING)) { if (ioctl(fd, SIOCFRSYN, &frsyn) == -1) { perror("SIOCFRSYN"); exit(1); } } printf("filter sync'd\n"); } void zerostats() { friostat_t fio; friostat_t *fiop = &fio; if (opendevice(ipfname) == -1) exit(1); if (!(opts & OPT_DONOTHING)) { if (ioctl(fd, SIOCFRZST, &fiop) == -1) { perror("ioctl(SIOCFRZST)"); exit(-1); } showstats(fiop); } } /* * Read the kernel stats for packets blocked and passed */ static void showstats(fp) friostat_t *fp; { #if SOLARIS printf("dropped packets:\tin %lu\tout %lu\n", fp->f_st[0].fr_drop, fp->f_st[1].fr_drop); printf("non-ip packets:\t\tin %lu\tout %lu\n", fp->f_st[0].fr_notip, fp->f_st[1].fr_notip); printf(" bad packets:\t\tin %lu\tout %lu\n", fp->f_st[0].fr_bad, fp->f_st[1].fr_bad); #endif printf(" input packets:\t\tblocked %lu passed %lu nomatch %lu", fp->f_st[0].fr_block, fp->f_st[0].fr_pass, fp->f_st[0].fr_nom); printf(" counted %lu\n", fp->f_st[0].fr_acct); printf("output packets:\t\tblocked %lu passed %lu nomatch %lu", fp->f_st[1].fr_block, fp->f_st[1].fr_pass, fp->f_st[1].fr_nom); printf(" counted %lu\n", fp->f_st[0].fr_acct); printf(" input packets logged:\tblocked %lu passed %lu\n", fp->f_st[0].fr_bpkl, fp->f_st[0].fr_ppkl); printf("output packets logged:\tblocked %lu passed %lu\n", fp->f_st[1].fr_bpkl, fp->f_st[1].fr_ppkl); printf(" packets logged:\tinput %lu-%lu output %lu-%lu\n", fp->f_st[0].fr_pkl, fp->f_st[0].fr_skip, fp->f_st[1].fr_pkl, fp->f_st[1].fr_skip); } #if SOLARIS static void blockunknown() { int flag; if (opendevice(ipfname) == -1) exit(1); if (get_flags(&flag)) exit(1); if ((opts & (OPT_DONOTHING|OPT_VERBOSE)) == OPT_VERBOSE) printf("log flag is currently %#x\n", flag); flag ^= FF_BLOCKNONIP; if (opendevice(ipfname) == -1) exit(1); if (!(opts & OPT_DONOTHING)) { if (ioctl(fd, SIOCSETFF, &flag)) perror("ioctl(SIOCSETFF)"); } if ((opts & (OPT_DONOTHING|OPT_VERBOSE)) == OPT_VERBOSE) { if (ioctl(fd, SIOCGETFF, &flag)) perror("ioctl(SIOCGETFF)"); printf("log flag is now %#x\n", flag); } } #endif /* * nonzero return value means caller should exit with error */ static int showversion() { struct friostat fio; struct friostat *fiop=&fio; int flags, vfd; char *s; printf("ipf: %s (%d)\n", IPL_VERSION, (int)sizeof(frentry_t)); if ((vfd = open(ipfname, O_RDONLY)) == -1) { perror("open device"); return 1; } if (ioctl(vfd, SIOCGETFS, &fiop)) { perror("ioctl(SIOCGETFS)"); close(vfd); return 1; } close(vfd); printf("Kernel: %-*.*s\n", (int)sizeof(fio.f_version), (int)sizeof(fio.f_version), fio.f_version); printf("Running: %s\n", fio.f_running ? "yes" : "no"); if (get_flags(&flags)) { return 1; } printf("Log Flags: %#x = ", flags); s = ""; if (flags & FF_LOGPASS) { printf("pass"); s = ", "; } if (flags & FF_LOGBLOCK) { printf("%sblock", s); s = ", "; } if (flags & FF_LOGNOMATCH) { printf("%snomatch", s); s = ", "; } if (flags & FF_BLOCKNONIP) { printf("%snonip", s); s = ", "; } if (!*s) printf("none set"); putchar('\n'); printf("Default: "); if (fio.f_defpass & FR_PASS) s = "pass"; else if (fio.f_defpass & FR_BLOCK) s = "block"; else s = "nomatch -> block"; printf("%s all, Logging: %savailable\n", s, fio.f_logging ? "" : "un"); printf("Active list: %d\n", fio.f_active); return 0; }