svn commit: r282070 - in head/sys: netinet netpfil/ipfw

Alexander V. Chernikov melifaro at FreeBSD.org
Mon Apr 27 08:29:41 UTC 2015


Author: melifaro
Date: Mon Apr 27 08:29:39 2015
New Revision: 282070
URL: https://svnweb.freebsd.org/changeset/base/282070

Log:
  Make rule table kernel-index rewriting support any kind of objects.
  
  Currently we have tables identified by their names in userland
  with internal kernel-assigned indices. This works the following way:
  
  When userland wishes to communicate with kernel to add or change rule(s),
  it makes indexed sorted array of table names
  (internally ipfw_obj_ntlv entries), and refer to indices in that
  array in rule manipulation.
  Prior to committing new rule to the ruleset kernel
  a) finds all referenced tables, bump their refcounts and change
   values inside the opcodes to be real kernel indices
  b) auto-creates all referenced but not existing tables and then
   do a) for them.
  
  Kernel does almost the same when exporting rules to userland:
   prepares array of used tables in all rules in range, and
   prepends it before the actual ruleset retaining actual in-kernel
   indexes for that.
  
  There is also special translation layer for legacy clients which is
  able to provide 'real' indices for table names (basically doing atoi()).
  
  While it is arguable that every subsystem really needs names instead of
  numbers, there are several things that should be noted:
  
  1) every non-singleton subsystem needs to store its runtime state
  somewhere inside ipfw chain (and be able to get it fast)
  2) we can't assume object numbers provided by humans will be dense.
  
  Existing nat implementation (O(n) access and LIST inside chain) is a
  good example.
  
  Hence the following:
  * Convert table-centric rewrite code to be more generic, callback-based
  * Move most of the code from ip_fw_table.c to ip_fw_sockopt.c
  * Provide abstract API to permit subsystems convert their objects
    between userland string identifier and in-kernel index.
    (See struct opcode_obj_rewrite) for more details
  * Create another per-chain index (in next commit) shared among all subsystems
  * Convert current NAT44 implementation to use new API, O(1) lookups,
   shared index and names instead of numbers (in next commit).
  
  Sponsored by:	Yandex LLC

Modified:
  head/sys/netinet/ip_fw.h
  head/sys/netpfil/ipfw/ip_fw2.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
  head/sys/netpfil/ipfw/ip_fw_table.h

Modified: head/sys/netinet/ip_fw.h
==============================================================================
--- head/sys/netinet/ip_fw.h	Mon Apr 27 08:21:16 2015	(r282069)
+++ head/sys/netinet/ip_fw.h	Mon Apr 27 08:29:39 2015	(r282070)
@@ -40,10 +40,12 @@
 #define	IPFW_MAX_SETS		32	/* Number of sets supported by ipfw*/
 
 /*
- * Default number of ipfw tables.
+ * Compat values for old clients
  */
+#ifndef	_KERNEL
 #define	IPFW_TABLES_MAX		65535
 #define	IPFW_TABLES_DEFAULT	128
+#endif
 
 /*
  * Most commands (queue, pipe, tag, untag, limit...) can have a 16-bit
@@ -963,7 +965,6 @@ typedef struct _ipfw_ta_info {
 	uint64_t	spare1;
 } ipfw_ta_info;
 
-#define	IPFW_OBJTYPE_TABLE	1
 typedef struct _ipfw_obj_header {
 	ip_fw3_opheader	opheader;	/* IP_FW3 opcode		*/
 	uint32_t	spare;

Modified: head/sys/netpfil/ipfw/ip_fw2.c
==============================================================================
--- head/sys/netpfil/ipfw/ip_fw2.c	Mon Apr 27 08:21:16 2015	(r282069)
+++ head/sys/netpfil/ipfw/ip_fw2.c	Mon Apr 27 08:29:39 2015	(r282070)
@@ -2762,6 +2762,10 @@ vnet_ipfw_init(const void *unused)
 	LIST_INIT(&chain->nat);
 #endif
 
+	/* Init shared services hash table */
+	ipfw_init_srv(chain);
+
+	ipfw_init_obj_rewriter();
 	ipfw_init_counters();
 	/* insert the default rule and create the initial map */
 	chain->n_rules = 1;
@@ -2860,9 +2864,11 @@ vnet_ipfw_uninit(const void *unused)
 	if (reap != NULL)
 		ipfw_reap_rules(reap);
 	vnet_ipfw_iface_destroy(chain);
+	ipfw_destroy_srv(chain);
 	IPFW_LOCK_DESTROY(chain);
 	ipfw_dyn_uninit(1);	/* free the remaining parts */
 	ipfw_destroy_counters();
+	ipfw_destroy_obj_rewriter();
 	return (0);
 }
 

