Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 7 Aug 2014 21:37:32 +0000 (UTC)
From:      Alexander V. Chernikov <melifaro@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r269689 - in projects/ipfw: sbin/ipfw sys/netinet sys/netpfil/ipfw
Message-ID:  <53e3f19c.2e78.f884e9d@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: melifaro
Date: Thu Aug  7 21:37:31 2014
New Revision: 269689
URL: http://svnweb.freebsd.org/changeset/base/269689

Log:
  Kernel changes:
  * Implement proper checks for switching between global and set-aware tables
  * Split IP_FW_DEL mess into the following opcodes:
    * IP_FW_XDEL (del rules matching pattern)
    * IP_FW_XMOVE (move rules matching pattern to another set)
    * IP_FW_SET_SWAP (swap between 2 sets)
    * IP_FW_SET_MOVE (move one set to another one)
    * IP_FW_SET_ENABLE (enable/disable sets)
  * Add IP_FW_XZERO / IP_FW_XRESETLOG to finish IP_FW3 migration.
  * Use unified ipfw_range_tlv as range description for all of the above.
  * Check dynamic states IFF there was non-zero number of deleted dyn rules,
  * Del relevant dynamic states with singe traversal instead of per-rule one.
  
  Userland changes:
  * Switch ipfw(8) to use new opcodes.

Modified:
  projects/ipfw/sbin/ipfw/ipfw2.c
  projects/ipfw/sys/netinet/ip_fw.h
  projects/ipfw/sys/netpfil/ipfw/ip_fw2.c
  projects/ipfw/sys/netpfil/ipfw/ip_fw_dynamic.c
  projects/ipfw/sys/netpfil/ipfw/ip_fw_private.h
  projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c
  projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c
  projects/ipfw/sys/netpfil/ipfw/ip_fw_table.h

Modified: projects/ipfw/sbin/ipfw/ipfw2.c
==============================================================================
--- projects/ipfw/sbin/ipfw/ipfw2.c	Thu Aug  7 21:37:03 2014	(r269688)
+++ projects/ipfw/sbin/ipfw/ipfw2.c	Thu Aug  7 21:37:31 2014	(r269689)
@@ -2110,6 +2110,19 @@ show_dyn_state(struct cmdline_opts *co, 
 		bprintf(bp, " UNKNOWN <-> UNKNOWN\n");
 }
 
+static int
+do_range_cmd(int cmd, ipfw_range_tlv *rt)
+{
+	ipfw_range_header rh;
+
+	memset(&rh, 0, sizeof(rh));
+	memcpy(&rh.range, rt, sizeof(*rt));
+	rh.range.head.length = sizeof(*rt);
+	rh.range.head.type = IPFW_TLV_RANGE;
+
+	return (do_set3(cmd, &rh.opheader, sizeof(rh)));
+}
+
 /*
  * This one handles all set-related commands
  * 	ipfw set { show | enable | disable }
@@ -2122,12 +2135,13 @@ ipfw_sets_handler(char *av[])
 {
 	uint32_t masks[2];
 	int i;
-	uint16_t rulenum;
-	uint8_t cmd, new_set;
+	uint8_t cmd, new_set, rulenum;
+	ipfw_range_tlv rt;
 	char *msg;
 	size_t size;
 
 	av++;
+	memset(&rt, 0, sizeof(rt));
 
 	if (av[0] == NULL)
 		errx(EX_USAGE, "set needs command");
@@ -2156,33 +2170,38 @@ ipfw_sets_handler(char *av[])
 		av++;
 		if ( av[0] == NULL || av[1] == NULL )
 			errx(EX_USAGE, "set swap needs 2 set numbers\n");
-		rulenum = atoi(av[0]);
-		new_set = atoi(av[1]);
-		if (!isdigit(*(av[0])) || rulenum > RESVD_SET)
+		rt.set = atoi(av[0]);
+		rt.new_set = atoi(av[1]);
+		if (!isdigit(*(av[0])) || rt.set > RESVD_SET)
 			errx(EX_DATAERR, "invalid set number %s\n", av[0]);
-		if (!isdigit(*(av[1])) || new_set > RESVD_SET)
+		if (!isdigit(*(av[1])) || rt.new_set > RESVD_SET)
 			errx(EX_DATAERR, "invalid set number %s\n", av[1]);
-		masks[0] = (4 << 24) | (new_set << 16) | (rulenum);
-		i = do_cmd(IP_FW_DEL, masks, sizeof(uint32_t));
+		i = do_range_cmd(IP_FW_SET_SWAP, &rt);
 	} else if (_substrcmp(*av, "move") == 0) {
 		av++;
 		if (av[0] && _substrcmp(*av, "rule") == 0) {
-			cmd = 2;
+			rt.flags = IPFW_RCFLAG_RANGE; /* move rules to new set */
+			cmd = IP_FW_XMOVE;
 			av++;
 		} else
-			cmd = 3;
+			cmd = IP_FW_SET_MOVE; /* Move set to new one */
 		if (av[0] == NULL || av[1] == NULL || av[2] == NULL ||
 				av[3] != NULL ||  _substrcmp(av[1], "to") != 0)
 			errx(EX_USAGE, "syntax: set move [rule] X to Y\n");
 		rulenum = atoi(av[0]);
