Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 18 Oct 2002 23:11:16 +0200 (CEST)
From:      Oliver Fromme <olli@secnetix.de>
To:        FreeBSD-gnats-submit@FreeBSD.org
Subject:   bin/44238: [PATCH] syntax-check option for ipfw2
Message-ID:  <200210182111.g9ILBG0d049618@lurza.secnetix.de>

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

>Number:         44238
>Category:       bin
>Synopsis:       [PATCH] syntax-check option for ipfw2
>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:   Fri Oct 18 14:20:01 PDT 2002
>Closed-Date:
>Last-Modified:
>Originator:     Oliver Fromme
>Release:        FreeBSD 4.7-RELEASE i386
>Organization:
secnetix GmbH & Co. KG
>Environment:
System: FreeBSD monos.secnetix.net 4.7-RELEASE FreeBSD 4.7-RELEASE #0: Thu Oct 17 12:56:21 CEST 2002 olli@monos.secnetix.net:/usr/src/sys/compile/MONOS i386
Version of src/sbin/ipfw/ipfw2.c is 1.4.2.5 2002/08/21 05:46:14.

>Description:

I was surprised to discover that there is not already an
option in ipfw (or ipfw2) to perform a syntax-check on the
command line (or on the rules contained within a file),
without actually changing anything.

As I needed such an option urgently for a setup where a
script dynamically creates rules from a set of configured
services, I implemented it myself.  I guess that others
might have use for this as well, so I'm submitting the
patch hereby.

It implements an option -T.  When this option is specified,
no changes will be made to the kernel's firewall rules,
but the command line will be parsed completely and checked
for syntax errors.  If a pathname is specified, it will be
preprocessed and parsed as usual, also checking for syntax
errors.  Because no raw IP socket is opened when the -T
option is used, the ipfw utility can be executed by
non-root users.

