Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 27 May 2009 05:32:52 GMT
From:      Cyberman Wu <cyberman.wu@gmail.com>
To:        freebsd-gnats-submit@FreeBSD.org
Subject:   bin/134975: ipfw can't work with set in rule file.
Message-ID:  <200905270532.n4R5Wqsb085240@www.freebsd.org>
Resent-Message-ID: <200905270540.n4R5e1rg027377@freefall.freebsd.org>

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

>Number:         134975
>Category:       bin
>Synopsis:       ipfw can't work with set in rule file.
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Wed May 27 05:40:00 UTC 2009
>Closed-Date:
>Last-Modified:
>Originator:     Cyberman Wu
>Release:        FreeBSD 7.0r-p5
>Organization:
Meganovo
>Environment:
FreeBSD test.freebsd.com 7.0-RELEASE-p5 FreeBSD 7.0-RELEASE-p5 #0: Wed Oct  1 10:10:12 UTC 2008     root@i386-builder.daemonology.net:/usr/obj/usr/src/sys/GENERIC  i386
>Description:
First to say, I've tested on both FreeBSD 7.1 and 7.2 and the problem is still here. The only change is the later versions use a structure to organize global variables. Since I'm more familiar with 7.0 I'll use its code to illustrate the problem.

These days I'm thinking to port ipfw to another system, and when I read the source code I found here will cause a problem in ipfw_main from ipfw2.c:
	/*
	 * Optional: pipe, queue or nat.
	 */
	do_nat = 0;
	do_pipe = 0;
	if (!strncmp(*av, "nat", strlen(*av)))
 	        do_nat = 1;
 	else if (!strncmp(*av, "pipe", strlen(*av)))
		do_pipe = 1;
	else if (_substrcmp(*av, "queue") == 0)
		do_pipe = 2;
	else if (!strncmp(*av, "set", strlen(*av))) {
		if (ac > 1 && isdigit(av[1][0])) {
			use_set = strtonum(av[1], 0, RESVD_SET, &errstr);
			if (errstr)
				errx(EX_DATAERR,
				    "invalid set number %s\n", av[1]);
			ac -= 2; av += 2; use_set++;
		}
	}

If we specify rule file using 'pathname' on command line rather than a command, ipfw_main will be called in loop, but once 'use_set' is set, it will never be clear again, if the next rule is some command like 'add', since 'use_set' is not  zero, it will either say 'bad command' or do some other things:
	int try_next = 0;
	if (use_set == 0) {
		if (_substrcmp(*av, "add") == 0)
			add(ac, av);
		else if (do_nat && _substrcmp(*av, "show") == 0)
 			show_nat(ac, av);
		else if (do_pipe && _substrcmp(*av, "config") == 0)
			config_pipe(ac, av);
		else if (do_nat && _substrcmp(*av, "config") == 0)
 			config_nat(ac, av);
			else if (_substrcmp(*av, "set") == 0)
				sets_handler(ac, av);
			else if (_substrcmp(*av, "table") == 0)
				table_handler(ac, av);
			else if (_substrcmp(*av, "enable") == 0)
				sysctl_handler(ac, av, 1);
			else if (_substrcmp(*av, "disable") == 0)
				sysctl_handler(ac, av, 0);
			else
				try_next = 1;
	}

	if (use_set || try_next) {
		if (_substrcmp(*av, "delete") == 0)
			delete(ac, av);
		else if (_substrcmp(*av, "flush") == 0)
			flush(do_force);
		else if (_substrcmp(*av, "zero") == 0)
			zero(ac, av, IP_FW_ZERO);
		else if (_substrcmp(*av, "resetlog") == 0)
			zero(ac, av, IP_FW_RESETLOG);
		else if (_substrcmp(*av, "print") == 0 ||
		         _substrcmp(*av, "list") == 0)
			list(ac, av, do_acct);
		else if (_substrcmp(*av, "show") == 0)
			list(ac, av, 1 /* show counters */);
		else
			errx(EX_USAGE, "bad command `%s'", *av);
	}

Then I test it on FreeBSD 7.0/7.1/7.2, all the same.
>How-To-Repeat:
First create a file 'fw_test.rule' which including the following commands:

-q set 12 flush
add set 12 allow tcp from any to me 12345 in
add set 12 allow tcp from me 12345 to any out


Then use ipfw to execute it:

test# ipfw /root/fw_test.rule
Line 2: bad command `add'
>Fix:

	/*
	 * Optional: pipe, queue or nat.
	 */
	do_nat = 0;
	do_pipe = 0;
	/*
	 * Added by Cyberman Wu on May 16th, 2009.
	 */
	use_set = 0;
	if (!strncmp(*av, "nat", strlen(*av)))
 	        do_nat = 1;
 	else if (!strncmp(*av, "pipe", strlen(*av)))
		do_pipe = 1;
	else if (_substrcmp(*av, "queue") == 0)
		do_pipe = 2;
	else if (!strncmp(*av, "set", strlen(*av))) {
		if (ac > 1 && isdigit(av[1][0])) {
			use_set = strtonum(av[1], 0, RESVD_SET, &errstr);
			if (errstr)
				errx(EX_DATAERR,
				    "invalid set number %s\n", av[1]);
			ac -= 2; av += 2; use_set++;
		}
	}

>Release-Note:
>Audit-Trail:
>Unformatted:



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