Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 3 Aug 2014 21:37:12 +0000 (UTC)
From:      Alexander V. Chernikov <melifaro@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r269486 - in projects/ipfw: sbin/ipfw sys/netinet sys/netpfil/ipfw
Message-ID:  <53deab89.5351.4d6c313@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: melifaro
Date: Sun Aug  3 21:37:12 2014
New Revision: 269486
URL: http://svnweb.freebsd.org/changeset/base/269486

Log:
  Implement atomic ipfw table swap.
  
  Kernel changes:
  * Add opcode IP_FW_TABLE_XSWAP
  * Add support for swapping 2 tables with the same type/ftype/vtype.
  * Make skipto cache init after ipfw locks init.
  
  Userland changes:
  * Add "table X swap Y" command.

Modified:
  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_fw2.c
  projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c
  projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c
  projects/ipfw/sys/netpfil/ipfw/ip_fw_table.h

Modified: projects/ipfw/sbin/ipfw/ipfw2.h
==============================================================================
--- projects/ipfw/sbin/ipfw/ipfw2.h	Sun Aug  3 20:40:51 2014	(r269485)
+++ projects/ipfw/sbin/ipfw/ipfw2.h	Sun Aug  3 21:37:12 2014	(r269486)
@@ -216,6 +216,7 @@ enum tokens {
 	TOK_INFO,
 	TOK_DETAIL,
 	TOK_FLUSH,
+	TOK_SWAP,
 	TOK_ADD,
 	TOK_DEL,
 	TOK_VALTYPE,

Modified: projects/ipfw/sbin/ipfw/tables.c
==============================================================================
--- projects/ipfw/sbin/ipfw/tables.c	Sun Aug  3 20:40:51 2014	(r269485)
+++ projects/ipfw/sbin/ipfw/tables.c	Sun Aug  3 21:37:12 2014	(r269486)
@@ -56,6 +56,8 @@ static int table_destroy(ipfw_obj_header
 static int table_do_create(ipfw_obj_header *oh, ipfw_xtable_info *i);
 static void table_create(ipfw_obj_header *oh, int ac, char *av[]);
 static void table_lookup(ipfw_obj_header *oh, int ac, char *av[]);
+static int table_do_swap(ipfw_obj_header *oh, char *second);
+static int table_swap(ipfw_obj_header *oh, char *second);
 static int table_get_info(ipfw_obj_header *oh, ipfw_xtable_info *i);
 static int table_show_info(ipfw_xtable_info *i, void *arg);
 static void table_fill_ntlv(ipfw_obj_ntlv *ntlv, char *name, uint32_t set,
@@ -96,10 +98,11 @@ static struct _s_x tablevaltypes[] = {
 
 static struct _s_x tablecmds[] = {
       { "add",		TOK_ADD },
-      { "create",	TOK_CREATE },
       { "delete",	TOK_DEL },
+      { "create",	TOK_CREATE },
       { "destroy",	TOK_DESTROY },
       { "flush",	TOK_FLUSH },
+      { "swap",		TOK_SWAP },
       { "info",		TOK_INFO },
       { "detail",	TOK_DETAIL },
       { "list",		TOK_LIST },
@@ -204,6 +207,11 @@ ipfw_table_handler(int ac, char *av[])
 				err(EX_OSERR, "failed to flush tables list");
 		}
 		break;
+	case TOK_SWAP:
+		ac--; av++;
+		NEED1("second table name required");
+		table_swap(&oh, *av);
+		break;
 	case TOK_DETAIL:
 	case TOK_INFO:
 		arg = (tcmd == TOK_DETAIL) ? (void *)1 : NULL;
@@ -450,6 +458,46 @@ table_flush(ipfw_obj_header *oh)
 	return (0);
 }
 
+static int
+table_do_swap(ipfw_obj_header *oh, char *second)
+{
+	char tbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_obj_ntlv)];
+	int error;
+
+	memset(tbuf, 0, sizeof(tbuf));
+	memcpy(tbuf, oh, sizeof(*oh));
+	oh = (ipfw_obj_header *)tbuf;
+	table_fill_ntlv((ipfw_obj_ntlv *)(oh + 1), second, oh->ntlv.set, 1);
+
+	error = do_set3(IP_FW_TABLE_XSWAP, &oh->opheader, sizeof(tbuf));
+
+	return (error);
+}
+
+/*
+ * Swaps given table with @second one.
+ */
+static int
+table_swap(ipfw_obj_header *oh, char *second)
+{
+	int error;
+
+	if (table_check_name(second) != 0)
+		errx(EX_USAGE, "table name %s is invalid", second);
+
+	error = table_do_swap(oh, second);
+
+	switch (error) {
+	case EINVAL:
+		errx(EX_USAGE, "Unable to swap table: check types");
+	case EFBIG:
+		errx(EX_USAGE, "Unable to swap table: check limits");
+	}
+
+	return (0);
+}
+
+
 /*
  * Retrieves table in given table specified by @oh->ntlv.
  * it inside @i.

Modified: projects/ipfw/sys/netinet/ip_fw.h
==============================================================================
--- projects/ipfw/sys/netinet/ip_fw.h	Sun Aug  3 20:40:51 2014	(r269485)
+++ projects/ipfw/sys/netinet/ip_fw.h	Sun Aug  3 21:37:12 2014	(r269486)
@@ -90,6 +90,7 @@ typedef struct _ip_fw3_opheader {
 #define	IP_FW_TABLE_XFIND	99	/* finds an entry */
 #define	IP_FW_XIFLIST		100	/* list tracked interfaces */
 #define	IP_FW_TABLES_ALIST	101	/* list table algorithms */
+#define	IP_FW_TABLE_XSWAP	102	/* swap two tables */
 
 /*
  * Usage guidelines:

Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw2.c
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw2.c	Sun Aug  3 20:40:51 2014	(r269485)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw2.c	Sun Aug  3 21:37:12 2014	(r269486)
@@ -2691,12 +2691,12 @@ vnet_ipfw_init(const void *unused)
 	rule->cmd[0].opcode = default_to_accept ? O_ACCEPT : O_DENY;
 	chain->default_rule = chain->map[0] = rule;
 	chain->id = rule->id = 1;
-	ipfw_init_skipto_cache(chain);
 	/* Pre-calculate rules length for legacy dump format */
 	chain->static_len = sizeof(struct ip_fw_rule0);
 
 	IPFW_LOCK_INIT(chain);
 	ipfw_dyn_init(chain);
+	ipfw_init_skipto_cache(chain);
 
 	/* First set up some values that are compile time options */
 	V_ipfw_vnet_ready = 1;		/* Open for business */

Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c	Sun Aug  3 20:40:51 2014	(r269485)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c	Sun Aug  3 21:37:12 2014	(r269486)
@@ -1970,6 +1970,10 @@ ipfw_ctl3(struct sockopt *sopt)
 		error = ipfw_find_table_entry(chain, op3, &sdata);
 		break;
 
+	case IP_FW_TABLE_XSWAP:
+		error = ipfw_swap_table(chain, op3, &sdata);
+		break;
+
 	case IP_FW_TABLES_ALIST:
 		error = ipfw_list_table_algo(chain, &sdata);
 		break;

Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c	Sun Aug  3 20:40:51 2014	(r269485)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c	Sun Aug  3 21:37:12 2014	(r269486)
@@ -117,6 +117,8 @@ static int ipfw_manage_table_ent_v0(stru
     struct sockopt_data *sd);
 static int ipfw_manage_table_ent_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
     struct sockopt_data *sd);