-		new_set = atoi(av[2]);
-		if (!isdigit(*(av[0])) || (cmd == 3 && rulenum > RESVD_SET) ||
-			(cmd == 2 && rulenum == IPFW_DEFAULT_RULE) )
+		rt.new_set = atoi(av[2]);
+		if (cmd == IP_FW_XMOVE) {
+			rt.start_rule = rulenum;
+			rt.end_rule = rulenum;
+		} else
+			rt.set = rulenum;
+		rt.new_set = atoi(av[2]);
+		if (!isdigit(*(av[0])) || (cmd == 3 && rt.set > RESVD_SET) ||
+			(cmd == 2 && rt.start_rule == IPFW_DEFAULT_RULE) )
 			errx(EX_DATAERR, "invalid source number %s\n", av[0]);
 		if (!isdigit(*(av[2])) || new_set > RESVD_SET)
 			errx(EX_DATAERR, "invalid dest. set %s\n", av[1]);
-		masks[0] = (cmd << 24) | (new_set << 16) | (rulenum);
-		i = do_cmd(IP_FW_DEL, masks, sizeof(uint32_t));
+		i = do_range_cmd(cmd, &rt);
 	} else if (_substrcmp(*av, "disable") == 0 ||
 		   _substrcmp(*av, "enable") == 0 ) {
 		int which = _substrcmp(*av, "enable") == 0 ? 1 : 0;
@@ -2210,9 +2229,11 @@ ipfw_sets_handler(char *av[])
 			errx(EX_DATAERR,
 			    "cannot enable and disable the same set\n");
 
-		i = do_cmd(IP_FW_DEL, masks, sizeof(masks));
+		rt.set = masks[0];
+		rt.new_set = masks[1];
+		i = do_range_cmd(IP_FW_SET_ENABLE, &rt);
 		if (i)
-			warn("set enable/disable: setsockopt(IP_FW_DEL)");
+			warn("set enable/disable: setsockopt(IP_FW_SET_ENABLE)");
 	} else
 		errx(EX_USAGE, "invalid set command %s\n", *av);
 }
@@ -2984,9 +3005,11 @@ ipfw_delete(char *av[])
 	int i;
 	int exitval = EX_OK;
 	int do_set = 0;
+	ipfw_range_tlv rt;
 
 	av++;
 	NEED1("missing rule specification");
