svn commit: r300021 - in head: sbin/ipfw sys/netinet sys/netpfil/ipfw

Andrey V. Elsukov ae at FreeBSD.org
Tue May 17 07:47:24 UTC 2016


Author: ae
Date: Tue May 17 07:47:23 2016
New Revision: 300021
URL: https://svnweb.freebsd.org/changeset/base/300021

Log:
  Make named objects set-aware. Now it is possible to create named
  objects with the same name in different sets.
  
  Add optional manage_sets() callback to objects rewriting framework.
  It is intended to implement handler for moving and swapping named
  object's sets. Add ipfw_obj_manage_sets() function that implements
  generic sets handler. Use new callback to implement sets support for
  lookup tables.
  External actions objects are global and they don't support sets.
  Modify eaction_findbyname() to reflect this.
  ipfw(8) now may fail to move rules or sets, because some named objects
  in target set may have conflicting names.
  Note that ipfw_obj_ntlv type was changed, but since lookup tables
  actually didn't support sets, this change is harmless.
  
  Obtained from:	Yandex LLC
  Sponsored by:	Yandex LLC

Modified:
  head/sbin/ipfw/ipfw2.c
  head/sys/netinet/ip_fw.h
  head/sys/netpfil/ipfw/ip_fw_eaction.c
  head/sys/netpfil/ipfw/ip_fw_private.h
  head/sys/netpfil/ipfw/ip_fw_sockopt.c
  head/sys/netpfil/ipfw/ip_fw_table.c

Modified: head/sbin/ipfw/ipfw2.c
==============================================================================
--- head/sbin/ipfw/ipfw2.c	Tue May 17 07:15:25 2016	(r300020)
+++ head/sbin/ipfw/ipfw2.c	Tue May 17 07:47:23 2016	(r300021)
@@ -2280,6 +2280,9 @@ ipfw_sets_handler(char *av[])
 		if (!isdigit(*(av[2])) || rt.new_set > RESVD_SET)
 			errx(EX_DATAERR, "invalid dest. set %s\n", av[1]);
 		i = do_range_cmd(cmd, &rt);
