* Require explicit table creation before use on kernel side.
* Add resize callbacks for upcoming table-based algorithms. Kernel changes: * s/ipfw_modify_table/ipfw_manage_table_ent/ * Simplify add_table_entry(): make table creation a separate piece of code. Do not perform creation if not in "compat" mode. * Add ability to perform modification of algorithm state (like table resize). The following callbacks were added: - prepare_mod (allocate new state, without locks) - fill_mod (UH_WLOCK, copy old state to new one) - modify (UH_WLOCK + WLOCK, switch state) - flush_mod (no locks, flushes allocated data) Given callbacks are called if table modification has been requested by add or delete callbacks. Additional u64 tc->'flags' field was added to pass these requests. * Change add/del table ent format: permit adding/removing multiple entries at once (only 1 supported at the moment). Userland changes: * Auto-create tables with warning
This commit is contained in:
parent
e0a8b9ee29
commit
db785d3199
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/projects/ipfw/; revision=269121
@ -465,7 +465,8 @@ static int
|
|||||||
table_do_modify_record(int cmd, ipfw_obj_header *oh,
|
table_do_modify_record(int cmd, ipfw_obj_header *oh,
|
||||||
ipfw_obj_tentry *tent, int update)
|
ipfw_obj_tentry *tent, int update)
|
||||||
{
|
{
|
||||||
char xbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_obj_tentry)];
|
ipfw_obj_ctlv *ctlv;
|
||||||
|
char xbuf[sizeof(*oh) + sizeof(ipfw_obj_ctlv) + sizeof(*tent)];
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
memset(xbuf, 0, sizeof(xbuf));
|
memset(xbuf, 0, sizeof(xbuf));
|
||||||
@ -473,8 +474,12 @@ table_do_modify_record(int cmd, ipfw_obj_header *oh,
|
|||||||
oh = (ipfw_obj_header *)xbuf;
|
oh = (ipfw_obj_header *)xbuf;
|
||||||
oh->opheader.version = 1;
|
oh->opheader.version = 1;
|
||||||
|
|
||||||
memcpy(oh + 1, tent, sizeof(*tent));
|
ctlv = (ipfw_obj_ctlv *)(oh + 1);
|
||||||
tent = (ipfw_obj_tentry *)(oh + 1);
|
ctlv->count = 1;
|
||||||
|
ctlv->head.length = sizeof(*ctlv) + sizeof(*tent);
|
||||||
|
|
||||||
|
memcpy(ctlv + 1, tent, sizeof(*tent));
|
||||||
|
tent = (ipfw_obj_tentry *)(ctlv + 1);
|
||||||
if (update != 0)
|
if (update != 0)
|
||||||
tent->head.flags |= IPFW_TF_UPDATE;
|
tent->head.flags |= IPFW_TF_UPDATE;
|
||||||
tent->head.length = sizeof(ipfw_obj_tentry);
|
tent->head.length = sizeof(ipfw_obj_tentry);
|
||||||
@ -501,6 +506,19 @@ table_modify_record(ipfw_obj_header *oh, int ac, char *av[], int add, int update
|
|||||||
tent.idx = 1;
|
tent.idx = 1;
|
||||||
|
|
||||||
tentry_fill_key(oh, &tent, *av, &type, &vtype, &xi);
|
tentry_fill_key(oh, &tent, *av, &type, &vtype, &xi);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* compability layer: auto-create table if not exists
|
||||||
|
*/
|
||||||
|
if (xi.tablename[0] == '\0') {
|
||||||
|
xi.type = type;
|
||||||
|
xi.vtype = vtype;
|
||||||
|
strlcpy(xi.tablename, oh->ntlv.name, sizeof(xi.tablename));
|
||||||
|
fprintf(stderr, "DEPRECATED: inserting data info non-existent "
|
||||||
|
"table %s. (auto-created)\n", xi.tablename);
|
||||||
|
table_do_create(oh, &xi);
|
||||||
|
}
|
||||||
|
|
||||||
oh->ntlv.type = type;
|
oh->ntlv.type = type;
|
||||||
ac--; av++;
|
ac--; av++;
|
||||||
|
|
||||||
@ -659,6 +677,7 @@ tentry_fill_key(ipfw_obj_header *oh, ipfw_obj_tentry *tent, char *key,
|
|||||||
{
|
{
|
||||||
uint8_t type, vtype;
|
uint8_t type, vtype;
|
||||||
int error;
|
int error;
|
||||||
|
char *del;
|
||||||
|
|
||||||
type = 0;
|
type = 0;
|
||||||
vtype = 0;
|
vtype = 0;
|
||||||
@ -678,6 +697,8 @@ tentry_fill_key(ipfw_obj_header *oh, ipfw_obj_tentry *tent, char *key,
|
|||||||
* Compability layer: try to interpret data as CIDR
|
* Compability layer: try to interpret data as CIDR
|
||||||
* before failing.
|
* before failing.
|
||||||
*/
|
*/
|
||||||
|
if ((del = strchr(key, '/')) != NULL)
|
||||||
|
*del = '\0';
|
||||||
if (inet_pton(AF_INET, key, &tent->k.addr6) == 1 ||
|
if (inet_pton(AF_INET, key, &tent->k.addr6) == 1 ||
|
||||||
inet_pton(AF_INET6, key, &tent->k.addr6) == 1) {
|
inet_pton(AF_INET6, key, &tent->k.addr6) == 1) {
|
||||||
/* OK Prepare and send */
|
/* OK Prepare and send */
|
||||||
@ -690,8 +711,10 @@ tentry_fill_key(ipfw_obj_header *oh, ipfw_obj_tentry *tent, char *key,
|
|||||||
} else {
|
} else {
|
||||||
/* Inknown key */
|
/* Inknown key */
|
||||||
errx(EX_USAGE, "Table %s does not exist, cannot guess "
|
errx(EX_USAGE, "Table %s does not exist, cannot guess "
|
||||||
"key type", oh->ntlv.name);
|
"key '%s' type", oh->ntlv.name, key);
|
||||||
}
|
}
|
||||||
|
if (del != NULL)
|
||||||
|
*del = '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
tentry_fill_key_type(key, tent, type);
|
tentry_fill_key_type(key, tent, type);
|
||||||
|
@ -1992,7 +1992,7 @@ ipfw_ctl(struct sockopt *sopt)
|
|||||||
|
|
||||||
case IP_FW_TABLE_XADD: /* IP_FW3 */
|
case IP_FW_TABLE_XADD: /* IP_FW3 */
|
||||||
case IP_FW_TABLE_XDEL: /* IP_FW3 */
|
case IP_FW_TABLE_XDEL: /* IP_FW3 */
|
||||||
error = ipfw_modify_table(chain, op3, &sdata);
|
error = ipfw_manage_table_ent(chain, op3, &sdata);
|
||||||
break;
|
break;
|
||||||
case IP_FW_TABLE_XFIND: /* IP_FW3 */
|
case IP_FW_TABLE_XFIND: /* IP_FW3 */
|
||||||
error = ipfw_find_table_entry(chain, op3, &sdata);
|
error = ipfw_find_table_entry(chain, op3, &sdata);
|
||||||
|
@ -77,14 +77,14 @@ struct table_config {
|
|||||||
struct named_object no;
|
struct named_object no;
|
||||||
uint8_t vtype; /* format table type */
|
uint8_t vtype; /* format table type */
|
||||||
uint8_t linked; /* 1 if already linked */
|
uint8_t linked; /* 1 if already linked */
|
||||||
uint16_t spare0;
|
uint16_t spare;
|
||||||
uint32_t count; /* Number of records */
|
uint32_t count; /* Number of records */
|
||||||
|
uint64_t flags; /* state flags */
|
||||||
char tablename[64]; /* table name */
|
char tablename[64]; /* table name */
|
||||||
struct table_algo *ta; /* Callbacks for given algo */
|
struct table_algo *ta; /* Callbacks for given algo */
|
||||||
void *astate; /* algorithm state */
|
void *astate; /* algorithm state */
|
||||||
struct table_info ti; /* data to put to table_info */
|
struct table_info ti; /* data to put to table_info */
|
||||||
};
|
};
|
||||||
#define TABLE_SET(set) ((V_fw_tables_sets != 0) ? set : 0)
|
|
||||||
|
|
||||||
struct tables_config {
|
struct tables_config {
|
||||||
struct namedobj_instance *namehash;
|
struct namedobj_instance *namehash;
|
||||||
@ -95,9 +95,11 @@ struct tables_config {
|
|||||||
static struct table_config *find_table(struct namedobj_instance *ni,
|
static struct table_config *find_table(struct namedobj_instance *ni,
|
||||||
struct tid_info *ti);
|
struct tid_info *ti);
|
||||||
static struct table_config *alloc_table_config(struct namedobj_instance *ni,
|
static struct table_config *alloc_table_config(struct namedobj_instance *ni,
|
||||||
struct tid_info *ti, struct table_algo *ta, char *adata);
|
struct tid_info *ti, struct table_algo *ta, char *adata, uint8_t vtype);
|
||||||
static void free_table_config(struct namedobj_instance *ni,
|
static void free_table_config(struct namedobj_instance *ni,
|
||||||
struct table_config *tc);
|
struct table_config *tc);
|
||||||
|
static int create_table_internal(struct ip_fw_chain *ch, struct tid_info *ti,
|
||||||
|
char *aname, uint8_t vtype);
|
||||||
static void link_table(struct ip_fw_chain *chain, struct table_config *tc);
|
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 unlink_table(struct ip_fw_chain *chain, struct table_config *tc);
|
||||||
static void free_table_state(void **state, void **xstate, uint8_t type);
|
static void free_table_state(void **state, void **xstate, uint8_t type);
|
||||||
@ -110,11 +112,13 @@ static int dump_table_xentry(void *e, void *arg);
|
|||||||
|
|
||||||
static int ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt_data *sd);
|
static int ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt_data *sd);
|
||||||
static int ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt_data *sd);
|
static int ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt_data *sd);
|
||||||
static int ipfw_modify_table_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
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_modify_table_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 modify_table(struct ip_fw_chain *ch, struct table_config *tc,
|
||||||
|
struct table_algo *ta, void *ta_buf, uint64_t pflags);
|
||||||
static int destroy_table(struct ip_fw_chain *ch, struct tid_info *ti);
|
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,
|
||||||
@ -130,11 +134,12 @@ int
|
|||||||
add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
|
add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
|
||||||
struct tentry_info *tei)
|
struct tentry_info *tei)
|
||||||
{
|
{
|
||||||
struct table_config *tc, *tc_new;
|
struct table_config *tc;
|
||||||
struct table_algo *ta;
|
struct table_algo *ta;
|
||||||
struct namedobj_instance *ni;
|
struct namedobj_instance *ni;
|
||||||
uint16_t kidx;
|
uint16_t kidx;
|
||||||
int error;
|
int error;
|
||||||
|
uint64_t aflags;
|
||||||
char ta_buf[128];
|
char ta_buf[128];
|
||||||
|
|
||||||
IPFW_UH_WLOCK(ch);
|
IPFW_UH_WLOCK(ch);
|
||||||
@ -154,72 +159,65 @@ add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
|
|||||||
/* Reference and unlock */
|
/* Reference and unlock */
|
||||||
tc->no.refcnt++;
|
tc->no.refcnt++;
|
||||||
ta = tc->ta;
|
ta = tc->ta;
|
||||||
|
aflags = tc->flags;
|
||||||
}
|
}
|
||||||
IPFW_UH_WUNLOCK(ch);
|
IPFW_UH_WUNLOCK(ch);
|
||||||
|
|
||||||
tc_new = NULL;
|
|
||||||
if (tc == NULL) {
|
if (tc == NULL) {
|
||||||
/* Table not found. We have to create new one */
|
/* Compability mode: create new table for old clients */
|
||||||
if ((ta = find_table_algo(CHAIN_TO_TCFG(ch), ti, NULL)) == NULL)
|
if ((tei->flags & TEI_FLAGS_COMPAT) == 0)
|
||||||
return (ENOTSUP);
|
return (ESRCH);
|
||||||
|
|
||||||
tc_new = alloc_table_config(ni, ti, ta, NULL);
|
error = create_table_internal(ch, ti, NULL, IPFW_VTYPE_U32);
|
||||||
if (tc_new == NULL)
|
|
||||||
return (ENOMEM);
|
if (error != 0)
|
||||||
|
return (error);
|
||||||
|
|
||||||
|
/* Let's try to find & reference another time */
|
||||||
|
IPFW_UH_WLOCK(ch);
|
||||||
|
if ((tc = find_table(ni, ti)) == NULL) {
|
||||||
|
IPFW_UH_WUNLOCK(ch);
|
||||||
|
return (EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tc->no.type != ti->type) {
|
||||||
|
IPFW_UH_WUNLOCK(ch);
|
||||||
|
return (EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reference and unlock */
|
||||||
|
tc->no.refcnt++;
|
||||||
|
ta = tc->ta;
|
||||||
|
aflags = tc->flags;
|
||||||
|
|
||||||
|
IPFW_UH_WUNLOCK(ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aflags != 0) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Previous add/delete call returned non-zero state.
|
||||||
|
* Run appropriate handler.
|
||||||
|
*/
|
||||||
|
error = modify_table(ch, tc, ta, &ta_buf, aflags);
|
||||||
|
if (error != 0)
|
||||||
|
return (error);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Prepare record (allocate memory) */
|
/* Prepare record (allocate memory) */
|
||||||
memset(&ta_buf, 0, sizeof(ta_buf));
|
memset(&ta_buf, 0, sizeof(ta_buf));
|
||||||
error = ta->prepare_add(tei, &ta_buf);
|
error = ta->prepare_add(tei, &ta_buf);
|
||||||
if (error != 0) {
|
if (error != 0)
|
||||||
if (tc_new != NULL)
|
|
||||||
free_table_config(ni, tc_new);
|
|
||||||
return (error);
|
return (error);
|
||||||
}
|
|
||||||
|
|
||||||
IPFW_UH_WLOCK(ch);
|
IPFW_UH_WLOCK(ch);
|
||||||
|
|
||||||
ni = CHAIN_TO_NI(ch);
|
ni = CHAIN_TO_NI(ch);
|
||||||
|
|
||||||
if (tc == NULL) {
|
/* Drop reference we've used in first search */
|
||||||
/* Check if another table has been allocated by other thread */
|
tc->no.refcnt--;
|
||||||
if ((tc = find_table(ni, ti)) != NULL) {
|
/* Update aflags since it can be changed after previous read */
|
||||||
|
aflags = tc->flags;
|
||||||
/*
|
|
||||||
* Check if algoritm is the same since we've
|
|
||||||
* already allocated state using @ta algoritm
|
|
||||||
* callbacks.
|
|
||||||
*/
|
|
||||||
if (tc->ta != ta) {
|
|
||||||
IPFW_UH_WUNLOCK(ch);
|
|
||||||
error = EINVAL;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* Table still does not exists */
|
|
||||||
|
|
||||||
/* Allocate table index. */
|
|
||||||
if (ipfw_objhash_alloc_idx(ni, &kidx) != 0) {
|
|
||||||
/* Index full. */
|
|
||||||
IPFW_UH_WUNLOCK(ch);
|
|
||||||
printf("Unable to allocate index for table %s"
|
|
||||||
"in set %u. Consider increasing "
|
|
||||||
"net.inet.ip.fw.tables_max",
|
|
||||||
tc_new->no.name, ti->set);
|
|
||||||
error = EBUSY;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set tc_new to zero not to free it afterwards. */
|
|
||||||
tc = tc_new;
|
|
||||||
tc_new = NULL;
|
|
||||||
/* Save kidx */
|
|
||||||
tc->no.kidx = kidx;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* Drop reference we've used in first search */
|
|
||||||
tc->no.refcnt--;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We've got valid table in @tc. Let's add data */
|
/* We've got valid table in @tc. Let's add data */
|
||||||
kidx = tc->no.kidx;
|
kidx = tc->no.kidx;
|
||||||
@ -227,10 +225,7 @@ add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
|
|||||||
|
|
||||||
IPFW_WLOCK(ch);
|
IPFW_WLOCK(ch);
|
||||||
|
|
||||||
if (tc->linked == 0)
|
error = ta->add(tc->astate, KIDX_TO_TI(ch, kidx), tei, &ta_buf, &aflags);
|
||||||
link_table(ch, tc);
|
|
||||||
|
|
||||||
error = ta->add(tc->astate, KIDX_TO_TI(ch, kidx), tei, &ta_buf);
|
|
||||||
|
|
||||||
IPFW_WUNLOCK(ch);
|
IPFW_WUNLOCK(ch);
|
||||||
|
|
||||||
@ -238,11 +233,10 @@ add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
|
|||||||
if (error == 0 && (tei->flags & TEI_FLAGS_UPDATED) == 0)
|
if (error == 0 && (tei->flags & TEI_FLAGS_UPDATED) == 0)
|
||||||
tc->count++;
|
tc->count++;
|
||||||
|
|
||||||
|
tc->flags = aflags;
|
||||||
|
|
||||||
IPFW_UH_WUNLOCK(ch);
|
IPFW_UH_WUNLOCK(ch);
|
||||||
|
|
||||||
done:
|
|
||||||
if (tc_new != NULL)
|
|
||||||
free_table_config(ni, tc_new);
|
|
||||||
/* Run cleaning callback anyway */
|
/* Run cleaning callback anyway */
|
||||||
ta->flush_entry(tei, &ta_buf);
|
ta->flush_entry(tei, &ta_buf);
|
||||||
|
|
||||||
@ -258,6 +252,7 @@ del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
|
|||||||
struct namedobj_instance *ni;
|
struct namedobj_instance *ni;
|
||||||
uint16_t kidx;
|
uint16_t kidx;
|
||||||
int error;
|
int error;
|
||||||
|
uint64_t aflags;
|
||||||
char ta_buf[128];
|
char ta_buf[128];
|
||||||
|
|
||||||
IPFW_UH_WLOCK(ch);
|
IPFW_UH_WLOCK(ch);
|
||||||
@ -272,8 +267,34 @@ del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
|
|||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
aflags = tc->flags;
|
||||||
ta = tc->ta;
|
ta = tc->ta;
|
||||||
|
|
||||||
|
if (aflags != 0) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Give the chance to algo to shrink its state.
|
||||||
|
*/
|
||||||
|
tc->no.refcnt++;
|
||||||
|
IPFW_UH_WUNLOCK(ch);
|
||||||
|
memset(&ta_buf, 0, sizeof(ta_buf));
|
||||||
|
|
||||||
|
error = modify_table(ch, tc, ta, &ta_buf, aflags);
|
||||||
|
|
||||||
|
IPFW_UH_WLOCK(ch);
|
||||||
|
tc->no.refcnt--;
|
||||||
|
aflags = tc->flags;
|
||||||
|
|
||||||
|
if (error != 0) {
|
||||||
|
IPFW_UH_WUNLOCK(ch);
|
||||||
|
return (error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We assume ta_buf size is enough for storing
|
||||||
|
* prepare_del() key, so we're running under UH_LOCK here.
|
||||||
|
*/
|
||||||
memset(&ta_buf, 0, sizeof(ta_buf));
|
memset(&ta_buf, 0, sizeof(ta_buf));
|
||||||
if ((error = ta->prepare_del(tei, &ta_buf)) != 0) {
|
if ((error = ta->prepare_del(tei, &ta_buf)) != 0) {
|
||||||
IPFW_UH_WUNLOCK(ch);
|
IPFW_UH_WUNLOCK(ch);
|
||||||
@ -283,11 +304,12 @@ del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
|
|||||||
kidx = tc->no.kidx;
|
kidx = tc->no.kidx;
|
||||||
|
|
||||||
IPFW_WLOCK(ch);
|
IPFW_WLOCK(ch);
|
||||||
error = ta->del(tc->astate, KIDX_TO_TI(ch, kidx), tei, &ta_buf);
|
error = ta->del(tc->astate, KIDX_TO_TI(ch, kidx), tei, &ta_buf,&aflags);
|
||||||
IPFW_WUNLOCK(ch);
|
IPFW_WUNLOCK(ch);
|
||||||
|
|
||||||
if (error == 0)
|
if (error == 0)
|
||||||
tc->count--;
|
tc->count--;
|
||||||
|
tc->flags = aflags;
|
||||||
|
|
||||||
IPFW_UH_WUNLOCK(ch);
|
IPFW_UH_WUNLOCK(ch);
|
||||||
|
|
||||||
@ -296,18 +318,62 @@ del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
|
|||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Runs callbacks to modify algo state (typically, table resize).
|
||||||
|
*
|
||||||
|
* Callbacks order:
|
||||||
|
* 1) alloc_modify (no locks, M_WAITOK) - alloc new state based on @pflags.
|
||||||
|
* 2) prepare_modifyt (UH_WLOCK) - copy old data into new storage
|
||||||
|
* 3) modify (UH_WLOCK + WLOCK) - switch pointers
|
||||||
|
* 4) flush_modify (no locks) - free state, if needed
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
modify_table(struct ip_fw_chain *ch, struct table_config *tc,
|
||||||
|
struct table_algo *ta, void *ta_buf, uint64_t pflags)
|
||||||
|
{
|
||||||
|
struct table_info *ti;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
error = ta->prepare_mod(ta_buf, &pflags);
|
||||||
|
if (error != 0)
|
||||||
|
return (error);
|
||||||
|
|
||||||
|
IPFW_UH_WLOCK(ch);
|
||||||
|
ti = KIDX_TO_TI(ch, tc->no.kidx);
|
||||||
|
|
||||||
|
error = ta->fill_mod(tc->astate, ti, &ta_buf, &pflags);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* prepare_mofify may return zero in @pflags to
|
||||||
|
* indicate that modifications are not unnesessary.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (error == 0 && pflags != 0) {
|
||||||
|
/* Do actual modification */
|
||||||
|
IPFW_WLOCK(ch);
|
||||||
|
ta->modify(tc->astate, ti, &ta_buf, pflags);
|
||||||
|
IPFW_WUNLOCK(ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
IPFW_UH_WUNLOCK(ch);
|
||||||
|
|
||||||
|
ta->flush_mod(ta_buf);
|
||||||
|
|
||||||
|
return (error);
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
ipfw_modify_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
ipfw_manage_table_ent(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
||||||
struct sockopt_data *sd)
|
struct sockopt_data *sd)
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
switch (op3->version) {
|
switch (op3->version) {
|
||||||
case 0:
|
case 0:
|
||||||
error = ipfw_modify_table_v0(ch, op3, sd);
|
error = ipfw_manage_table_ent_v0(ch, op3, sd);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
error = ipfw_modify_table_v1(ch, op3, sd);
|
error = ipfw_manage_table_ent_v1(ch, op3, sd);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
error = ENOTSUP;
|
error = ENOTSUP;
|
||||||
@ -324,7 +390,7 @@ ipfw_modify_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
|||||||
* Returns 0 on success
|
* Returns 0 on success
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
ipfw_modify_table_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
ipfw_manage_table_ent_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
||||||
struct sockopt_data *sd)
|
struct sockopt_data *sd)
|
||||||
{
|
{
|
||||||
ipfw_table_xentry *xent;
|
ipfw_table_xentry *xent;
|
||||||
@ -350,6 +416,7 @@ ipfw_modify_table_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
|||||||
tei.masklen = xent->masklen;
|
tei.masklen = xent->masklen;
|
||||||
tei.value = xent->value;
|
tei.value = xent->value;
|
||||||
/* Old requests compability */
|
/* Old requests compability */
|
||||||
|
tei.flags = TEI_FLAGS_COMPAT;
|
||||||
if (xent->type == IPFW_TABLE_CIDR) {
|
if (xent->type == IPFW_TABLE_CIDR) {
|
||||||
if (xent->len - hdrlen == sizeof(in_addr_t))
|
if (xent->len - hdrlen == sizeof(in_addr_t))
|
||||||
tei.subtype = AF_INET;
|
tei.subtype = AF_INET;
|
||||||
@ -371,22 +438,25 @@ ipfw_modify_table_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
|||||||
/*
|
/*
|
||||||
* Adds or deletes record in table.
|
* Adds or deletes record in table.
|
||||||
* Data layout (v1)(current):
|
* Data layout (v1)(current):
|
||||||
* Request: [ ipfw_obj_header ipfw_obj_tentry ]
|
* Request: [ ipfw_obj_header
|
||||||
|
* ipfw_obj_ctlv(IPFW_TLV_TBLENT_LIST) [ ipfw_obj_tentry x N ]
|
||||||
|
* ]
|
||||||
*
|
*
|
||||||
* Returns 0 on success
|
* Returns 0 on success
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
ipfw_modify_table_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
ipfw_manage_table_ent_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
||||||
struct sockopt_data *sd)
|
struct sockopt_data *sd)
|
||||||
{
|
{
|
||||||
ipfw_obj_tentry *tent;
|
ipfw_obj_tentry *tent;
|
||||||
|
ipfw_obj_ctlv *ctlv;
|
||||||
ipfw_obj_header *oh;
|
ipfw_obj_header *oh;
|
||||||
struct tentry_info tei;
|
struct tentry_info tei;
|
||||||
struct tid_info ti;
|
struct tid_info ti;
|
||||||
int error, read;
|
int error, read;
|
||||||
|
|
||||||
/* Check minimum header size */
|
/* Check minimum header size */
|
||||||
if (sd->valsize < (sizeof(*oh) + sizeof(*tent)))
|
if (sd->valsize < (sizeof(*oh) + sizeof(*ctlv)))
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
|
|
||||||
/* Check if passed data is too long */
|
/* Check if passed data is too long */
|
||||||
@ -401,8 +471,21 @@ ipfw_modify_table_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
|||||||
|
|
||||||
read = sizeof(*oh);
|
read = sizeof(*oh);
|
||||||
|
|
||||||
|
ctlv = (ipfw_obj_ctlv *)(oh + 1);
|
||||||
|
if (ctlv->head.length + read != sd->valsize)
|
||||||
|
return (EINVAL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: permit adding multiple entries for given table
|
||||||
|
* at once
|
||||||
|
*/
|
||||||
|
if (ctlv->count != 1)
|
||||||
|
return (EOPNOTSUPP);
|
||||||
|
|
||||||
|
read += sizeof(*ctlv);
|
||||||
|
|
||||||
/* Assume tentry may grow to support larger keys */
|
/* Assume tentry may grow to support larger keys */
|
||||||
tent = (ipfw_obj_tentry *)(oh + 1);
|
tent = (ipfw_obj_tentry *)(ctlv + 1);
|
||||||
if (tent->head.length < sizeof(*tent) ||
|
if (tent->head.length < sizeof(*tent) ||
|
||||||
tent->head.length + read > sd->valsize)
|
tent->head.length + read > sd->valsize)
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
@ -1071,8 +1154,6 @@ ipfw_create_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
|||||||
struct tid_info ti;
|
struct tid_info ti;
|
||||||
struct namedobj_instance *ni;
|
struct namedobj_instance *ni;
|
||||||
struct table_config *tc;
|
struct table_config *tc;
|
||||||
struct table_algo *ta;
|
|
||||||
uint16_t kidx;
|
|
||||||
|
|
||||||
if (sd->valsize != sizeof(*oh) + sizeof(ipfw_xtable_info))
|
if (sd->valsize != sizeof(*oh) + sizeof(ipfw_xtable_info))
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
@ -1105,23 +1186,50 @@ ipfw_create_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
|||||||
IPFW_UH_RUNLOCK(ch);
|
IPFW_UH_RUNLOCK(ch);
|
||||||
return (EEXIST);
|
return (EEXIST);
|
||||||
}
|
}
|
||||||
ta = find_table_algo(CHAIN_TO_TCFG(ch), &ti, aname);
|
|
||||||
IPFW_UH_RUNLOCK(ch);
|
IPFW_UH_RUNLOCK(ch);
|
||||||
|
|
||||||
|
return (create_table_internal(ch, &ti, aname, i->vtype));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Creates new table based on @ti and @aname.
|
||||||
|
*
|
||||||
|
* Relies on table name checking inside find_name_tlv()
|
||||||
|
* Assume @aname to be checked and valid.
|
||||||
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
create_table_internal(struct ip_fw_chain *ch, struct tid_info *ti,
|
||||||
|
char *aname, uint8_t vtype)
|
||||||
|
{
|
||||||
|
struct namedobj_instance *ni;
|
||||||
|
struct table_config *tc;
|
||||||
|
struct table_algo *ta;
|
||||||
|
uint16_t kidx;
|
||||||
|
|
||||||
|
ni = CHAIN_TO_NI(ch);
|
||||||
|
|
||||||
|
ta = find_table_algo(CHAIN_TO_TCFG(ch), ti, aname);
|
||||||
if (ta == NULL)
|
if (ta == NULL)
|
||||||
return (ENOTSUP);
|
return (ENOTSUP);
|
||||||
|
|
||||||
if ((tc = alloc_table_config(ni, &ti, ta, aname)) == NULL)
|
if ((tc = alloc_table_config(ni, ti, ta, aname, vtype)) == NULL)
|
||||||
return (ENOMEM);
|
return (ENOMEM);
|
||||||
/* TODO: move inside alloc_table_config() */
|
|
||||||
tc->vtype = i->vtype;
|
|
||||||
|
|
||||||
IPFW_UH_WLOCK(ch);
|
IPFW_UH_WLOCK(ch);
|
||||||
|
|
||||||
|
/* Check if table has been already created */
|
||||||
|
if (find_table(ni, ti) != NULL) {
|
||||||
|
IPFW_UH_WUNLOCK(ch);
|
||||||
|
free_table_config(ni, tc);
|
||||||
|
return (EEXIST);
|
||||||
|
}
|
||||||
|
|
||||||
if (ipfw_objhash_alloc_idx(ni, &kidx) != 0) {
|
if (ipfw_objhash_alloc_idx(ni, &kidx) != 0) {
|
||||||
IPFW_UH_WUNLOCK(ch);
|
IPFW_UH_WUNLOCK(ch);
|
||||||
printf("Unable to allocate table index for table %s in set %u."
|
printf("Unable to allocate table index."
|
||||||
" Consider increasing net.inet.ip.fw.tables_max",
|
" Consider increasing net.inet.ip.fw.tables_max");
|
||||||
tname, ti.set);
|
|
||||||
free_table_config(ni, tc);
|
free_table_config(ni, tc);
|
||||||
return (EBUSY);
|
return (EBUSY);
|
||||||
}
|
}
|
||||||
@ -1692,7 +1800,7 @@ find_table(struct namedobj_instance *ni, struct tid_info *ti)
|
|||||||
|
|
||||||
static struct table_config *
|
static struct table_config *
|
||||||
alloc_table_config(struct namedobj_instance *ni, struct tid_info *ti,
|
alloc_table_config(struct namedobj_instance *ni, struct tid_info *ti,
|
||||||
struct table_algo *ta, char *aname)
|
struct table_algo *ta, char *aname, uint8_t vtype)
|
||||||
{
|
{
|
||||||
char *name, bname[16];
|
char *name, bname[16];
|
||||||
struct table_config *tc;
|
struct table_config *tc;
|
||||||
@ -1719,7 +1827,10 @@ alloc_table_config(struct namedobj_instance *ni, struct tid_info *ti,
|
|||||||
tc->ta = ta;
|
tc->ta = ta;
|
||||||
strlcpy(tc->tablename, name, sizeof(tc->tablename));
|
strlcpy(tc->tablename, name, sizeof(tc->tablename));
|
||||||
/* Set default value type to u32 for compability reasons */
|
/* Set default value type to u32 for compability reasons */
|
||||||
tc->vtype = IPFW_VTYPE_U32;
|
if (vtype == 0)
|
||||||
|
tc->vtype = IPFW_VTYPE_U32;
|
||||||
|
else
|
||||||
|
tc->vtype = vtype;
|
||||||
|
|
||||||
if (ti->tlvs == NULL) {
|
if (ti->tlvs == NULL) {
|
||||||
tc->no.compat = 1;
|
tc->no.compat = 1;
|
||||||
@ -2058,7 +2169,7 @@ ipfw_rewrite_table_uidx(struct ip_fw_chain *chain,
|
|||||||
error = ENOTSUP;
|
error = ENOTSUP;
|
||||||
goto free;
|
goto free;
|
||||||
}
|
}
|
||||||
tc = alloc_table_config(ni, &ti, ta, NULL);
|
tc = alloc_table_config(ni, &ti, ta, NULL, IPFW_VTYPE_U32);
|
||||||
|
|
||||||
if (tc == NULL) {
|
if (tc == NULL) {
|
||||||
error = ENOMEM;
|
error = ENOMEM;
|
||||||
|
@ -41,7 +41,6 @@ struct table_info {
|
|||||||
u_long data; /* Hints for given func */
|
u_long data; /* Hints for given func */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/* Internal structures for handling sockopt data */
|
/* Internal structures for handling sockopt data */
|
||||||
struct tid_info {
|
struct tid_info {
|
||||||
uint32_t set; /* table set */
|
uint32_t set; /* table set */
|
||||||
@ -61,16 +60,25 @@ struct tentry_info {
|
|||||||
};
|
};
|
||||||
#define TEI_FLAGS_UPDATE 0x01 /* Update record if exists */
|
#define TEI_FLAGS_UPDATE 0x01 /* Update record if exists */
|
||||||
#define TEI_FLAGS_UPDATED 0x02 /* Entry has been updated */
|
#define TEI_FLAGS_UPDATED 0x02 /* Entry has been updated */
|
||||||
|
#define TEI_FLAGS_COMPAT 0x04 /* Called from old ABI */
|
||||||
|
|
||||||
typedef int (ta_init)(void **ta_state, struct table_info *ti, char *data);
|
typedef int (ta_init)(void **ta_state, struct table_info *ti, char *data);
|
||||||
typedef void (ta_destroy)(void *ta_state, struct table_info *ti);
|
typedef void (ta_destroy)(void *ta_state, struct table_info *ti);
|
||||||
typedef int (ta_prepare_add)(struct tentry_info *tei, void *ta_buf);
|
typedef int (ta_prepare_add)(struct tentry_info *tei, void *ta_buf);
|
||||||
typedef int (ta_prepare_del)(struct tentry_info *tei, void *ta_buf);
|
typedef int (ta_prepare_del)(struct tentry_info *tei, void *ta_buf);
|
||||||
typedef int (ta_add)(void *ta_state, struct table_info *ti,
|
typedef int (ta_add)(void *ta_state, struct table_info *ti,
|
||||||
struct tentry_info *tei, void *ta_buf);
|
struct tentry_info *tei, void *ta_buf, uint64_t *pflags);
|
||||||
typedef int (ta_del)(void *ta_state, struct table_info *ti,
|
typedef int (ta_del)(void *ta_state, struct table_info *ti,
|
||||||
struct tentry_info *tei, void *ta_buf);
|
struct tentry_info *tei, void *ta_buf, uint64_t *pflags);
|
||||||
typedef void (ta_flush_entry)(struct tentry_info *tei, void *ta_buf);
|
typedef void (ta_flush_entry)(struct tentry_info *tei, void *ta_buf);
|
||||||
|
|
||||||
|
typedef int (ta_prepare_mod)(void *ta_buf, uint64_t *pflags);
|
||||||
|
typedef int (ta_fill_mod)(void *ta_state, struct table_info *ti,
|
||||||
|
void *ta_buf, uint64_t *pflags);
|
||||||
|
typedef int (ta_modify)(void *ta_state, struct table_info *ti,
|
||||||
|
void *ta_buf, uint64_t pflags);
|
||||||
|
typedef void (ta_flush_mod)(void *ta_buf);
|
||||||
|
|
||||||
typedef void (ta_print_config)(void *ta_state, struct table_info *ti, char *buf,
|
typedef void (ta_print_config)(void *ta_state, struct table_info *ti, char *buf,
|
||||||
size_t bufsize);
|
size_t bufsize);
|
||||||
|
|
||||||
@ -93,6 +101,10 @@ struct table_algo {
|
|||||||
ta_add *add;
|
ta_add *add;
|
||||||
ta_del *del;
|
ta_del *del;
|
||||||
ta_flush_entry *flush_entry;
|
ta_flush_entry *flush_entry;
|
||||||
|
ta_prepare_mod *prepare_mod;
|
||||||
|
ta_fill_mod *fill_mod;
|
||||||
|
ta_modify *modify;
|
||||||
|
ta_flush_mod *flush_mod;
|
||||||
ta_foreach *foreach;
|
ta_foreach *foreach;
|
||||||
ta_dump_tentry *dump_tentry;
|
ta_dump_tentry *dump_tentry;
|
||||||
ta_print_config *print_config;
|
ta_print_config *print_config;
|
||||||
@ -116,7 +128,7 @@ int ipfw_find_table_entry(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
|||||||
struct sockopt_data *sd);
|
struct sockopt_data *sd);
|
||||||
int ipfw_create_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
int ipfw_create_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
||||||
struct sockopt_data *sd);
|
struct sockopt_data *sd);
|
||||||
int ipfw_modify_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
int ipfw_manage_table_ent(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
||||||
struct sockopt_data *sd);
|
struct sockopt_data *sd);
|
||||||
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);
|
||||||
|
@ -380,7 +380,7 @@ ta_prepare_add_cidr(struct tentry_info *tei, void *ta_buf)
|
|||||||
|
|
||||||
static int
|
static int
|
||||||
ta_add_cidr(void *ta_state, struct table_info *ti,
|
ta_add_cidr(void *ta_state, struct table_info *ti,
|
||||||
struct tentry_info *tei, void *ta_buf)
|
struct tentry_info *tei, void *ta_buf, uint64_t *pflags)
|
||||||
{
|
{
|
||||||
struct radix_node_head *rnh;
|
struct radix_node_head *rnh;
|
||||||
struct radix_node *rn;
|
struct radix_node *rn;
|
||||||
@ -488,7 +488,7 @@ ta_prepare_del_cidr(struct tentry_info *tei, void *ta_buf)
|
|||||||
|
|
||||||
static int
|
static int
|
||||||
ta_del_cidr(void *ta_state, struct table_info *ti,
|
ta_del_cidr(void *ta_state, struct table_info *ti,
|
||||||
struct tentry_info *tei, void *ta_buf)
|
struct tentry_info *tei, void *ta_buf, uint64_t *pflags)
|
||||||
{
|
{
|
||||||
struct radix_node_head *rnh;
|
struct radix_node_head *rnh;
|
||||||
struct radix_node *rn;
|
struct radix_node *rn;
|
||||||
@ -644,7 +644,7 @@ ta_prepare_add_iface(struct tentry_info *tei, void *ta_buf)
|
|||||||
|
|
||||||
static int
|
static int
|
||||||
ta_add_iface(void *ta_state, struct table_info *ti,
|
ta_add_iface(void *ta_state, struct table_info *ti,
|
||||||
struct tentry_info *tei, void *ta_buf)
|
struct tentry_info *tei, void *ta_buf, uint64_t *pflags)
|
||||||
{
|
{
|
||||||
struct radix_node_head *rnh;
|
struct radix_node_head *rnh;
|
||||||
struct radix_node *rn;
|
struct radix_node *rn;
|
||||||
@ -718,7 +718,7 @@ ta_prepare_del_iface(struct tentry_info *tei, void *ta_buf)
|
|||||||
|
|
||||||
static int
|
static int
|
||||||
ta_del_iface(void *ta_state, struct table_info *ti,
|
ta_del_iface(void *ta_state, struct table_info *ti,
|
||||||
struct tentry_info *tei, void *ta_buf)
|
struct tentry_info *tei, void *ta_buf, uint64_t *pflags)
|
||||||
{
|
{
|
||||||
struct radix_node_head *rnh;
|
struct radix_node_head *rnh;
|
||||||
struct radix_node *rn;
|
struct radix_node *rn;
|
||||||
|
Loading…
Reference in New Issue
Block a user