svn commit: r267954 - projects/ipfw/sys/netpfil/ipfw

Alexander V. Chernikov melifaro at FreeBSD.org
Fri Jun 27 10:07:01 UTC 2014


Author: melifaro
Date: Fri Jun 27 10:07:00 2014
New Revision: 267954
URL: http://svnweb.freebsd.org/changeset/base/267954

Log:
  Use different approach for filling large datasets to userspace:
  
  Instead of trying to allocate bing contiguous chunk of memory,
  use intermediate-sized (page size) buffer as sliding window
  reducing number of sooptcopyout() calls to perform.
  
  This reduces dump functions complexity and provides additional
  layer of abstraction.
  
  User-visible api consists of 2 functions:
  ipfw_get_sopt_space() - gets contigious amount of storage (or NULL)
  and
  ipfw_get_sopt_header() - the same, but zeroes the rest of the buffer.

Modified:
  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/sys/netpfil/ipfw/ip_fw_private.h
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_private.h	Fri Jun 27 09:11:24 2014	(r267953)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_private.h	Fri Jun 27 10:07:00 2014	(r267954)
@@ -241,6 +241,15 @@ struct ip_fw_chain {
 };
 
 struct sockopt;	/* used by tcp_var.h */
+struct sockopt_data {
+	caddr_t		kbuf;		/* allocated buffer */
+	size_t		ksize;		/* given buffer size */
+	size_t		koff;		/* data already used */
+	size_t		kavail;		/* number of bytes available */
+	size_t		ktotal;		/* total bytes pushed */
+	struct sockopt	*sopt;		/* socket data */
+	size_t		valsize;	/* original data size */
+};
 
 /* Macro for working with various counters */
 #define	IPFW_INC_RULE_COUNTER(_cntr, _bytes)	do {	\
@@ -333,6 +342,9 @@ int ipfw_ctl(struct sockopt *sopt);
 int ipfw_chk(struct ip_fw_args *args);
 void ipfw_reap_rules(struct ip_fw *head);
 