+		if (i < 0)
+			err(EX_OSERR, "failed to move %s",
+			    cmd == IP_FW_SET_MOVE ? "set": "rule");
 	} else if (_substrcmp(*av, "disable") == 0 ||
 		   _substrcmp(*av, "enable") == 0 ) {
 		int which = _substrcmp(*av, "enable") == 0 ? 1 : 0;

Modified: head/sys/netinet/ip_fw.h
==============================================================================
--- head/sys/netinet/ip_fw.h	Tue May 17 07:15:25 2016	(r300020)
+++ head/sys/netinet/ip_fw.h	Tue May 17 07:47:23 2016	(r300021)
@@ -791,9 +791,9 @@ typedef struct  _ipfw_obj_tlv {
 typedef struct _ipfw_obj_ntlv {
 	ipfw_obj_tlv	head;		/* TLV header			*/
 	uint16_t	idx;		/* Name index			*/
-	uint8_t		spare;		/* unused			*/
+	uint8_t		set;		/* set, if applicable		*/
 	uint8_t		type;		/* object type, if applicable	*/
-	uint32_t	set;		/* set, if applicable		*/
+	uint32_t	spare;		/* unused			*/
 	char		name[64];	/* Null-terminated name		*/
 } ipfw_obj_ntlv;
 

Modified: head/sys/netpfil/ipfw/ip_fw_eaction.c
==============================================================================
--- head/sys/netpfil/ipfw/ip_fw_eaction.c	Tue May 17 07:15:25 2016	(r300020)
+++ head/sys/netpfil/ipfw/ip_fw_eaction.c	Tue May 17 07:47:23 2016	(r300021)
@@ -137,10 +137,28 @@ static int
 eaction_findbyname(struct ip_fw_chain *ch, struct tid_info *ti,
     struct named_object **pno)
 {
+	ipfw_obj_ntlv *ntlv;
 
-	EACTION_DEBUG("uidx %u, type %u", ti->uidx, ti->type);
-	return (ipfw_objhash_find_type(CHAIN_TO_SRV(ch), ti,
-	    IPFW_TLV_EACTION, pno));
+	if (ti->tlvs == NULL)
+		return (EINVAL);
+
+	/* Search ntlv in the buffer provided by user */
+	ntlv = ipfw_find_name_tlv_type(ti->tlvs, ti->tlen, ti->uidx,
+	    IPFW_TLV_EACTION);
+	if (ntlv == NULL)
+		return (EINVAL);
+	EACTION_DEBUG("name %s, uidx %u, type %u", ntlv->name,
+	    ti->uidx, ti->type);
+	/*
+	 * Search named object with corresponding name.
+	 * Since eaction objects are global - ignore the set value
+	 * and use zero instead.
+	 */
+	*pno = ipfw_objhash_lookup_name_type(CHAIN_TO_SRV(ch),
+	    0, IPFW_TLV_EACTION, ntlv->name);
+	if (*pno == NULL)
+		return (ESRCH);
+	return (0);
 }
 
 static struct named_object *

Modified: head/sys/netpfil/ipfw/ip_fw_private.h
==============================================================================
--- head/sys/netpfil/ipfw/ip_fw_private.h	Tue May 17 07:15:25 2016	(r300020)
+++ head/sys/netpfil/ipfw/ip_fw_private.h	Tue May 17 07:47:23 2016	(r300021)
@@ -315,9 +315,10 @@ struct named_object {
 	char			*name;	/* object name */
 	uint16_t		etlv;	/* Export TLV id */
 	uint8_t			subtype;/* object subtype within class */
-	uint8_t			spare[3];
+	uint8_t			set;	/* set object belongs to */
 	uint16_t		kidx;	/* object kernel index */
-	uint32_t		set;	/* set object belongs to */
+	uint16_t		spare;
+	uint32_t		ocnt;	/* object counter for internal use */
 	uint32_t		refcnt;	/* number of references */
 };
 TAILQ_HEAD(namedobjects_head, named_object);
@@ -571,6 +572,21 @@ typedef int (ipfw_obj_create_cb)(struct 
  */
 typedef void (ipfw_obj_destroy_cb)(struct ip_fw_chain *ch,
     struct named_object *no);
+/*
+ * Sets handler callback. Handles moving and swaping set of named object.
+ *  SWAP_ALL moves all named objects from set `set' to `new_set' and vise versa;
+ *  TEST_ALL checks that there aren't any named object with conflicting names;
+ *  MOVE_ALL moves all named objects from set `set' to `new_set';
+ *  COUNT_ONE used to count number of references used by object with kidx `set';
+ *  TEST_ONE checks that named object with kidx `set' can be moved to `new_set`;
+ *  MOVE_ONE moves named object with kidx `set' to set `new_set'.
+ */
+enum ipfw_sets_cmd {
+	SWAP_ALL = 0, TEST_ALL, MOVE_ALL, COUNT_ONE, TEST_ONE, MOVE_ONE
+};
+typedef int (ipfw_obj_sets_cb)(struct ip_fw_chain *ch,
+    uint16_t set, uint8_t new_set, enum ipfw_sets_cmd cmd);
+
 
 struct opcode_obj_rewrite {
 	uint32_t		opcode;		/* Opcode to act upon */
@@ -581,6 +597,7 @@ struct opcode_obj_rewrite {
 	ipfw_obj_fidx_cb	*find_bykidx;	/* Find named object by kidx */
 	ipfw_obj_create_cb	*create_object;	/* Create named object */
 	ipfw_obj_destroy_cb	*destroy_object;/* Destroy named object */
+	ipfw_obj_sets_cb	*manage_sets;	/* Swap or move sets */
 };
 
 #define	IPFW_ADD_OBJ_REWRITER(f, c)	do {	\
@@ -675,8 +692,11 @@ int ipfw_objhash_same_name(struct namedo
 void ipfw_objhash_add(struct namedobj_instance *ni, struct named_object *no);
 void ipfw_objhash_del(struct namedobj_instance *ni, struct named_object *no);
 uint32_t ipfw_objhash_count(struct namedobj_instance *ni);
+uint32_t ipfw_objhash_count_type(struct namedobj_instance *ni, uint16_t type);
 int ipfw_objhash_foreach(struct namedobj_instance *ni, objhash_cb_t *f,
     void *arg);
+int ipfw_objhash_foreach_type(struct namedobj_instance *ni, objhash_cb_t *f,
+    void *arg, uint16_t type);
 int ipfw_objhash_free_idx(struct namedobj_instance *ni, uint16_t idx);
 int ipfw_objhash_alloc_idx(void *n, uint16_t *pidx);
 void ipfw_objhash_set_funcs(struct namedobj_instance *ni,
@@ -698,6 +718,8 @@ int classify_opcode_kidx(ipfw_insn *cmd,
 void ipfw_init_srv(struct ip_fw_chain *ch);
 void ipfw_destroy_srv(struct ip_fw_chain *ch);
 int ipfw_check_object_name_generic(const char *name);
+int ipfw_obj_manage_sets(struct namedobj_instance *ni, uint16_t type,
+    uint16_t set, uint8_t new_set, enum ipfw_sets_cmd cmd);
 
 /* In ip_fw_eaction.c */
 typedef int (ipfw_eaction_t)(struct ip_fw_chain *ch, struct ip_fw_args *args,

Modified: head/sys/netpfil/ipfw/ip_fw_sockopt.c
==============================================================================
--- head/sys/netpfil/ipfw/ip_fw_sockopt.c	Tue May 17 07:15:25 2016	(r300020)
+++ head/sys/netpfil/ipfw/ip_fw_sockopt.c	Tue May 17 07:47:23 2016	(r300021)
@@ -851,6 +851,113 @@ ipfw_match_range(struct ip_fw *rule, ipf
 	return (1);
 }
 
+struct manage_sets_args {
+	uint16_t	set;
+	uint8_t		new_set;
+};
+
+static int
+swap_sets_cb(struct namedobj_instance *ni, struct named_object *no,
+    void *arg)
+{
+	struct manage_sets_args *args;
+
+	args = (struct manage_sets_args *)arg;
+	if (no->set == (uint8_t)args->set)
+		no->set = args->new_set;
+	else if (no->set == args->new_set)
+		no->set = (uint8_t)args->set;
+	return (0);
+}
+
+static int
+move_sets_cb(struct namedobj_instance *ni, struct named_object *no,
+    void *arg)
+{
+	struct manage_sets_args *args;
+
+	args = (struct manage_sets_args *)arg;
+	if (no->set == (uint8_t)args->set)
+		no->set = args->new_set;
+	return (0);
+}
+
+static int
+test_sets_cb(struct namedobj_instance *ni, struct named_object *no,
+    void *arg)
+{
+	struct manage_sets_args *args;
+
+	args = (struct manage_sets_args *)arg;
+	if (no->set != (uint8_t)args->set)
+		return (0);
+	if (ipfw_objhash_lookup_name_type(ni, args->new_set,
+	    no->etlv, no->name) != NULL)
+		return (EEXIST);
+	return (0);
+}
+
+/*
+ * Generic function to handler moving and swapping sets.
+ */
+int
+ipfw_obj_manage_sets(struct namedobj_instance *ni, uint16_t type,
+    uint16_t set, uint8_t new_set, enum ipfw_sets_cmd cmd)
+{
+	struct manage_sets_args args;
+	struct named_object *no;
+
+	args.set = set;
+	args.new_set = new_set;
+	switch (cmd) {
+	case SWAP_ALL:
+		return (ipfw_objhash_foreach_type(ni, swap_sets_cb,
+		    &args, type));
+	case TEST_ALL:
+		return (ipfw_objhash_foreach_type(ni, test_sets_cb,
+		    &args, type));
+	case MOVE_ALL:
+		return (ipfw_objhash_foreach_type(ni, move_sets_cb,
+		    &args, type));
+	case COUNT_ONE:
+		/*
+		 * @set used to pass kidx.
+		 * When @new_set is zero - reset object counter,
+		 * otherwise increment it.
+		 */
+		no = ipfw_objhash_lookup_kidx(ni, set);
+		if (new_set != 0)
+			no->ocnt++;
+		else
+			no->ocnt = 0;
+		return (0);
+	case TEST_ONE:
+		/* @set used to pass kidx */
+		no = ipfw_objhash_lookup_kidx(ni, set);
+		/*
+		 * First check number of references:
+		 * when it differs, this mean other rules are holding
+		 * reference to given object, so it is not possible to
+		 * change its set. Note that refcnt may account references
+		 * to some going-to-be-added rules. Since we don't know
+		 * their numbers (and even if they will be added) it is
+		 * perfectly OK to return error here.
+		 */
+		if (no->ocnt != no->refcnt)
+			return (EBUSY);
+		if (ipfw_objhash_lookup_name_type(ni, new_set, type,
+		    no->name) != NULL)
+			return (EEXIST);
+		return (0);
+	case MOVE_ONE:
+		/* @set used to pass kidx */
+		no = ipfw_objhash_lookup_kidx(ni, set);
+		no->set = new_set;
+		return (0);
+	}
+	return (EINVAL);
+}
+
 /*
  * Delete rules matching range @rt.
  * Saves number of deleted rules in @ndel.
@@ -935,7 +1042,89 @@ delete_range(struct ip_fw_chain *chain, 
 	return (0);
 }
 
-/*
+static int
+move_objects(struct ip_fw_chain *ch, ipfw_range_tlv *rt)
+{
+	struct opcode_obj_rewrite *rw;
+	struct ip_fw *rule;
+	ipfw_insn *cmd;
+	int cmdlen, i, l, c;
+	uint16_t kidx;
+
+	IPFW_UH_WLOCK_ASSERT(ch);
+
+	/* Stage 1: count number of references by given rules */
+	for (c = 0, i = 0; i < ch->n_rules - 1; i++) {
+		rule = ch->map[i];
+		if (ipfw_match_range(rule, rt) == 0)
+			continue;
+		if (rule->set == rt->new_set) /* nothing to do */
+			continue;
+		/* Search opcodes with named objects */
+		for (l = rule->cmd_len, cmdlen = 0, cmd = rule->cmd;
+		    l > 0; l -= cmdlen, cmd += cmdlen) {
+			cmdlen = F_LEN(cmd);
+			rw = find_op_rw(cmd, &kidx, NULL);
+			if (rw == NULL || rw->manage_sets == NULL)
+				continue;
+			/*
+			 * When manage_sets() returns non-zero value to
+			 * COUNT_ONE command, consider this as an object
+			 * doesn't support sets (e.g. disabled with sysctl).
+			 * So, skip checks for this object.
+			 */
+			if (rw->manage_sets(ch, kidx, 1, COUNT_ONE) != 0)
+				continue;
+			c++;
+		}
+	}
+	if (c == 0) /* No objects found */
+		return (0);
+	/* Stage 2: verify "ownership" */
+	for (c = 0, i = 0; (i < ch->n_rules - 1) && c == 0; i++) {
+		rule = ch->map[i];
+		if (ipfw_match_range(rule, rt) == 0)
+			continue;
+		if (rule->set == rt->new_set) /* nothing to do */
+			continue;
+		/* Search opcodes with named objects */
+		for (l = rule->cmd_len, cmdlen = 0, cmd = rule->cmd;
+		    l > 0 && c == 0; l -= cmdlen, cmd += cmdlen) {
+			cmdlen = F_LEN(cmd);
+			rw = find_op_rw(cmd, &kidx, NULL);
+			if (rw == NULL || rw->manage_sets == NULL)
+				continue;
+			/* Test for ownership and conflicting names */
+			c = rw->manage_sets(ch, kidx,
+			    (uint8_t)rt->new_set, TEST_ONE);
+		}
+	}
+	/* Stage 3: change set and cleanup */
+	for (i = 0; i < ch->n_rules - 1; i++) {
+		rule = ch->map[i];
+		if (ipfw_match_range(rule, rt) == 0)
+			continue;
+		if (rule->set == rt->new_set) /* nothing to do */
+			continue;
+		/* Search opcodes with named objects */
+		for (l = rule->cmd_len, cmdlen = 0, cmd = rule->cmd;
+		    l > 0; l -= cmdlen, cmd += cmdlen) {
+			cmdlen = F_LEN(cmd);
+			rw = find_op_rw(cmd, &kidx, NULL);
+			if (rw == NULL || rw->manage_sets == NULL)
+				continue;
+			/* cleanup object counter */
+			rw->manage_sets(ch, kidx,
+			    0 /* reset counter */, COUNT_ONE);
+			if (c != 0)
+				continue;
+			/* change set */
+			rw->manage_sets(ch, kidx,
+			    (uint8_t)rt->new_set, MOVE_ONE);
+		}
+	}
+	return (c);
+}/*
  * Changes set of given rule rannge @rt
  * with each other.
  *
@@ -956,11 +1145,9 @@ move_range(struct ip_fw_chain *chain, ip
 	 * 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);
-		}
+	if ((i = move_objects(chain, rt)) != 0) {
+		IPFW_UH_WUNLOCK(chain);
+		return (i);
 	}
 
 	/* XXX: We have to do swap holding WLOCK */
@@ -1156,24 +1343,48 @@ enable_sets(struct ip_fw_chain *chain, i
 	IPFW_WUNLOCK(chain);
 }
 
-static void
+static int
 swap_sets(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int mv)
 {
+	struct opcode_obj_rewrite *rw;
 	struct ip_fw *rule;
 	int i;
 
 	IPFW_UH_WLOCK_ASSERT(chain);
 
+	if (rt->set == rt->new_set) /* nothing to do */
+		return (0);
+
+	if (mv != 0) {
+		/*
+		 * Berfore moving the rules we need to check that
+		 * there aren't any conflicting named objects.
+		 */
+		for (rw = ctl3_rewriters;
+		    rw < ctl3_rewriters + ctl3_rsize; rw++) {
+			if (rw->manage_sets == NULL)
+				continue;
+			i = rw->manage_sets(chain, (uint8_t)rt->set,
+			    (uint8_t)rt->new_set, TEST_ALL);
+			if (i != 0)
+				return (EEXIST);
+		}
+	}
 	/* 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 (rule->set == (uint8_t)rt->set)
+			rule->set = (uint8_t)rt->new_set;
+		else if (rule->set == (uint8_t)rt->new_set && mv == 0)
+			rule->set = (uint8_t)rt->set;
+	}
+	for (rw = ctl3_rewriters; rw < ctl3_rewriters + ctl3_rsize; rw++) {
+		if (rw->manage_sets == NULL)
+			continue;
+		rw->manage_sets(chain, (uint8_t)rt->set,
+		    (uint8_t)rt->new_set, mv != 0 ? MOVE_ALL: SWAP_ALL);
 	}
-	if (V_fw_tables_sets != 0)
-		ipfw_swap_tables_sets(chain, rt->set, rt->new_set, mv);
+	return (0);
 }
 
 /*
@@ -1188,6 +1399,7 @@ manage_sets(struct ip_fw_chain *chain, i
     struct sockopt_data *sd)
 {
 	ipfw_range_header *rh;
+	int ret;
 
 	if (sd->valsize != sizeof(*rh))
 		return (EINVAL);
@@ -1196,12 +1408,17 @@ manage_sets(struct ip_fw_chain *chain, i
 
 	if (rh->range.head.length != sizeof(ipfw_range_tlv))
 		return (1);
+	if (rh->range.set >= IPFW_MAX_SETS ||
+	    rh->range.new_set >= IPFW_MAX_SETS)
+		return (EINVAL);
 
+	ret = 0;
 	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);
+		ret = swap_sets(chain, &rh->range,
+		    op3->opcode == IP_FW_SET_MOVE);
 		break;
 	case IP_FW_SET_ENABLE:
 		enable_sets(chain, &rh->range);
@@ -1209,7 +1426,7 @@ manage_sets(struct ip_fw_chain *chain, i
 	}
 	IPFW_UH_WUNLOCK(chain);
 
-	return (0);
+	return (ret);
 }
 
 /**
@@ -1280,14 +1497,14 @@ del_entry(struct ip_fw_chain *chain, uin
 		break;
 	case 3: /* move rules from set "rulenum" to set "new_set" */
 		IPFW_UH_WLOCK(chain);
-		swap_sets(chain, &rt, 1);
+		error = swap_sets(chain, &rt, 1);
 		IPFW_UH_WUNLOCK(chain);
-		return (0);
+		return (error);
 	case 4: /* swap sets "rulenum" and "new_set" */
 		IPFW_UH_WLOCK(chain);
-		swap_sets(chain, &rt, 0);
+		error = swap_sets(chain, &rt, 0);
 		IPFW_UH_WUNLOCK(chain);
-		return (0);
+		return (error);
 	default:
 		return (ENOTSUP);
 	}
@@ -2526,11 +2743,8 @@ rewrite_rule_uidx(struct ip_fw_chain *ch
 	type = 0;
 	memset(&ti, 0, sizeof(ti));
 
-	/*
-	 * Use default set for looking up tables (old way) or
-	 * use set rule is assigned to (new way).
-	 */
-	ti.set = (V_fw_tables_sets != 0) ? ci->krule->set : 0;
+	/* Use set rule is assigned to. */
+	ti.set = ci->krule->set;
 	if (ci->ctlv != NULL) {
 		ti.tlvs = (void *)(ci->ctlv + 1);
 		ti.tlen = ci->ctlv->head.length - sizeof(ipfw_obj_ctlv);
@@ -4248,6 +4462,23 @@ ipfw_objhash_count(struct namedobj_insta
 	return (ni->count);
 }
 
+uint32_t
+ipfw_objhash_count_type(struct namedobj_instance *ni, uint16_t type)
+{
+	struct named_object *no;
+	uint32_t count;
+	int i;
+
+	count = 0;
+	for (i = 0; i < ni->nn_size; i++) {
+		TAILQ_FOREACH(no, &ni->names[i], nn_next) {
+			if (no->etlv == type)
+				count++;
+		}
+	}
+	return (count);
+}
+
 /*
  * Runs @func for each found named object.
  * It is safe to delete objects from callback
@@ -4269,6 +4500,29 @@ ipfw_objhash_foreach(struct namedobj_ins
 }
 
 /*
+ * Runs @f for each found named object with type @type.
+ * It is safe to delete objects from callback
+ */
+int
+ipfw_objhash_foreach_type(struct namedobj_instance *ni, objhash_cb_t *f,
+    void *arg, uint16_t type)
+{
+	struct named_object *no, *no_tmp;
+	int i, ret;
+
+	for (i = 0; i < ni->nn_size; i++) {
+		TAILQ_FOREACH_SAFE(no, &ni->names[i], nn_next, no_tmp) {
+			if (no->etlv != type)
+				continue;
+			ret = f(ni, no, arg);
+			if (ret != 0)
+				return (ret);
+		}
+	}
+	return (0);
+}
+
+/*
  * Removes index from given set.
  * Returns 0 on success.
  */

Modified: head/sys/netpfil/ipfw/ip_fw_table.c
==============================================================================
--- head/sys/netpfil/ipfw/ip_fw_table.c	Tue May 17 07:15:25 2016	(r300020)
+++ head/sys/netpfil/ipfw/ip_fw_table.c	Tue May 17 07:47:23 2016	(r300021)
@@ -1602,64 +1602,6 @@ ipfw_resize_tables(struct ip_fw_chain *c
 }
 
 /*
- * Switch between "set 0" and "rule's set" table binding,
- * Check all ruleset bindings and permits changing
- * IFF each binding has both rule AND table in default set (set 0).
- *
- * Returns 0 on success.
- */
-int
-ipfw_switch_tables_namespace(struct ip_fw_chain *ch, unsigned int sets)
-{
-	struct namedobj_instance *ni;
-	struct named_object *no;
-	struct ip_fw *rule;
-	ipfw_insn *cmd;
-	int cmdlen, i, l;
-	uint16_t kidx;
-
-	IPFW_UH_WLOCK(ch);
-
-	if (V_fw_tables_sets == sets) {
-		IPFW_UH_WUNLOCK(ch);
-		return (0);
-	}
-
-	ni = CHAIN_TO_NI(ch);
-
-	/*
-	 * Scan all rules and examine tables opcodes.
-	 */
-	for (i = 0; i < ch->n_rules; i++) {
-		rule = ch->map[i];
-
-		l = rule->cmd_len;
-		cmd = rule->cmd;
-		cmdlen = 0;
-		for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
-			cmdlen = F_LEN(cmd);
-
-			if (classify_opcode_kidx(cmd, &kidx) != 0)
-				continue;
-
-			no = ipfw_objhash_lookup_kidx(ni, kidx);
-
-			/* Check if both table object and rule has the set 0 */
-			if (no->set != 0 || rule->set != 0) {
-				IPFW_UH_WUNLOCK(ch);
-				return (EBUSY);
-			}
-
-		}
-	}
-	V_fw_tables_sets = sets;
-
-	IPFW_UH_WUNLOCK(ch);
-
-	return (0);
-}
-
-/*
  * Lookup an IP @addr in table @tbl.
  * Stores found value in @val.
  *
@@ -2875,39 +2817,190 @@ table_findbykidx(struct ip_fw_chain *ch,
 	return (&tc->no);
 }
 
+static int
+table_manage_sets(struct ip_fw_chain *ch, uint16_t set, uint8_t new_set,
+    enum ipfw_sets_cmd cmd)
+{
+
+	switch (cmd) {
+	case SWAP_ALL:
+	case TEST_ALL:
+		/*
+		 * Return success for TEST_ALL, since nothing prevents
+		 * move rules from one set to another. All tables are
+		 * accessible from all sets when per-set tables sysctl
+		 * is disabled.
+		 */
+	case MOVE_ALL:
+	case TEST_ONE:
+	case MOVE_ONE:
+		/*
+		 * NOTE: we need to use ipfw_objhash_del/ipfw_objhash_add
+		 * if set number will be used in hash function. Currently
+		 * we can just use generic handler that replaces set value.
+		 */
+		if (V_fw_tables_sets == 0)
+			return (0);
+		break;
+	case COUNT_ONE:
+		/*
+		 * Return EOPNOTSUPP for COUNT_ONE when per-set sysctl is
+		 * disabled. This allow skip table's opcodes from additional
+		 * checks when specific rules moved to another set.
+		 */
+		if (V_fw_tables_sets == 0)
+			return (EOPNOTSUPP);
+	}
+	/* Use generic sets handler when per-set sysctl is enabled. */
+	return (ipfw_obj_manage_sets(CHAIN_TO_NI(ch), IPFW_TLV_TBL_NAME,
+	    set, new_set, cmd));
+}
+
 static struct opcode_obj_rewrite opcodes[] = {
 	{
-		O_IP_SRC_LOOKUP, IPFW_TLV_TBL_NAME,
-		classify_srcdst, update_arg1,
-		table_findbyname, table_findbykidx, create_table_compat
+		.opcode = O_IP_SRC_LOOKUP,
+		.etlv = IPFW_TLV_TBL_NAME,
+		.classifier = classify_srcdst,
+		.update = update_arg1,
+		.find_byname = table_findbyname,
+		.find_bykidx = table_findbykidx,
+		.create_object = create_table_compat,
+		.manage_sets = table_manage_sets,
 	},
 	{
-		O_IP_DST_LOOKUP, IPFW_TLV_TBL_NAME,
-		classify_srcdst, update_arg1,
-		table_findbyname, table_findbykidx, create_table_compat
+		.opcode = O_IP_DST_LOOKUP,
+		.etlv = IPFW_TLV_TBL_NAME,
+		.classifier = classify_srcdst,
+		.update = update_arg1,
+		.find_byname = table_findbyname,
+		.find_bykidx = table_findbykidx,
+		.create_object = create_table_compat,
+		.manage_sets = table_manage_sets,
 	},
 	{
-		O_IP_FLOW_LOOKUP, IPFW_TLV_TBL_NAME,
-		classify_flow, update_arg1,
-		table_findbyname, table_findbykidx, create_table_compat
+		.opcode = O_IP_FLOW_LOOKUP,
+		.etlv = IPFW_TLV_TBL_NAME,
+		.classifier = classify_flow,
+		.update = update_arg1,
+		.find_byname = table_findbyname,
+		.find_bykidx = table_findbykidx,
+		.create_object = create_table_compat,
+		.manage_sets = table_manage_sets,
 	},
 	{
-		O_XMIT, IPFW_TLV_TBL_NAME,
-		classify_via, update_via,
-		table_findbyname, table_findbykidx, create_table_compat
+		.opcode = O_XMIT,
+		.etlv = IPFW_TLV_TBL_NAME,
+		.classifier = classify_via,
+		.update = update_via,
+		.find_byname = table_findbyname,
+		.find_bykidx = table_findbykidx,
+		.create_object = create_table_compat,
+		.manage_sets = table_manage_sets,
 	},
 	{
-		O_RECV, IPFW_TLV_TBL_NAME,
-		classify_via, update_via,
-		table_findbyname, table_findbykidx, create_table_compat
+		.opcode = O_RECV,
+		.etlv = IPFW_TLV_TBL_NAME,
+		.classifier = classify_via,
+		.update = update_via,
+		.find_byname = table_findbyname,
+		.find_bykidx = table_findbykidx,
+		.create_object = create_table_compat,
+		.manage_sets = table_manage_sets,
 	},
 	{
-		O_VIA, IPFW_TLV_TBL_NAME,
-		classify_via, update_via,
-		table_findbyname, table_findbykidx, create_table_compat
+		.opcode = O_VIA,
+		.etlv = IPFW_TLV_TBL_NAME,
+		.classifier = classify_via,
+		.update = update_via,
+		.find_byname = table_findbyname,
+		.find_bykidx = table_findbykidx,
+		.create_object = create_table_compat,
+		.manage_sets = table_manage_sets,
 	},
 };
 
+static int
+test_sets_cb(struct namedobj_instance *ni __unused, struct named_object *no,
+    void *arg __unused)
+{
+
+	/* Check that there aren't any tables in not default set */
+	if (no->set != 0)
+		return (EBUSY);
+	return (0);
+}
+
+/*
+ * Switch between "set 0" and "rule's set" table binding,
+ * Check all ruleset bindings and permits changing
+ * IFF each binding has both rule AND table in default set (set 0).
+ *
+ * Returns 0 on success.
+ */
+int
+ipfw_switch_tables_namespace(struct ip_fw_chain *ch, unsigned int sets)
+{
+	struct opcode_obj_rewrite *rw;
+	struct namedobj_instance *ni;
+	struct named_object *no;
+	struct ip_fw *rule;
+	ipfw_insn *cmd;
+	int cmdlen, i, l;
+	uint16_t kidx;
+	uint8_t subtype;
+
+	IPFW_UH_WLOCK(ch);
+
+	if (V_fw_tables_sets == sets) {
+		IPFW_UH_WUNLOCK(ch);
+		return (0);
+	}
+	ni = CHAIN_TO_NI(ch);
+	if (sets == 0) {
+		/*
+		 * Prevent disabling sets support if we have some tables
+		 * in not default sets.
+		 */
+		if (ipfw_objhash_foreach_type(ni, test_sets_cb,
+		    NULL, IPFW_TLV_TBL_NAME) != 0) {
+			IPFW_UH_WUNLOCK(ch);
+			return (EBUSY);
+		}
+	}
+	/*
+	 * Scan all rules and examine tables opcodes.
+	 */
+	for (i = 0; i < ch->n_rules; i++) {
+		rule = ch->map[i];
+
+		l = rule->cmd_len;
+		cmd = rule->cmd;
+		cmdlen = 0;
+		for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
+			cmdlen = F_LEN(cmd);
+			/* Check only tables opcodes */
+			for (kidx = 0, rw = opcodes;
+			    rw < opcodes + nitems(opcodes); rw++) {
+				if (rw->opcode != cmd->opcode)
+					continue;
+				if (rw->classifier(cmd, &kidx, &subtype) == 0)
+					break;
+			}
+			if (kidx == 0)
+				continue;
+			no = ipfw_objhash_lookup_kidx(ni, kidx);
+			/* Check if both table object and rule has the set 0 */
+			if (no->set != 0 || rule->set != 0) {
+				IPFW_UH_WUNLOCK(ch);
+				return (EBUSY);
+			}
+
+		}
+	}
+	V_fw_tables_sets = sets;
+	IPFW_UH_WUNLOCK(ch);
+	return (0);
+}
 
 /*
  * Checks table name for validity.
@@ -2954,7 +3047,7 @@ find_table_err(struct namedobj_instance 
 		 * This is needed due to different sets behavior
 		 * controlled by V_fw_tables_sets.
 		 */
-		set = ti->set;
+		set = (V_fw_tables_sets != 0) ? ti->set : 0;
 	} else {
 		snprintf(bname, sizeof(bname), "%d", ti->uidx);
 		name = bname;
@@ -3112,196 +3205,6 @@ unlink_table(struct ip_fw_chain *ch, str
 		tc->ta->change_ti(tc->astate, NULL);
 }
 
-struct swap_table_args {
-	int set;
-	int new_set;
-	int mv;
-};
-
-/*
- * Change set for each matching table.
- *
- * Ensure we dispatch each table once by setting/checking ochange
- * fields.
- */
-static int
-swap_table_set(struct namedobj_instance *ni, struct named_object *no,
-    void *arg)
-{
-	struct table_config *tc;
-	struct swap_table_args *sta;
-
-	tc = (struct table_config *)no;
-	sta = (struct swap_table_args *)arg;
-
-	if (no->set != sta->set && (no->set != sta->new_set || sta->mv != 0))
-		return (0);
-
-	if (tc->ochanged != 0)
-		return (0);
-
-	tc->ochanged = 1;
-	ipfw_objhash_del(ni, no);
-	if (no->set == sta->set)
-		no->set = sta->new_set;
-	else
-		no->set = sta->set;
-	ipfw_objhash_add(ni, no);
-	return (0);
-}
-
-/*
- * Cleans up ochange field for all tables.
- */
-static int
-clean_table_set_data(struct namedobj_instance *ni, struct named_object *no,
-    void *arg)
-{
-	struct table_config *tc;
-	struct swap_table_args *sta;
-
-	tc = (struct table_config *)no;
-	sta = (struct swap_table_args *)arg;
-
-	tc->ochanged = 0;
-	return (0);
-}
-
-/*
- * Swaps tables within two sets.
- */
-void
-ipfw_swap_tables_sets(struct ip_fw_chain *ch, uint32_t set,
-    uint32_t new_set, int mv)
-{
-	struct swap_table_args sta;
-
-	IPFW_UH_WLOCK_ASSERT(ch);
-
-	sta.set = set;
-	sta.new_set = new_set;
-	sta.mv = mv;
-
-	ipfw_objhash_foreach(CHAIN_TO_NI(ch), swap_table_set, &sta);
-	ipfw_objhash_foreach(CHAIN_TO_NI(ch), clean_table_set_data, &sta);
-}
-
-/*
- * Move all tables which are reference by rules in @rr to set @new_set.
- * Makes sure that all relevant tables are referenced ONLLY by given rules.
- *
- * Returns 0 on success,
- */
-int
-ipfw_move_tables_sets(struct ip_fw_chain *ch, ipfw_range_tlv *rt,
-    uint32_t new_set)
-{
-	struct ip_fw *rule;
-	struct table_config *tc;
-	struct named_object *no;
-	struct namedobj_instance *ni;
-	int bad, i, l, cmdlen;
-	uint16_t kidx;
-	ipfw_insn *cmd;
-
-	IPFW_UH_WLOCK_ASSERT(ch);
-
-	ni = CHAIN_TO_NI(ch);
-
-	/* Stage 1: count number of references by given rules */
-	for (i = 0; i < ch->n_rules - 1; i++) {
-		rule = ch->map[i];
-		if (ipfw_match_range(rule, rt) == 0)
-			continue;
-
-		l = rule->cmd_len;
-		cmd = rule->cmd;
-		cmdlen = 0;
-		for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
-			cmdlen = F_LEN(cmd);
-			if (classify_opcode_kidx(cmd, &kidx) != 0)
-				continue;
-			no = ipfw_objhash_lookup_kidx(ni, kidx);
-			KASSERT(no != NULL, 
-			    ("objhash lookup failed on index %d", kidx));
-			tc = (struct table_config *)no;
-			tc->ocount++;
-		}
-
-	}
-
-	/* Stage 2: verify "ownership" */
-	bad = 0;
-	for (i = 0; i < ch->n_rules - 1; i++) {
-		rule = ch->map[i];
-		if (ipfw_match_range(rule, rt) == 0)
-			continue;
-
-		l = rule->cmd_len;
-		cmd = rule->cmd;
-		cmdlen = 0;
-		for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
-			cmdlen = F_LEN(cmd);
-			if (classify_opcode_kidx(cmd, &kidx) != 0)
-				continue;
-			no = ipfw_objhash_lookup_kidx(ni, kidx);
-			KASSERT(no != NULL, 
-			    ("objhash lookup failed on index %d", kidx));
-			tc = (struct table_config *)no;
-			if (tc->no.refcnt != tc->ocount) {
-
-				/*
-				 * Number of references differ:
-				 * Other rule(s) are holding reference to given
-				 * table, so it is not possible to change its set.
-				 *
-				 * Note that refcnt may account
-				 * references to some going-to-be-added rules.
-				 * Since we don't know their numbers (and event
-				 * if they will be added) it is perfectly OK
-				 * to return error here.
-				 */
-				bad = 1;
-				break;
-			}
-		}
-
-		if (bad != 0)
-			break;
-	}
-
-	/* Stage 3: change set or cleanup */
-	for (i = 0; i < ch->n_rules - 1; i++) {
-		rule = ch->map[i];
-		if (ipfw_match_range(rule, rt) == 0)
-			continue;
-
-		l = rule->cmd_len;
-		cmd = rule->cmd;
-		cmdlen = 0;
-		for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
-			cmdlen = F_LEN(cmd);
-			if (classify_opcode_kidx(cmd, &kidx) != 0)
-				continue;
-			no = ipfw_objhash_lookup_kidx(ni, kidx);
-			KASSERT(no != NULL, 
-			    ("objhash lookup failed on index %d", kidx));
-			tc = (struct table_config *)no;
-
-			tc->ocount = 0;
-			if (bad != 0)
-				continue;
-
-			/* Actually change set. */
-			ipfw_objhash_del(ni, no);
-			no->set = new_set;
-			ipfw_objhash_add(ni, no);

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


More information about the svn-src-all mailing list