Modified: head/sys/netpfil/ipfw/ip_fw_private.h
==============================================================================
--- head/sys/netpfil/ipfw/ip_fw_private.h	Mon Apr 27 08:21:16 2015	(r282069)
+++ head/sys/netpfil/ipfw/ip_fw_private.h	Mon Apr 27 08:29:39 2015	(r282070)
@@ -264,10 +264,10 @@ struct ip_fw_chain {
 	struct ip_fw	**map;		/* array of rule ptrs to ease lookup */
 	uint32_t	id;		/* ruleset id */
 	int		n_rules;	/* number of static rules */
-	LIST_HEAD(nat_list, cfg_nat) nat;       /* list of nat entries */
 	void		*tablestate;	/* runtime table info */
 	void		*valuestate;	/* runtime table value info */
 	int		*idxmap;	/* skipto array of rules */
+	void		**srvstate;	/* runtime service mappings */
 #if defined( __linux__ ) || defined( _WIN32 )
 	spinlock_t rwmtx;
 #else
@@ -275,10 +275,12 @@ struct ip_fw_chain {
 #endif
 	int		static_len;	/* total len of static rules (v0) */
 	uint32_t	gencnt;		/* NAT generation count */
+	LIST_HEAD(nat_list, cfg_nat) nat;       /* list of nat entries */
 	struct ip_fw	*default_rule;
 	struct tables_config *tblcfg;	/* tables module data */
 	void		*ifcfg;		/* interface module data */
 	int		*idxmap_back;	/* standby skipto array of rules */
+	struct namedobj_instance	*srvmap; /* cfg name->number mappings */
 #if defined( __linux__ ) || defined( _WIN32 )
 	spinlock_t uh_lock;
 #else
@@ -306,16 +308,15 @@ struct table_value {
 	uint64_t	refcnt;		/* Number of references */
 };
 
-struct namedobj_instance;
 
 struct named_object {
 	TAILQ_ENTRY(named_object)	nn_next;	/* namehash */
 	TAILQ_ENTRY(named_object)	nv_next;	/* valuehash */
 	char			*name;	/* object name */
-	uint8_t			type;	/* object type */
-	uint8_t			compat;	/* Object name is number */
+	uint8_t			subtype;	/* object subtype within class */
+	uint8_t			etlv;	/* Export TLV id */
+	uint16_t		spare[2];
 	uint16_t		kidx;	/* object kernel index */
-	uint16_t		uidx;	/* userland idx for compat records */
 	uint32_t		set;	/* set object belongs to */
 	uint32_t		refcnt;	/* number of references */
 };
@@ -450,7 +451,7 @@ struct obj_idx {
 
 struct rule_check_info {
 	uint16_t	flags;		/* rule-specific check flags */
-	uint16_t	table_opcodes;	/* count of opcodes referencing table */
+	uint16_t	object_opcodes;	/* num of opcodes referencing objects */
 	uint16_t	urule_numoff;	/* offset of rulenum in bytes */
 	uint8_t		version;	/* rule version */
 	uint8_t		spare;
@@ -507,6 +508,84 @@ struct ip_fw_bcounter0 {
     (r)->cmd_len * 4 - 4, 8))
 #define	RULEKSIZE1(r)	roundup2((sizeof(struct ip_fw) + (r)->cmd_len*4 - 4), 8)
 
+/*
+ * Tables/Objects index rewriting code
+ */
+
+/* Default and maximum number of ipfw tables/objects. */
+#define	IPFW_TABLES_MAX		65536
+#define	IPFW_TABLES_DEFAULT	128
+#define	IPFW_OBJECTS_MAX	65536
+#define	IPFW_OBJECTS_DEFAULT	128
+
+#define	CHAIN_TO_SRV(ch)	((ch)->srvmap)
+
+struct tid_info {
+	uint32_t	set;	/* table set */
+	uint16_t	uidx;	/* table index */
+	uint8_t		type;	/* table type */
+	uint8_t		atype;
+	uint8_t		spare;
+	int		tlen;	/* Total TLV size block */
+	void		*tlvs;	/* Pointer to first TLV */
+};
+
+/*
+ * Classifier callback. Checks if @cmd opcode contains kernel object reference.
+ * If true, returns its index and type.
+ * Returns 0 if match is found, 1 overwise.
+ */
+typedef int (ipfw_obj_rw_cl)(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype);
+/*
+ * Updater callback. Sets kernel object reference index to @puidx
+ */
+typedef void (ipfw_obj_rw_upd)(ipfw_insn *cmd, uint16_t puidx);
+/*
+ * Finder callback. Tries to find named object by name (specified via @ti).
+ * Stores found named object pointer in @pno.
+ * If object was not found, NULL is stored.
+ *
+ * Return 0 if input data was valid.
+ */
+typedef int (ipfw_obj_fname_cb)(struct ip_fw_chain *ch,
+    struct tid_info *ti, struct named_object **pno);
+/*
+ * Another finder callback. Tries to findex named object by kernel index.
+ *
+ * Returns pointer to named object or NULL.
+ */
+typedef struct named_object *(ipfw_obj_fidx_cb)(struct ip_fw_chain *ch,
+    uint16_t kidx);
+/*
+ * Object creator callback. Tries to create object specified by @ti.
+ * Stores newly-allocated object index in @pkidx.
+ *
+ * Returns 0 on success.
+ */
+typedef int (ipfw_obj_create_cb)(struct ip_fw_chain *ch, struct tid_info *ti,
+    uint16_t *pkidx);
+
+
+struct opcode_obj_rewrite {
+	uint32_t		opcode;		/* Opcode to act upon */
+	uint32_t		etlv;		/* Relevant export TLV id  */
+	ipfw_obj_rw_cl		*classifier;	/* Check if rewrite is needed */
+	ipfw_obj_rw_upd		*update;	/* update cmd with new value */
+	ipfw_obj_fname_cb	*find_byname;	/* Find named object by name */
+	ipfw_obj_fidx_cb	*find_bykidx;	/* Find named object by kidx */
+	ipfw_obj_create_cb	*create_object;	/* Create named object */
+};
+
+#define	IPFW_ADD_OBJ_REWRITER(f, c)	do {	\
+	if ((f) != 0) 				\
+		ipfw_add_obj_rewriter(c,	\
+		    sizeof(c) / sizeof(c[0]));	\
+	} while(0)
+#define	IPFW_DEL_OBJ_REWRITER(l, c)	do {	\
+	if ((l) != 0) 				\
+		ipfw_del_obj_rewriter(c,	\
+		    sizeof(c) / sizeof(c[0]));	\
+	} while(0)
 
 /* In ip_fw_iface.c */
 int ipfw_iface_init(void);
@@ -562,6 +641,7 @@ caddr_t ipfw_get_sopt_header(struct sock
 		    sizeof(c) / sizeof(c[0]));	\
 	} while(0)
 