+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);
+
 struct namedobj_instance;
 
 struct named_object {

Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c	Fri Jun 27 09:11:24 2014	(r267953)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c	Fri Jun 27 10:07:00 2014	(r267954)
@@ -88,6 +88,7 @@ static uint32_t objhash_hash_name(struct
 static uint32_t objhash_hash_val(struct namedobj_instance *ni, uint32_t set,
     uint32_t val);
 
+static int ipfw_flush_sopt_data(struct sockopt_data *sd);
 
 MALLOC_DEFINE(M_IPFW, "IpFw/IpAcct", "IpFw/IpAcct chain's");
 
@@ -989,6 +990,7 @@ ipfw_getrules(struct ip_fw_chain *chain,
 
 
 #define IP_FW3_OPLENGTH(x)	((x)->sopt_valsize - sizeof(ip_fw3_opheader))
+#define	IP_FW3_OPTBUF	4096	/* page-size */
 /**
  * {set|get}sockopt parser.
  */
@@ -1002,7 +1004,8 @@ ipfw_ctl(struct sockopt *sopt)
 	struct ip_fw_chain *chain;
 	u_int32_t rulenum[2];
 	uint32_t opt;
-	char xbuf[256];
+	char xbuf[128];
+	struct sockopt_data sdata;
 	ip_fw3_opheader *op3 = NULL;
 	struct rule_check_info ci;
 
@@ -1026,15 +1029,40 @@ ipfw_ctl(struct sockopt *sopt)
 
 	/* Save original valsize before it is altered via sooptcopyin() */
 	valsize = sopt->sopt_valsize;
+	memset(&sdata, 0, sizeof(sdata));
 	if ((opt = sopt->sopt_name) == IP_FW3) {
-		/* 
-		 * Copy not less than sizeof(ip_fw3_opheader).
-		 * We hope any IP_FW3 command will fit into 128-byte buffer.
+		/*
+		 * Fill in sockopt_data structure that may be useful for
+		 * IP_FW3 get requests
 		 */
-		if ((error = sooptcopyin(sopt, xbuf, sizeof(xbuf),
-			sizeof(ip_fw3_opheader))) != 0)
+		if (valsize <= sizeof(xbuf)) {
+			sdata.kbuf = xbuf;
+			sdata.ksize = sizeof(xbuf);
+			sdata.kavail = valsize;
+		} else {
+			if (valsize < IP_FW3_OPTBUF)
+				size = valsize;
+			else
+				size = IP_FW3_OPTBUF;
+
+			sdata.kbuf = malloc(size, M_TEMP, M_WAITOK | M_ZERO);
+			sdata.ksize = size;
+			sdata.kavail = size;
+		}
+
+		sdata.sopt = sopt;
+		sdata.valsize = valsize;
+
+		/*
+		 * Copy either all request (if valsize < IP_FW3_OPTBUF)
+		 * or first IP_FW3_OPTBUF bytes to guarantee most consumers
+		 * that all necessary data has been copied).
+		 * Anyway, copy not less than sizeof(ip_fw3_opheader).
+		 */
+		if ((error = sooptcopyin(sopt, sdata.kbuf, sdata.ksize,
+		    sizeof(ip_fw3_opheader))) != 0)
 			return (error);
-		op3 = (ip_fw3_opheader *)xbuf;
+		op3 = (ip_fw3_opheader *)sdata.kbuf;
 		opt = op3->opcode;
 	}
 
@@ -1205,19 +1233,19 @@ ipfw_ctl(struct sockopt *sopt)
 		}
 
 	case IP_FW_TABLE_XINFO: /* IP_FW3 */
-		error = ipfw_describe_table(chain, sopt, op3, valsize);
+		error = ipfw_describe_table(chain, &sdata);
 		break;
 
 	case IP_FW_TABLES_XGETSIZE: /* IP_FW3 */
-		error = ipfw_listsize_tables(chain, sopt, op3, valsize);
+		error = ipfw_listsize_tables(chain, &sdata);
 		break;
 
 	case IP_FW_TABLES_XLIST: /* IP_FW3 */
-		error = ipfw_list_tables(chain, sopt, op3, valsize);
+		error = ipfw_list_tables(chain, &sdata);
 		break;
 
 	case IP_FW_TABLE_XLIST: /* IP_FW3 */
-		error = ipfw_dump_table(chain, sopt, op3, valsize);
+		error = ipfw_dump_table(chain, op3, &sdata);
 		break;
 
 	case IP_FW_TABLE_XADD: /* IP_FW3 */
@@ -1425,10 +1453,78 @@ ipfw_ctl(struct sockopt *sopt)
 		error = EINVAL;
 	}
 
+	if (op3 != NULL) {
+		/* Flush state and free buffers */
+		if (error == 0)
+			error = ipfw_flush_sopt_data(&sdata);
+		else
+			ipfw_flush_sopt_data(&sdata);
+
+		if (sdata.kbuf != xbuf)
+			free(sdata.kbuf, M_TEMP);
+	}
+
 	return (error);
 #undef RULE_MAXSIZE
 }
 
+static int
+ipfw_flush_sopt_data(struct sockopt_data *sd)
+{
+	int error;
+
+	if (sd->koff == 0)
+		return (0);
+
+	if ((error = sooptcopyout(sd->sopt, sd->kbuf, sd->koff)) != 0)
+		return (error);
+
+	memset(sd->kbuf, 0, sd->ksize);
+	sd->ktotal += sd->koff;
+	sd->koff = 0;
+	if (sd->ktotal + sd->ksize < sd->valsize)
+		sd->kavail = sd->ksize;
+	else
+		sd->kavail = sd->valsize - sd->ktotal;
+
+	return (0);
+}
+
+caddr_t
+ipfw_get_sopt_space(struct sockopt_data *sd, size_t needed)
+{
+	int error;
+	caddr_t addr;
+
+	if (sd->kavail < needed) {
+		/*
+		 * Flush data and try another time.
+		 */
+		error = ipfw_flush_sopt_data(sd);
+
+		if (sd->kavail < needed || error != 0)
+			return (NULL);
+	}
+
+	addr = sd->kbuf + sd->koff;
+	sd->koff += needed;
+	sd->kavail -= needed;
+	return (addr);
+}
+
+caddr_t
+ipfw_get_sopt_header(struct sockopt_data *sd, size_t needed)
+{
+	caddr_t addr;
+
+	if ((addr = ipfw_get_sopt_space(sd, needed)) == NULL)
+		return (NULL);
+
+	if (sd->kavail > 0)
+		memset(sd->kbuf + sd->koff, 0, sd->kavail);
+	
+	return (addr);
+}
 
 #define	RULE_MAXSIZE	(256*sizeof(u_int32_t))
 

Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c	Fri Jun 27 09:11:24 2014	(r267953)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c	Fri Jun 27 10:07:00 2014	(r267954)
@@ -102,17 +102,13 @@ static void free_table_config(struct nam
 static void link_table(struct ip_fw_chain *chain, struct table_config *tc);
 static void unlink_table(struct ip_fw_chain *chain, struct table_config *tc);
 static void free_table_state(void **state, void **xstate, uint8_t type);
-static int export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh);
+static int export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh,
+    struct sockopt_data *sd);
 static void export_table_info(struct table_config *tc, ipfw_xtable_info *i);
 static int dump_table_xentry(void *e, void *arg);
 
