svn commit: r269386 - in projects/ipfw: sbin/ipfw sys/netinet sys/netpfil/ipfw

Alexander V. Chernikov melifaro at FreeBSD.org
Fri Aug 1 15:17:48 UTC 2014


Author: melifaro
Date: Fri Aug  1 15:17:46 2014
New Revision: 269386
URL: http://svnweb.freebsd.org/changeset/base/269386

Log:
  * Permit limiting number of items in table.
  
  Kernel changes:
  * Add TEI_FLAGS_DONTADD entry flag to indicate that insert is not possible
  * Support given flag in all algorithms
  * Add "limit" field to ipfw_xtable_info
  * Add actual limiting code into add_table_entry()
  
  Userland changes:
  * Add "limit" option as "create" table sub-option. Limit modification
    is currently impossible.
  * Print human-readable errors in table enry addition/deletion code.

Modified:
  projects/ipfw/sbin/ipfw/ipfw2.c
  projects/ipfw/sbin/ipfw/ipfw2.h
  projects/ipfw/sbin/ipfw/tables.c
  projects/ipfw/sys/netinet/ip_fw.h
  projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c
  projects/ipfw/sys/netpfil/ipfw/ip_fw_table.h
  projects/ipfw/sys/netpfil/ipfw/ip_fw_table_algo.c

Modified: projects/ipfw/sbin/ipfw/ipfw2.c
==============================================================================
--- projects/ipfw/sbin/ipfw/ipfw2.c	Fri Aug  1 15:10:55 2014	(r269385)
+++ projects/ipfw/sbin/ipfw/ipfw2.c	Fri Aug  1 15:17:46 2014	(r269386)
@@ -580,11 +580,12 @@ do_cmd(int optname, void *optval, uintpt
  *
  * Assumes op3 header is already embedded.
  * Calls setsockopt() with IP_FW3 as kernel-visible opcode.
- * Returns 0 on success or -1 otherwise.
+ * Returns 0 on success or errno otherwise.
  */
 int
 do_set3(int optname, ip_fw3_opheader *op3, uintptr_t optlen)
 {
+	int errno;
 
 	if (co.test_only)
 		return (0);
@@ -596,7 +597,10 @@ do_set3(int optname, ip_fw3_opheader *op
 
 	op3->opcode = optname;
 
-	return (setsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, optlen));
+	if (setsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, optlen) != 0)
+		return (errno);
+
+	return (0);
 }
 
 int

