From Gerhard.Sittig@gmx.net  Sat Oct 14 09:55:23 2000
Return-Path: <Gerhard.Sittig@gmx.net>
Received: from mail.gmx.net (pop.gmx.net [194.221.183.20])
	by hub.freebsd.org (Postfix) with SMTP id 021C237B503
	for <FreeBSD-gnats-submit@freebsd.org>; Sat, 14 Oct 2000 09:55:20 -0700 (PDT)
Received: (qmail 10232 invoked by uid 0); 14 Oct 2000 16:55:17 -0000
Received: from p3ee21601.dip.t-dialin.net (HELO speedy.gsinet) (62.226.22.1)
  by mail.gmx.net with SMTP; 14 Oct 2000 16:55:17 -0000
Received: (from sittig@localhost)
	by speedy.gsinet (8.8.8/8.8.8) id SAA29587
	for FreeBSD-gnats-submit@freebsd.org; Sat, 14 Oct 2000 18:24:39 +0200
Message-Id: <20001014182439.Q25237@speedy.gsinet>
Date: Sat, 14 Oct 2000 18:24:39 +0200
From: Gerhard Sittig <Gerhard.Sittig@gmx.net>
To: FreeBSD-gnats-submit@freebsd.org
Subject: [PATCH] preprocessor for ipfilter rules

>Number:         21989
>Category:       bin
>Synopsis:       preprocessor for ipfilter rules
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    darrenr
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Sat Oct 14 10:00:01 PDT 2000
>Closed-Date:    Thu Nov 23 14:22:16 PST 2000
>Last-Modified:  Thu Nov 23 14:22:59 PST 2000
>Originator:     Gerhard Sittig
>Release:        FreeBSD 4.1-STABLE i386
>Organization:
in private
>Environment:

Any FreeBSD version with ipfilter included as well as any other
platform ipfilter runs on, I guess. :)  It's just that FreeBSD
users might notice the absense (lack?) of a preprocessor more
than others since ipfw has the -p option. :>

>Description:

When loading (or manipulating) rules with ipf(8) and handing a
rules file to the command, the input has to be in exact ipf
language.  There's no possibility to have
- variables substituted
- loops unrolled
- translation from some more abstract language down to ipf syntax
  done

All of this of couse could be done by specifying "-" as the file
(or omitting the -f option) and feeding ipf's stdin from the
appropriate source.  But this doesn't fit well into the
(typical?) command invocation in the rc.conf and rc.network
environment (or whatever the boot scripts are called on the
platform ipf is used at).

>How-To-Repeat:

It should be obvious. :)  And reading "man ipfw" with a search
for "/-p" will give the idea that there's something "that would
be useful here, too".

>Fix:

Apply the following patch.  The code got tested here, but the doc
wasn't.  For some reason "man -M . ipf" refused to work from
within the src/contrib/ipfilter directory.  But it needs
rewording and markup corrections, anyway, and doesn't fit for
immediate application. :)

Since many option flags were in use already I had to do the
passing with one command line string.  But this is not a real
drawback.  And I slowly get the feeling that "-" isn't even an
exception to the passing to the preprocessor.


Index: ../../contrib/ipfilter/ipf.c
===================================================================
RCS file: /home/fcvs/src/contrib/ipfilter/ipf.c,v
retrieving revision 1.1.1.9
diff -u -r1.1.1.9 ipf.c
--- ../../contrib/ipfilter/ipf.c	2000/08/13 04:57:48	1.1.1.9
+++ ../../contrib/ipfilter/ipf.c	2000/10/14 15:19:07
@@ -66,7 +66,8 @@
 
 static	int	fd = -1;
 
-static	void	procfile __P((char *, char *)), flushfilter __P((char *));
+static	void	procfile __P((char *, char *, char *));
+static	void	flushfilter __P((char *));
 static	void	set_state __P((u_int)), showstats __P((friostat_t *));
 static	void	packetlogon __P((char *)), swapactive __P((void));
 static	int	opendevice __P((char *));
@@ -81,7 +82,8 @@
 static void usage()
 {
 	fprintf(stderr, "usage: ipf [-6AdDEInoPrsUvVyzZ] %s %s %s\n",
-		"[-l block|pass|nomatch]", "[-F i|o|a|s|S]", "[-f filename]");
+		"[-l block|pass|nomatch]", "[-F i|o|a|s|S]",
+		"[-p preprocessor_command] [-f filename]");
 	exit(1);
 }
 