-static int check_buffer(size_t items, size_t item_size, size_t header,
-    size_t bufsize);
-
-static int ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt *sopt,
-    ip_fw3_opheader *op3, size_t valsize);
-static int ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt *sopt,
-    ip_fw3_opheader *op3, size_t valsize);
+static int ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt_data *sd);
+static int ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt_data *sd);
 
 static struct table_algo *find_table_algo(struct tables_config *tableconf,
     struct tid_info *ti, char *name);
@@ -560,24 +556,21 @@ ipfw_lookup_table_extended(struct ip_fw_
  * Returns 0 on success
  */
 int
-ipfw_listsize_tables(struct ip_fw_chain *ch, struct sockopt *sopt,
-    ip_fw3_opheader *op3, size_t valsize)
+ipfw_listsize_tables(struct ip_fw_chain *ch, struct sockopt_data *sd)
 {
 	struct _ipfw_obj_lheader *olh;
 
-	if (sopt->sopt_valsize < sizeof(*olh))
+	olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh));
+	if (olh == NULL)
 		return (EINVAL);
 
-	olh = (struct _ipfw_obj_lheader *)op3;
-	
 	olh->size = sizeof(*olh); /* Make export_table store needed size */
 
 	IPFW_UH_RLOCK(ch);
-	export_tables(ch, olh);
+	export_tables(ch, olh, sd);
 	IPFW_UH_RUNLOCK(ch);
 
-	sopt->sopt_valsize = sizeof(*olh);
-	return (sooptcopyout(sopt, olh, sopt->sopt_valsize));
+	return (0);
 }
 
 /*
@@ -589,61 +582,32 @@ ipfw_listsize_tables(struct ip_fw_chain 
  * Returns 0 on success
  */
 int
-ipfw_list_tables(struct ip_fw_chain *ch, struct sockopt *sopt,
-    ip_fw3_opheader *op3, size_t valsize)
+ipfw_list_tables(struct ip_fw_chain *ch, struct sockopt_data *sd)
 {
 	struct _ipfw_obj_lheader *olh;
 	uint32_t sz;
 	int error;
 
-	if (sopt->sopt_valsize < sizeof(*olh))
+	olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh));
+	if (olh == NULL)
 		return (EINVAL);
 
-	olh = (struct _ipfw_obj_lheader *)op3;
-
-	if (valsize != olh->size)
-		return (EINVAL);
-
-	/*
-	 * Check if array size is "reasonable":
-	 * Permit valsize between current size and
-	 * 2x current size + 1
-	 */
 	IPFW_UH_RLOCK(ch);
 	sz = ipfw_objhash_count(CHAIN_TO_NI(ch));
-	IPFW_UH_RUNLOCK(ch);
 
