svn commit: r269486 - in projects/ipfw: sbin/ipfw sys/netinet sys/netpfil/ipfw
Alexander V. Chernikov
melifaro at FreeBSD.org
Sun Aug 3 21:37:14 UTC 2014
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 */
More information about the svn-src-projects
mailing list