Modified: projects/ipfw/sbin/ipfw/ipfw2.h
==============================================================================
--- projects/ipfw/sbin/ipfw/ipfw2.h	Fri Aug  1 15:10:55 2014	(r269385)
+++ projects/ipfw/sbin/ipfw/ipfw2.h	Fri Aug  1 15:17:46 2014	(r269386)
@@ -255,8 +255,6 @@ char const *match_value(struct _s_x *p, 
 size_t concat_tokens(char *buf, size_t bufsize, struct _s_x *table,
     char *delimiter);
 void fill_flags(struct _s_x *flags, char *p, uint8_t *set, uint8_t *clear);
-void print_flags(char const *name, struct _s_x *list, uint8_t set,
-    uint8_t clear);
 void print_flags_buffer(char *buf, size_t sz, struct _s_x *list, uint8_t set);
 
 struct _ip_fw3_opheader;

Modified: projects/ipfw/sbin/ipfw/tables.c
==============================================================================
--- projects/ipfw/sbin/ipfw/tables.c	Fri Aug  1 15:10:55 2014	(r269385)
+++ projects/ipfw/sbin/ipfw/tables.c	Fri Aug  1 15:17:46 2014	(r269386)
@@ -251,9 +251,10 @@ table_fill_objheader(ipfw_obj_header *oh
 }
 
 static struct _s_x tablenewcmds[] = {
-      { "type",		TOK_TYPE},
+      { "type",		TOK_TYPE },
       { "valtype",	TOK_VALTYPE },
       { "algo",		TOK_ALGO },
+      { "limit",	TOK_LIMIT },
       { NULL, 0 }
 };
 
@@ -341,6 +342,11 @@ table_create(ipfw_obj_header *oh, int ac
 		ac--; av++;
 
 		switch (tcmd) {
+		case TOK_LIMIT:
+			NEED1("limit value required");
+			xi.limit = strtol(*av, NULL, 10);
+			ac--; av++;
+			break;
 		case TOK_TYPE:
 			NEED1("table type required");
 			/* Type may have suboptions after ':' */
@@ -485,6 +491,8 @@ table_show_info(ipfw_xtable_info *i, voi
 	printf(" valtype: %s, references: %u\n", vtype, i->refcnt);
 	printf(" algorithm: %s\n", i->algoname);
 	printf(" items: %u, size: %u\n", i->count, i->size);
+	if (i->limit > 0)
+		printf(" limit: %u\n", i->limit);
 
 	return (0);
 }
@@ -561,8 +569,8 @@ table_modify_record(ipfw_obj_header *oh,
 	ipfw_obj_tentry tent;
 	ipfw_xtable_info xi;
 	uint8_t type, vtype;
-	int cmd;
-	char *texterr;
+	int cmd, error;
+	char *texterr, *etxt;
 
 	if (ac == 0)
 		errx(EX_USAGE, "address required");
@@ -592,14 +600,34 @@ table_modify_record(ipfw_obj_header *oh,
 		if (ac > 0)
 			tentry_fill_value(oh, &tent, *av, type, vtype);
 		cmd = IP_FW_TABLE_XADD;
-		texterr = "setsockopt(IP_FW_TABLE_XADD)";
+		texterr = "Adding record failed";
 	} else {
 		cmd = IP_FW_TABLE_XDEL;
-		texterr = "setsockopt(IP_FW_TABLE_XDEL)";
+		texterr = "Deleting record failed";
+	}
+
+	if ((error = table_do_modify_record(cmd, oh, &tent, update)) == 0)
+		return;
+
+	/* Try to provide more human-readable error */
+	switch (error) {
+	case EEXIST:
+		etxt = "record already exists";
+		break;
+	case EFBIG:
+		etxt = "limit hit";
+		break;
+	case ESRCH:
+		etxt = "table not found";
+		break;
+	case ENOENT:
+		etxt = "record not found";
+		break;
+	default:
+		etxt = strerror(error);
 	}
 
-	if (table_do_modify_record(cmd, oh, &tent, update) != 0)
-		err(EX_OSERR, "%s", texterr);
+	errx(EX_OSERR, "%s: %s", texterr, etxt);
 }
 
 static int

Modified: projects/ipfw/sys/netinet/ip_fw.h
==============================================================================
--- projects/ipfw/sys/netinet/ip_fw.h	Fri Aug  1 15:10:55 2014	(r269385)
+++ projects/ipfw/sys/netinet/ip_fw.h	Fri Aug  1 15:17:46 2014	(r269386)
@@ -827,6 +827,8 @@ typedef struct _ipfw_xtable_info {
 	uint32_t	refcnt;		/* number of references		*/
 	uint32_t	count;		/* Number of records		*/
 	uint32_t	size;		/* Total size of records(export)*/
+	uint32_t	limit;		/* Max number of records	*/
+	uint32_t	spare;
 	char		tablename[64];	/* table name */
 	char		algoname[64];	/* algorithm name		*/
 	ifpw_ta_tinfo	ta_info;	/* additional algo stats	*/

Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c	Fri Aug  1 15:10:55 2014	(r269385)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c	Fri Aug  1 15:17:46 2014	(r269386)
@@ -78,8 +78,9 @@ struct table_config {
 	uint8_t		vtype;		/* format table type */
 	uint8_t		linked;		/* 1 if already linked */
 	uint8_t		tflags;		/* type flags */
-	uint8_t	spare;
+	uint8_t		spare;
 	uint32_t	count;		/* Number of records */
+	uint32_t	limit;		/* Max number of records */
 	uint64_t	flags;		/* state flags */
 	char		tablename[64];	/* table name */
 	struct table_algo	*ta;	/* Callbacks for given algo */
@@ -102,7 +103,7 @@ static struct table_config *alloc_table_
 static void free_table_config(struct namedobj_instance *ni,
     struct table_config *tc);
 static int create_table_internal(struct ip_fw_chain *ch, struct tid_info *ti,
-    char *aname, uint8_t tflags, uint8_t vtype);
+    char *aname, ipfw_xtable_info *i);
 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);
@@ -132,7 +133,6 @@ static struct table_algo *find_table_alg
 #define	KIDX_TO_TI(ch, k)	(&(((struct table_info *)(ch)->tablestate)[k]))
 
 
-
 int
 add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
     struct tentry_info *tei)
@@ -144,6 +144,7 @@ add_table_entry(struct ip_fw_chain *ch, 
 	int error;
 	uint32_t num;
 	uint64_t aflags;
+	ipfw_xtable_info xi;
 	char ta_buf[128];
 
 	IPFW_UH_WLOCK(ch);
@@ -160,6 +161,13 @@ add_table_entry(struct ip_fw_chain *ch, 
 			return (EINVAL);
 		}
 
+		/* Try to exit early on limit hit */
+		if (tc->limit != 0 && tc->count == tc->limit &&
+		    (tei->flags & TEI_FLAGS_UPDATE) == 0) {
+				IPFW_UH_WUNLOCK(ch);
+				return (EFBIG);
+		}
+
 		/* Reference and unlock */
 		tc->no.refcnt++;
 		ta = tc->ta;
@@ -172,7 +180,10 @@ add_table_entry(struct ip_fw_chain *ch, 
 		if ((tei->flags & TEI_FLAGS_COMPAT) == 0)
 			return (ESRCH);
 
-		error = create_table_internal(ch, ti, NULL, 0, IPFW_VTYPE_U32);
+		memset(&xi, 0, sizeof(xi));
+		xi.vtype = IPFW_VTYPE_U32;
+
+		error = create_table_internal(ch, ti, NULL, &xi);
 
 		if (error != 0)
 			return (error);
@@ -223,6 +234,22 @@ add_table_entry(struct ip_fw_chain *ch, 
 	/* Update aflags since it can be changed after previous read */
 	aflags = tc->flags;
 	
+	/* Check limit before adding */
+	if (tc->limit != 0 && tc->count == tc->limit) {
+		if ((tei->flags & TEI_FLAGS_UPDATE) == 0) {
+			IPFW_UH_WUNLOCK(ch);
+			return (EFBIG);
+		}
+
+		/*
+		 * We have UPDATE flag set.
+		 * Permit updating record (if found),
+		 * but restrict adding new one since we've
+		 * already hit the limit.
+		 */
+		tei->flags |= TEI_FLAGS_DONTADD;
+	}
+
 	/* We've got valid table in @tc. Let's add data */
 	kidx = tc->no.kidx;
 	ta = tc->ta;
@@ -1187,7 +1214,7 @@ ipfw_create_table(struct ip_fw_chain *ch
 	}
 	IPFW_UH_RUNLOCK(ch);
 
-	return (create_table_internal(ch, &ti, aname, i->tflags, i->vtype));
+	return (create_table_internal(ch, &ti, aname, i));
 }
 
 /*
@@ -1200,7 +1227,7 @@ ipfw_create_table(struct ip_fw_chain *ch
  */
 static int
 create_table_internal(struct ip_fw_chain *ch, struct tid_info *ti,
-    char *aname, uint8_t tflags, uint8_t vtype)
+    char *aname, ipfw_xtable_info *i)
 {
 	struct namedobj_instance *ni;
 	struct table_config *tc;
@@ -1212,10 +1239,13 @@ create_table_internal(struct ip_fw_chain
 	ta = find_table_algo(CHAIN_TO_TCFG(ch), ti, aname);
 	if (ta == NULL)
 		return (ENOTSUP);
-	
-	if ((tc = alloc_table_config(ch, ti, ta, aname, tflags, vtype)) == NULL)
+
+	tc = alloc_table_config(ch, ti, ta, aname, i->tflags, i->vtype);
+	if (tc == NULL)
 		return (ENOMEM);
 
+	tc->limit = i->limit;
+
 	IPFW_UH_WLOCK(ch);
 
 	/* Check if table has been already created */
@@ -1293,6 +1323,7 @@ export_table_info(struct ip_fw_chain *ch
 	i->kidx = tc->no.kidx;
 	i->refcnt = tc->no.refcnt;
 	i->count = tc->count;
+	i->limit = tc->limit;
 	i->size = tc->count * sizeof(ipfw_obj_tentry);
 	i->size += sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info);
 	strlcpy(i->tablename, tc->tablename, sizeof(i->tablename));

Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_table.h
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_table.h	Fri Aug  1 15:10:55 2014	(r269385)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_table.h	Fri Aug  1 15:17:46 2014	(r269386)
@@ -58,9 +58,10 @@ struct tentry_info {
 	uint16_t	flags;		/* record flags			*/
 	uint32_t	value;		/* value			*/
 };
-#define	TEI_FLAGS_UPDATE	0x01	/* Update record if exists	*/
+#define	TEI_FLAGS_UPDATE	0x01	/* Add or update rec if exists	*/
 #define	TEI_FLAGS_UPDATED	0x02	/* Entry has been updated	*/
 #define	TEI_FLAGS_COMPAT	0x04	/* Called from old ABI		*/
+#define	TEI_FLAGS_DONTADD	0x08	/* Do not create new rec	*/
 
 typedef int (ta_init)(struct ip_fw_chain *ch, void **ta_state,
     struct table_info *ti, char *data, uint8_t tflags);

Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_table_algo.c
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_table_algo.c	Fri Aug  1 15:10:55 2014	(r269385)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_table_algo.c	Fri Aug  1 15:17:46 2014	(r269386)
@@ -411,22 +411,12 @@ ta_add_cidr(void *ta_state, struct table
 	else
 		rnh = ti->xstate;
 
-	rn = rnh->rnh_addaddr(tb->addr_ptr, tb->mask_ptr, rnh, tb->ent_ptr);
-	
-	if (rn == NULL) {
+	/* Search for an entry first */
+	rn = rnh->rnh_lookup(tb->addr_ptr, tb->mask_ptr, rnh);
+	if (rn != NULL) {
 		if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
 			return (EEXIST);
 		/* Record already exists. Update value if we're asked to */
-		rn = rnh->rnh_lookup(tb->addr_ptr, tb->mask_ptr, rnh);
-		if (rn == NULL) {
-
-			/*
-			 * Radix may have failed addition for other reasons
-			 * like failure in mask allocation code.
-			 */
-			return (EINVAL);
-		}
-		
 		if (tei->subtype == AF_INET) {
 			/* IPv4. */
 			value = ((struct radix_cidr_entry *)tb->ent_ptr)->value;
@@ -444,6 +434,15 @@ ta_add_cidr(void *ta_state, struct table
 		return (0);
 	}
 
+	if ((tei->flags & TEI_FLAGS_DONTADD) != 0)
+		return (EFBIG);
+
+	rn = rnh->rnh_addaddr(tb->addr_ptr, tb->mask_ptr, rnh, tb->ent_ptr);
+	if (rn == NULL) {
+		/* Unknown error */
+		return (EINVAL);
+	}
+	
 	tb->ent_ptr = NULL;
 	*pnum = 1;
 
@@ -1167,6 +1166,8 @@ ta_add_chash(void *ta_state, struct tabl
 		tei->flags |= TEI_FLAGS_UPDATED;
 		*pnum = 0;
 	} else {
+		if ((tei->flags & TEI_FLAGS_DONTADD) != 0)
+			return (EFBIG);
 		SLIST_INSERT_HEAD(&head[hash], ent, next);
 		tb->ent_ptr = NULL;
 		*pnum = 1;
@@ -1715,6 +1716,9 @@ ta_add_ifidx(void *ta_state, struct tabl
 		return (0);
 	}
 
+	if ((tei->flags & TEI_FLAGS_DONTADD) != 0)
+		return (EFBIG);
+
 	/* Link to internal list */
 	ipfw_objhash_add(icfg->ii, &ife->no);
 
@@ -2206,6 +2210,9 @@ ta_add_numarray(void *ta_state, struct t
 		return (0);
 	}
 
+	if ((tei->flags & TEI_FLAGS_DONTADD) != 0)
+		return (EFBIG);
+
 	res = badd(&tb->na.number, &tb->na, cfg->main_ptr, cfg->used,
 	    sizeof(struct numarray), compare_numarray);
 
@@ -2891,6 +2898,9 @@ ta_add_fhash(void *ta_state, struct tabl
 		tei->flags |= TEI_FLAGS_UPDATED;
 		*pnum = 0;
 	} else {
+		if ((tei->flags & TEI_FLAGS_DONTADD) != 0)
+			return (EFBIG);
+
 		SLIST_INSERT_HEAD(&head[hash], ent, next);
 		tb->ent_ptr = NULL;
 		*pnum = 1;


More information about the svn-src-projects mailing list