-	if (check_buffer(sz, sizeof(ipfw_xtable_info),
-	    sizeof(*olh), valsize) != 0)
-		return (EINVAL);
-	
-	olh = malloc(valsize, M_TEMP, M_ZERO | M_WAITOK);
-	/* Copy header to new storage */
-	memcpy(olh, op3, sizeof(*olh));
-
-	IPFW_UH_RLOCK(ch);
-	error = export_tables(ch, olh);
-	IPFW_UH_RUNLOCK(ch);
-
-	if (error != 0) {
-		free(olh, M_TEMP);
-		return (error);
+	if (sd->valsize < sz) {
+		IPFW_UH_RUNLOCK(ch);
+		return (ENOMEM);
 	}
 
-	/* 
-	 * Since we call sooptcopyin() with small buffer,
-	 * sopt_valsize is decreased to reflect supplied
-	 * buffer size. Set it back to original value.
-	 */
-	sopt->sopt_valsize = valsize;
-	error = sooptcopyout(sopt, olh, olh->size);
-	free(olh, M_TEMP);
+	error = export_tables(ch, olh, sd);
+	IPFW_UH_RUNLOCK(ch);
 
-	return (0);
+	return (error);
 }
 
 /*
- * Store table info to buffer provided by @op3.
+ * Store table info to buffer provided by @sd.
  * Data layout:
  * Request: [ ipfw_obj_header ipfw_xtable_info(empty)]
  * Reply: [ ipfw_obj_header ipfw_xtable_info ]
@@ -651,21 +615,18 @@ ipfw_list_tables(struct ip_fw_chain *ch,
  * Returns 0 on success.
  */
 int