+struct namedobj_instance;
 typedef void (objhash_cb_t)(struct namedobj_instance *ni, struct named_object *,
     void *arg);
 typedef uint32_t (objhash_hash_f)(struct namedobj_instance *ni, void *key,
@@ -578,6 +658,8 @@ void ipfw_objhash_bitmap_free(void *idx,
 void ipfw_objhash_set_hashf(struct namedobj_instance *ni, objhash_hash_f *f);
 struct named_object *ipfw_objhash_lookup_name(struct namedobj_instance *ni,
     uint32_t set, char *name);
+struct named_object *ipfw_objhash_lookup_name_type(struct namedobj_instance *ni,
+    uint32_t set, uint32_t type, char *name);
 struct named_object *ipfw_objhash_lookup_kidx(struct namedobj_instance *ni,
     uint16_t idx);
 int ipfw_objhash_same_name(struct namedobj_instance *ni, struct named_object *a,
@@ -591,6 +673,25 @@ int ipfw_objhash_free_idx(struct namedob
 int ipfw_objhash_alloc_idx(void *n, uint16_t *pidx);
 void ipfw_objhash_set_funcs(struct namedobj_instance *ni,
     objhash_hash_f *hash_f, objhash_cmp_f *cmp_f);
+void ipfw_init_obj_rewriter(void);
+void ipfw_destroy_obj_rewriter(void);
+void ipfw_add_obj_rewriter(struct opcode_obj_rewrite *rw, size_t count);
+int ipfw_del_obj_rewriter(struct opcode_obj_rewrite *rw, size_t count);
+
+int ipfw_rewrite_rule_uidx(struct ip_fw_chain *chain,
+    struct rule_check_info *ci);
+int ipfw_mark_object_kidx(struct ip_fw_chain *chain, struct ip_fw *rule,
+    uint32_t *bmask);
+int ref_opcode_object(struct ip_fw_chain *ch, ipfw_insn *cmd, struct tid_info *ti,
+    struct obj_idx *pidx, int *found, int *unresolved);
+void unref_oib_objects(struct ip_fw_chain *ch, ipfw_insn *cmd,
+    struct obj_idx *oib, struct obj_idx *end);
+int create_objects_compat(struct ip_fw_chain *ch, ipfw_insn *cmd,
+    struct obj_idx *oib, struct obj_idx *pidx, struct tid_info *ti);
+void update_opcode_kidx(ipfw_insn *cmd, uint16_t idx);
+int classify_opcode_kidx(ipfw_insn *cmd, uint16_t *puidx);
+void ipfw_init_srv(struct ip_fw_chain *ch);
+void ipfw_destroy_srv(struct ip_fw_chain *ch);
 
 /* In ip_fw_table.c */
 struct table_info;

Modified: head/sys/netpfil/ipfw/ip_fw_sockopt.c
==============================================================================
--- head/sys/netpfil/ipfw/ip_fw_sockopt.c	Mon Apr 27 08:21:16 2015	(r282069)
+++ head/sys/netpfil/ipfw/ip_fw_sockopt.c	Mon Apr 27 08:29:39 2015	(r282070)
@@ -148,6 +148,21 @@ static struct ipfw_sopt_handler	scodes[]
 	{ IP_FW_DUMP_SOPTCODES,	0,	HDIR_GET,	dump_soptcodes },
 };
 
+static int
+set_legacy_obj_kidx(struct ip_fw_chain *ch, struct ip_fw_rule0 *rule);
+struct opcode_obj_rewrite *ipfw_find_op_rw(uint16_t opcode);
+static int mark_object_kidx(struct ip_fw_chain *ch, struct ip_fw *rule,
+    uint32_t *bmask);
+static void unref_rule_objects(struct ip_fw_chain *chain, struct ip_fw *rule);
+static int export_objhash_ntlv(struct namedobj_instance *ni, uint16_t kidx,
+    struct sockopt_data *sd);
+
+/*
+ * Opcode object rewriter variables
+ */
+struct opcode_obj_rewrite *ctl3_rewriters;
+static size_t ctl3_rsize;
+
 /*
  * static variables followed by global ones
  */
@@ -646,17 +661,18 @@ commit_rules(struct ip_fw_chain *chain, 
 	struct ip_fw *krule;
 	struct ip_fw **map;	/* the new array of pointers */
 
-	/* Check if we need to do table remap */
+	/* Check if we need to do table/obj index remap */
 	tcount = 0;
 	for (ci = rci, i = 0; i < count; ci++, i++) {
-		if (ci->table_opcodes == 0)
+		if (ci->object_opcodes == 0)
 			continue;
 
 		/*
-		 * Rule has some table opcodes.
-		 * Reference & allocate needed tables/
+		 * Rule has some object opcodes.
+		 * We need to find (and create non-existing)
+		 * kernel objects, and reference existing ones.
 		 */
-		error = ipfw_rewrite_table_uidx(chain, ci);
+		error = ipfw_rewrite_rule_uidx(chain, ci);
 		if (error != 0) {
 
 			/*
@@ -674,9 +690,9 @@ commit_rules(struct ip_fw_chain *chain, 
 				IPFW_UH_WLOCK(chain);
 				while (ci != rci) {
 					ci--;
-					if (ci->table_opcodes == 0)
+					if (ci->object_opcodes == 0)
 						continue;
-					ipfw_unref_rule_tables(chain,ci->krule);
+					unref_rule_objects(chain,ci->krule);
 
 				}
 				IPFW_UH_WUNLOCK(chain);
@@ -696,10 +712,10 @@ commit_rules(struct ip_fw_chain *chain, 
 			/* Unbind tables */
 			IPFW_UH_WLOCK(chain);
 			for (ci = rci, i = 0; i < count; ci++, i++) {
-				if (ci->table_opcodes == 0)
+				if (ci->object_opcodes == 0)
 					continue;
 
-				ipfw_unref_rule_tables(chain, ci->krule);
+				unref_rule_objects(chain, ci->krule);
 			}
 			IPFW_UH_WUNLOCK(chain);
 		}
@@ -759,7 +775,7 @@ ipfw_reap_add(struct ip_fw_chain *chain,
 	IPFW_UH_WLOCK_ASSERT(chain);
 
 	/* Unlink rule from everywhere */
-	ipfw_unref_rule_tables(chain, rule);
+	unref_rule_objects(chain, rule);
 
 	*((struct ip_fw **)rule) = *head;
 	*head = rule;
@@ -1542,7 +1558,7 @@ check_ipfw_rule_body(ipfw_insn *cmd, int
 			    cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 1 &&
 			    cmdlen != F_INSN_SIZE(ipfw_insn_u32))
 				goto bad_size;
-			ci->table_opcodes++;
+			ci->object_opcodes++;
 			break;
 		case O_IP_FLOW_LOOKUP:
 			if (cmd->arg1 >= V_fw_tables_max) {
@@ -1553,7 +1569,7 @@ check_ipfw_rule_body(ipfw_insn *cmd, int
 			if (cmdlen != F_INSN_SIZE(ipfw_insn) &&
 			    cmdlen != F_INSN_SIZE(ipfw_insn_u32))
 				goto bad_size;
-			ci->table_opcodes++;
+			ci->object_opcodes++;
 			break;
 		case O_MACADDR2:
 			if (cmdlen != F_INSN_SIZE(ipfw_insn_mac))
@@ -1587,7 +1603,7 @@ check_ipfw_rule_body(ipfw_insn *cmd, int
 		case O_XMIT:
 		case O_VIA:
 			if (((ipfw_insn_if *)cmd)->name[0] == '\1')
-				ci->table_opcodes++;
+				ci->object_opcodes++;
 			if (cmdlen != F_INSN_SIZE(ipfw_insn_if))
 				goto bad_size;
 			break;
@@ -1631,6 +1647,7 @@ check_ipfw_rule_body(ipfw_insn *cmd, int
 				return EINVAL;
 			if (cmdlen != F_INSN_SIZE(ipfw_insn_nat))
  				goto bad_size;		
+			ci->object_opcodes++;
  			goto check_action;
 		case O_FORWARD_MAC: /* XXX not implemented yet */
 		case O_CHECK_STATE:
@@ -1788,7 +1805,7 @@ ipfw_getrules(struct ip_fw_chain *chain,
 		    l = RULESIZE7(rule);
 		    if (bp + l + sizeof(uint32_t) <= ep) {
 			bcopy(rule, bp, l + sizeof(uint32_t));
-			error = ipfw_rewrite_table_kidx(chain,
+			error = set_legacy_obj_kidx(chain,
 			    (struct ip_fw_rule0 *)bp);
 			if (error != 0)
 				return (0);
@@ -1817,7 +1834,7 @@ ipfw_getrules(struct ip_fw_chain *chain,
 		}
 		dst = (struct ip_fw_rule0 *)bp;
 		export_rule0(rule, dst, l);
-		error = ipfw_rewrite_table_kidx(chain, dst);
+		error = set_legacy_obj_kidx(chain, dst);
 
 		/*
 		 * XXX HACK. Store the disable mask in the "next"
@@ -1861,6 +1878,34 @@ struct dump_args {
 };
 
 /*
+ * Export named object info in instance @ni, identified by @kidx
+ * to ipfw_obj_ntlv. TLV is allocated from @sd space.
+ *
+ * Returns 0 on success.
+ */
+static int
+export_objhash_ntlv(struct namedobj_instance *ni, uint16_t kidx,
+    struct sockopt_data *sd)
+{
+	struct named_object *no;
+	ipfw_obj_ntlv *ntlv;
+
+	no = ipfw_objhash_lookup_kidx(ni, kidx);
+	KASSERT(no != NULL, ("invalid object kernel index passed"));
+
+	ntlv = (ipfw_obj_ntlv *)ipfw_get_sopt_space(sd, sizeof(*ntlv));
+	if (ntlv == NULL)
+		return (ENOMEM);
+
+	ntlv->head.type = no->etlv;
+	ntlv->head.length = sizeof(*ntlv);
+	ntlv->idx = no->kidx;
+	strlcpy(ntlv->name, no->name, sizeof(ntlv->name));
+
+	return (0);
+}
+
+/*
  * Dumps static rules with table TLVs in buffer @sd.
  *
  * Returns 0 on success.
@@ -1874,6 +1919,7 @@ dump_static_rules(struct ip_fw_chain *ch
 	uint32_t tcount;
 	ipfw_obj_ctlv *ctlv;
 	struct ip_fw *krule;
+	struct namedobj_instance *ni;
 	caddr_t dst;
 
 	/* Dump table names first (if any) */
@@ -1891,13 +1937,21 @@ dump_static_rules(struct ip_fw_chain *ch
 
 	i = 0;
 	tcount = da->tcount;
+	ni = ipfw_get_table_objhash(chain);
 	while (tcount > 0) {
 		if ((bmask[i / 32] & (1 << (i % 32))) == 0) {
 			i++;
 			continue;
 		}
 
-		if ((error = ipfw_export_table_ntlv(chain, i, sd)) != 0)
+		/* Jump to shared named object bitmask */
+		if (i >= IPFW_TABLES_MAX) {
+			ni = CHAIN_TO_SRV(chain);
+			i -= IPFW_TABLES_MAX;
+			bmask += IPFW_TABLES_MAX / 32;
+		}
+
+		if ((error = export_objhash_ntlv(ni, i, sd)) != 0)
 			return (error);
 
 		i++;
@@ -1929,6 +1983,52 @@ dump_static_rules(struct ip_fw_chain *ch
 }
 
 /*
+ * Marks every object index used in @rule with bit in @bmask.
+ * Used to generate bitmask of referenced tables/objects for given ruleset
+ * or its part.
+ *
+ * Returns number of newly-referenced objects.
+ */
+static int
+mark_object_kidx(struct ip_fw_chain *ch, struct ip_fw *rule,
+    uint32_t *bmask)
+{
+	int cmdlen, l, count;
+	ipfw_insn *cmd;
+	uint16_t kidx;
+	struct opcode_obj_rewrite *rw;
+	int bidx;
+	uint8_t subtype;
+
+	l = rule->cmd_len;
+	cmd = rule->cmd;
+	cmdlen = 0;
+	count = 0;
+	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
+		cmdlen = F_LEN(cmd);
+
+		rw = ipfw_find_op_rw(cmd->opcode);
+		if (rw == NULL)
+			continue;
+
+		if (rw->classifier(cmd, &kidx, &subtype) != 0)
+			continue;
+
+		bidx = kidx / 32;
+		/* Maintain separate bitmasks for table and non-table objects */
+		if (rw->etlv != IPFW_TLV_TBL_NAME)
+			bidx += IPFW_TABLES_MAX / 32;
+
+		if ((bmask[bidx] & (1 << (kidx % 32))) == 0)
+			count++;
+
+		bmask[bidx] |= 1 << (kidx % 32);
+	}
+
+	return (count);
+}
+
+/*
  * Dumps requested objects data
  * Data layout (version 0)(current):
  * Request: [ ipfw_cfg_lheader ] + IPFW_CFG_GET_* flags
@@ -1963,9 +2063,9 @@ dump_config(struct ip_fw_chain *chain, i
 
 	error = 0;
 	bmask = NULL;
-	/* Allocate needed state */
+	/* Allocate needed state. Note we allocate 2xspace mask, for table&srv  */
 	if (hdr->flags & IPFW_CFG_GET_STATIC)
-		bmask = malloc(IPFW_TABLES_MAX / 8, M_TEMP, M_WAITOK | M_ZERO);
+		bmask = malloc(IPFW_TABLES_MAX / 4, M_TEMP, M_WAITOK | M_ZERO);
 
 	IPFW_UH_RLOCK(chain);
 
@@ -1994,7 +2094,8 @@ dump_config(struct ip_fw_chain *chain, i
 			rule = chain->map[i];
 			da.rsize += RULEUSIZE1(rule) + sizeof(ipfw_obj_tlv);
 			da.rcount++;
-			da.tcount += ipfw_mark_table_kidx(chain, rule, bmask);
+			/* Update bitmask of used objects for given range */
+			da.tcount += mark_object_kidx(chain, rule, bmask);
 		}
 		/* Add counters if requested */
 		if (hdr->flags & IPFW_CFG_GET_COUNTERS) {
@@ -2064,6 +2165,241 @@ check_object_name(ipfw_obj_ntlv *ntlv)
 }
 
 /*
+ * Creates non-existent objects referenced by rule.
+ *
+ * Return 0 on success.
+ */
+int
+create_objects_compat(struct ip_fw_chain *ch, ipfw_insn *cmd,
+    struct obj_idx *oib, struct obj_idx *pidx, struct tid_info *ti)
+{
+	struct opcode_obj_rewrite *rw;
+	struct obj_idx *p;
+	uint16_t kidx;
+	int error;
+
+	/*
+	 * Compatibility stuff: do actual creation for non-existing,
+	 * but referenced objects.
+	 */
+	for (p = oib; p < pidx; p++) {
+		if (p->kidx != 0)
+			continue;
+
+		ti->uidx = p->uidx;
+		ti->type = p->type;
+		ti->atype = 0;
+
+		rw = ipfw_find_op_rw((cmd + p->off)->opcode);
+		KASSERT(rw != NULL, ("Unable to find handler for op %d",
+		    (cmd + p->off)->opcode));
+
+		error = rw->create_object(ch, ti, &kidx);
+		if (error == 0) {
+			p->kidx = kidx;
+			continue;
+		}
+
+		/*
+		 * Error happened. We have to rollback everything.
+		 * Drop all already acquired references.
+		 */
+		IPFW_UH_WLOCK(ch);
+		unref_oib_objects(ch, cmd, oib, pidx);
+		IPFW_UH_WUNLOCK(ch);
+
+		return (error);
+	}
+
+	return (error);
+}
+
+/*
+ * Compatibility function for old ipfw(8) binaries.
+ * Rewrites table/nat kernel indices with userland ones.
+ * Convert tables matching '/^\d+$/' to their atoi() value.
+ * Use number 65535 for other tables.
+ *
+ * Returns 0 on success.
+ */
+static int
+set_legacy_obj_kidx(struct ip_fw_chain *ch, struct ip_fw_rule0 *rule)
+{
+	int cmdlen, error, l;
+	ipfw_insn *cmd;
+	uint16_t kidx, uidx;
+	struct named_object *no;
+	struct opcode_obj_rewrite *rw;
+	uint8_t subtype;
+	char *end;
+	long val;
+
+	error = 0;
+
+	l = rule->cmd_len;
+	cmd = rule->cmd;
+	cmdlen = 0;
+	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
+		cmdlen = F_LEN(cmd);
+
+		rw = ipfw_find_op_rw(cmd->opcode);
+		if (rw == NULL)
+			continue;
+
+		/* Check if is index in given opcode */
+		if (rw->classifier(cmd, &kidx, &subtype) != 0)
+			continue;
+
+		/* Try to find referenced kernel object */
+		no = rw->find_bykidx(ch, kidx);
+		if (no == NULL)
+			continue;
+
+		val = strtol(no->name, &end, 10);
+		if (*end == '\0' && val < 65535) {
+			uidx = val;
+		} else {
+
+			/*
+			 * We are called via legacy opcode.
+			 * Save error and show table as fake number
+			 * not to make ipfw(8) hang.
+			 */
+			uidx = 65535;
+			error = 2;
+		}
+
+		rw->update(cmd, uidx);
+	}
+
+	return (error);
+}
+
+
+/*
+ * Unreferences all already-referenced objects in given @cmd rule,
+ * using information in @oib.
+ *
+ * Used to rollback partially converted rule on error.
+ */
+void
+unref_oib_objects(struct ip_fw_chain *ch, ipfw_insn *cmd, struct obj_idx *oib,
+    struct obj_idx *end)
+{
+	struct opcode_obj_rewrite *rw;
+	struct named_object *no;
+	struct obj_idx *p;
+
+	IPFW_UH_WLOCK_ASSERT(ch);
+
+	for (p = oib; p < end; p++) {
+		if (p->kidx == 0)
+			continue;
+
+		rw = ipfw_find_op_rw((cmd + p->off)->opcode);
+		KASSERT(rw != NULL, ("Unable to find handler for op %d",
+		    (cmd + p->off)->opcode));
+
+		/* Find & unref by existing idx */
+		no = rw->find_bykidx(ch, p->kidx);
+		KASSERT(no != NULL, ("Ref'd object %d disappeared", p->kidx));
+		no->refcnt--;
+	}
+}
+
+/*
+ * Remove references from every object used in @rule.
+ * Used at rule removal code.
+ */
+static void
+unref_rule_objects(struct ip_fw_chain *ch, struct ip_fw *rule)
+{
+	int cmdlen, l;
+	ipfw_insn *cmd;
+	struct named_object *no;
+	uint16_t kidx;
+	struct opcode_obj_rewrite *rw;
+	uint8_t subtype;
+
+	IPFW_UH_WLOCK_ASSERT(ch);
+
+	l = rule->cmd_len;
+	cmd = rule->cmd;
+	cmdlen = 0;
+	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
+		cmdlen = F_LEN(cmd);
+
+		rw = ipfw_find_op_rw(cmd->opcode);
+		if (rw == NULL)
+			continue;
+		if (rw->classifier(cmd, &kidx, &subtype) != 0)
+			continue;
+
+		no = rw->find_bykidx(ch, kidx);
+
+		KASSERT(no != NULL, ("table id %d not found", kidx));
+		KASSERT(no->subtype == subtype,
+		    ("wrong type %d (%d) for table id %d",
+		    no->subtype, subtype, kidx));
+		KASSERT(no->refcnt > 0, ("refcount for table %d is %d",
+		    kidx, no->refcnt));
+
+		no->refcnt--;
+	}
+}
+
+
+/*
+ * Find and reference object (if any) stored in instruction @cmd.
+ *
+ * Saves object info in @pidx, sets
+ *  - @found to 1 if object was found and references
+ *  - @unresolved to 1 if object should exists but not found
+ *
+ * Returns non-zero value in case of error.
+ */
+int
+ref_opcode_object(struct ip_fw_chain *ch, ipfw_insn *cmd, struct tid_info *ti,
+    struct obj_idx *pidx, int *found, int *unresolved)
+{
+	struct named_object *no;
+	struct opcode_obj_rewrite *rw;
+	int error;
+
+	*found = 0;
+	*unresolved = 0;
+
+	/* Check if this opcode is candidate for rewrite */
+	rw = ipfw_find_op_rw(cmd->opcode);
+	if (rw == NULL)
+		return (0);
+
+	/* Check if we need to rewrite this opcode */
+	if (rw->classifier(cmd, &ti->uidx, &ti->type) != 0)
+		return (0);
+
+	/* Need to rewrite. Save necessary fields */
+	pidx->uidx = ti->uidx;
+	pidx->type = ti->type;
+
+	/* Try to find referenced kernel object */
+	error = rw->find_byname(ch, ti, &no);
+	if (error != 0)
+		return (error);
+	if (no == NULL) {
+		*unresolved = 1;
+		return (0);
+	}
+
+	/* Found. bump refcount */
+	*found = 1;
+	no->refcnt++;
+	pidx->kidx = no->kidx;
+
+	return (0);
+}
+
+/*
  * Adds one or more rules to ipfw @chain.
  * Data layout (version 0)(current):
  * Request:
@@ -2315,6 +2651,160 @@ dump_soptcodes(struct ip_fw_chain *chain
 }
 
 /*
+ * Compares two opcodes.
+ * Used both in qsort() and bsearch().
+ *
+ * Returns 0 if match is found.
+ */
+static int
+compare_opcodes(const void *_a, const void *_b)
+{
+	const struct opcode_obj_rewrite *a, *b;
+
+	a = (const struct opcode_obj_rewrite *)_a;
+	b = (const struct opcode_obj_rewrite *)_b;
+
+	if (a->opcode < b->opcode)
+		return (-1);
+	else if (a->opcode > b->opcode)
+		return (1);
+
+	return (0);
+}
+
+/*
+ * Finds opcode object rewriter based on @code.
+ *
+ * Returns pointer to handler or NULL.
+ */
+struct opcode_obj_rewrite *
+ipfw_find_op_rw(uint16_t opcode)
+{
+	struct opcode_obj_rewrite *rw, h;
+
+	memset(&h, 0, sizeof(h));
+	h.opcode = opcode;
+
+	rw = (struct opcode_obj_rewrite *)bsearch(&h, ctl3_rewriters,
+	    ctl3_rsize, sizeof(h), compare_opcodes);
+
+	return (rw);
+}
+
+int
+classify_opcode_kidx(ipfw_insn *cmd, uint16_t *puidx)
+{
+	struct opcode_obj_rewrite *rw;
+	uint8_t subtype;
+
+	rw = ipfw_find_op_rw(cmd->opcode);
+	if (rw == NULL)
+		return (1);
+
+	return (rw->classifier(cmd, puidx, &subtype));
+}
+
+void
+update_opcode_kidx(ipfw_insn *cmd, uint16_t idx)
+{
+	struct opcode_obj_rewrite *rw;
+
+	rw = ipfw_find_op_rw(cmd->opcode);
+	KASSERT(rw != NULL, ("No handler to update opcode %d", cmd->opcode));
+	rw->update(cmd, idx);
+}
+
+void
+ipfw_init_obj_rewriter()
+{
+
+	ctl3_rewriters = NULL;
+	ctl3_rsize = 0;
+}
+
+void
+ipfw_destroy_obj_rewriter()
+{
+
+	if (ctl3_rewriters != NULL)
+		free(ctl3_rewriters, M_IPFW);
+	ctl3_rewriters = NULL;
+	ctl3_rsize = 0;
+}
+
+/*
+ * Adds one or more opcode object rewrite handlers to the global array.
+ * Function may sleep.
+ */
+void
+ipfw_add_obj_rewriter(struct opcode_obj_rewrite *rw, size_t count)
+{
+	size_t sz;
+	struct opcode_obj_rewrite *tmp;
+
+	CTL3_LOCK();
+
+	for (;;) {
+		sz = ctl3_rsize + count;
+		CTL3_UNLOCK();
+		tmp = malloc(sizeof(*rw) * sz, M_IPFW, M_WAITOK | M_ZERO);
+		CTL3_LOCK();
+		if (ctl3_rsize + count <= sz)
+			break;
+
+		/* Retry */
+		free(tmp, M_IPFW);
+	}
+
+	/* Merge old & new arrays */
+	sz = ctl3_rsize + count;
+	memcpy(tmp, ctl3_rewriters, ctl3_rsize * sizeof(*rw));
+	memcpy(&tmp[ctl3_rsize], rw, count * sizeof(*rw));
+	qsort(tmp, sz, sizeof(*rw), compare_opcodes);
+	/* Switch new and free old */
+	if (ctl3_rewriters != NULL)
+		free(ctl3_rewriters, M_IPFW);
+	ctl3_rewriters = tmp;
+	ctl3_rsize = sz;
+
+	CTL3_UNLOCK();
+}
+
+/*
+ * Removes one or more object rewrite handlers from the global array.
+ */
+int
+ipfw_del_obj_rewriter(struct opcode_obj_rewrite *rw, size_t count)
+{
+	size_t sz;
+	struct opcode_obj_rewrite *tmp, *h;
+	int i;
+
+	CTL3_LOCK();
+
+	for (i = 0; i < count; i++) {
+		tmp = &rw[i];
+		h = ipfw_find_op_rw(tmp->opcode);
+		if (h == NULL)
+			continue;
+
+		sz = (ctl3_rewriters + ctl3_rsize - (h + 1)) * sizeof(*h);
+		memmove(h, h + 1, sz);
+		ctl3_rsize--;
+	}
+
+	if (ctl3_rsize == 0) {
+		if (ctl3_rewriters != NULL)
+			free(ctl3_rewriters, M_IPFW);
+		ctl3_rewriters = NULL;
+	}
+
+	CTL3_UNLOCK();
+
+	return (0);
+}
+
+/*
  * Compares two sopt handlers (code, version and handler ptr).
  * Used both as qsort() and bsearch().
  * Does not compare handler for latter case.
@@ -3150,6 +3640,23 @@ convert_rule_to_8(struct ip_fw_rule0 *ru
  *
  */
 
+void
+ipfw_init_srv(struct ip_fw_chain *ch)
+{
+
+	ch->srvmap = ipfw_objhash_create(IPFW_OBJECTS_DEFAULT);
+	ch->srvstate = malloc(sizeof(void *) * IPFW_OBJECTS_DEFAULT,
+	    M_IPFW, M_WAITOK | M_ZERO);
+}
+
+void
+ipfw_destroy_srv(struct ip_fw_chain *ch)
+{
+
+	free(ch->srvstate, M_IPFW);
+	ipfw_objhash_destroy(ch->srvmap);
+}
+
 /*
  * Allocate new bitmask which can be used to enlarge/shrink
  * named instance index.
@@ -3323,6 +3830,26 @@ ipfw_objhash_lookup_name(struct namedobj
 	return (NULL);
 }
 
+/*
+ * Find named object by name, considering also its TLV type.
+ */
+struct named_object *
+ipfw_objhash_lookup_name_type(struct namedobj_instance *ni, uint32_t set,
+    uint32_t type, char *name)
+{
+	struct named_object *no;
+	uint32_t hash;
+
+	hash = ni->hash_f(ni, name, set) % ni->nn_size;
+
+	TAILQ_FOREACH(no, &ni->names[hash], nn_next) {
+		if (ni->cmp_f(no, name, set) == 0 && no->etlv == type)
+			return (no);
+	}
+
+	return (NULL);
+}
+
 struct named_object *
 ipfw_objhash_lookup_kidx(struct namedobj_instance *ni, uint16_t kidx)
 {

Modified: head/sys/netpfil/ipfw/ip_fw_table.c
==============================================================================
--- head/sys/netpfil/ipfw/ip_fw_table.c	Mon Apr 27 08:21:16 2015	(r282069)
+++ head/sys/netpfil/ipfw/ip_fw_table.c	Mon Apr 27 08:29:39 2015	(r282070)
@@ -89,6 +89,8 @@ struct table_config {
 	struct namedobj_instance	*vi;
 };
 
+static int find_table_err(struct namedobj_instance *ni, struct tid_info *ti,
+    struct table_config **tc);
 static struct table_config *find_table(struct namedobj_instance *ni,
     struct tid_info *ti);
 static struct table_config *alloc_table_config(struct ip_fw_chain *ch,
@@ -122,7 +124,6 @@ static struct table_algo *find_table_alg
 
 static void objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti);
 static void ntlv_to_ti(struct _ipfw_obj_ntlv *ntlv, struct tid_info *ti);
-static int classify_table_opcode(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype);
 
 #define	CHAIN_TO_NI(chain)	(CHAIN_TO_TCFG(chain)->namehash)
 #define	KIDX_TO_TI(ch, k)	(&(((struct table_info *)(ch)->tablestate)[k]))
@@ -297,7 +298,7 @@ find_ref_table(struct ip_fw_chain *ch, s
 	tc = NULL;
 	if ((tc = find_table(ni, ti)) != NULL) {
 		/* check table type */
-		if (tc->no.type != ti->type)
+		if (tc->no.subtype != ti->type)
 			return (EINVAL);
 
 		if (tc->locked != 0)
@@ -1116,7 +1117,7 @@ find_table_entry(struct ip_fw_chain *ch,
 	}
 
 	/* check table type */
-	if (tc->no.type != ti.type) {
+	if (tc->no.subtype != ti.type) {
 		IPFW_UH_RUNLOCK(ch);
 		return (EINVAL);
 	}

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


More information about the svn-src-all mailing list