+	memset(&rt, 0, sizeof(rt));
 	if ( *av && _substrcmp(*av, "set") == 0) {
 		/* Do not allow using the following syntax:
 		 *	ipfw set N delete set M
@@ -3009,15 +3032,25 @@ ipfw_delete(char *av[])
  		} else if (co.do_pipe) {
 			exitval = ipfw_delete_pipe(co.do_pipe, i);
 		} else {
-			if (co.use_set)
-				rulenum = (i & 0xffff) | (5 << 24) |
-				    ((co.use_set - 1) << 16);
-			else
-			rulenum =  (i & 0xffff) | (do_set << 24);
-			i = do_cmd(IP_FW_DEL, &rulenum, sizeof rulenum);
-			if (i) {
+			if (do_set != 0) {
+				rt.set = i & 31;
+				rt.flags = IPFW_RCFLAG_SET;
+			} else {
+				rt.start_rule = i & 0xffff;
+				rt.end_rule = i & 0xffff;
+				if (rt.start_rule == 0 && rt.end_rule == 0)
+					rt.flags |= IPFW_RCFLAG_ALL;
+				else
+					rt.flags |= IPFW_RCFLAG_RANGE;
+				if (co.use_set != 0) {
+					rt.set = co.use_set - 1;
+					rt.flags |= IPFW_RCFLAG_SET;
+				}
+			}
+			i = do_range_cmd(IP_FW_XDEL, &rt);
+			if (i != 0) {
 				exitval = EX_UNAVAILABLE;
-				warn("rule %u: setsockopt(IP_FW_DEL)",
+				warn("rule %u: setsockopt(IP_FW_XDEL)",
 				    rulenum);
 			}
 		}
@@ -4681,25 +4714,31 @@ ipfw_add(char *av[])
 
 /*
  * clear the counters or the log counters.
+ * optname has the following values:
+ *  0 (zero both counters and logging)
+ *  1 (zero logging only)
  */
 void
-ipfw_zero(int ac, char *av[], int optname /* 0 = IP_FW_ZERO, 1 = IP_FW_RESETLOG */)
+ipfw_zero(int ac, char *av[], int optname)
 {
-	uint32_t arg, saved_arg;
+	ipfw_range_tlv rt;
+	uint32_t arg;
 	int failed = EX_OK;
 	char const *errstr;
 	char const *name = optname ? "RESETLOG" : "ZERO";
 
-	optname = optname ? IP_FW_RESETLOG : IP_FW_ZERO;
+	optname = optname ? IP_FW_XRESETLOG : IP_FW_XZERO;
+	memset(&rt, 0, sizeof(rt));
 
 	av++; ac--;
 
-	if (!ac) {
+	if (ac == 0) {
 		/* clear all entries */
-		if (do_cmd(optname, NULL, 0) < 0)
-			err(EX_UNAVAILABLE, "setsockopt(IP_FW_%s)", name);
+		rt.flags = IPFW_RCFLAG_ALL;
+		if (do_range_cmd(optname, &rt) < 0)
+			err(EX_UNAVAILABLE, "setsockopt(IP_FW_X%s)", name);
 		if (!co.do_quiet)
-			printf("%s.\n", optname == IP_FW_ZERO ?
+			printf("%s.\n", optname == IP_FW_XZERO ?
 			    "Accounting cleared":"Logging counts reset");
 
 		return;
@@ -4712,18 +4751,20 @@ ipfw_zero(int ac, char *av[], int optnam
 			if (errstr)
 				errx(EX_DATAERR,
 				    "invalid rule number %s\n", *av);
-			saved_arg = arg;
-			if (co.use_set)
-				arg |= (1 << 24) | ((co.use_set - 1) << 16);
-			av++;
-			ac--;
-			if (do_cmd(optname, &arg, sizeof(arg))) {
-				warn("rule %u: setsockopt(IP_FW_%s)",
-				    saved_arg, name);
+			rt.start_rule = arg;
+			rt.end_rule = arg;
+			rt.flags |= IPFW_RCFLAG_RANGE;
+			if (co.use_set != 0) {
+				rt.set = co.use_set - 1;
+				rt.flags |= IPFW_RCFLAG_SET;
+			}
+			if (do_range_cmd(optname, &rt) != 0) {
+				warn("rule %u: setsockopt(IP_FW_X%s)",
+				    arg, name);
 				failed = EX_UNAVAILABLE;
 			} else if (!co.do_quiet)
-				printf("Entry %d %s.\n", saved_arg,
-				    optname == IP_FW_ZERO ?
+				printf("Entry %d %s.\n", arg,
+				    optname == IP_FW_XZERO ?
 					"cleared" : "logging count reset");
 		} else {
 			errx(EX_USAGE, "invalid rule number ``%s''", *av);
@@ -4736,7 +4777,7 @@ ipfw_zero(int ac, char *av[], int optnam
 void
 ipfw_flush(int force)
 {
-	int cmd = co.do_pipe ? IP_DUMMYNET_FLUSH : IP_FW_FLUSH;
+	ipfw_range_tlv rt;
 
 	if (!force && !co.do_quiet) { /* need to ask user */
 		int c;
@@ -4758,13 +4799,14 @@ ipfw_flush(int force)
 		return;
 	}
 	/* `ipfw set N flush` - is the same that `ipfw delete set N` */
-	if (co.use_set) {
-		uint32_t arg = ((co.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)",
-		    co.do_pipe ? "DUMMYNET" : "FW");
+	memset(&rt, 0, sizeof(rt));
+	if (co.use_set != 0) {
+		rt.set = co.use_set - 1;
+		rt.flags = IPFW_RCFLAG_SET;
+	} else
+		rt.flags = IPFW_RCFLAG_ALL;
+	if (do_range_cmd(IP_FW_XDEL, &rt) != 0)
+			err(EX_UNAVAILABLE, "setsockopt(IP_FW_XDEL)");
 	if (!co.do_quiet)
 		printf("Flushed all %s.\n", co.do_pipe ? "pipes" : "rules");
 }

Modified: projects/ipfw/sys/netinet/ip_fw.h
==============================================================================
--- projects/ipfw/sys/netinet/ip_fw.h	Thu Aug  7 21:37:03 2014	(r269688)
+++ projects/ipfw/sys/netinet/ip_fw.h	Thu Aug  7 21:37:31 2014	(r269689)
@@ -86,11 +86,18 @@ typedef struct _ip_fw3_opheader {
 #define	IP_FW_TABLE_XCREATE	95	/* create new table  */
 //#define	IP_FW_TABLE_XMODIFY	96	/* modify existing table */
 #define	IP_FW_XGET		97	/* Retrieve configuration */
-#define	IP_FW_XADD		98	/* add entry */
-#define	IP_FW_TABLE_XFIND	99	/* finds an entry */
-#define	IP_FW_XIFLIST		100	/* list tracked interfaces */
-#define	IP_FW_TABLES_ALIST	101	/* list table algorithms */
-#define	IP_FW_TABLE_XSWAP	102	/* swap two tables */
+#define	IP_FW_XADD		98	/* add rule */
+#define	IP_FW_XDEL		99	/* del rule */
+#define	IP_FW_XMOVE		100	/* move rules to different set  */
+#define	IP_FW_XZERO		101	/* clear accounting */
+#define	IP_FW_XRESETLOG		102	/* zero rules logs */
+#define	IP_FW_SET_SWAP		103	/* Swap between 2 sets */
+#define	IP_FW_SET_MOVE		104	/* Move one set to another one */
+#define	IP_FW_SET_ENABLE	105	/* Enable/disable sets */
+#define	IP_FW_TABLE_XFIND	106	/* finds an entry */
+#define	IP_FW_XIFLIST		107	/* list tracked interfaces */
+#define	IP_FW_TABLES_ALIST	108	/* list table algorithms */
+#define	IP_FW_TABLE_XSWAP	109	/* swap two tables */
 
 /*
  * Usage guidelines:
@@ -735,6 +742,7 @@ typedef struct  _ipfw_obj_tlv {
 #define	IPFW_TLV_DYN_ENT	6
 #define	IPFW_TLV_RULE_ENT	7
 #define	IPFW_TLV_TBLENT_LIST	8
+#define	IPFW_TLV_RANGE		9
 
 /* Object name TLV */
 typedef struct _ipfw_obj_ntlv {
@@ -799,6 +807,19 @@ typedef struct _ipfw_obj_ctlv {
 	uint8_t		spare;
 } ipfw_obj_ctlv;
 
+/* Range TLV */
+typedef struct _ipfw_range_tlv {
+	ipfw_obj_tlv	head;		/* TLV header			*/
+	uint32_t	flags;		/* Range flags			*/
+	uint16_t	start_rule;	/* Range start			*/
+	uint16_t	end_rule;	/* Range end			*/
+	uint32_t	set;		/* Range set to match		 */
+	uint32_t	new_set;	/* New set to move/swap to	*/
+} ipfw_range_tlv;
+#define	IPFW_RCFLAG_RANGE	0x01	/* rule range is set		*/
+#define	IPFW_RCFLAG_ALL		0x02	/* match ALL rules		*/
+#define	IPFW_RCFLAG_SET		0x04	/* match rules in given set	*/
+
 typedef struct _ipfw_ta_tinfo {
 	uint32_t	flags;		/* Format flags			*/
 	uint32_t	spare;
@@ -893,4 +914,9 @@ typedef struct _ipfw_cfg_lheader {
 	uint32_t	end_rule;
 } ipfw_cfg_lheader;
 
+typedef struct _ipfw_range_header {
+	ip_fw3_opheader	opheader;	/* IP_FW3 opcode		*/
+	ipfw_range_tlv	range;
+} ipfw_range_header;
+
 #endif /* _IPFW2_H */

Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw2.c
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw2.c	Thu Aug  7 21:37:03 2014	(r269688)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw2.c	Thu Aug  7 21:37:31 2014	(r269689)
@@ -158,6 +158,7 @@ ipfw_nat_cfg_t *ipfw_nat_get_log_ptr;
 #ifdef SYSCTL_NODE
 uint32_t dummy_def = IPFW_DEFAULT_RULE;
 static int sysctl_ipfw_table_num(SYSCTL_HANDLER_ARGS);
+static int sysctl_ipfw_tables_sets(SYSCTL_HANDLER_ARGS);
 
 SYSBEGIN(f3)
 
@@ -180,8 +181,8 @@ SYSCTL_UINT(_net_inet_ip_fw, OID_AUTO, d
 SYSCTL_VNET_PROC(_net_inet_ip_fw, OID_AUTO, tables_max,
     CTLTYPE_UINT|CTLFLAG_RW, 0, 0, sysctl_ipfw_table_num, "IU",
     "Maximum number of concurrently used tables");
-SYSCTL_VNET_INT(_net_inet_ip_fw, OID_AUTO, tables_sets,
-    CTLFLAG_RW, &VNET_NAME(fw_tables_sets), 0,
+SYSCTL_VNET_PROC(_net_inet_ip_fw, OID_AUTO, tables_sets,
+    CTLTYPE_UINT|CTLFLAG_RW, 0, 0, sysctl_ipfw_tables_sets, "IU",
     "Use per-set namespace for tables");
 SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, default_to_accept, CTLFLAG_RDTUN,
     &default_to_accept, 0,
@@ -2569,7 +2570,27 @@ sysctl_ipfw_table_num(SYSCTL_HANDLER_ARG
 
 	return (ipfw_resize_tables(&V_layer3_chain, ntables));
 }
+
+/*
+ * Switches table namespace between global and per-set.
+ */
+static int
+sysctl_ipfw_tables_sets(SYSCTL_HANDLER_ARGS)
+{
+	int error;
+	unsigned int sets;
+
+	sets = V_fw_tables_sets;
+
+	error = sysctl_handle_int(oidp, &sets, 0, req);
+	/* Read operation or some error */
+	if ((error != 0) || (req->newptr == NULL))
+		return (error);
+
+	return (ipfw_switch_tables_namespace(&V_layer3_chain, sets));
+}
 #endif
+
 /*
  * Module and VNET glue
  */
@@ -2752,8 +2773,7 @@ vnet_ipfw_uninit(const void *unused)
 		rule->x_next = reap;
 		reap = rule;
 	}
-	if (chain->map)
-		free(chain->map, M_IPFW);
+	free(chain->map, M_IPFW);
 	ipfw_destroy_skipto_cache(chain);
 	IPFW_WUNLOCK(chain);
 	IPFW_UH_WUNLOCK(chain);

Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_dynamic.c
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_dynamic.c	Thu Aug  7 21:37:03 2014	(r269688)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_dynamic.c	Thu Aug  7 21:37:31 2014	(r269689)
@@ -196,8 +196,7 @@ static int ipfw_dyn_count;	/* number of 
 static int last_log;	/* Log ratelimiting */
 
 static void ipfw_dyn_tick(void *vnetx);
-static void check_dyn_rules(struct ip_fw_chain *, struct ip_fw *,
-    int, int, int);
+static void check_dyn_rules(struct ip_fw_chain *, ipfw_range_tlv *, int, int);
 #ifdef SYSCTL_NODE
 
 static int sysctl_ipfw_dyn_count(SYSCTL_HANDLER_ARGS);
@@ -1008,7 +1007,7 @@ ipfw_dyn_tick(void * vnetx) 
 		check_ka = 1;
 	}
 
-	check_dyn_rules(chain, NULL, RESVD_SET, check_ka, 1);
+	check_dyn_rules(chain, NULL, check_ka, 1);
 
 	callout_reset_on(&V_ipfw_timeout, hz, ipfw_dyn_tick, vnetx, 0);
 
@@ -1040,8 +1039,8 @@ ipfw_dyn_tick(void * vnetx) 
  * are not freed by other instance (see stage 2, 3)
  */
 static void
-check_dyn_rules(struct ip_fw_chain *chain, struct ip_fw *rule,
-    int set, int check_ka, int timer)
+check_dyn_rules(struct ip_fw_chain *chain, ipfw_range_tlv *rt,
+    int check_ka, int timer)
 {
 	struct mbuf *m0, *m, *mnext, **mtailp;
 	struct ip *h;
@@ -1105,12 +1104,10 @@ check_dyn_rules(struct ip_fw_chain *chai
 			/*
 			 * Remove rules which are:
 			 * 1) expired
-			 * 2) created by given rule
-			 * 3) created by any rule in given set
+			 * 2) matches deletion range
 			 */
 			if ((TIME_LEQ(q->expire, time_uptime)) ||
-			    ((rule != NULL) && (q->rule == rule)) ||
-			    ((set != RESVD_SET) && (q->rule->set == set))) {
+			    (rt != NULL && ipfw_match_range(q->rule, rt))) {
 				if (TIME_LE(time_uptime, q->expire) &&
 				    q->dyn_type == O_KEEP_STATE &&
 				    V_dyn_keep_states != 0) {
@@ -1324,8 +1321,7 @@ check_dyn_rules(struct ip_fw_chain *chai
  * Deletes all dynamic rules originated by given rule or all rules in
  * given set. Specify RESVD_SET to indicate set should not be used.
  * @chain - pointer to current ipfw rules chain
- * @rule - delete all states originated by given rule if != NULL
- * @set - delete all states originated by any rule in set @set if != RESVD_SET
+ * @rr - delete all states originated by rules in matched range.
  *
  * Function has to be called with IPFW_UH_WLOCK held.
  * Additionally, function assume that dynamic rule/set is
@@ -1333,10 +1329,39 @@ check_dyn_rules(struct ip_fw_chain *chai
  * 'deleted' rules.
  */
 void
-ipfw_expire_dyn_rules(struct ip_fw_chain *chain, struct ip_fw *rule, int set)
+ipfw_expire_dyn_rules(struct ip_fw_chain *chain, ipfw_range_tlv *rt)
 {
 
-	check_dyn_rules(chain, rule, set, 0, 0);
+	check_dyn_rules(chain, rt, 0, 0);
+}
+
+/*
+ * Check if rule contains at least one dynamic opcode.
+ *
+ * Returns 1 if such opcode is found, 0 otherwise.
+ */
+int
+ipfw_is_dyn_rule(struct ip_fw *rule)
+{
+	int cmdlen, l;
+	ipfw_insn *cmd;
+
+	l = rule->cmd_len;
+	cmd = rule->cmd;
+	cmdlen = 0;
+	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
+		cmdlen = F_LEN(cmd);
+
+		switch (cmd->opcode) {
+		case O_LIMIT:
+		case O_KEEP_STATE:
+		case O_PROBE_STATE:
+		case O_CHECK_STATE:
+			return (1);
+		}
+	}
+
+	return (0);
 }
 
 void

Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_private.h
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_private.h	Thu Aug  7 21:37:03 2014	(r269688)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_private.h	Thu Aug  7 21:37:31 2014	(r269689)
@@ -177,7 +177,8 @@ enum { /* result for matching dynamic ru
  */
 struct ip_fw_chain;
 struct sockopt_data;
-void ipfw_expire_dyn_rules(struct ip_fw_chain *, struct ip_fw *, int);
+int ipfw_is_dyn_rule(struct ip_fw *rule);
+void ipfw_expire_dyn_rules(struct ip_fw_chain *, ipfw_range_tlv *);
 void ipfw_dyn_unlock(ipfw_dyn_rule *q);
 
 struct tcphdr;
@@ -272,7 +273,6 @@ struct ip_fw_chain {
 #endif
 	int		static_len;	/* total len of static rules (v0) */
 	uint32_t	gencnt;		/* NAT generation count */
-	struct ip_fw	*reap;		/* list of rules to reap */
 	struct ip_fw	*default_rule;
 	struct tables_config *tblcfg;	/* tables module data */
 	void		*ifcfg;		/* interface module data */
@@ -507,6 +507,7 @@ void ipfw_reap_rules(struct ip_fw *head)
 void ipfw_init_counters(void);
 void ipfw_destroy_counters(void);
 struct ip_fw *ipfw_alloc_rule(struct ip_fw_chain *chain, size_t rulesize);
+int ipfw_match_range(struct ip_fw *rule, ipfw_range_tlv *rt);
 
 caddr_t ipfw_get_sopt_space(struct sockopt_data *sd, size_t needed);
 caddr_t ipfw_get_sopt_header(struct sockopt_data *sd, size_t needed);
@@ -547,6 +548,7 @@ int ipfw_lookup_table_extended(struct ip
     void *paddr, uint32_t *val);
 int ipfw_init_tables(struct ip_fw_chain *ch);
 int ipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables);
+int ipfw_switch_tables_namespace(struct ip_fw_chain *ch, unsigned int nsets);
 void ipfw_destroy_tables(struct ip_fw_chain *ch);
 
 /* In ip_fw_nat.c -- XXX to be moved to ip_var.h */

Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c	Thu Aug  7 21:37:03 2014	(r269688)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c	Thu Aug  7 21:37:31 2014	(r269689)
@@ -305,11 +305,12 @@ get_map(struct ip_fw_chain *chain, int e
 
 	for (;;) {
 		struct ip_fw **map;
-		int i;
+		int i, mflags;
+
+		mflags = M_ZERO | ((locked != 0) ? M_NOWAIT : M_WAITOK);
 
 		i = chain->n_rules + extra;
-		map = malloc(i * sizeof(struct ip_fw *), M_IPFW,
-			locked ? M_NOWAIT : M_WAITOK);
+		map = malloc(i * sizeof(struct ip_fw *), M_IPFW, mflags);
 		if (map == NULL) {
 			printf("%s: cannot allocate map\n", __FUNCTION__);
 			return NULL;
@@ -623,16 +624,6 @@ ipfw_reap_rules(struct ip_fw *head)
 }
 
 /*
- * Used by del_entry() to check if a rule should be kept.
- * Returns 1 if the rule must be kept, 0 otherwise.
- *
- * Called with cmd = {0,1,5}.
- * cmd == 0 matches on rule numbers, excludes rules in RESVD_SET if n == 0 ;
- * cmd == 1 matches on set numbers only, rule numbers are ignored;
- * cmd == 5 matches on rule and set numbers.
- *
- * n == 0 is a wildcard for rule numbers, there is no wildcard for sets.
- *
  * Rules to keep are
  *	(default || reserved || !match_set || !match_number)
  * where
@@ -649,14 +640,386 @@ ipfw_reap_rules(struct ip_fw *head)
  *	// number is ignored for cmd == 1 or n == 0
  *
  */
+int
+ipfw_match_range(struct ip_fw *rule, ipfw_range_tlv *rt)
+{
+
+	/* Don't match default rule regardless of query */
+	if (rule->rulenum == IPFW_DEFAULT_RULE)
+		return (0);
+
+	/* Don't match rules in reserved set for flush requests */
+	if ((rt->flags & IPFW_RCFLAG_ALL) != 0 && rule->set == RESVD_SET)
+		return (0);
+
+	/* If we're filtering by set, don't match other sets */
+	if ((rt->flags & IPFW_RCFLAG_SET) != 0 && rule->set != rt->set)
+		return (0);
+
+	if ((rt->flags & IPFW_RCFLAG_RANGE) != 0 &&
+	    (rule->rulenum < rt->start_rule || rule->rulenum > rt->end_rule))
+		return (0);
+
+	return (1);
+}
+
+/*
+ * Delete rules matching range @rt.
+ * Saves number of deleted rules in @ndel.
+ *
+ * Returns 0 on success.
+ */
+static int
+delete_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int *ndel)
+{
+	struct ip_fw *reap, *rule, **map;
+	int end, start;
+	int i, n, ndyn, ofs;
+
+	reap = NULL;
+	IPFW_UH_WLOCK(chain);	/* arbitrate writers */
+
+	/*
+	 * Stage 1: Determine range to inspect.
+	 * Range is half-inclusive, e.g [start, end).
+	 */
+	start = 0;
+	end = chain->n_rules - 1;
+
+	if ((rt->flags & IPFW_RCFLAG_RANGE) != 0) {
+		start = ipfw_find_rule(chain, rt->start_rule, 0);
+
+		end = ipfw_find_rule(chain, rt->end_rule, 0);
+		if (rt->end_rule != IPFW_DEFAULT_RULE)
+			while (chain->map[end]->rulenum == rt->end_rule)
+				end++;
+	}
+
+	/* Allocate new map of the same size */
+	map = get_map(chain, 0, 1 /* locked */);
+	if (map == NULL) {
+		IPFW_UH_WUNLOCK(chain);
+		return (ENOMEM);
+	}
+
+	n = 0;
+	ndyn = 0;
+	ofs = start;
+	/* 1. bcopy the initial part of the map */
+	if (start > 0)
+		bcopy(chain->map, map, start * sizeof(struct ip_fw *));
+	/* 2. copy active rules between start and end */
+	for (i = start; i < end; i++) {
+		rule = chain->map[i];
+		if (ipfw_match_range(rule, rt) == 0) {
+			map[ofs++] = rule;
+			continue;
+		}
+
+		n++;
+		if (ipfw_is_dyn_rule(rule) != 0)
+			ndyn++;
+	}
+	/* 3. copy the final part of the map */
+	bcopy(chain->map + end, map + ofs,
+		(chain->n_rules - end) * sizeof(struct ip_fw *));
+	/* 4. recalculate skipto cache */
+	update_skipto_cache(chain, map);
+	/* 5. swap the maps (under UH_WLOCK + WHLOCK) */
+	map = swap_map(chain, map, chain->n_rules - n);
+	/* 6. Remove all dynamic states originated by deleted rules */
+	if (ndyn > 0)
+		ipfw_expire_dyn_rules(chain, rt);
+	/* 7. now remove the rules deleted from the old map */
+	for (i = start; i < end; i++) {
+		rule = map[i];
+		if (ipfw_match_range(rule, rt) == 0)
+			continue;
+		chain->static_len -= RULEUSIZE0(rule);
+		rule->x_next = reap;
+		reap = rule;
+	}
+
+	ipfw_unbind_table_list(chain, reap);
+	IPFW_UH_WUNLOCK(chain);
+	ipfw_reap_rules(reap);
+	if (map != NULL)
+		free(map, M_IPFW);
+	*ndel = n;
+	return (0);
+}
+
+/*
+ * Changes set of given rule rannge @rt
+ * with each other.
+ *
+ * Returns 0 on success.
+ */
+static int
+move_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt)
+{
+	struct ip_fw *rule;
+	int i;
+
+	IPFW_UH_WLOCK(chain);
+
+	/*
+	 * Move rules with matching paramenerts to a new set.
+	 * This one is much more complex. We have to ensure
+	 * that all referenced tables (if any) are referenced
+	 * by given rule subset only. Otherwise, we can't move
+	 * them to new set and have to return error.
+	 */
+	if (V_fw_tables_sets != 0) {
+		if (ipfw_move_tables_sets(chain, rt, rt->new_set) != 0) {
+			IPFW_UH_WUNLOCK(chain);
+			return (EBUSY);
+		}
+	}
+
+	/* XXX: We have to do swap holding WLOCK */
+	for (i = 0; i < chain->n_rules - 1; i++) {
+		rule = chain->map[i];
+		if (ipfw_match_range(rule, rt) == 0)
+			continue;
+		rule->set = rt->new_set;
+	}
+
+	IPFW_UH_WUNLOCK(chain);
+
+	return (0);
+}
+
+/*
+ * Clear counters for a specific rule.
+ * Normally run under IPFW_UH_RLOCK, but these are idempotent ops
+ * so we only care that rules do not disappear.
+ */
+static void
+clear_counters(struct ip_fw *rule, int log_only)
+{
+	ipfw_insn_log *l = (ipfw_insn_log *)ACTION_PTR(rule);
+
+	if (log_only == 0)
+		IPFW_ZERO_RULE_COUNTER(rule);
+	if (l->o.opcode == O_LOG)
+		l->log_left = l->max_log;
+}
+
+/*
+ * Flushes rules counters and/or log values on matching range.
+ *
+ * Returns number of items cleared.
+ */
+static int
+clear_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int log_only)
+{
+	struct ip_fw *rule;
+	int num;
+	int i;
+
+	num = 0;
+
+	IPFW_UH_WLOCK(chain);	/* arbitrate writers */
+	for (i = 0; i < chain->n_rules - 1; i++) {
+		rule = chain->map[i];
+		if (ipfw_match_range(rule, rt) == 0)
+			continue;
+		clear_counters(rule, log_only);
+		num++;
+	}
+	IPFW_UH_WUNLOCK(chain);
+
+	return (num);
+}
+
 static int
-keep_rule(struct ip_fw *rule, uint8_t cmd, uint8_t set, uint32_t n)
+check_range_tlv(ipfw_range_tlv *rt)
 {
-	return
-		 (rule->rulenum == IPFW_DEFAULT_RULE)		||
-		 (cmd == 0 && n == 0 && rule->set == RESVD_SET)	||
-		!(cmd == 0 || rule->set == set)			||
-		!(cmd == 1 || n == 0 || n == rule->rulenum);
+
+	if (rt->head.length != sizeof(*rt))
+		return (1);
+	if (rt->start_rule > rt->end_rule)
+		return (1);
+	if (rt->set >= IPFW_MAX_SETS || rt->new_set >= IPFW_MAX_SETS)
+		return (1);
+
+	return (0);
+}
+
+/*
+ * Delete rules matching specified parameters
+ * Data layout (v0)(current):
+ * Request: [ ipfw_obj_header ipfw_range_tlv ]
+ * Reply: [ ipfw_obj_header ipfw_range_tlv ]
+ *
+ * Saves number of deleted rules in ipfw_range_tlv->new_set.
+ *
+ * Returns 0 on success.
+ */
+static int
+del_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
+    struct sockopt_data *sd)
+{
+	ipfw_range_header *rh;
+	int error, ndel;
+
+	if (sd->valsize != sizeof(*rh))
+		return (EINVAL);
+
+	rh = (ipfw_range_header *)ipfw_get_sopt_space(sd, sd->valsize);
+
+	if (check_range_tlv(&rh->range) != 0)
+		return (EINVAL);
+
+	ndel = 0;
+	if ((error = delete_range(chain, &rh->range, &ndel)) != 0)
+		return (error);
+
+	/* Save number of rules deleted */
+	rh->range.new_set = ndel;
+	return (0);
+}
+
+/*
+ * Move rules/sets matching specified parameters
+ * Data layout (v0)(current):
+ * Request: [ ipfw_obj_header ipfw_range_tlv ]
+ *
+ * Returns 0 on success.
+ */
+static int
+move_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
+    struct sockopt_data *sd)
+{
+	ipfw_range_header *rh;
+
+	if (sd->valsize != sizeof(*rh))
+		return (EINVAL);
+
+	rh = (ipfw_range_header *)ipfw_get_sopt_space(sd, sd->valsize);
+
+	if (check_range_tlv(&rh->range) != 0)
+		return (EINVAL);
+
+	return (move_range(chain, &rh->range));
+}
+
+/*
+ * Clear rule accounting data matching specified parameters
+ * Data layout (v0)(current):
+ * Request: [ ipfw_obj_header ipfw_range_tlv ]
+ * Reply: [ ipfw_obj_header ipfw_range_tlv ]
+ *
+ * Saves number of cleared rules in ipfw_range_tlv->new_set.
+ *
+ * Returns 0 on success.
+ */
+static int
+clear_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
+    struct sockopt_data *sd)
+{
+	ipfw_range_header *rh;
+	int log_only, num;
+	char *msg;
+
+	if (sd->valsize != sizeof(*rh))
+		return (EINVAL);
+
+	rh = (ipfw_range_header *)ipfw_get_sopt_space(sd, sd->valsize);
+
+	if (check_range_tlv(&rh->range) != 0)
+		return (EINVAL);
+
+	log_only = (op3->opcode == IP_FW_XRESETLOG);
+
+	num = clear_range(chain, &rh->range, log_only);
+
+	if (rh->range.flags & IPFW_RCFLAG_ALL)
+		msg = log_only ? "All logging counts reset" :
+		    "Accounting cleared";
+	else
+		msg = log_only ? "logging count reset" : "cleared";
+
+	if (V_fw_verbose) {
+		int lev = LOG_SECURITY | LOG_NOTICE;
+		log(lev, "ipfw: %s.\n", msg);
+	}
+
+	/* Save number of rules cleared */
+	rh->range.new_set = num;
+	return (0);
+}
+
+static void
+enable_sets(struct ip_fw_chain *chain, ipfw_range_tlv *rt)
+{
+	uint32_t v_set;
+
+	IPFW_UH_WLOCK_ASSERT(chain);
+
+	/* Change enabled/disabled sets mask */
+	v_set = (V_set_disable | rt->set) & ~rt->new_set;
+	v_set &= ~(1 << RESVD_SET); /* set RESVD_SET always enabled */
+	IPFW_WLOCK(chain);
+	V_set_disable = v_set;
+	IPFW_WUNLOCK(chain);
+}
+
+static void
+swap_sets(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int mv)
+{
+	struct ip_fw *rule;
+	int i;
+
+	IPFW_UH_WLOCK_ASSERT(chain);
+
+	/* Swap or move two sets */
+	for (i = 0; i < chain->n_rules - 1; i++) {
+		rule = chain->map[i];
+		if (rule->set == rt->set)
+			rule->set = rt->new_set;
+		else if (rule->set == rt->new_set && mv == 0)
+			rule->set = rt->set;
+	}
+	if (V_fw_tables_sets != 0)
+		ipfw_swap_tables_sets(chain, rt->set, rt->new_set, mv);
+}
+
+/*
+ * Swaps or moves set
+ * Data layout (v0)(current):
+ * Request: [ ipfw_obj_header ipfw_range_tlv ]
+ *
+ * Returns 0 on success.
+ */
+static int
+manage_sets(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
+    struct sockopt_data *sd)
+{
+	ipfw_range_header *rh;
+
+	if (sd->valsize != sizeof(*rh))
+		return (EINVAL);
+
+	rh = (ipfw_range_header *)ipfw_get_sopt_space(sd, sd->valsize);
+
+	if (rh->range.head.length != sizeof(ipfw_range_tlv))
+		return (1);
+
+	IPFW_UH_WLOCK(chain);
+	switch (op3->opcode) {
+	case IP_FW_SET_SWAP:
+	case IP_FW_SET_MOVE:
+		swap_sets(chain, &rh->range, op3->opcode == IP_FW_SET_MOVE);
+		break;
+	case IP_FW_SET_ENABLE:
+		enable_sets(chain, &rh->range);
+		break;
+	}
+	IPFW_UH_WUNLOCK(chain);
+
+	return (0);
 }
 
 /**
@@ -676,12 +1039,11 @@ keep_rule(struct ip_fw *rule, uint8_t cm
 static int
 del_entry(struct ip_fw_chain *chain, uint32_t arg)
 {
-	struct ip_fw *rule;
 	uint32_t num;	/* rule number or old_set */
 	uint8_t cmd, new_set;
-	int start, end, i, ofs, n;
-	struct ip_fw **map = NULL;
+	int do_del, ndel;
 	int error = 0;
+	ipfw_range_tlv rt;
 
 	num = arg & 0xffff;
 	cmd = (arg >> 24) & 0xff;
@@ -697,151 +1059,60 @@ del_entry(struct ip_fw_chain *chain, uin
 			return EINVAL;
 	}
 
-	IPFW_UH_WLOCK(chain);	/* arbitrate writers */
-	chain->reap = NULL;	/* prepare for deletions */
+	/* Convert old requests into new representation */
+	memset(&rt, 0, sizeof(rt));
+	rt.start_rule = num;
+	rt.end_rule = num;
+	rt.set = num;
+	rt.new_set = new_set;
+	do_del = 0;
 
 	switch (cmd) {
-	case 0:	/* delete rules "num" (num == 0 matches all) */
-	case 1:	/* delete all rules in set N */
-	case 5: /* delete rules with number N and set "new_set". */
-
-		/*
-		 * Locate first rule to delete (start), the rule after
-		 * the last one to delete (end), and count how many
-		 * rules to delete (n). Always use keep_rule() to
-		 * determine which rules to keep.
-		 */
-		n = 0;
-		if (cmd == 1) {
-			/* look for a specific set including RESVD_SET.
-			 * Must scan the entire range, ignore num.
-			 */
-			new_set = num;
-			for (start = -1, end = i = 0; i < chain->n_rules; i++) {
-				if (keep_rule(chain->map[i], cmd, new_set, 0))
-					continue;
-				if (start < 0)
-					start = i;
-				end = i;
-				n++;
-			}
-			end++;	/* first non-matching */
-		} else {
-			/* Optimized search on rule numbers */
-			start = ipfw_find_rule(chain, num, 0);
-			for (end = start; end < chain->n_rules; end++) {
-				rule = chain->map[end];
-				if (num > 0 && rule->rulenum != num)
-					break;
-				if (!keep_rule(rule, cmd, new_set, num))

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?53e3f19c.2e78.f884e9d>