-ipfw_describe_table(struct ip_fw_chain *ch, struct sockopt *sopt,
-    ip_fw3_opheader *op3, size_t valsize)
+ipfw_describe_table(struct ip_fw_chain *ch, struct sockopt_data *sd)
 {
 	struct _ipfw_obj_header *oh;
 	struct table_config *tc;
 	struct tid_info ti;
 	size_t sz;
-	int error;
 
 	sz = sizeof(*oh) + sizeof(ipfw_xtable_info);
-	if (sopt->sopt_valsize < sz)
+	oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
+	if (oh == NULL)
 		return (EINVAL);
 
-	oh = (struct _ipfw_obj_header *)op3;
-
 	objheader_to_ti(oh, &ti);
 
 	IPFW_UH_RLOCK(ch);
@@ -677,33 +638,31 @@ ipfw_describe_table(struct ip_fw_chain *
 	export_table_info(tc, (ipfw_xtable_info *)(oh + 1));
 	IPFW_UH_RUNLOCK(ch);
 
-	error = sooptcopyout(sopt, oh, sz);
-
-	return (error);
+	return (0);
 }
 
 struct dump_args {
 	struct table_info *ti;
 	struct table_config *tc;
-	ipfw_table_entry *ent;
-	ipfw_table_xentry *xent;
+	struct sockopt_data *sd;
 	uint32_t cnt;
-	uint32_t size;
 	uint16_t uidx;
+	ipfw_table_entry *ent;
+	uint32_t size;
 };
 
 int
-ipfw_dump_table(struct ip_fw_chain *ch, struct sockopt *sopt,
-    ip_fw3_opheader *op3, size_t valsize)
+ipfw_dump_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
+    struct sockopt_data *sd)
 {
 	int error;
 
 	switch (op3->version) {
 	case 0:
-		error = ipfw_dump_table_v0(ch, sopt, op3, valsize);
+		error = ipfw_dump_table_v0(ch, sd);
 		break;
 	case 1:
-		error = ipfw_dump_table_v1(ch, sopt, op3, valsize);
+		error = ipfw_dump_table_v1(ch, sd);
 		break;
 	default:
 		error = ENOTSUP;
@@ -714,15 +673,14 @@ ipfw_dump_table(struct ip_fw_chain *ch, 
 
 /*
  * Dumps all table data
- * Data layout (version 1):
- * Request: [ ipfw_obj_lheader ], size = ipfw_xtable_info.size
- * Reply: [ ipfw_obj_lheader ipfw_xtable_info ipfw_table_xentry x N ]
+ * Data layout (version 1)(current):
+ * Request: [ ipfw_obj_header ], size = ipfw_xtable_info.size
+ * Reply: [ ipfw_obj_header ipfw_xtable_info ipfw_table_xentry x N ]
  *
  * Returns 0 on success
  */
 static int
-ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt *sopt,
-    ip_fw3_opheader *op3, size_t valsize)
+ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt_data *sd)
 {
 	struct _ipfw_obj_header *oh;
 	ipfw_xtable_info *i;
@@ -731,13 +689,13 @@ ipfw_dump_table_v1(struct ip_fw_chain *c
 	struct table_algo *ta;
 	struct dump_args da;
 	uint32_t sz;
-	int error;
 
-	if (sopt->sopt_valsize < sizeof(*oh))
+	sz = sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info);
+	oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
+	if (oh == NULL)
 		return (EINVAL);
 
-	oh = (struct _ipfw_obj_header *)op3;
-
+	i = (ipfw_xtable_info *)(oh + 1);
 	objheader_to_ti(oh, &ti);
 
 	IPFW_UH_RLOCK(ch);
@@ -745,32 +703,19 @@ ipfw_dump_table_v1(struct ip_fw_chain *c
 		IPFW_UH_RUNLOCK(ch);
 		return (ESRCH);
 	}
+	export_table_info(tc, i);
 	sz = tc->count;
-	IPFW_UH_RUNLOCK(ch);
 
-	if (check_buffer(sz, sizeof(ipfw_table_xentry),
-	    sizeof(ipfw_xtable_info) + sizeof(*oh), valsize) != 0)
-		return (EINVAL);
+	if (sd->valsize < sz + tc->count * sizeof(ipfw_table_xentry)) {
 
-	oh = malloc(valsize, M_TEMP, M_ZERO | M_WAITOK);
-	i = (ipfw_xtable_info *)(oh + 1);
-	/* Copy header to new storage */
-	memcpy(oh, op3, sizeof(*oh));
-
-	IPFW_UH_RLOCK(ch);
-	/* Find table and export info */
-	if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
-		IPFW_UH_RUNLOCK(ch);
-		free(oh, M_TEMP);
-		return (ESRCH);
-	}
-
-	export_table_info(tc, i);
-	if (i->size > valsize) {
+		/*
+		 * Submitted buffer size is not enough.
+		 * WE've already filled in @i structure with
+		 * relevant table info including size, so we
+		 * can return. Buffer will be flushed automatically.
+		 */
 		IPFW_UH_RUNLOCK(ch);
-		/* XXX: Should we pass size structure back ? */
-		free(oh, M_TEMP);
-		return (EINVAL);
+		return (ENOMEM);
 	}
 
 	/*
@@ -779,52 +724,38 @@ ipfw_dump_table_v1(struct ip_fw_chain *c
 	memset(&da, 0, sizeof(da));
 	da.ti = KIDX_TO_TI(ch, tc->no.kidx);
 	da.tc = tc;
-	da.xent = (ipfw_table_xentry *)(i + 1);
-	da.size = (valsize - sizeof(*oh) - sizeof(ipfw_xtable_info)) /
-	    sizeof(ipfw_table_xentry);
+	da.sd = sd;
 
 	ta = tc->ta;
 
 	ta->foreach(tc->astate, da.ti, dump_table_xentry, &da);
 	IPFW_UH_RUNLOCK(ch);
 
-	/* 
-	 * Since we call sooptcopyin() with small buffer,
-	 * sopt_valsize is decreased to reflect supplied
-	 * buffer size. Set it back to original value.
-	 */
-	sopt->sopt_valsize = valsize;
-	error = sooptcopyout(sopt, oh, i->size);
-	free(oh, M_TEMP);
-
 	return (0);
 }
 
 /*
  * Dumps all table data
- * Data layout (version 0):
+ * Data layout (version 0)(legacy):
  * Request: [ ipfw_xtable ], size = IP_FW_TABLE_XGETSIZE()
  * Reply: [ ipfw_xtable ipfw_table_xentry x N ]
  *
  * Returns 0 on success
  */
 static int
-ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt *sopt,
-    ip_fw3_opheader *op3, size_t valsize)
+ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt_data *sd)
 {
 	ipfw_xtable *xtbl;
 	struct tid_info ti;
 	struct table_config *tc;
 	struct table_algo *ta;
 	struct dump_args da;
-	int error;
 	size_t sz;
 
-	if (valsize < sizeof(ipfw_xtable))
+	xtbl = (ipfw_xtable *)ipfw_get_sopt_header(sd, sizeof(ipfw_xtable));
+	if (xtbl == NULL)
 		return (EINVAL);
 
-	xtbl = (ipfw_xtable *)op3;
-
 	memset(&ti, 0, sizeof(ti));
 	ti.set = 0; /* XXX: No way to specify set */
 	ti.uidx = xtbl->tbl;
@@ -834,56 +765,37 @@ ipfw_dump_table_v0(struct ip_fw_chain *c
 		IPFW_UH_RUNLOCK(ch);
 		return (0);
 	}
-	sz = tc->count;
-	IPFW_UH_RUNLOCK(ch);
+	sz = tc->count * sizeof(ipfw_table_xentry) + sizeof(ipfw_xtable);
 
-	if (check_buffer(sz, sizeof(ipfw_table_xentry),
-	    sizeof(ipfw_xtable) - sizeof(ipfw_table_xentry), valsize) != 0)
-		return (EINVAL);
+	xtbl->cnt = tc->count;
+	xtbl->size = sz;
+	xtbl->type = tc->no.type;
+	xtbl->tbl = ti.uidx;
 
-	xtbl = malloc(valsize, M_TEMP, M_ZERO | M_WAITOK);
-	memcpy(xtbl, op3, sizeof(ipfw_xtable));
+	if (sd->valsize < sz) {
 
-	IPFW_UH_RLOCK(ch);
-	if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
-		IPFW_UH_RUNLOCK(ch);
-		free(xtbl, M_TEMP);
-		return (0);
-	}
-
-	/* Check size another time */
-	sz = tc->count * sizeof(ipfw_table_xentry) + sizeof(ipfw_xtable);
-	if (sz > valsize) {
+		/*
+		 * Submitted buffer size is not enough.
+		 * WE've already filled in @i structure with
+		 * relevant table info including size, so we
+		 * can return. Buffer will be flushed automatically.
+		 */
 		IPFW_UH_RUNLOCK(ch);
-		free(xtbl, M_TEMP);
-		return (EINVAL);
+		return (ENOMEM);
 	}
 
 	/* Do the actual dump in eXtended format */
 	memset(&da, 0, sizeof(da));
 	da.ti = KIDX_TO_TI(ch, tc->no.kidx);
 	da.tc = tc;
-	da.xent = &xtbl->xent[0];
-	da.size = tc->count;
-	xtbl->type = tc->no.type;
-	xtbl->tbl = ti.uidx;
+	da.sd = sd;
+
 	ta = tc->ta;
 
 	ta->foreach(tc->astate, da.ti, dump_table_xentry, &da);
-	xtbl->cnt = da.cnt;
-	xtbl->size = sz;
-
 	IPFW_UH_RUNLOCK(ch);
 
-	/* 
-	 * Since we call sooptcopyin() with small buffer, sopt_valsize is
-	 * decreased to reflect supplied buffer size. Set it back to original value
-	 */
-	sopt->sopt_valsize = valsize;
-	error = sooptcopyout(sopt, xtbl, sz);
-	free(xtbl, M_TEMP);
-
-	return (error);
+	return (0);
 }
 
 /*
@@ -925,7 +837,7 @@ ipfw_create_table(struct ip_fw_chain *ch
 
 	/*
 	 * Verify user-supplied strings.
-	 * Check for null-terminated/zero-lenght strings/
+	 * Check for null-terminated/zero-length strings/
 	 */
 	tname = i->tablename;
 	aname = i->algoname;