The patch is against ipfw2.c (I didn't bother to hack on
the "old" ipfw.c, as I guess it'll become obsolete soon)
in 4.7-Release.  An appropriate patch for the manpage
to document the new option is included (might need some
fixes, as English is not my native language).

These are the CVS revisions agans which my patches apply:
$FreeBSD: src/sbin/ipfw/ipfw2.c,v 1.4.2.5 2002/08/21 05:46:14 luigi Exp $
$FreeBSD: src/sbin/ipfw/ipfw.8,v 1.63.2.28 2002/09/30 20:57:05 blackend Exp $

>How-To-Repeat:

n/a

>Fix:

--- ipfw2.c.diff begins here ---
--- src/sbin/ipfw/ipfw2.c.orig	Wed Aug 21 07:46:14 2002
+++ src/sbin/ipfw/ipfw2.c	Thu Oct 17 13:17:50 2002
@@ -54,7 +54,7 @@
 #include <netinet/tcp.h>
 #include <arpa/inet.h>
 
-int		s,			/* main RAW socket */
+int		s = -1,			/* main RAW socket */
 		do_resolv,		/* Would try to resolve all */
 		do_acct,		/* Show packet/byte count */
 		do_time,		/* Show time stamps */
@@ -65,6 +65,7 @@
 		do_dynamic,		/* display dynamic rules */
 		do_expired,		/* display expired dynamic rules */
 		do_compact,		/* show rules in compact mode */
+		do_test,		/* parse and check syntax only */
 		show_sets,		/* display rule sets */
 		verbose;
 
@@ -1432,6 +1433,8 @@
 		nbytes = sizeof(struct ip_fw);
 		if ((data = malloc(nbytes)) == NULL)
 			err(EX_OSERR, "malloc");
+		if (do_test)
+			return;
 		if (getsockopt(s, IPPROTO_IP, IP_FW_GET, data, &nbytes) < 0)
 			err(EX_OSERR, "getsockopt(IP_FW_GET)");
 		set_disable = (u_int32_t)(((struct ip_fw *)data)->next_rule);
@@ -1459,6 +1462,8 @@
 		if (!isdigit(*(av[1])) || new_set > 30)
 			errx(EX_DATAERR, "invalid set number %s\n", av[1]);
 		masks[0] = (4 << 24) | (new_set << 16) | (rulenum);
+		if (do_test)
+			return;
 		i = setsockopt(s, IPPROTO_IP, IP_FW_DEL,
 			masks, sizeof(u_int32_t));
 	} else if (!strncmp(*av, "move", strlen(*av))) {
@@ -1478,6 +1483,8 @@
 		if (!isdigit(*(av[2])) || new_set > 30)
 			errx(EX_DATAERR, "invalid dest. set %s\n", av[1]);
 		masks[0] = (cmd << 24) | (new_set << 16) | (rulenum);
+		if (do_test)
+			return;
 		i = setsockopt(s, IPPROTO_IP, IP_FW_DEL,
 			masks, sizeof(u_int32_t));
 	} else if (!strncmp(*av, "disable", strlen(*av)) ||
@@ -1506,6 +1513,8 @@
 		if ( (masks[0] & masks[1]) != 0 )
 			errx(EX_DATAERR,
 			    "cannot enable and disable the same set\n");
+		if (do_test)
+			return;
 
 		i = setsockopt(s, IPPROTO_IP, IP_FW_DEL, masks, sizeof(masks));
 		if (i)
@@ -1538,6 +1547,9 @@
 	/* get rules or pipes from kernel, resizing array as necessary */
 	nbytes = nalloc;
 
+	if (do_test)
+		return;
+
 	while (nbytes >= nalloc) {
 		nalloc = nalloc * 2 + 200;
 		nbytes = nalloc;
@@ -1870,8 +1882,11 @@
 				pipe.pipe_nr = i;
 			else
 				pipe.fs.fs_nr = i;
-			i = setsockopt(s, IPPROTO_IP, IP_DUMMYNET_DEL,
-			    &pipe, sizeof pipe);
+			if (do_test)
+				i = 0;
+			else
+				i = setsockopt(s, IPPROTO_IP, IP_DUMMYNET_DEL,
+				    &pipe, sizeof pipe);
 			if (i) {
 				exitval = 1;
 				warn("rule %u: setsockopt(IP_DUMMYNET_DEL)",
@@ -1880,8 +1895,11 @@
 			}
 		} else {
 			rulenum =  (i & 0xffff) | (do_set << 24);
-			i = setsockopt(s, IPPROTO_IP, IP_FW_DEL, &rulenum,
-			    sizeof rulenum);
+			if (do_test)
+				i = 0;
+			else
+				i = setsockopt(s, IPPROTO_IP, IP_FW_DEL,
+				    &rulenum, sizeof rulenum);
 			if (i) {
 				exitval = EX_UNAVAILABLE;
 				warn("rule %u: setsockopt(IP_FW_DEL)",
@@ -2255,8 +2273,11 @@
 			weight *= weight;
 		pipe.fs.lookup_weight = (int)(weight * (1 << SCALE_RED));
 	}
-	i = setsockopt(s, IPPROTO_IP, IP_DUMMYNET_CONFIGURE, &pipe,
-			    sizeof pipe);
+	if (do_test)
+		i = 0;
+	else
+		i = setsockopt(s, IPPROTO_IP, IP_DUMMYNET_CONFIGURE, &pipe,
+				    sizeof pipe);
 	if (i)
 		err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE");
 }
@@ -3178,8 +3199,9 @@
 
 	rule->cmd_len = (u_int32_t *)dst - (u_int32_t *)(rule->cmd);
 	i = (void *)dst - (void *)rule;
-	if (getsockopt(s, IPPROTO_IP, IP_FW_ADD, rule, &i) == -1)
-		err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_ADD");
+	if (!do_test)
+		if (getsockopt(s, IPPROTO_IP, IP_FW_ADD, rule, &i) == -1)
+			err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_ADD");
 	if (!do_quiet)
 		show_ipfw(rule);
 }
@@ -3194,6 +3216,8 @@
 
 	if (!ac) {
 		/* clear all entries */
+		if (do_test)
+			return;
 		if (setsockopt(s, IPPROTO_IP, IP_FW_ZERO, NULL, 0) < 0)
 			err(EX_UNAVAILABLE, "setsockopt(%s)", "IP_FW_ZERO");
 		if (!do_quiet)
@@ -3208,6 +3232,8 @@
 			rulenum = atoi(*av);
 			av++;
 			ac--;
+			if (do_test)
+				continue;
 			if (setsockopt(s, IPPROTO_IP,
 			    IP_FW_ZERO, &rulenum, sizeof rulenum)) {
 				warn("rule %u: setsockopt(IP_FW_ZERO)",
@@ -3233,6 +3259,8 @@
 
 	if (!ac) {
 		/* clear all entries */
+		if (do_test)
+			return;
 		if (setsockopt(s, IPPROTO_IP, IP_FW_RESETLOG, NULL, 0) < 0)
 			err(EX_UNAVAILABLE, "setsockopt(IP_FW_RESETLOG)");
 		if (!do_quiet)
@@ -3247,6 +3275,8 @@
 			rulenum = atoi(*av);
 			av++;
 			ac--;
+			if (do_test)
+				continue;
 			if (setsockopt(s, IPPROTO_IP,
 			    IP_FW_RESETLOG, &rulenum, sizeof rulenum)) {
 				warn("rule %u: setsockopt(IP_FW_RESETLOG)",
@@ -3283,6 +3313,8 @@
 		if (c == 'N')	/* user said no */
 			return;
 	}
+	if (do_test)
+		return;
 	if (setsockopt(s, IPPROTO_IP, cmd, NULL, 0) < 0)
 		err(EX_UNAVAILABLE, "setsockopt(IP_%s_FLUSH)",
 		    do_pipe ? "DUMMYNET" : "FW");
@@ -3302,7 +3334,7 @@
 	do_force = !isatty(STDIN_FILENO);
 
 	optind = optreset = 1;
-	while ((ch = getopt(ac, av, "hs:acdefNqStv")) != -1)
+	while ((ch = getopt(ac, av, "hs:acdefNqSTtv")) != -1)
 		switch (ch) {
 		case 'h': /* help */
 			help();
@@ -3335,6 +3367,9 @@
 		case 'S':
 			show_sets = 1;
 			break;
+		case 'T':
+			do_test = 1;
+			break;
 		case 't':
 			do_time = 1;
 			break;
@@ -3363,6 +3398,12 @@
 	}
 	NEED1("missing command");
 
+	if (!do_test && s < 0) {
+		s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+		if (s < 0)
+			err(EX_UNAVAILABLE, "socket");
+	}
+
 	/*
 	 * for pipes and queues we normally say 'pipe NN config'
 	 * but the code is easier to parse as 'pipe config NN'
@@ -3412,7 +3453,7 @@
 	pid_t	preproc = 0;
 	int	c;
 
-	while ((c = getopt(ac, av, "D:U:p:q")) != -1)
+	while ((c = getopt(ac, av, "D:U:p:Tq")) != -1)
 		switch(c) {
 		case 'D':
 			if (!pflag)
@@ -3441,6 +3482,10 @@
 			i = 1;
 			break;
 
+		case 'T':
+			do_test = 1;
+			break;
+
 		case 'q':
 			qflag = 1;
 			break;
@@ -3455,6 +3500,12 @@
 	if (ac != 1)
 		errx(EX_USAGE, "extraneous filename arguments");
 
+	if (!do_test) {
+		s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+		if (s < 0)
+			err(EX_UNAVAILABLE, "socket");
+	}
+
 	if ((f = fopen(av[0], "r")) == NULL)
 		err(EX_UNAVAILABLE, "fopen: %s", av[0]);
 
@@ -3538,10 +3589,6 @@
 int
 main(int ac, char *av[])
 {
-	s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
-	if (s < 0)
-		err(EX_UNAVAILABLE, "socket");
-
 	/*
 	 * If the last argument is an absolute pathname, interpret it
 	 * as a file to be preprocessed.
--- ipfw2.c.diff ends here ---

--- ipfw.8.diff begins here ---
--- src/sbin/ipfw/ipfw.8.orig	Fri Oct  4 19:22:36 2002
+++ src/sbin/ipfw/ipfw.8	Fri Oct 18 23:02:54 2002
@@ -14,46 +14,52 @@
 .Nd IP firewall and traffic shaper control program
 .Sh SYNOPSIS
 .Nm
-.Op Fl cq
+.Op Fl Tcq
 .Cm add
 .Ar rule
 .Nm
-.Op Fl acdeftNS
+.Op Fl TacdeftNS
 .Brq Cm list | show
 .Op Ar number ...
 .Nm
+.Op Fl T
 .Op Fl f | q
 .Cm flush
 .Nm
-.Op Fl q
+.Op Fl Tq
 .Brq Cm delete | zero | resetlog
 .Op Cm set
 .Op Ar number ...
 .Pp
 .Nm
+.Op Fl T
 .Cm set Oo Cm disable Ar number ... Oc Op Cm enable Ar number ...
 .Nm
+.Op Fl T
 .Cm set move
 .Op Cm rule
 .Ar number Cm to Ar number
 .Nm
+.Op Fl T
 .Cm set swap Ar number number
 .Nm
+.Op Fl T
 .Cm set show
 .Pp
 .Nm
+.Op Fl T
 .Brq Cm pipe | queue
 .Ar number
 .Cm config
 .Ar config-options
 .Nm
-.Op Fl s Op Ar field
+.Op Fl Ts Op Ar field
 .Brq Cm pipe | queue
 .Brq Cm delete | list | show
 .Op Ar number ...
 .Pp
 .Nm
-.Op Fl q
+.Op Fl Tq
 .Oo
 .Fl p Ar preproc
 .Oo Fl D
@@ -272,6 +278,12 @@
 .It Fl s Op Ar field
 While listing pipes, sort according to one of the four
 counters (total and current packets or bytes).
+.It Fl T
+Parse and check everything for syntax errors, but do not
+actually modify the kernel's ruleset or even read from it.
+With this flag,
+.Nm
+can be executed by non-root users.
 .It Fl t
 While listing, show last match timestamp.
 .El
--- ipfw.8.diff ends here ---


>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?200210182111.g9ILBG0d049618>