kern/113388: [ipfw][patch] Addition actions with rules within specified set's

Andrey V. Elsukov bu7cher at yandex.ru
Tue Jun 5 19:50:01 UTC 2007


>Number:         113388
>Category:       kern
>Synopsis:       [ipfw][patch] Addition actions with rules within specified set's
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Tue Jun 05 19:50:01 GMT 2007
>Closed-Date:
>Last-Modified:
>Originator:     Andrey V. Elsukov
>Release:        FreeBSD 7.0-CURRENT i386
>Organization:
>Environment:
7.0-CURRENT

>Description:

This patch extends a current actions that can be applied to 
rules from a specified sets. 
1. List rules only from specified set:
# ipfw set 3 show
# ipfw set 19 list

2. Delete rules from specified set:
# ipfw set 9 delete 100 200 300

3. Flush rules from specified set:
# ipfw set 4 flush
the old way not removed:
# ipfw delete set 4

4. Reset rule counters:
# ipfw set 1 zero
# ipfw set 3 resetlog 500

>How-To-Repeat:
	
>Fix:

	

--- sets_patch.txt begins here ---
--- sys/netinet/ip_fw2.c.orig	Tue Jun  5 16:20:07 2007
+++ sys/netinet/ip_fw2.c	Tue Jun  5 16:06:17 2007
@@ -3878,6 +3878,7 @@
  *	2	move rules with given number to new set
  *	3	move rules with given set number to new set
  *	4	swap sets with given numbers
+ *	5	delete rules with given number and with given set number
  */
 static int
 del_entry(struct ip_fw_chain *chain, u_int32_t arg)
@@ -3890,16 +3891,14 @@
 	cmd = (arg >> 24) & 0xff;
 	new_set = (arg >> 16) & 0xff;
 
-	if (cmd > 4)
-		return EINVAL;
-	if (new_set > RESVD_SET)
-		return EINVAL;
-	if (cmd == 0 || cmd == 2) {
+	if (cmd > 5 || new_set > RESVD_SET)
+		return (EINVAL);
+	if (cmd == 0 || cmd == 2 || cmd == 5) {
 		if (rulenum >= IPFW_DEFAULT_RULE)
-			return EINVAL;
+			return (EINVAL);
 	} else {
 		if (rulenum > RESVD_SET)	/* old_set */
-			return EINVAL;
+			return (EINVAL);
 	}
 
 	IPFW_WLOCK(chain);
@@ -3958,6 +3957,24 @@
 			else if (rule->set == new_set)
 				rule->set = rulenum;
 		break;
+	case 5: /* delete rules with given number and with given set number.
+		 * rulenum - given rule number;
+		 * new_set - given set number.
+		 */
+		for (; rule->rulenum < rulenum; prev = rule, rule = rule->next);
+		if (rule->rulenum != rulenum) {
+			IPFW_WUNLOCK(chain);
+			return (EINVAL);
+		}
+		flush_rule_ptrs(chain);
+		while (rule->rulenum == rulenum) {
+			if (rule->set == new_set)
+				rule = remove_rule(chain, rule, prev);
+			else {
+				prev = rule;
+				rule = rule->next;
+			}
+		}
 	}
 	/*
 	 * Look for rules to reclaim.  We grab the list before
@@ -3991,21 +4008,37 @@
 
 /**
  * Reset some or all counters on firewall rules.
- * @arg frwl is null to clear all entries, or contains a specific
- * rule number.
- * @arg log_only is 1 if we only want to reset logs, zero otherwise.
+ * The argument `arg' is an u_int32_t. The low 16 bit are the rule number,
+ * the next 8 bits are the set number, the top 8 bits are the command:
+ *	0	work with rules from all set's;
+ *	1	work with rules only from specified set.
+ * Specified rule number is zero if we want to clear all entries.
+ * log_only is 1 if we only want to reset logs, zero otherwise.
  */
 static int