@@ -978,26 +890,6 @@ ipfw_create_table(struct ip_fw_chain *ch
 	return (0);
 }
 
-
-/*
- * Checks if supplied buffer size is "reasonable".
- * Permit valsize between current needed size and
- * 2x  needed size + 1
- */
-static int
-check_buffer(size_t items, size_t item_size, size_t header, size_t bufsize)
-{
-	size_t sz_min, sz_max;
-
-	sz_min = items * item_size + header;
-	sz_max = (2 * items + 1) * item_size + header;
-
-	if (bufsize < sz_min || bufsize > sz_max)
-		return (EINVAL);
-
-	return (0);	
-}
-
 void
 objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti)
 {
@@ -1029,43 +921,43 @@ static void
 export_table_internal(struct namedobj_instance *ni, struct named_object *no,
     void *arg)
 {
-	ipfw_obj_lheader *olh;
 	ipfw_xtable_info *i;
+	struct sockopt_data *sd;
 
-	olh = (ipfw_obj_lheader *)arg;
-	i = (ipfw_xtable_info *)(caddr_t)(olh + 1) + olh->count;
-	olh->count++;
+	sd = (struct sockopt_data *)arg;
+	i = (ipfw_xtable_info *)ipfw_get_sopt_space(sd, sizeof(*i));
+	KASSERT(i == 0, ("previously checked buffer is not enough"));
 
 	export_table_info((struct table_config *)no, i);
 }
 
 /*
  * Export all tables as ipfw_xtable_info structures to
- * storage provided by @olh.
+ * storage provided by @sd.
  * Returns 0 on success.
  */
 static int
-export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh)
+export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh,
+    struct sockopt_data *sd)
 {
 	uint32_t size;
 	uint32_t count;
 
 	count = ipfw_objhash_count(CHAIN_TO_NI(ch));
 	size = count * sizeof(ipfw_xtable_info) + sizeof(ipfw_obj_lheader);
+
+	/* Fill in header regadless of buffer size */
+	olh->count = count;
+	olh->objsize = sizeof(ipfw_xtable_info);
+
 	if (size > olh->size) {
-		/* Store new values anyway */
-		olh->count = count;
+		/* Store necessary size */
 		olh->size = size;
-		olh->objsize = sizeof(ipfw_xtable_info);
 		return (ENOMEM);
 	}
-
-	olh->count = 0;
-	ipfw_objhash_foreach(CHAIN_TO_NI(ch), export_table_internal, olh);
-
-	olh->count = count;
 	olh->size = size;
-	olh->objsize = sizeof(ipfw_xtable_info);
+
+	ipfw_objhash_foreach(CHAIN_TO_NI(ch), export_table_internal, sd);
 
 	return (0);
 }