+static int swap_table(struct ip_fw_chain *ch, struct tid_info *a,
+    struct tid_info *b);
 
 static int check_table_space(struct ip_fw_chain *ch, struct table_config *tc,
     struct table_info *ti, uint32_t count);
@@ -125,6 +127,9 @@ static int destroy_table(struct ip_fw_ch
 static struct table_algo *find_table_algo(struct tables_config *tableconf,
     struct tid_info *ti, char *name);
 
+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);
+
 #define	CHAIN_TO_TCFG(chain)	((struct tables_config *)(chain)->tblcfg)
 #define	CHAIN_TO_NI(chain)	(CHAIN_TO_TCFG(chain)->namehash)
 #define	KIDX_TO_TI(ch, k)	(&(((struct table_info *)(ch)->tablestate)[k]))
@@ -639,6 +644,13 @@ ipfw_find_table_entry(struct ip_fw_chain
 	return (error);
 }
 
+/*
+ * Flushes all entries or destroys given table.
+ * Data layout (v0)(current):
+ * Request: [ ipfw_obj_header ]
+ *
+ * Returns 0 on success
+ */
 int
 ipfw_flush_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
     struct sockopt_data *sd)
@@ -663,13 +675,6 @@ ipfw_flush_table(struct ip_fw_chain *ch,
 	return (error);
 }
 
