diff options
Diffstat (limited to 'ipf.c')
-rw-r--r-- | ipf.c | 764 |
1 files changed, 764 insertions, 0 deletions
@@ -0,0 +1,764 @@ +/* + * Copyright (C) 1993-2001 by Darren Reed. + * + * See the IPFILTER.LICENCE file for details on licencing. + */ +#ifdef __FreeBSD__ +# ifndef __FreeBSD_cc_version +# include <osreldate.h> +# else +# if __FreeBSD_cc_version < 430000 +# include <osreldate.h> +# endif +# endif +#endif +#if defined(__sgi) && (IRIX > 602) +# include <sys/ptimers.h> +#endif +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#if !defined(__SVR4) && !defined(__GNUC__) +#include <strings.h> +#endif +#include <sys/types.h> +#include <sys/param.h> +#include <sys/file.h> +#include <stdlib.h> +#include <stddef.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <sys/time.h> +#include <net/if.h> +#if __FreeBSD_version >= 300000 +# include <net/if_var.h> +#endif +#include <netinet/ip.h> +#include <netdb.h> +#include <arpa/nameser.h> +#include <resolv.h> +#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; +} |