@@ -91,8 +93,9 @@
 char *argv[];
 {
 	int c;
+	char *pp_name = NULL;
 
-	while ((c = getopt(argc, argv, "6AdDEf:F:Il:noPrsUvVyzZ")) != -1) {
+	while ((c = getopt(argc, argv, "6AdDEf:F:Il:nop:PrsUvVyzZ")) != -1) {
 		switch (c)
 		{
 		case '?' :
@@ -115,7 +118,7 @@
 			opts |= OPT_DEBUG;
 			break;
 		case 'f' :
-			procfile(argv[0], optarg);
+			procfile(argv[0], optarg, pp_name);
 			break;
 		case 'F' :
 			flushfilter(optarg);
@@ -131,6 +134,9 @@
 			break;
 		case 'o' :
 			break;
+		case 'p' :
+			pp_name = optarg;
+			break;
 		case 'P' :
 			ipfname = IPL_AUTH;
 			break;
@@ -221,14 +227,21 @@
 	return;
 }
 
-static	void	procfile(name, file)
-char	*name, *file;
+static	void	procfile(name, file, pp_fn)
+char	*name, *file, *pp_fn;
 {
 	FILE	*fp;
 	char	line[513], *s;
 	struct	frentry	*fr;
 	u_int	add, del;
 	int     linenum = 0;
+#define	PP_ARG_MAX	32
+#define	PP_ARG_SEP	" \t"
+	int	pp_pipe[2];
+	int	pp_pid;
+	int	pp_argc;
+	char	*pp_argv[PP_ARG_MAX + 1];
+	char	*pp_tmp;
 
 	(void) opendevice(ipfname);
 
@@ -250,6 +263,69 @@
 		fprintf(stderr, "%s: fopen(%s) failed: %s\n", name, file,
 			STRERROR(errno));
 		exit(1);
+	}
+
+	/* pipe the rules through a preprocessor if specified */
+	if (pp_fn && *pp_fn) {
+
+		if (pipe(pp_pipe) == -1) {
+			fprintf(stderr, "%s: pipe() failed: %s\n",
+					name, STRERROR(errno));
+			exit(1);
+		}
+
+		/*
+		 * FreeBSD has strsep(3), but others might not;
+		 * maybe we could use system(3) or popen(3)
+		 * and get rid of all the dimensioning/strtok stuff?
+		 */
+		for (	pp_argc = 0, pp_tmp = strtok(pp_fn, PP_ARG_SEP);
+			(pp_tmp) && (pp_argc < PP_ARG_MAX);
+			pp_tmp = strtok(NULL, PP_ARG_SEP)
+		) {
+			/* cope with adjacent delimiters */
+			if (*pp_tmp)
+				pp_argv[pp_argc++] = pp_tmp;
+		}
+		pp_argv[pp_argc] = NULL;
+		if (pp_tmp) {
+			fprintf(stderr, "%s: too many preprocessor args, "
+					"ignored \"%s\" and following\n",
+					name, pp_tmp);
+		}
+
+		switch(pp_pid = fork()) {
+		case -1:
+			fprintf(stderr, "%s: fork() failed: %s\n",
+					name, STRERROR(errno));
+			exit(1);
+
+		case 0:	 /* preprocessor job (child) */
+			if ((dup2(fileno(fp), 0) == -1) ||
+			    (dup2(pp_pipe[1], 1) == -1)) {
+				fprintf(stderr, "%s: dup2() failed: %s\n",
+						name, STRERROR(errno));
+				exit(1);
+			}
+			fclose(fp);
+			close(pp_pipe[1]);
+			close(pp_pipe[0]);
+			execvp(pp_argv[0], pp_argv);
+			fprintf(stderr, "%s: exec(%s) failed: %s\n",
+					name, pp_argv[0], STRERROR(errno));
+			exit(1);
+
+		default: /* ipf job (parent) */
+			fclose(fp);
+			close(pp_pipe[1]);
+			if (! (fp = fdopen(pp_pipe[0], "r"))) {
+				fprintf(stderr, "%s: fdopen() failed: %s\n",
+						name, STRERROR(errno));
+				kill(pp_pid, SIGTERM);
+				exit(1);
+			}
+			break;
+		}
 	}
 
 	while (getline(line, sizeof(line), fp)) {
Index: ../../contrib/ipfilter/man/ipf.8
===================================================================
RCS file: /home/fcvs/src/contrib/ipfilter/man/ipf.8,v
retrieving revision 1.4
diff -u -r1.4 ipf.8
--- ../../contrib/ipfilter/man/ipf.8	2000/05/24 02:19:15	1.4
+++ ../../contrib/ipfilter/man/ipf.8	2000/10/14 15:58:17
@@ -12,6 +12,9 @@
 ] [
 .B \-F
 <i|o|a|s|S>
+] [
+.B \-p
+<\fIpreprocessor\fP>
 ]
 .B \-f
 <\fIfilename\fP>
@@ -84,6 +87,17 @@
 .B \-o
 Force rules by default to be added/deleted to/from the output list, rather
 than the (default) input list.
+.TP
+.BR \-p \0<preprocessor>
+All of the files in subsequent \fB-f\fP options
+are fed into this command's stdin, its stdout
+is read back and processed instead of the file's content.
+This command gets invoked anew for every specified filename.
+This option only applies to real files,
+not to stdin specified with its "-" alias.
+Specifying an empty command will turn this feature off, again.
+Shell escapes are needed when passing arguments to this command.
+Up to 31 parameters (like \fB-D\fP and \fB-U\fP) can be passed.
 .TP
 .B \-P
 Add rules as temporary entries in the authentication rule table.


The test case looks like this:

$ head pp_in.txt
#define WORLD world
#define HELLO hello

HELLO WORLD
EXTDEF works too

$ ./ipf -v -p /usr/bin/cpp -f ./pp_in.txt
open device: Permission denied
[hello  world ]
2: unknown keyword (hello)
[EXTDEF works too]
3: unknown keyword (EXTDEF)
$ ./ipf -v -p '/usr/bin/cpp -DEXTDEF=external' -f ./pp_in.txt
open device: Permission denied
[hello  world ]
2: unknown keyword (hello)
[external  works too]
3: unknown keyword (external)
$

And of course it doesn't touch the ones who don't use -p:

$ ./ipf -v -f ./pp_in.txt
open device: Permission denied
[HELLO WORLD]
3: unknown keyword (HELLO)
[EXTDEF works too]
4: unknown keyword (EXTDEF)
$


virtually yours   82D1 9B9C 01DC 4FB4 D7B4  61BE 3F49 4F77 72DE DA76
Gerhard Sittig   true | mail -s "get gpg key" Gerhard.Sittig@gmx.net
-- 
     If you don't understand or are scared by any of the above
             ask your parents or an adult to help you.

>Release-Note:
>Audit-Trail:
Responsible-Changed-From-To: freebsd-bugs->darrenr 
Responsible-Changed-By: johan 
Responsible-Changed-When: Sat Oct 14 10:43:42 PDT 2000 
Responsible-Changed-Why:  
Over to ipfilter maintainer. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=21989 

From: Gerhard Sittig <Gerhard.Sittig@gmx.net>
To: FreeBSD-gnats-submit@freebsd.org
Cc: Darren Reed <darrenr@FreeBSD.org>
Subject: Re: bin/21989: [PATCH] preprocessor for ipfilter rules
Date: Sun, 15 Oct 2000 11:58:29 +0200

 On Sat, Oct 14, 2000 at 18:24 +0200, Gerhard Sittig wrote:
 > 
 > And I slowly get the feeling that "-" isn't even an
 > exception to the passing to the preprocessor.
 
 Well, it turns out to be. :(  Following the ipf.8 diff the code
 should have
 - either suppressed preprocessing when the "file" is stdin
 - or taken special care to not clobber its stdin handle
 
 And something else showed up:  Separating pp_name into pp_argv[]
 on every procfile() invocation mangled pp_name in a way that the
 next loop found one word only.  Therefor I made pp_argv[] global
 and introduced a use_pproc flag to scan the command only once
 when specified with -p.
 
 And while I were testing, I wasted some time searching for errors
 just because ipf didn't tell me option -p was not supported.
 That's what the usage() and getopt() arg mangling is for:  When
 there's no code for an option, I would like to know about it.
 
 And I wonder if there would be any way of making the manpage stuff
 "conditional" either by cpp'ing the troff input or by specifying
 "when compiled with this option" in the -6 and -p sections.
 
 Feel free to ignore the patch from the previous message and
 consider the following one to be the relevant ipf extension:
 
 Index: ../../contrib/ipfilter/ipf.c
 ===================================================================
 RCS file: /home/fcvs/src/contrib/ipfilter/ipf.c,v
 retrieving revision 1.1.1.9
 diff -u -r1.1.1.9 ipf.c
 --- ../../contrib/ipfilter/ipf.c	2000/08/13 04:57:48	1.1.1.9
 +++ ../../contrib/ipfilter/ipf.c	2000/10/15 09:43:19
 @@ -66,6 +66,13 @@
  
  static	int	fd = -1;
  
 +#ifdef	USE_PPROC
 +#define	PP_ARG_MAX	32
 +#define	PP_ARG_SEP	" \t"
 +static	int	use_pproc = 0;
 +static	char	*pp_argv[PP_ARG_MAX + 1] = { NULL, };
 +#endif
 +
  static	void	procfile __P((char *, char *)), flushfilter __P((char *));
  static	void	set_state __P((u_int)), showstats __P((friostat_t *));
  static	void	packetlogon __P((char *)), swapactive __P((void));
 @@ -77,11 +84,32 @@
  static	void	showversion __P((void));
  static	int	get_flags __P((void));
  
 +/* cause getopt to barf when code for an option is not present */
 +#ifdef	USE_INET6
 +  #define SYNOPSIS_INET6 "6"
 +  #define GETOPT_INET6 "6"
 +#else
 +  #define SYNOPSIS_INET6 ""
 +  #define GETOPT_INET6 ""
 +#endif
 +#ifdef	USE_PPROC
 +  #define SYNOPSIS_PPROC "[-p preprocessor] "
 +  #define GETOPT_PPROC "p:"
 +#else
 +  #define SYNOPSIS_PPROC ""
 +  #define GETOPT_PPROC ""
 +#endif
 +#define	SYNOPSIS_COMMON "AdDEInoPrsUvVyzZ"
 +#define	GETOPT_COMMON "AdDEf:F:Il:noPrsUvVyzZ"
  
 +#define	GETOPT_OPTIONS	GETOPT_COMMON GETOPT_INET6 GETOPT_PPROC
 +
  static void usage()
  {
 -	fprintf(stderr, "usage: ipf [-6AdDEInoPrsUvVyzZ] %s %s %s\n",
 -		"[-l block|pass|nomatch]", "[-F i|o|a|s|S]", "[-f filename]");
 +	fprintf(stderr, "usage: ipf [-%s] %s %s %s\n",
 +		SYNOPSIS_INET6 SYNOPSIS_COMMON,
 +		"[-l block|pass|nomatch]", "[-F i|o|a|s|S]",
 +		SYNOPSIS_PPROC "[-f filename]");
  	exit(1);
  }
  
 @@ -91,8 +119,12 @@
  char *argv[];
  {
  	int c;
 +#ifdef	USE_PPROC
 +	int pp_argc;
 +	char *pp_tmp;
 +#endif
  
 -	while ((c = getopt(argc, argv, "6AdDEf:F:Il:noPrsUvVyzZ")) != -1) {
 +	while ((c = getopt(argc, argv, GETOPT_OPTIONS)) != -1) {
  		switch (c)
  		{
  		case '?' :
 @@ -131,6 +163,30 @@
  			break;
  		case 'o' :
  			break;
 +#ifdef	USE_PPROC
 +		case 'p' :
 +			/*
 +			 * FreeBSD has strsep(3), but others might not;
 +			 * maybe we could use system(3) or popen(3)
 +			 * and get rid of all the dimensioning/strtok stuff?
 +			 */
 +			for (	pp_argc = 0, pp_tmp = strtok(optarg, PP_ARG_SEP);
 +				(pp_tmp) && (pp_argc < PP_ARG_MAX);
 +				pp_tmp = strtok(NULL, PP_ARG_SEP)
 +			) {
 +				/* cope with adjacent delimiters */
 +				if (*pp_tmp)
 +					pp_argv[pp_argc++] = pp_tmp;
 +			}
 +			pp_argv[pp_argc] = NULL;
 +			if (pp_tmp) {
 +				fprintf(stderr, "%s: too many preprocessor args, "
 +						"ignored \"%s\" and following\n",
 +						argv[0], pp_tmp);
 +			}
 +			use_pproc = (pp_argc && pp_argv[0] && *pp_argv[0]);
 +			break;
 +#endif
  		case 'P' :
  			ipfname = IPL_AUTH;
  			break;
 @@ -229,6 +285,10 @@
  	struct	frentry	*fr;
  	u_int	add, del;
  	int     linenum = 0;
 +#ifdef	USE_PPROC
 +	int	pp_pipe[2];
 +	int	pp_pid;
 +#endif
  
  	(void) opendevice(ipfname);
  
 @@ -251,6 +311,62 @@
  			STRERROR(errno));
  		exit(1);
  	}
 +
 +#ifdef	USE_PPROC
 +	/* pipe the rules through a preprocessor if specified */
 +	if (use_pproc) {
 +
 +		if (pipe(pp_pipe) == -1) {
 +			fprintf(stderr, "%s: pipe() failed: %s\n",
 +					name, STRERROR(errno));
 +			exit(1);
 +		}
 +
 +		switch(pp_pid = fork()) {
 +		case -1:
 +			fprintf(stderr, "%s: fork() failed: %s\n",
 +					name, STRERROR(errno));
 +			exit(1);
 +			/* NOTREACHED */
 +
 +		case 0:	 /* preprocessor job (child) */
 +			if (dup2(pp_pipe[1], 1) == -1) {
 +				fprintf(stderr, "%s: dup2() failed: %s\n",
 +						name, STRERROR(errno));
 +				exit(1);
 +			}
 +			close(pp_pipe[1]);
 +			/* only close fp & friends when it's not stdin */
 +			if (strcmp(file, "-")) {
 +				if (dup2(fileno(fp), 0) == -1) {
 +					fprintf(stderr, "%s: dup2() failed: %s\n",
 +							name, STRERROR(errno));
 +					exit(1);
 +				}
 +				fclose(fp);
 +			}
 +			close(pp_pipe[0]);
 +			execvp(pp_argv[0], pp_argv);
 +			fprintf(stderr, "%s: exec(%s) failed: %s\n",
 +					name, pp_argv[0], STRERROR(errno));
 +			exit(1);
 +			/* NOTREACHED */
 +
 +		default: /* ipf job (parent) */
 +			close(pp_pipe[1]);
 +			/* only close fp & friends when it's not stdin */
 +			if (strcmp(file, "-"))
 +				fclose(fp);
 +			if (! (fp = fdopen(pp_pipe[0], "r"))) {
 +				fprintf(stderr, "%s: fdopen() failed: %s\n",
 +						name, STRERROR(errno));
 +				kill(pp_pid, SIGTERM);
 +				exit(1);
 +			}
 +			break;
 +		}
 +	}
 +#endif
  
  	while (getline(line, sizeof(line), fp)) {
  	        linenum++;
 Index: ../../contrib/ipfilter/man/ipf.8
 ===================================================================
 RCS file: /home/fcvs/src/contrib/ipfilter/man/ipf.8,v
 retrieving revision 1.4
 diff -u -r1.4 ipf.8
 --- ../../contrib/ipfilter/man/ipf.8	2000/05/24 02:19:15	1.4
 +++ ../../contrib/ipfilter/man/ipf.8	2000/10/15 09:27:26
 @@ -12,6 +12,9 @@
  ] [
  .B \-F
  <i|o|a|s|S>
 +] [
 +.B \-p
 +<\fIpreprocessor\fP>
  ]
  .B \-f
  <\fIfilename\fP>
 @@ -84,6 +87,15 @@
  .B \-o
  Force rules by default to be added/deleted to/from the output list, rather
  than the (default) input list.
 +.TP
 +.BR \-p \0<preprocessor>
 +All of the files in subsequent \fB-f\fP options
 +are fed into this command's stdin, its stdout
 +is read back and processed instead of the file's content.
 +This command gets invoked anew for every specified filename.
 +Specifying an empty command will turn this feature off, again.
 +Shell escapes are needed when passing arguments to this command.
 +Up to 31 parameters (usually some \fB-D\fP and \fB-U\fP) can be passed.
  .TP
  .B \-P
  Add rules as temporary entries in the authentication rule table.
 
 
 Let's see if some tests still work:
 
 $ cd ~/FreeBSD-source/src-current/sbin/ipf
 $ make CFLAGS+=-DUSE_PPROC
 [ ... snip ... ]
 $ head pp_in*.txt
 ==> pp_in.txt <==
 #define WORLD world
 #define HELLO hello
 
 HELLO WORLD
 EXTDEF works too
 
 ==> pp_in1.txt <==
 #define INT internal
 file F1 with INT and EXT
 
 ==> pp_in2.txt <==
 #define INT internal
 file F2 with INT and EXT
 
 ==> pp_in3.txt <==
 #define INT internal
 file F3 with INT and EXT
 $ ./ipf -v -f pp_in.txt
 open device: Permission denied
 [HELLO WORLD]
 3: unknown keyword (HELLO)
 [EXTDEF works too]
 4: unknown keyword (EXTDEF)
 $ ./ipf -v -f pp_in1.txt -f pp_in2.txt
 open device: Permission denied
 [file F1 with INT and EXT]
 2: unknown keyword (file)
 open device: Permission denied
 [file F2 with INT and EXT]
 2: unknown keyword (file)
 $ ./ipf -v -f pp_in1.txt -f - -f pp_in3.txt < pp_in2.txt
 open device: Permission denied
 [file F1 with INT and EXT]
 2: unknown keyword (file)
 open device: Permission denied
 [file F2 with INT and EXT]
 2: unknown keyword (file)
 open device: Permission denied
 [file F3 with INT and EXT]
 2: unknown keyword (file)
 $ ./ipf -v -p 'cpp' -f pp_in1.txt -f - -f pp_in3.txt < pp_in2.txt
 open device: Permission denied
 [file F1 with internal  and EXT]
 2: unknown keyword (file)
 open device: Permission denied
 [file F2 with internal  and EXT]
 2: unknown keyword (file)
 open device: Permission denied
 [file F3 with internal  and EXT]
 2: unknown keyword (file)
 $ ./ipf -v -p 'cpp -DEXT=external' -f pp_in1.txt -f - -f pp_in3.txt < pp_in2.txt
 open device: Permission denied
 [file F1 with internal  and external ]
 2: unknown keyword (file)
 open device: Permission denied
 [file F2 with internal  and external ]
 2: unknown keyword (file)
 open device: Permission denied
 [file F3 with internal  and external ]
 2: unknown keyword (file)
 $ ./ipf -v -p 'cpp -DEXT=external' -f pp_in1.txt -f - -p '' -f pp_in3.txt < pp_in2.txt
 open device: Permission denied
 [file F1 with internal  and external ]
 2: unknown keyword (file)
 open device: Permission denied
 [file F2 with internal  and external ]
 2: unknown keyword (file)
 open device: Permission denied
 [file F3 with INT and EXT]
 2: unknown keyword (file)
 $ ./ipf -v -p 'cpp -DEXT=ext1' -f pp_in1.txt -p 'cpp -DEXT=ext2' -f - -p 'cpp -DEXT=ext3' -f pp_in3.txt < pp_in2.txt
 open device: Permission denied
 [file F1 with internal  and ext1 ]
 2: unknown keyword (file)
 open device: Permission denied
 [file F2 with internal  and ext2 ]
 2: unknown keyword (file)
 open device: Permission denied
 [file F3 with internal  and ext3 ]
 2: unknown keyword (file)
 
 
 Now that was fun. :)
 
 
 virtually yours   82D1 9B9C 01DC 4FB4 D7B4  61BE 3F49 4F77 72DE DA76
 Gerhard Sittig   true | mail -s "get gpg key" Gerhard.Sittig@gmx.net
 -- 
      If you don't understand or are scared by any of the above
              ask your parents or an adult to help you.
 
State-Changed-From-To: open->closed 
State-Changed-By: darrenr 
State-Changed-When: Thu Nov 23 14:22:16 PST 2000 
State-Changed-Why:  
I am not interested in putting a preprocessor into ipf 

http://www.freebsd.org/cgi/query-pr.cgi?pr=21989 
>Unformatted:
