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>