-zero_entry(struct ip_fw_chain *chain, int rulenum, int log_only)
+zero_entry(struct ip_fw_chain *chain, u_int32_t arg, int log_only)
 {
 	struct ip_fw *rule;
 	char *msg;
 
+	uint16_t rulenum = arg & 0xffff;
+	uint8_t set = (arg >> 16) & 0xff;
+	uint8_t cmd = (arg >> 24) & 0xff;
+
+	if (cmd > 1)
+		return (EINVAL);
+	if (cmd == 1 && set > RESVD_SET)
+		return (EINVAL);
+
 	IPFW_WLOCK(chain);
 	if (rulenum == 0) {
 		norule_counter = 0;
-		for (rule = chain->rules; rule; rule = rule->next)
+		for (rule = chain->rules; rule; rule = rule->next) {
+			/* skip rules from another set */
+			if (cmd == 1 && rule->set != set)
+				continue;
 			clear_counters(rule, log_only);
+		}
 		msg = log_only ? "ipfw: All logging counts reset.\n" :
 				"ipfw: Accounting cleared.\n";
 	} else {
@@ -4017,6 +4050,9 @@
 		for (rule = chain->rules; rule; rule = rule->next)
 			if (rule->rulenum == rulenum) {
 				while (rule && rule->rulenum == rulenum) {
+					if (cmd == 1 && rule->set != set) {
+						/* do nothing */
+					} else
 					clear_counters(rule, log_only);
 					rule = rule->next;
 				}
@@ -4378,6 +4414,13 @@
 					bcopy(&(p->rule->rulenum), &(dst->rule),
 					    sizeof(p->rule->rulenum));
 					/*
+					 * store set number into high word of
+					 * dst->rule poiner.
+					 */
+					bcopy(&(p->rule->set), &dst->rule +
+					    sizeof(p->rule->rulenum),
+					    sizeof(p->rule->set));
+					/*
 					 * store a non-null value in "next".
 					 * The userland code will interpret a
 					 * NULL here as a marker
@@ -4406,7 +4449,7 @@
 ipfw_ctl(struct sockopt *sopt)
 {
 #define	RULE_MAXSIZE	(256*sizeof(u_int32_t))
-	int error, rule_num;
+	int error;
 	size_t size;
 	struct ip_fw *buf, *rule;
 	u_int32_t rulenum[2];
@@ -4525,14 +4568,14 @@
 
 	case IP_FW_ZERO:
 	case IP_FW_RESETLOG: /* argument is an int, the rule number */
-		rule_num = 0;
+		rulenum[0] = 0;
 		if (sopt->sopt_val != 0) {
-		    error = sooptcopyin(sopt, &rule_num,
-			    sizeof(int), sizeof(int));
+		    error = sooptcopyin(sopt, rulenum,
+			    sizeof(u_int32_t), sizeof(u_int32_t));
 		    if (error)
 			break;
 		}
-		error = zero_entry(&layer3_chain, rule_num,
+		error = zero_entry(&layer3_chain, rulenum[0],
 			sopt->sopt_name == IP_FW_RESETLOG);
 		break;
 
--- sbin/ipfw/ipfw.8.orig	Tue Jun  5 16:05:43 2007
+++ sbin/ipfw/ipfw.8	Tue Jun  5 16:28:18 2007
@@ -1,7 +1,7 @@
 .\"
 .\" $FreeBSD: src/sbin/ipfw/ipfw.8,v 1.200 2007/05/04 11:15:41 bz Exp $
 .\"
-.Dd May 4, 2007
+.Dd Jun 5, 2007
 .Dt IPFW 8
 .Os
 .Sh NAME
@@ -14,15 +14,17 @@
 .Ar rule
 .Nm
 .Op Fl acdefnNStT
+.Op Cm set Ar N
 .Brq Cm list | show
 .Op Ar rule | first-last ...
 .Nm
 .Op Fl f | q
+.Op Cm set Ar N
 .Cm flush
 .Nm
 .Op Fl q
+.Op Cm set Ar N
 .Brq Cm delete | zero | resetlog
-.Op Cm set
 .Op Ar number ...
 .Nm
 .Cm enable
--- sbin/ipfw/ipfw2.c.orig	Tue Jun  5 13:56:00 2007
+++ sbin/ipfw/ipfw2.c	Tue Jun  5 16:15:11 2007
@@ -74,6 +74,7 @@
 		do_expired,		/* display expired dynamic rules */
 		do_compact,		/* show rules in compact mode */
 		do_force,		/* do not ask for confirmation */
+		use_set,		/* work with specified set number */
 		show_sets,		/* display rule sets */
 		test_only,		/* only check syntax */
 		comment_only,		/* only print action and comment */
@@ -2498,6 +2499,7 @@
 	u_long rnum, last;
 	char *endptr;
 	int seen = 0;
+	uint8_t set;
 
 	const int ocmd = do_pipe ? IP_DUMMYNET_GET : IP_FW_GET;
 	int nalloc = 1024;	/* start somewhere... */
@@ -2552,6 +2554,10 @@
 	bcwidth = pcwidth = 0;
 	if (show_counters) {
 		for (n = 0, r = data; n < nstat; n++, r = NEXT(r)) {
+			/* skip rules from another set */
+			if (use_set && r->set != use_set - 1)
+				continue;
+
 			/* packet counter */
 			width = snprintf(NULL, 0, "%llu",
 			    align_uint64(&r->pcnt));
@@ -2567,6 +2573,13 @@
 	}
 	if (do_dynamic && ndyn) {
 		for (n = 0, d = dynrules; n < ndyn; n++, d++) {
+			if (use_set) {
+			/* skip rules from another set */
+				bcopy(&d->rule + sizeof(uint16_t),
+				      &set, sizeof(uint8_t));
+				if (set != use_set - 1)
+					continue;
+			}
 			width = snprintf(NULL, 0, "%llu",
 			    align_uint64(&d->pcnt));
 			if (width > pcwidth)
@@ -2580,14 +2593,24 @@
 	}
 	/* if no rule numbers were specified, list all rules */
 	if (ac == 0) {
-		for (n = 0, r = data; n < nstat; n++, r = NEXT(r) )
+		for (n = 0, r = data; n < nstat; n++, r = NEXT(r)) {
+			if (use_set && r->set != use_set - 1)
+				continue;
 			show_ipfw(r, pcwidth, bcwidth);
+		}
 
 		if (do_dynamic && ndyn) {
 			printf("## Dynamic rules (%d):\n", ndyn);
-			for (n = 0, d = dynrules; n < ndyn; n++, d++)
+			for (n = 0, d = dynrules; n < ndyn; n++, d++) {
+				if (use_set) {
+					bcopy(&d->rule + sizeof(uint16_t),
+					      &set, sizeof(uint8_t));
+					if (set != use_set - 1)
+						continue;
+				}
 				show_dyn_ipfw(d, pcwidth, bcwidth);
 		}
+		}
 		goto done;
 	}
 
@@ -2606,6 +2629,8 @@
 		for (n = seen = 0, r = data; n < nstat; n++, r = NEXT(r) ) {
 			if (r->rulenum > last)
 				break;
+			if (use_set && r->set != use_set - 1)
+				continue;
 			if (r->rulenum >= rnum && r->rulenum <= last) {
 				show_ipfw(r, pcwidth, bcwidth);
 				seen = 1;
@@ -2634,6 +2659,12 @@
 				bcopy(&d->rule, &rulenum, sizeof(rulenum));
 				if (rulenum > rnum)
 					break;
+				if (use_set) {
+					bcopy(&d->rule + sizeof(uint16_t),
+					      &set, sizeof(uint8_t));
+					if (set != use_set - 1)
+						continue;
+				}
 				if (r->rulenum >= rnum && r->rulenum <= last)
 					show_dyn_ipfw(d, pcwidth, bcwidth);
 			}
@@ -2672,6 +2703,7 @@
 "		reverse|proxy_only|redirect_addr linkspec|\n"
 "		redirect_port linkspec|redirect_proto linkspec}\n"
 "set [disable N... enable N...] | move [rule] X to Y | swap X Y | show\n"
+"set N {show|list|zero|resetlog|delete} [N{,N}] | flush\n"
 "table N {add ip[/bits] [value] | delete ip[/bits] | flush | list}\n"
 "\n"
 "RULE-BODY:	check-state [PARAMS] | ACTION [PARAMS] ADDR [OPTION_LIST]\n"
@@ -3189,6 +3221,11 @@
 	av++; ac--;
 	NEED1("missing rule specification");
 	if (ac > 0 && _substrcmp(*av, "set") == 0) {
+		/* don't allow using the following syntax:
+		 *  ipfw set N delete set M
+		 */
+		if (use_set)
+			errx(EX_DATAERR, "invalid syntax");
 		do_set = 1;	/* delete set */
 		ac--; av++;
 	}
@@ -3214,6 +3251,10 @@
 				    do_pipe == 1 ? p.pipe_nr : p.fs.fs_nr);
 			}
 		} else {
+			if (use_set)
+				rulenum = (i & 0xffff) | (5 << 24) |
+					  ((use_set - 1) << 16);
+			else
 			rulenum =  (i & 0xffff) | (do_set << 24);
 			i = do_cmd(IP_FW_DEL, &rulenum, sizeof rulenum);
 			if (i) {
@@ -5674,7 +5715,7 @@
 static void
 zero(int ac, char *av[], int optname /* IP_FW_ZERO or IP_FW_RESETLOG */)
 {
-	int rulenum;
+	uint32_t arg;
 	int failed = EX_OK;
 	char const *name = optname == IP_FW_ZERO ?  "ZERO" : "RESETLOG";
 
@@ -5694,15 +5735,17 @@
 	while (ac) {
 		/* Rule number */
 		if (isdigit(**av)) {
-			rulenum = atoi(*av);
+			arg = strtol(*av, (char **)NULL, 10) & 0xffff;
+			if (use_set)
+				arg |= (1 << 24) | ((use_set - 1) << 16);
 			av++;
 			ac--;
-			if (do_cmd(optname, &rulenum, sizeof rulenum)) {
+			if (do_cmd(optname, &arg, sizeof(arg))) {
 				warn("rule %u: setsockopt(IP_FW_%s)",
-				    rulenum, name);
+				    arg, name);
 				failed = EX_UNAVAILABLE;
 			} else if (!do_quiet)
-				printf("Entry %d %s.\n", rulenum,
+				printf("Entry %d %s.\n", arg,
 				    optname == IP_FW_ZERO ?
 					"cleared" : "logging count reset");
 		} else {
@@ -5733,7 +5776,12 @@
 		if (c == 'N')	/* user said no */
 			return;
 	}
-	if (do_cmd(cmd, NULL, 0) < 0)
+	/* `ipfw set N flush` - is the same that `ipfw delete set N` */
+	if (use_set) {
+		uint32_t arg = ((use_set - 1) & 0xffff) | (1 << 24);
+		if (do_cmd(IP_FW_DEL, &arg, sizeof(arg)) < 0)
+			err(EX_UNAVAILABLE, "setsockopt(IP_FW_DEL)");
+	} else if (do_cmd(cmd, NULL, 0) < 0)
 		err(EX_UNAVAILABLE, "setsockopt(IP_%s_FLUSH)",
 		    do_pipe ? "DUMMYNET" : "FW");
 	if (!do_quiet)
@@ -6134,6 +6182,15 @@
 		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 = strtol(av[1], (char **)NULL, 10);
+			if (use_set > RESVD_SET)
+				errx(EX_DATAERR, "invalid set number %s\n", av[1]);
+			ac -= 2; av += 2; use_set++;
+		}
+	}
+
 	if (do_pipe || do_nat) {
 		ac--;
 		av++;
@@ -6152,6 +6209,8 @@
 		av[1] = p;
 	}
 
+	int try_next = 0;
+	if (use_set == 0) {
 	if (_substrcmp(*av, "add") == 0)
 		add(ac, av);
 	else if (do_nat && _substrcmp(*av, "show") == 0)
@@ -6160,7 +6219,20 @@
 		config_pipe(ac, av);
 	else if (do_nat && _substrcmp(*av, "config") == 0) 
  		config_nat(ac, av);
-	else if (_substrcmp(*av, "delete") == 0)
+		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);
@@ -6171,18 +6243,11 @@
 	else if (_substrcmp(*av, "print") == 0 ||
 	         _substrcmp(*av, "list") == 0)
 		list(ac, av, do_acct);
-	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 if (_substrcmp(*av, "show") == 0)
 		list(ac, av, 1 /* show counters */);
 	else
 		errx(EX_USAGE, "bad command `%s'", *av);
+	}
 
 	/* Free memory allocated in the argument parsing. */
 	free_args(save_ac, save_av);
--- sets_patch.txt ends here ---


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


More information about the freebsd-bugs mailing list