From owner-freebsd-bugs Fri Oct 18 14:20:28 2002 Delivered-To: freebsd-bugs@hub.freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id B009837B404 for ; Fri, 18 Oct 2002 14:20:11 -0700 (PDT) Received: from freefall.freebsd.org (freefall.freebsd.org [216.136.204.21]) by mx1.FreeBSD.org (Postfix) with ESMTP id 1113543EAA for ; Fri, 18 Oct 2002 14:20:02 -0700 (PDT) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (gnats@localhost [127.0.0.1]) by freefall.freebsd.org (8.12.6/8.12.6) with ESMTP id g9ILK1x3059687 for ; Fri, 18 Oct 2002 14:20:01 -0700 (PDT) (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.12.6/8.12.6/Submit) id g9ILK1Ru059686; Fri, 18 Oct 2002 14:20:01 -0700 (PDT) Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id D429C37B401 for ; Fri, 18 Oct 2002 14:11:21 -0700 (PDT) Received: from lurza.secnetix.de (lurza.secnetix.de [212.66.1.130]) by mx1.FreeBSD.org (Postfix) with ESMTP id 9995443E3B for ; Fri, 18 Oct 2002 14:11:20 -0700 (PDT) (envelope-from olli@lurza.secnetix.de) Received: from lurza.secnetix.de (localhost [IPv6:::1]) by lurza.secnetix.de (8.12.5/8.12.5) with ESMTP id g9ILBGmC049619 for ; Fri, 18 Oct 2002 23:11:18 +0200 (CEST) (envelope-from oliver.fromme@secnetix.de) Received: (from olli@localhost) by lurza.secnetix.de (8.12.5/8.12.5/Submit) id g9ILBG0d049618; Fri, 18 Oct 2002 23:11:16 +0200 (CEST) Message-Id: <200210182111.g9ILBG0d049618@lurza.secnetix.de> Date: Fri, 18 Oct 2002 23:11:16 +0200 (CEST) From: Oliver Fromme Reply-To: Oliver Fromme To: FreeBSD-gnats-submit@FreeBSD.org X-Send-Pr-Version: 3.113 Subject: bin/44238: [PATCH] syntax-check option for ipfw2 Sender: owner-freebsd-bugs@FreeBSD.ORG Precedence: bulk List-ID: List-Archive: (Web Archive) List-Help: (List Instructions) List-Subscribe: List-Unsubscribe: X-Loop: FreeBSD.org >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 #include -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