From aa045fa499198eb2eb8470a800289fd6c40cff0d Mon Sep 17 00:00:00 2001 From: Joerg Wunsch Date: Mon, 23 Nov 1998 10:54:28 +0000 Subject: [PATCH] Preprocessor support for `ipfw [-q] ... file'. This allows for more flexible ipfw configuration files using `variables' to describe frequently used items in the file, like the local IP address(es), interface names etc. Both m4 and cpp are useful and supported; with m4 being a little more unusual to the common C programmer, things like automatic rule numbering can be achieved fairly easy. While i was at it, i've also untangled some of the ugly style inside main(), and fixed a bug or two (like not being able to use blank lines when running with -q). A typical call with preprocessor invocation looks like ipfw -p m4 -Dhostname=$(hostname) /etc/fwrules Someone should probably add support for this feature to /etc/rc.firewall. --- sbin/ipfw/ipfw.8 | 33 +++++++++++- sbin/ipfw/ipfw.c | 127 +++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 145 insertions(+), 15 deletions(-) diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8 index bd70c5822db8..d7f8cd81b142 100644 --- a/sbin/ipfw/ipfw.8 +++ b/sbin/ipfw/ipfw.8 @@ -6,8 +6,11 @@ .Nd controlling utility for IP firewall .Sh SYNOPSIS .Nm ipfw +.Op Fl q .Oo -.Fl q +.Fl p Ar preproc +.Op Fl D Ar macro Ns Op Ns =value +.Op Fl U Ar macro .Oc file .Nm ipfw @@ -58,6 +61,32 @@ will be read line by line and applied as arguments to the .Nm command. .Pp +Optionally, a preprocessor can be specified using +.Fl p Ar preproc +where +.Ar file +is to be piped through. Useful preprocessors include +.Xr cpp 1 +and +.Xr m4 1 . +If +.Ar preproc +doesn't start with a slash as its first character, the usual +.Ev PATH +name search is performed. Care should be taken with this in environments +where not all filesystems are mounted (yet) by the time +.Nm +is being run (e. g. since they are mounted over NFS). Once +.Fl p +has been specified, optional +.Fl D +and +.Fl U +specifcations can follow and will be passed on to the preprocessor. +This allows for flexible configuration files (like conditionalizing +them on the local hostname) and the use of macros to centralize +frequently required arguments like IP addresses. +.Pp The .Nm code works by going through the rule-list for each packet, @@ -515,6 +544,8 @@ This rule diverts all incoming packets from 192.168.2.0/24 to divert port 5000: .Pp .Dl ipfw divert 5000 all from 192.168.2.0/24 to any in .Sh SEE ALSO +.Xr cpp 1 , +.Xr m4 1 , .Xr divert 4 , .Xr ip 4 , .Xr ipfirewall 4 , diff --git a/sbin/ipfw/ipfw.c b/sbin/ipfw/ipfw.c index 3f6807b11dc0..d0f06ce5c2d9 100644 --- a/sbin/ipfw/ipfw.c +++ b/sbin/ipfw/ipfw.c @@ -16,7 +16,7 @@ * * NEW command line interface for IP firewall facility * - * $Id: ipfw.c,v 1.59 1998/08/04 14:41:37 thepish Exp $ + * $Id: ipfw.c,v 1.60 1998/09/28 22:56:37 alex Exp $ * */ @@ -25,18 +25,21 @@ #include #include #include +#include #include #include +#include #include #include +#include #include #include #include #include +#include #include #include -#include #include #include @@ -1199,7 +1202,7 @@ ipfw_main(ac,av) { int ch; - extern int optind; + extern int optreset; /* XXX should be declared in */ if ( ac == 1 ) { show_usage(NULL); @@ -1208,7 +1211,7 @@ ipfw_main(ac,av) /* Set the force flag for non-interactive processes */ do_force = !isatty(STDIN_FILENO); - optind = 1; + optind = optreset = 1; while ((ch = getopt(ac, av, "afqtN")) != -1) switch(ch) { case 'a': @@ -1289,10 +1292,11 @@ main(ac, av) #define MAX_ARGS 32 #define WHITESP " \t\f\v\n\r" char buf[BUFSIZ]; - char *a, *args[MAX_ARGS]; + char *a, *p, *args[MAX_ARGS], *cmd = NULL; char linename[10]; - int i, qflag=0; - FILE *f; + int i, c, qflag, pflag, status; + FILE *f = NULL; + pid_t preproc = 0; s = socket( AF_INET, SOCK_RAW, IPPROTO_RAW ); if ( s < 0 ) @@ -1300,14 +1304,94 @@ main(ac, av) setbuf(stdout,0); - if (av[1] && (!access(av[1], R_OK) || - (av[2] && (qflag=!strcmp(av[1],"-q")) && !access(av[2], R_OK)))){ + if (ac > 1 && access(av[ac - 1], R_OK) == 0) { + qflag = pflag = i = 0; lineno = 0; - if ((f = fopen(av[ac-1], "r")) == NULL) - err(EX_UNAVAILABLE, "fopen: %s", av[ac-1]); - while (fgets(buf, BUFSIZ, f)) { - char *p; + while ((c = getopt(ac, av, "D:U:p:q")) != -1) + switch(c) { + case 'D': + if (!pflag) + errx(EX_USAGE, "-D requires -p"); + if (i > MAX_ARGS - 2) + errx(EX_USAGE, + "too many -D or -U options"); + args[i++] = "-D"; + args[i++] = optarg; + break; + + case 'U': + if (!pflag) + errx(EX_USAGE, "-U requires -p"); + if (i > MAX_ARGS - 2) + errx(EX_USAGE, + "too many -D or -U options"); + args[i++] = "-U"; + args[i++] = optarg; + break; + + case 'p': + pflag = 1; + cmd = optarg; + args[0] = cmd; + i = 1; + break; + + case 'q': + qflag = 1; + break; + + default: + show_usage(NULL); + } + + av += optind; + ac -= optind; + if (ac != 1) + show_usage("extraneous filename arguments"); + + if ((f = fopen(av[0], "r")) == NULL) + err(EX_UNAVAILABLE, "fopen: %s", av[0]); + + if (pflag) { + /* pipe through preprocessor (cpp or m4) */ + int pipedes[2]; + + args[i] = 0; + + if (pipe(pipedes) == -1) + err(EX_OSERR, "cannot create pipe"); + + switch((preproc = fork())) { + case -1: + err(EX_OSERR, "cannot fork"); + + case 0: + /* child */ + if (dup2(fileno(f), 0) == -1 || + dup2(pipedes[1], 1) == -1) + err(EX_OSERR, "dup2()"); + fclose(f); + close(pipedes[1]); + close(pipedes[0]); + execvp(cmd, args); + err(EX_OSERR, "execvp(%s) failed", cmd); + + default: + /* parent */ + fclose(f); + close(pipedes[1]); + if ((f = fdopen(pipedes[0], "r")) == NULL) { + int savederrno = errno; + + (void)kill(preproc, SIGTERM); + errno = savederrno; + err(EX_OSERR, "fdopen()"); + } + } + } + + while (fgets(buf, BUFSIZ, f)) { lineno++; sprintf(linename, "Line %d", lineno); args[0] = linename; @@ -1321,7 +1405,7 @@ main(ac, av) for (a = strtok(buf, WHITESP); a && i < MAX_ARGS; a = strtok(NULL, WHITESP), i++) args[i] = a; - if (i == 1) + if (i == (qflag? 2: 1)) continue; if (i == MAX_ARGS) errx(EX_USAGE, "%s: too many arguments", linename); @@ -1330,6 +1414,21 @@ main(ac, av) ipfw_main(i, args); } fclose(f); + if (pflag) { + if (waitpid(preproc, &status, 0) != -1) { + if (WIFEXITED(status)) { + if (WEXITSTATUS(status) != EX_OK) + errx(EX_UNAVAILABLE, + "preprocessor exited with status %d", + WEXITSTATUS(status)); + } else if (WIFSIGNALED(status)) { + errx(EX_UNAVAILABLE, + "preprocessor exited with signal %d", + WTERMSIG(status)); + } + } + } + } else ipfw_main(ac,av); return EX_OK;