-/*
- * Flushes all entries in given table.
- * Data layout (v0)(current):
- * Request: [ ip_fw3_opheader ]
- *
- * Returns 0 on success
- */
 int
 flush_table(struct ip_fw_chain *ch, struct tid_info *ti)
 {
@@ -748,6 +753,114 @@ flush_table(struct ip_fw_chain *ch, stru
 }
 
 /*
+ * Swaps two tables.
+ * Data layout (v0)(current):
+ * Request: [ ipfw_obj_header ipfw_obj_ntlv ]
+ *
+ * Returns 0 on success
+ */
+int
+ipfw_swap_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
+    struct sockopt_data *sd)
+{
+	int error;
+	struct _ipfw_obj_header *oh;
+	struct tid_info ti_a, ti_b;
+
+	if (sd->valsize != sizeof(*oh) + sizeof(ipfw_obj_ntlv))
+		return (EINVAL);
+
+	oh = (struct _ipfw_obj_header *)op3;
+	ntlv_to_ti(&oh->ntlv, &ti_a);
+	ntlv_to_ti((ipfw_obj_ntlv *)(oh + 1), &ti_b);
+
+	error = swap_table(ch, &ti_a, &ti_b);
+
+	return (error);
+}
+
+static int
+swap_table(struct ip_fw_chain *ch, struct tid_info *a,
+    struct tid_info *b)
+{
+	struct namedobj_instance *ni;
+	struct table_config *tc_a, *tc_b;
+	struct table_algo *ta;
+	struct table_info ti, *tablestate;
+	void *astate;
+	uint32_t count;
+
+	/*
+	 * Stage 1: find both tables and ensure they are of
+	 * the same type and algo.
+	 */
+	IPFW_UH_WLOCK(ch);
+	ni = CHAIN_TO_NI(ch);
+	if ((tc_a = find_table(ni, a)) == NULL) {
+		IPFW_UH_WUNLOCK(ch);
+		return (ESRCH);
+	}
+	if ((tc_b = find_table(ni, b)) == NULL) {
+		IPFW_UH_WUNLOCK(ch);
+		return (ESRCH);
+	}
+
+	/* It is very easy to swap between the same table */
+	if (tc_a == tc_b) {
+		IPFW_UH_WUNLOCK(ch);
+		return (0);
+	}
+
+	/* Check type and value are the same */
+	if (tc_a->no.type != tc_b->no.type || tc_a->tflags != tc_b->tflags ||
+	    tc_a->vtype != tc_b->vtype) {
+		IPFW_UH_WUNLOCK(ch);
+		return (EINVAL);
+	}
+
+	/* Check limits before swap */
+	if ((tc_a->limit != 0 && tc_b->count > tc_a->limit) ||
+	    (tc_b->limit != 0 && tc_a->count > tc_b->limit)) {
+		IPFW_UH_WUNLOCK(ch);
+		return (EFBIG);
+	}
+
+	/* Everything is fine, prepare to swap */
+	tablestate = (struct table_info *)ch->tablestate;
+	ti = tablestate[tc_a->no.kidx];
+	ta = tc_a->ta;
+	astate = tc_a->astate;
+	count = tc_a->count;
+
+	IPFW_WLOCK(ch);
+	/* a <- b */
+	tablestate[tc_a->no.kidx] = tablestate[tc_b->no.kidx];
+	tc_a->ta = tc_b->ta;
+	tc_a->astate = tc_b->astate;
+	tc_a->count = tc_b->count;
+	/* b <- a */
+	tablestate[tc_b->no.kidx] = ti;
+	tc_b->ta = ta;
+	tc_b->astate = astate;
+	tc_b->count = count;
+	IPFW_WUNLOCK(ch);
+
+	/* Ensure tc.ti copies are in sync */
+	tc_a->ti = tablestate[tc_a->no.kidx];
+	tc_b->ti = tablestate[tc_b->no.kidx];
+
+	/* Notify both tables on @ti change */
+	if (tc_a->ta->change_ti != NULL)
+		tc_a->ta->change_ti(tc_a->astate, &tablestate[tc_a->no.kidx]);
+	if (tc_b->ta->change_ti != NULL)
+		tc_b->ta->change_ti(tc_b->astate, &tablestate[tc_b->no.kidx]);
+
+	IPFW_UH_WUNLOCK(ch);
+
+	return (0);
+}
+
+/*
  * Destroys table specified by @ti.
  * Data layout (v0)(current):
  * Request: [ ip_fw3_opheader ]
@@ -1300,15 +1413,22 @@ create_table_internal(struct ip_fw_chain
 	return (0);
 }
 
-void
-objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti)
+static void
+ntlv_to_ti(ipfw_obj_ntlv *ntlv, struct tid_info *ti)
 {
 
 	memset(ti, 0, sizeof(struct tid_info));
-	ti->set = oh->ntlv.set;
-	ti->uidx = oh->idx;
-	ti->tlvs = &oh->ntlv;
-	ti->tlen = oh->ntlv.head.length;
+	ti->set = ntlv->set;
+	ti->uidx = ntlv->idx;
+	ti->tlvs = ntlv;
+	ti->tlen = ntlv->head.length;
+}
+
+static void
+objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti)
+{
+
+	ntlv_to_ti(&oh->ntlv, ti);
 }
 
 int

Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_table.h
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_table.h	Sun Aug  3 20:40:51 2014	(r269485)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_table.h	Sun Aug  3 21:37:12 2014	(r269486)
@@ -152,6 +152,8 @@ int ipfw_manage_table_ent(struct ip_fw_c
 int ipfw_flush_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
     struct sockopt_data *sd);
 int ipfw_list_table_algo(struct ip_fw_chain *ch, struct sockopt_data *sd);
+int ipfw_swap_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
+    struct sockopt_data *sd);
 /* Exported to support legacy opcodes */
 int add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
     struct tentry_info *tei, uint32_t count);
@@ -171,7 +173,6 @@ void ipfw_unbind_table_rule(struct ip_fw
 void ipfw_unbind_table_list(struct ip_fw_chain *chain, struct ip_fw *head);
 
 /* utility functions  */
-void objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti);
 int ipfw_check_table_name(char *name);
 
 /* Legacy interfaces */



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?53deab89.5351.4d6c313>