Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 14 Oct 2000 18:24:39 +0200
From:      Gerhard Sittig <Gerhard.Sittig@gmx.net>
To:        FreeBSD-gnats-submit@freebsd.org
Subject:   bin/21989: [PATCH] preprocessor for ipfilter rules
Message-ID:  <20001014182439.Q25237@speedy.gsinet>

next in thread | raw e-mail | index | archive | help

>Number:         21989
>Category:       bin
>Synopsis:       preprocessor for ipfilter rules
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Sat Oct 14 10:00:01 PDT 2000
>Closed-Date:
>Last-Modified:
>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:
>Unformatted:


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20001014182439.Q25237>