@@ -1170,13 +1062,12 @@ dump_table_xentry(void *e, void *arg)
 	tc = da->tc;
 	ta = tc->ta;
 
+	xent = (ipfw_table_xentry *)ipfw_get_sopt_space(da->sd, sizeof(*xent));
 	/* Out of memory, returning */
-	if (da->cnt == da->size)
+	if (xent == NULL)
 		return (1);
-	xent = da->xent++;
 	xent->len = sizeof(ipfw_table_xentry);
 	xent->tbl = da->uidx;
-	da->cnt++;
 
 	return (ta->dump_xentry(tc->astate, da->ti, e, xent));
 }

Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_table.h
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_table.h	Fri Jun 27 09:11:24 2014	(r267953)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_table.h	Fri Jun 27 10:07:00 2014	(r267954)
@@ -91,14 +91,11 @@ void ipfw_table_algo_destroy(struct ip_f
 
 
 /* direct ipfw_ctl handlers */
-int ipfw_listsize_tables(struct ip_fw_chain *ch, struct sockopt *sopt,
-    ip_fw3_opheader *op3, size_t valsize);
-int ipfw_list_tables(struct ip_fw_chain *ch, struct sockopt *sopt,
-    ip_fw3_opheader *op3, size_t valsize);
-int ipfw_dump_table(struct ip_fw_chain *ch, struct sockopt *sopt,
-    ip_fw3_opheader *op3, size_t valsize);
-int ipfw_describe_table(struct ip_fw_chain *ch, struct sockopt *sopt,
-    ip_fw3_opheader *op3, size_t valsize);
+int ipfw_listsize_tables(struct ip_fw_chain *ch, struct sockopt_data *sd);
+int ipfw_list_tables(struct ip_fw_chain *ch, struct sockopt_data *sd);
+int ipfw_dump_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
+    struct sockopt_data *sd);
+int ipfw_describe_table(struct ip_fw_chain *ch, struct sockopt_data *sd);
 
 int ipfw_create_table(struct ip_fw_chain *ch, struct sockopt *sopt,
     ip_fw3_opheader *op3);


More information about the svn-src-projects mailing list