mirror of
https://git.hardenedbsd.org/hardenedbsd/HardenedBSD.git
synced 2024-11-27 03:11:52 +01:00
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.
This commit is contained in:
parent
358b9d09ed
commit
46d5200874
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/projects/ipfw/; revision=269486
@ -216,6 +216,7 @@ enum tokens {
|
|||||||
TOK_INFO,
|
TOK_INFO,
|
||||||
TOK_DETAIL,
|
TOK_DETAIL,
|
||||||
TOK_FLUSH,
|
TOK_FLUSH,
|
||||||
|
TOK_SWAP,
|
||||||
TOK_ADD,
|
TOK_ADD,
|
||||||
TOK_DEL,
|
TOK_DEL,
|
||||||
TOK_VALTYPE,
|
TOK_VALTYPE,
|
||||||
|
@ -56,6 +56,8 @@ static int table_destroy(ipfw_obj_header *oh);
|
|||||||
static int table_do_create(ipfw_obj_header *oh, ipfw_xtable_info *i);
|
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_create(ipfw_obj_header *oh, int ac, char *av[]);
|
||||||
static void table_lookup(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_get_info(ipfw_obj_header *oh, ipfw_xtable_info *i);
|
||||||
static int table_show_info(ipfw_xtable_info *i, void *arg);
|
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,
|
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[] = {
|
static struct _s_x tablecmds[] = {
|
||||||
{ "add", TOK_ADD },
|
{ "add", TOK_ADD },
|
||||||
{ "create", TOK_CREATE },
|
|
||||||
{ "delete", TOK_DEL },
|
{ "delete", TOK_DEL },
|
||||||
|
{ "create", TOK_CREATE },
|
||||||
{ "destroy", TOK_DESTROY },
|
{ "destroy", TOK_DESTROY },
|
||||||
{ "flush", TOK_FLUSH },
|
{ "flush", TOK_FLUSH },
|
||||||
|
{ "swap", TOK_SWAP },
|
||||||
{ "info", TOK_INFO },
|
{ "info", TOK_INFO },
|
||||||
{ "detail", TOK_DETAIL },
|
{ "detail", TOK_DETAIL },
|
||||||
{ "list", TOK_LIST },
|
{ "list", TOK_LIST },
|
||||||
@ -204,6 +207,11 @@ ipfw_table_handler(int ac, char *av[])
|
|||||||
err(EX_OSERR, "failed to flush tables list");
|
err(EX_OSERR, "failed to flush tables list");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case TOK_SWAP:
|
||||||
|
ac--; av++;
|
||||||
|
NEED1("second table name required");
|
||||||
|
table_swap(&oh, *av);
|
||||||
|
break;
|
||||||
case TOK_DETAIL:
|
case TOK_DETAIL:
|
||||||
case TOK_INFO:
|
case TOK_INFO:
|
||||||
arg = (tcmd == TOK_DETAIL) ? (void *)1 : NULL;
|
arg = (tcmd == TOK_DETAIL) ? (void *)1 : NULL;
|
||||||
@ -450,6 +458,46 @@ table_flush(ipfw_obj_header *oh)
|
|||||||
return (0);
|
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.
|
* Retrieves table in given table specified by @oh->ntlv.
|
||||||
* it inside @i.
|
* it inside @i.
|
||||||
|
@ -90,6 +90,7 @@ typedef struct _ip_fw3_opheader {
|
|||||||
#define IP_FW_TABLE_XFIND 99 /* finds an entry */
|
#define IP_FW_TABLE_XFIND 99 /* finds an entry */
|
||||||
#define IP_FW_XIFLIST 100 /* list tracked interfaces */
|
#define IP_FW_XIFLIST 100 /* list tracked interfaces */
|
||||||
#define IP_FW_TABLES_ALIST 101 /* list table algorithms */
|
#define IP_FW_TABLES_ALIST 101 /* list table algorithms */
|
||||||
|
#define IP_FW_TABLE_XSWAP 102 /* swap two tables */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Usage guidelines:
|
* Usage guidelines:
|
||||||
|
@ -2691,12 +2691,12 @@ vnet_ipfw_init(const void *unused)
|
|||||||
rule->cmd[0].opcode = default_to_accept ? O_ACCEPT : O_DENY;
|
rule->cmd[0].opcode = default_to_accept ? O_ACCEPT : O_DENY;
|
||||||
chain->default_rule = chain->map[0] = rule;
|
chain->default_rule = chain->map[0] = rule;
|
||||||
chain->id = rule->id = 1;
|
chain->id = rule->id = 1;
|
||||||
ipfw_init_skipto_cache(chain);
|
|
||||||
/* Pre-calculate rules length for legacy dump format */
|
/* Pre-calculate rules length for legacy dump format */
|
||||||
chain->static_len = sizeof(struct ip_fw_rule0);
|
chain->static_len = sizeof(struct ip_fw_rule0);
|
||||||
|
|
||||||
IPFW_LOCK_INIT(chain);
|
IPFW_LOCK_INIT(chain);
|
||||||
ipfw_dyn_init(chain);
|
ipfw_dyn_init(chain);
|
||||||
|
ipfw_init_skipto_cache(chain);
|
||||||
|
|
||||||
/* First set up some values that are compile time options */
|
/* First set up some values that are compile time options */
|
||||||
V_ipfw_vnet_ready = 1; /* Open for business */
|
V_ipfw_vnet_ready = 1; /* Open for business */
|
||||||
|
@ -1970,6 +1970,10 @@ ipfw_ctl3(struct sockopt *sopt)
|
|||||||
error = ipfw_find_table_entry(chain, op3, &sdata);
|
error = ipfw_find_table_entry(chain, op3, &sdata);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case IP_FW_TABLE_XSWAP:
|
||||||
|
error = ipfw_swap_table(chain, op3, &sdata);
|
||||||
|
break;
|
||||||
|
|
||||||
case IP_FW_TABLES_ALIST:
|
case IP_FW_TABLES_ALIST:
|
||||||
error = ipfw_list_table_algo(chain, &sdata);
|
error = ipfw_list_table_algo(chain, &sdata);
|
||||||
break;
|
break;
|
||||||
|
@ -117,6 +117,8 @@ static int ipfw_manage_table_ent_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3
|
|||||||
struct sockopt_data *sd);
|
struct sockopt_data *sd);
|
||||||
static int ipfw_manage_table_ent_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
static int ipfw_manage_table_ent_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
||||||
struct sockopt_data *sd);
|
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,
|
static int check_table_space(struct ip_fw_chain *ch, struct table_config *tc,
|
||||||
struct table_info *ti, uint32_t count);
|
struct table_info *ti, uint32_t count);
|
||||||
@ -125,6 +127,9 @@ static int destroy_table(struct ip_fw_chain *ch, struct tid_info *ti);
|
|||||||
static struct table_algo *find_table_algo(struct tables_config *tableconf,
|
static struct table_algo *find_table_algo(struct tables_config *tableconf,
|
||||||
struct tid_info *ti, char *name);
|
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_TCFG(chain) ((struct tables_config *)(chain)->tblcfg)
|
||||||
#define CHAIN_TO_NI(chain) (CHAIN_TO_TCFG(chain)->namehash)
|
#define CHAIN_TO_NI(chain) (CHAIN_TO_TCFG(chain)->namehash)
|
||||||
#define KIDX_TO_TI(ch, k) (&(((struct table_info *)(ch)->tablestate)[k]))
|
#define KIDX_TO_TI(ch, k) (&(((struct table_info *)(ch)->tablestate)[k]))
|
||||||
@ -639,6 +644,13 @@ ipfw_find_table_entry(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
|||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Flushes all entries or destroys given table.
|
||||||
|
* Data layout (v0)(current):
|
||||||
|
* Request: [ ipfw_obj_header ]
|
||||||
|
*
|
||||||
|
* Returns 0 on success
|
||||||
|
*/
|
||||||
int
|
int
|
||||||
ipfw_flush_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
ipfw_flush_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
||||||
struct sockopt_data *sd)
|
struct sockopt_data *sd)
|
||||||
@ -663,13 +675,6 @@ ipfw_flush_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
|||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Flushes all entries in given table.
|
|
||||||
* Data layout (v0)(current):
|
|
||||||
* Request: [ ip_fw3_opheader ]
|
|
||||||
*
|
|
||||||
* Returns 0 on success
|
|
||||||
*/
|
|
||||||
int
|
int
|
||||||
flush_table(struct ip_fw_chain *ch, struct tid_info *ti)
|
flush_table(struct ip_fw_chain *ch, struct tid_info *ti)
|
||||||
{
|
{
|
||||||
@ -747,6 +752,114 @@ flush_table(struct ip_fw_chain *ch, struct tid_info *ti)
|
|||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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.
|
* Destroys table specified by @ti.
|
||||||
* Data layout (v0)(current):
|
* Data layout (v0)(current):
|
||||||
@ -1300,15 +1413,22 @@ create_table_internal(struct ip_fw_chain *ch, struct tid_info *ti,
|
|||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti)
|
ntlv_to_ti(ipfw_obj_ntlv *ntlv, struct tid_info *ti)
|
||||||
{
|
{
|
||||||
|
|
||||||
memset(ti, 0, sizeof(struct tid_info));
|
memset(ti, 0, sizeof(struct tid_info));
|
||||||
ti->set = oh->ntlv.set;
|
ti->set = ntlv->set;
|
||||||
ti->uidx = oh->idx;
|
ti->uidx = ntlv->idx;
|
||||||
ti->tlvs = &oh->ntlv;
|
ti->tlvs = ntlv;
|
||||||
ti->tlen = oh->ntlv.head.length;
|
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
|
int
|
||||||
|
@ -152,6 +152,8 @@ int ipfw_manage_table_ent(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
|||||||
int ipfw_flush_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
int ipfw_flush_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
||||||
struct sockopt_data *sd);
|
struct sockopt_data *sd);
|
||||||
int ipfw_list_table_algo(struct ip_fw_chain *ch, 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 */
|
/* Exported to support legacy opcodes */
|
||||||
int add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
|
int add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
|
||||||
struct tentry_info *tei, uint32_t count);
|
struct tentry_info *tei, uint32_t count);
|
||||||
@ -171,7 +173,6 @@ void ipfw_unbind_table_rule(struct ip_fw_chain *chain, struct ip_fw *rule);
|
|||||||
void ipfw_unbind_table_list(struct ip_fw_chain *chain, struct ip_fw *head);
|
void ipfw_unbind_table_list(struct ip_fw_chain *chain, struct ip_fw *head);
|
||||||
|
|
||||||
/* utility functions */
|
/* utility functions */
|
||||||
void objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti);
|
|
||||||
int ipfw_check_table_name(char *name);
|
int ipfw_check_table_name(char *name);
|
||||||
|
|
||||||
/* Legacy interfaces */
|
/* Legacy interfaces */
|
||||||
|
Loading…
Reference in New Issue
Block a user