Make named objects set-aware. Now it is possible to create named
objects with the same name in different sets. Add optional manage_sets() callback to objects rewriting framework. It is intended to implement handler for moving and swapping named object's sets. Add ipfw_obj_manage_sets() function that implements generic sets handler. Use new callback to implement sets support for lookup tables. External actions objects are global and they don't support sets. Modify eaction_findbyname() to reflect this. ipfw(8) now may fail to move rules or sets, because some named objects in target set may have conflicting names. Note that ipfw_obj_ntlv type was changed, but since lookup tables actually didn't support sets, this change is harmless. Obtained from: Yandex LLC Sponsored by: Yandex LLC
This commit is contained in:
parent
eba4e79241
commit
f79f8e9de8
@ -2280,6 +2280,9 @@ ipfw_sets_handler(char *av[])
|
||||
if (!isdigit(*(av[2])) || rt.new_set > RESVD_SET)
|
||||
errx(EX_DATAERR, "invalid dest. set %s\n", av[1]);
|
||||
i = do_range_cmd(cmd, &rt);
|
||||
if (i < 0)
|
||||
err(EX_OSERR, "failed to move %s",
|
||||
cmd == IP_FW_SET_MOVE ? "set": "rule");
|
||||
} else if (_substrcmp(*av, "disable") == 0 ||
|
||||
_substrcmp(*av, "enable") == 0 ) {
|
||||
int which = _substrcmp(*av, "enable") == 0 ? 1 : 0;
|
||||
|
@ -791,9 +791,9 @@ typedef struct _ipfw_obj_tlv {
|
||||
typedef struct _ipfw_obj_ntlv {
|
||||
ipfw_obj_tlv head; /* TLV header */
|
||||
uint16_t idx; /* Name index */
|
||||
uint8_t spare; /* unused */
|
||||
uint8_t set; /* set, if applicable */
|
||||
uint8_t type; /* object type, if applicable */
|
||||
uint32_t set; /* set, if applicable */
|
||||
uint32_t spare; /* unused */
|
||||
char name[64]; /* Null-terminated name */
|
||||
} ipfw_obj_ntlv;
|
||||
|
||||
|
@ -137,10 +137,28 @@ static int
|
||||
eaction_findbyname(struct ip_fw_chain *ch, struct tid_info *ti,
|
||||
struct named_object **pno)
|
||||
{
|
||||
ipfw_obj_ntlv *ntlv;
|
||||
|
||||
EACTION_DEBUG("uidx %u, type %u", ti->uidx, ti->type);
|
||||
return (ipfw_objhash_find_type(CHAIN_TO_SRV(ch), ti,
|
||||
IPFW_TLV_EACTION, pno));
|
||||
if (ti->tlvs == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
/* Search ntlv in the buffer provided by user */
|
||||
ntlv = ipfw_find_name_tlv_type(ti->tlvs, ti->tlen, ti->uidx,
|
||||
IPFW_TLV_EACTION);
|
||||
if (ntlv == NULL)
|
||||
return (EINVAL);
|
||||
EACTION_DEBUG("name %s, uidx %u, type %u", ntlv->name,
|
||||
ti->uidx, ti->type);
|
||||
/*
|
||||
* Search named object with corresponding name.
|
||||
* Since eaction objects are global - ignore the set value
|
||||
* and use zero instead.
|
||||
*/
|
||||
*pno = ipfw_objhash_lookup_name_type(CHAIN_TO_SRV(ch),
|
||||
0, IPFW_TLV_EACTION, ntlv->name);
|
||||
if (*pno == NULL)
|
||||
return (ESRCH);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static struct named_object *
|
||||
|
@ -315,9 +315,10 @@ struct named_object {
|
||||
char *name; /* object name */
|
||||
uint16_t etlv; /* Export TLV id */
|
||||
uint8_t subtype;/* object subtype within class */
|
||||
uint8_t spare[3];
|
||||
uint8_t set; /* set object belongs to */
|
||||
uint16_t kidx; /* object kernel index */
|
||||
uint32_t set; /* set object belongs to */
|
||||
uint16_t spare;
|
||||
uint32_t ocnt; /* object counter for internal use */
|
||||
uint32_t refcnt; /* number of references */
|
||||
};
|
||||
TAILQ_HEAD(namedobjects_head, named_object);
|
||||
@ -571,6 +572,21 @@ typedef int (ipfw_obj_create_cb)(struct ip_fw_chain *ch, struct tid_info *ti,
|
||||
*/
|
||||
typedef void (ipfw_obj_destroy_cb)(struct ip_fw_chain *ch,
|
||||
struct named_object *no);
|
||||
/*
|
||||
* Sets handler callback. Handles moving and swaping set of named object.
|
||||
* SWAP_ALL moves all named objects from set `set' to `new_set' and vise versa;
|
||||
* TEST_ALL checks that there aren't any named object with conflicting names;
|
||||
* MOVE_ALL moves all named objects from set `set' to `new_set';
|
||||
* COUNT_ONE used to count number of references used by object with kidx `set';
|
||||
* TEST_ONE checks that named object with kidx `set' can be moved to `new_set`;
|
||||
* MOVE_ONE moves named object with kidx `set' to set `new_set'.
|
||||
*/
|
||||
enum ipfw_sets_cmd {
|
||||
SWAP_ALL = 0, TEST_ALL, MOVE_ALL, COUNT_ONE, TEST_ONE, MOVE_ONE
|
||||
};
|
||||
typedef int (ipfw_obj_sets_cb)(struct ip_fw_chain *ch,
|
||||
uint16_t set, uint8_t new_set, enum ipfw_sets_cmd cmd);
|
||||
|
||||
|
||||
struct opcode_obj_rewrite {
|
||||
uint32_t opcode; /* Opcode to act upon */
|
||||
@ -581,6 +597,7 @@ struct opcode_obj_rewrite {
|
||||
ipfw_obj_fidx_cb *find_bykidx; /* Find named object by kidx */
|
||||
ipfw_obj_create_cb *create_object; /* Create named object */
|
||||
ipfw_obj_destroy_cb *destroy_object;/* Destroy named object */
|
||||
ipfw_obj_sets_cb *manage_sets; /* Swap or move sets */
|
||||
};
|
||||
|
||||
#define IPFW_ADD_OBJ_REWRITER(f, c) do { \
|
||||
@ -675,8 +692,11 @@ int ipfw_objhash_same_name(struct namedobj_instance *ni, struct named_object *a,
|
||||
void ipfw_objhash_add(struct namedobj_instance *ni, struct named_object *no);
|
||||
void ipfw_objhash_del(struct namedobj_instance *ni, struct named_object *no);
|
||||
uint32_t ipfw_objhash_count(struct namedobj_instance *ni);
|
||||
uint32_t ipfw_objhash_count_type(struct namedobj_instance *ni, uint16_t type);
|
||||
int ipfw_objhash_foreach(struct namedobj_instance *ni, objhash_cb_t *f,
|
||||
void *arg);
|
||||
int ipfw_objhash_foreach_type(struct namedobj_instance *ni, objhash_cb_t *f,
|
||||
void *arg, uint16_t type);
|
||||
int ipfw_objhash_free_idx(struct namedobj_instance *ni, uint16_t idx);
|
||||
int ipfw_objhash_alloc_idx(void *n, uint16_t *pidx);
|
||||
void ipfw_objhash_set_funcs(struct namedobj_instance *ni,
|
||||
@ -698,6 +718,8 @@ int classify_opcode_kidx(ipfw_insn *cmd, uint16_t *puidx);
|
||||
void ipfw_init_srv(struct ip_fw_chain *ch);
|
||||
void ipfw_destroy_srv(struct ip_fw_chain *ch);
|
||||
int ipfw_check_object_name_generic(const char *name);
|
||||
int ipfw_obj_manage_sets(struct namedobj_instance *ni, uint16_t type,
|
||||
uint16_t set, uint8_t new_set, enum ipfw_sets_cmd cmd);
|
||||
|
||||
/* In ip_fw_eaction.c */
|
||||
typedef int (ipfw_eaction_t)(struct ip_fw_chain *ch, struct ip_fw_args *args,
|
||||
|
@ -851,6 +851,113 @@ ipfw_match_range(struct ip_fw *rule, ipfw_range_tlv *rt)
|
||||
return (1);
|
||||
}
|
||||
|
||||
struct manage_sets_args {
|
||||
uint16_t set;
|
||||
uint8_t new_set;
|
||||
};
|
||||
|
||||
static int
|
||||
swap_sets_cb(struct namedobj_instance *ni, struct named_object *no,
|
||||
void *arg)
|
||||
{
|
||||
struct manage_sets_args *args;
|
||||
|
||||
args = (struct manage_sets_args *)arg;
|
||||
if (no->set == (uint8_t)args->set)
|
||||
no->set = args->new_set;
|
||||
else if (no->set == args->new_set)
|
||||
no->set = (uint8_t)args->set;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
move_sets_cb(struct namedobj_instance *ni, struct named_object *no,
|
||||
void *arg)
|
||||
{
|
||||
struct manage_sets_args *args;
|
||||
|
||||
args = (struct manage_sets_args *)arg;
|
||||
if (no->set == (uint8_t)args->set)
|
||||
no->set = args->new_set;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
test_sets_cb(struct namedobj_instance *ni, struct named_object *no,
|
||||
void *arg)
|
||||
{
|
||||
struct manage_sets_args *args;
|
||||
|
||||
args = (struct manage_sets_args *)arg;
|
||||
if (no->set != (uint8_t)args->set)
|
||||
return (0);
|
||||
if (ipfw_objhash_lookup_name_type(ni, args->new_set,
|
||||
no->etlv, no->name) != NULL)
|
||||
return (EEXIST);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generic function to handler moving and swapping sets.
|
||||
*/
|
||||
int
|
||||
ipfw_obj_manage_sets(struct namedobj_instance *ni, uint16_t type,
|
||||
uint16_t set, uint8_t new_set, enum ipfw_sets_cmd cmd)
|
||||
{
|
||||
struct manage_sets_args args;
|
||||
struct named_object *no;
|
||||
|
||||
args.set = set;
|
||||
args.new_set = new_set;
|
||||
switch (cmd) {
|
||||
case SWAP_ALL:
|
||||
return (ipfw_objhash_foreach_type(ni, swap_sets_cb,
|
||||
&args, type));
|
||||
case TEST_ALL:
|
||||
return (ipfw_objhash_foreach_type(ni, test_sets_cb,
|
||||
&args, type));
|
||||
case MOVE_ALL:
|
||||
return (ipfw_objhash_foreach_type(ni, move_sets_cb,
|
||||
&args, type));
|
||||
case COUNT_ONE:
|
||||
/*
|
||||
* @set used to pass kidx.
|
||||
* When @new_set is zero - reset object counter,
|
||||
* otherwise increment it.
|
||||
*/
|
||||
no = ipfw_objhash_lookup_kidx(ni, set);
|
||||
if (new_set != 0)
|
||||
no->ocnt++;
|
||||
else
|
||||
no->ocnt = 0;
|
||||
return (0);
|
||||
case TEST_ONE:
|
||||
/* @set used to pass kidx */
|
||||
no = ipfw_objhash_lookup_kidx(ni, set);
|
||||
/*
|
||||
* First check number of references:
|
||||
* when it differs, this mean other rules are holding
|
||||
* reference to given object, so it is not possible to
|
||||
* change its set. Note that refcnt may account references
|
||||
* to some going-to-be-added rules. Since we don't know
|
||||
* their numbers (and even if they will be added) it is
|
||||
* perfectly OK to return error here.
|
||||
*/
|
||||
if (no->ocnt != no->refcnt)
|
||||
return (EBUSY);
|
||||
if (ipfw_objhash_lookup_name_type(ni, new_set, type,
|
||||
no->name) != NULL)
|
||||
return (EEXIST);
|
||||
return (0);
|
||||
case MOVE_ONE:
|
||||
/* @set used to pass kidx */
|
||||
no = ipfw_objhash_lookup_kidx(ni, set);
|
||||
no->set = new_set;
|
||||
return (0);
|
||||
}
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete rules matching range @rt.
|
||||
* Saves number of deleted rules in @ndel.
|
||||
@ -935,7 +1042,89 @@ delete_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int *ndel)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
static int
|
||||
move_objects(struct ip_fw_chain *ch, ipfw_range_tlv *rt)
|
||||
{
|
||||
struct opcode_obj_rewrite *rw;
|
||||
struct ip_fw *rule;
|
||||
ipfw_insn *cmd;
|
||||
int cmdlen, i, l, c;
|
||||
uint16_t kidx;
|
||||
|
||||
IPFW_UH_WLOCK_ASSERT(ch);
|
||||
|
||||
/* Stage 1: count number of references by given rules */
|
||||
for (c = 0, i = 0; i < ch->n_rules - 1; i++) {
|
||||
rule = ch->map[i];
|
||||
if (ipfw_match_range(rule, rt) == 0)
|
||||
continue;
|
||||
if (rule->set == rt->new_set) /* nothing to do */
|
||||
continue;
|
||||
/* Search opcodes with named objects */
|
||||
for (l = rule->cmd_len, cmdlen = 0, cmd = rule->cmd;
|
||||
l > 0; l -= cmdlen, cmd += cmdlen) {
|
||||
cmdlen = F_LEN(cmd);
|
||||
rw = find_op_rw(cmd, &kidx, NULL);
|
||||
if (rw == NULL || rw->manage_sets == NULL)
|
||||
continue;
|
||||
/*
|
||||
* When manage_sets() returns non-zero value to
|
||||
* COUNT_ONE command, consider this as an object
|
||||
* doesn't support sets (e.g. disabled with sysctl).
|
||||
* So, skip checks for this object.
|
||||
*/
|
||||
if (rw->manage_sets(ch, kidx, 1, COUNT_ONE) != 0)
|
||||
continue;
|
||||
c++;
|
||||
}
|
||||
}
|
||||
if (c == 0) /* No objects found */
|
||||
return (0);
|
||||
/* Stage 2: verify "ownership" */
|
||||
for (c = 0, i = 0; (i < ch->n_rules - 1) && c == 0; i++) {
|
||||
rule = ch->map[i];
|
||||
if (ipfw_match_range(rule, rt) == 0)
|
||||
continue;
|
||||
if (rule->set == rt->new_set) /* nothing to do */
|
||||
continue;
|
||||
/* Search opcodes with named objects */
|
||||
for (l = rule->cmd_len, cmdlen = 0, cmd = rule->cmd;
|
||||
l > 0 && c == 0; l -= cmdlen, cmd += cmdlen) {
|
||||
cmdlen = F_LEN(cmd);
|
||||
rw = find_op_rw(cmd, &kidx, NULL);
|
||||
if (rw == NULL || rw->manage_sets == NULL)
|
||||
continue;
|
||||
/* Test for ownership and conflicting names */
|
||||
c = rw->manage_sets(ch, kidx,
|
||||
(uint8_t)rt->new_set, TEST_ONE);
|
||||
}
|
||||
}
|
||||
/* Stage 3: change set and cleanup */
|
||||
for (i = 0; i < ch->n_rules - 1; i++) {
|
||||
rule = ch->map[i];
|
||||
if (ipfw_match_range(rule, rt) == 0)
|
||||
continue;
|
||||
if (rule->set == rt->new_set) /* nothing to do */
|
||||
continue;
|
||||
/* Search opcodes with named objects */
|
||||
for (l = rule->cmd_len, cmdlen = 0, cmd = rule->cmd;
|
||||
l > 0; l -= cmdlen, cmd += cmdlen) {
|
||||
cmdlen = F_LEN(cmd);
|
||||
rw = find_op_rw(cmd, &kidx, NULL);
|
||||
if (rw == NULL || rw->manage_sets == NULL)
|
||||
continue;
|
||||
/* cleanup object counter */
|
||||
rw->manage_sets(ch, kidx,
|
||||
0 /* reset counter */, COUNT_ONE);
|
||||
if (c != 0)
|
||||
continue;
|
||||
/* change set */
|
||||
rw->manage_sets(ch, kidx,
|
||||
(uint8_t)rt->new_set, MOVE_ONE);
|
||||
}
|
||||
}
|
||||
return (c);
|
||||
}/*
|
||||
* Changes set of given rule rannge @rt
|
||||
* with each other.
|
||||
*
|
||||
@ -956,11 +1145,9 @@ move_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt)
|
||||
* by given rule subset only. Otherwise, we can't move
|
||||
* them to new set and have to return error.
|
||||
*/
|
||||
if (V_fw_tables_sets != 0) {
|
||||
if (ipfw_move_tables_sets(chain, rt, rt->new_set) != 0) {
|
||||
IPFW_UH_WUNLOCK(chain);
|
||||
return (EBUSY);
|
||||
}
|
||||
if ((i = move_objects(chain, rt)) != 0) {
|
||||
IPFW_UH_WUNLOCK(chain);
|
||||
return (i);
|
||||
}
|
||||
|
||||
/* XXX: We have to do swap holding WLOCK */
|
||||
@ -1156,24 +1343,48 @@ enable_sets(struct ip_fw_chain *chain, ipfw_range_tlv *rt)
|
||||
IPFW_WUNLOCK(chain);
|
||||
}
|
||||
|
||||
static void
|
||||
static int
|
||||
swap_sets(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int mv)
|
||||
{
|
||||
struct opcode_obj_rewrite *rw;
|
||||
struct ip_fw *rule;
|
||||
int i;
|
||||
|
||||
IPFW_UH_WLOCK_ASSERT(chain);
|
||||
|
||||
if (rt->set == rt->new_set) /* nothing to do */
|
||||
return (0);
|
||||
|
||||
if (mv != 0) {
|
||||
/*
|
||||
* Berfore moving the rules we need to check that
|
||||
* there aren't any conflicting named objects.
|
||||
*/
|
||||
for (rw = ctl3_rewriters;
|
||||
rw < ctl3_rewriters + ctl3_rsize; rw++) {
|
||||
if (rw->manage_sets == NULL)
|
||||
continue;
|
||||
i = rw->manage_sets(chain, (uint8_t)rt->set,
|
||||
(uint8_t)rt->new_set, TEST_ALL);
|
||||
if (i != 0)
|
||||
return (EEXIST);
|
||||
}
|
||||
}
|
||||
/* Swap or move two sets */
|
||||
for (i = 0; i < chain->n_rules - 1; i++) {
|
||||
rule = chain->map[i];
|
||||
if (rule->set == rt->set)
|
||||
rule->set = rt->new_set;
|
||||
else if (rule->set == rt->new_set && mv == 0)
|
||||
rule->set = rt->set;
|
||||
if (rule->set == (uint8_t)rt->set)
|
||||
rule->set = (uint8_t)rt->new_set;
|
||||
else if (rule->set == (uint8_t)rt->new_set && mv == 0)
|
||||
rule->set = (uint8_t)rt->set;
|
||||
}
|
||||
if (V_fw_tables_sets != 0)
|
||||
ipfw_swap_tables_sets(chain, rt->set, rt->new_set, mv);
|
||||
for (rw = ctl3_rewriters; rw < ctl3_rewriters + ctl3_rsize; rw++) {
|
||||
if (rw->manage_sets == NULL)
|
||||
continue;
|
||||
rw->manage_sets(chain, (uint8_t)rt->set,
|
||||
(uint8_t)rt->new_set, mv != 0 ? MOVE_ALL: SWAP_ALL);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1188,6 +1399,7 @@ manage_sets(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
|
||||
struct sockopt_data *sd)
|
||||
{
|
||||
ipfw_range_header *rh;
|
||||
int ret;
|
||||
|
||||
if (sd->valsize != sizeof(*rh))
|
||||
return (EINVAL);
|
||||
@ -1196,12 +1408,17 @@ manage_sets(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
|
||||
|
||||
if (rh->range.head.length != sizeof(ipfw_range_tlv))
|
||||
return (1);
|
||||
if (rh->range.set >= IPFW_MAX_SETS ||
|
||||
rh->range.new_set >= IPFW_MAX_SETS)
|
||||
return (EINVAL);
|
||||
|
||||
ret = 0;
|
||||
IPFW_UH_WLOCK(chain);
|
||||
switch (op3->opcode) {
|
||||
case IP_FW_SET_SWAP:
|
||||
case IP_FW_SET_MOVE:
|
||||
swap_sets(chain, &rh->range, op3->opcode == IP_FW_SET_MOVE);
|
||||
ret = swap_sets(chain, &rh->range,
|
||||
op3->opcode == IP_FW_SET_MOVE);
|
||||
break;
|
||||
case IP_FW_SET_ENABLE:
|
||||
enable_sets(chain, &rh->range);
|
||||
@ -1209,7 +1426,7 @@ manage_sets(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
|
||||
}
|
||||
IPFW_UH_WUNLOCK(chain);
|
||||
|
||||
return (0);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1280,14 +1497,14 @@ del_entry(struct ip_fw_chain *chain, uint32_t arg)
|
||||
break;
|
||||
case 3: /* move rules from set "rulenum" to set "new_set" */
|
||||
IPFW_UH_WLOCK(chain);
|
||||
swap_sets(chain, &rt, 1);
|
||||
error = swap_sets(chain, &rt, 1);
|
||||
IPFW_UH_WUNLOCK(chain);
|
||||
return (0);
|
||||
return (error);
|
||||
case 4: /* swap sets "rulenum" and "new_set" */
|
||||
IPFW_UH_WLOCK(chain);
|
||||
swap_sets(chain, &rt, 0);
|
||||
error = swap_sets(chain, &rt, 0);
|
||||
IPFW_UH_WUNLOCK(chain);
|
||||
return (0);
|
||||
return (error);
|
||||
default:
|
||||
return (ENOTSUP);
|
||||
}
|
||||
@ -2526,11 +2743,8 @@ rewrite_rule_uidx(struct ip_fw_chain *chain, struct rule_check_info *ci)
|
||||
type = 0;
|
||||
memset(&ti, 0, sizeof(ti));
|
||||
|
||||
/*
|
||||
* Use default set for looking up tables (old way) or
|
||||
* use set rule is assigned to (new way).
|
||||
*/
|
||||
ti.set = (V_fw_tables_sets != 0) ? ci->krule->set : 0;
|
||||
/* Use set rule is assigned to. */
|
||||
ti.set = ci->krule->set;
|
||||
if (ci->ctlv != NULL) {
|
||||
ti.tlvs = (void *)(ci->ctlv + 1);
|
||||
ti.tlen = ci->ctlv->head.length - sizeof(ipfw_obj_ctlv);
|
||||
@ -4248,6 +4462,23 @@ ipfw_objhash_count(struct namedobj_instance *ni)
|
||||
return (ni->count);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ipfw_objhash_count_type(struct namedobj_instance *ni, uint16_t type)
|
||||
{
|
||||
struct named_object *no;
|
||||
uint32_t count;
|
||||
int i;
|
||||
|
||||
count = 0;
|
||||
for (i = 0; i < ni->nn_size; i++) {
|
||||
TAILQ_FOREACH(no, &ni->names[i], nn_next) {
|
||||
if (no->etlv == type)
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return (count);
|
||||
}
|
||||
|
||||
/*
|
||||
* Runs @func for each found named object.
|
||||
* It is safe to delete objects from callback
|
||||
@ -4268,6 +4499,29 @@ ipfw_objhash_foreach(struct namedobj_instance *ni, objhash_cb_t *f, void *arg)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Runs @f for each found named object with type @type.
|
||||
* It is safe to delete objects from callback
|
||||
*/
|
||||
int
|
||||
ipfw_objhash_foreach_type(struct namedobj_instance *ni, objhash_cb_t *f,
|
||||
void *arg, uint16_t type)
|
||||
{
|
||||
struct named_object *no, *no_tmp;
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < ni->nn_size; i++) {
|
||||
TAILQ_FOREACH_SAFE(no, &ni->names[i], nn_next, no_tmp) {
|
||||
if (no->etlv != type)
|
||||
continue;
|
||||
ret = f(ni, no, arg);
|
||||
if (ret != 0)
|
||||
return (ret);
|
||||
}
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes index from given set.
|
||||
* Returns 0 on success.
|
||||
|
@ -1601,64 +1601,6 @@ ipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Switch between "set 0" and "rule's set" table binding,
|
||||
* Check all ruleset bindings and permits changing
|
||||
* IFF each binding has both rule AND table in default set (set 0).
|
||||
*
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
int
|
||||
ipfw_switch_tables_namespace(struct ip_fw_chain *ch, unsigned int sets)
|
||||
{
|
||||
struct namedobj_instance *ni;
|
||||
struct named_object *no;
|
||||
struct ip_fw *rule;
|
||||
ipfw_insn *cmd;
|
||||
int cmdlen, i, l;
|
||||
uint16_t kidx;
|
||||
|
||||
IPFW_UH_WLOCK(ch);
|
||||
|
||||
if (V_fw_tables_sets == sets) {
|
||||
IPFW_UH_WUNLOCK(ch);
|
||||
return (0);
|
||||
}
|
||||
|
||||
ni = CHAIN_TO_NI(ch);
|
||||
|
||||
/*
|
||||
* Scan all rules and examine tables opcodes.
|
||||
*/
|
||||
for (i = 0; i < ch->n_rules; i++) {
|
||||
rule = ch->map[i];
|
||||
|
||||
l = rule->cmd_len;
|
||||
cmd = rule->cmd;
|
||||
cmdlen = 0;
|
||||
for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) {
|
||||
cmdlen = F_LEN(cmd);
|
||||
|
||||
if (classify_opcode_kidx(cmd, &kidx) != 0)
|
||||
continue;
|
||||
|
||||
no = ipfw_objhash_lookup_kidx(ni, kidx);
|
||||
|
||||
/* Check if both table object and rule has the set 0 */
|
||||
if (no->set != 0 || rule->set != 0) {
|
||||
IPFW_UH_WUNLOCK(ch);
|
||||
return (EBUSY);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
V_fw_tables_sets = sets;
|
||||
|
||||
IPFW_UH_WUNLOCK(ch);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Lookup an IP @addr in table @tbl.
|
||||
* Stores found value in @val.
|
||||
@ -2875,39 +2817,190 @@ table_findbykidx(struct ip_fw_chain *ch, uint16_t idx)
|
||||
return (&tc->no);
|
||||
}
|
||||
|
||||
static int
|
||||
table_manage_sets(struct ip_fw_chain *ch, uint16_t set, uint8_t new_set,
|
||||
enum ipfw_sets_cmd cmd)
|
||||
{
|
||||
|
||||
switch (cmd) {
|
||||
case SWAP_ALL:
|
||||
case TEST_ALL:
|
||||
/*
|
||||
* Return success for TEST_ALL, since nothing prevents
|
||||
* move rules from one set to another. All tables are
|
||||
* accessible from all sets when per-set tables sysctl
|
||||
* is disabled.
|
||||
*/
|
||||
case MOVE_ALL:
|
||||
case TEST_ONE:
|
||||
case MOVE_ONE:
|
||||
/*
|
||||
* NOTE: we need to use ipfw_objhash_del/ipfw_objhash_add
|
||||
* if set number will be used in hash function. Currently
|
||||
* we can just use generic handler that replaces set value.
|
||||
*/
|
||||
if (V_fw_tables_sets == 0)
|
||||
return (0);
|
||||
break;
|
||||
case COUNT_ONE:
|
||||
/*
|
||||
* Return EOPNOTSUPP for COUNT_ONE when per-set sysctl is
|
||||
* disabled. This allow skip table's opcodes from additional
|
||||
* checks when specific rules moved to another set.
|
||||
*/
|
||||
if (V_fw_tables_sets == 0)
|
||||
return (EOPNOTSUPP);
|
||||
}
|
||||
/* Use generic sets handler when per-set sysctl is enabled. */
|
||||
return (ipfw_obj_manage_sets(CHAIN_TO_NI(ch), IPFW_TLV_TBL_NAME,
|
||||
set, new_set, cmd));
|
||||
}
|
||||
|
||||
static struct opcode_obj_rewrite opcodes[] = {
|
||||
{
|
||||
O_IP_SRC_LOOKUP, IPFW_TLV_TBL_NAME,
|
||||
classify_srcdst, update_arg1,
|
||||
table_findbyname, table_findbykidx, create_table_compat
|
||||
.opcode = O_IP_SRC_LOOKUP,
|
||||
.etlv = IPFW_TLV_TBL_NAME,
|
||||
.classifier = classify_srcdst,
|
||||
.update = update_arg1,
|
||||
.find_byname = table_findbyname,
|
||||
.find_bykidx = table_findbykidx,
|
||||
.create_object = create_table_compat,
|
||||
.manage_sets = table_manage_sets,
|
||||
},
|
||||
{
|
||||
O_IP_DST_LOOKUP, IPFW_TLV_TBL_NAME,
|
||||
classify_srcdst, update_arg1,
|
||||
table_findbyname, table_findbykidx, create_table_compat
|
||||
.opcode = O_IP_DST_LOOKUP,
|
||||
.etlv = IPFW_TLV_TBL_NAME,
|
||||
.classifier = classify_srcdst,
|
||||
.update = update_arg1,
|
||||
.find_byname = table_findbyname,
|
||||
.find_bykidx = table_findbykidx,
|
||||
.create_object = create_table_compat,
|
||||
.manage_sets = table_manage_sets,
|
||||
},
|
||||
{
|
||||
O_IP_FLOW_LOOKUP, IPFW_TLV_TBL_NAME,
|
||||
classify_flow, update_arg1,
|
||||
table_findbyname, table_findbykidx, create_table_compat
|
||||
.opcode = O_IP_FLOW_LOOKUP,
|
||||
.etlv = IPFW_TLV_TBL_NAME,
|
||||
.classifier = classify_flow,
|
||||
.update = update_arg1,
|
||||
.find_byname = table_findbyname,
|
||||
.find_bykidx = table_findbykidx,
|
||||
.create_object = create_table_compat,
|
||||
.manage_sets = table_manage_sets,
|
||||
},
|
||||
{
|
||||
O_XMIT, IPFW_TLV_TBL_NAME,
|
||||
classify_via, update_via,
|
||||
table_findbyname, table_findbykidx, create_table_compat
|
||||
.opcode = O_XMIT,
|
||||
.etlv = IPFW_TLV_TBL_NAME,
|
||||
.classifier = classify_via,
|
||||
.update = update_via,
|
||||
.find_byname = table_findbyname,
|
||||
.find_bykidx = table_findbykidx,
|
||||
.create_object = create_table_compat,
|
||||
.manage_sets = table_manage_sets,
|
||||
},
|
||||
{
|
||||
O_RECV, IPFW_TLV_TBL_NAME,
|
||||
classify_via, update_via,
|
||||
table_findbyname, table_findbykidx, create_table_compat
|
||||
.opcode = O_RECV,
|
||||
.etlv = IPFW_TLV_TBL_NAME,
|
||||
.classifier = classify_via,
|
||||
.update = update_via,
|
||||
.find_byname = table_findbyname,
|
||||
.find_bykidx = table_findbykidx,
|
||||
.create_object = create_table_compat,
|
||||
.manage_sets = table_manage_sets,
|
||||
},
|
||||
{
|
||||
O_VIA, IPFW_TLV_TBL_NAME,
|
||||
classify_via, update_via,
|
||||
table_findbyname, table_findbykidx, create_table_compat
|
||||
.opcode = O_VIA,
|
||||
.etlv = IPFW_TLV_TBL_NAME,
|
||||
.classifier = classify_via,
|
||||
.update = update_via,
|
||||
.find_byname = table_findbyname,
|
||||
.find_bykidx = table_findbykidx,
|
||||
.create_object = create_table_compat,
|
||||
.manage_sets = table_manage_sets,
|
||||
},
|
||||
};
|
||||
|
||||
static int
|
||||
test_sets_cb(struct namedobj_instance *ni __unused, struct named_object *no,
|
||||
void *arg __unused)
|
||||
{
|
||||
|
||||
/* Check that there aren't any tables in not default set */
|
||||
if (no->set != 0)
|
||||
return (EBUSY);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Switch between "set 0" and "rule's set" table binding,
|
||||
* Check all ruleset bindings and permits changing
|
||||
* IFF each binding has both rule AND table in default set (set 0).
|
||||
*
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
int
|
||||
ipfw_switch_tables_namespace(struct ip_fw_chain *ch, unsigned int sets)
|
||||
{
|
||||
struct opcode_obj_rewrite *rw;
|
||||
struct namedobj_instance *ni;
|
||||
struct named_object *no;
|
||||
struct ip_fw *rule;
|
||||
ipfw_insn *cmd;
|
||||
int cmdlen, i, l;
|
||||
uint16_t kidx;
|
||||
uint8_t subtype;
|
||||
|
||||
IPFW_UH_WLOCK(ch);
|
||||
|
||||
if (V_fw_tables_sets == sets) {
|
||||
IPFW_UH_WUNLOCK(ch);
|
||||
return (0);
|
||||
}
|
||||
ni = CHAIN_TO_NI(ch);
|
||||
if (sets == 0) {
|
||||
/*
|
||||
* Prevent disabling sets support if we have some tables
|
||||
* in not default sets.
|
||||
*/
|
||||
if (ipfw_objhash_foreach_type(ni, test_sets_cb,
|
||||
NULL, IPFW_TLV_TBL_NAME) != 0) {
|
||||
IPFW_UH_WUNLOCK(ch);
|
||||
return (EBUSY);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Scan all rules and examine tables opcodes.
|
||||
*/
|
||||
for (i = 0; i < ch->n_rules; i++) {
|
||||
rule = ch->map[i];
|
||||
|
||||
l = rule->cmd_len;
|
||||
cmd = rule->cmd;
|
||||
cmdlen = 0;
|
||||
for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) {
|
||||
cmdlen = F_LEN(cmd);
|
||||
/* Check only tables opcodes */
|
||||
for (kidx = 0, rw = opcodes;
|
||||
rw < opcodes + nitems(opcodes); rw++) {
|
||||
if (rw->opcode != cmd->opcode)
|
||||
continue;
|
||||
if (rw->classifier(cmd, &kidx, &subtype) == 0)
|
||||
break;
|
||||
}
|
||||
if (kidx == 0)
|
||||
continue;
|
||||
no = ipfw_objhash_lookup_kidx(ni, kidx);
|
||||
/* Check if both table object and rule has the set 0 */
|
||||
if (no->set != 0 || rule->set != 0) {
|
||||
IPFW_UH_WUNLOCK(ch);
|
||||
return (EBUSY);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
V_fw_tables_sets = sets;
|
||||
IPFW_UH_WUNLOCK(ch);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks table name for validity.
|
||||
@ -2954,7 +3047,7 @@ find_table_err(struct namedobj_instance *ni, struct tid_info *ti,
|
||||
* This is needed due to different sets behavior
|
||||
* controlled by V_fw_tables_sets.
|
||||
*/
|
||||
set = ti->set;
|
||||
set = (V_fw_tables_sets != 0) ? ti->set : 0;
|
||||
} else {
|
||||
snprintf(bname, sizeof(bname), "%d", ti->uidx);
|
||||
name = bname;
|
||||
@ -3112,196 +3205,6 @@ unlink_table(struct ip_fw_chain *ch, struct table_config *tc)
|
||||
tc->ta->change_ti(tc->astate, NULL);
|
||||
}
|
||||
|
||||
struct swap_table_args {
|
||||
int set;
|
||||
int new_set;
|
||||
int mv;
|
||||
};
|
||||
|
||||
/*
|
||||
* Change set for each matching table.
|
||||
*
|
||||
* Ensure we dispatch each table once by setting/checking ochange
|
||||
* fields.
|
||||
*/
|
||||
static int
|
||||
swap_table_set(struct namedobj_instance *ni, struct named_object *no,
|
||||
void *arg)
|
||||
{
|
||||
struct table_config *tc;
|
||||
struct swap_table_args *sta;
|
||||
|
||||
tc = (struct table_config *)no;
|
||||
sta = (struct swap_table_args *)arg;
|
||||
|
||||
if (no->set != sta->set && (no->set != sta->new_set || sta->mv != 0))
|
||||
return (0);
|
||||
|
||||
if (tc->ochanged != 0)
|
||||
return (0);
|
||||
|
||||
tc->ochanged = 1;
|
||||
ipfw_objhash_del(ni, no);
|
||||
if (no->set == sta->set)
|
||||
no->set = sta->new_set;
|
||||
else
|
||||
no->set = sta->set;
|
||||
ipfw_objhash_add(ni, no);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Cleans up ochange field for all tables.
|
||||
*/
|
||||
static int
|
||||
clean_table_set_data(struct namedobj_instance *ni, struct named_object *no,
|
||||
void *arg)
|
||||
{
|
||||
struct table_config *tc;
|
||||
struct swap_table_args *sta;
|
||||
|
||||
tc = (struct table_config *)no;
|
||||
sta = (struct swap_table_args *)arg;
|
||||
|
||||
tc->ochanged = 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Swaps tables within two sets.
|
||||
*/
|
||||
void
|
||||
ipfw_swap_tables_sets(struct ip_fw_chain *ch, uint32_t set,
|
||||
uint32_t new_set, int mv)
|
||||
{
|
||||
struct swap_table_args sta;
|
||||
|
||||
IPFW_UH_WLOCK_ASSERT(ch);
|
||||
|
||||
sta.set = set;
|
||||
sta.new_set = new_set;
|
||||
sta.mv = mv;
|
||||
|
||||
ipfw_objhash_foreach(CHAIN_TO_NI(ch), swap_table_set, &sta);
|
||||
ipfw_objhash_foreach(CHAIN_TO_NI(ch), clean_table_set_data, &sta);
|
||||
}
|
||||
|
||||
/*
|
||||
* Move all tables which are reference by rules in @rr to set @new_set.
|
||||
* Makes sure that all relevant tables are referenced ONLLY by given rules.
|
||||
*
|
||||
* Returns 0 on success,
|
||||
*/
|
||||
int
|
||||
ipfw_move_tables_sets(struct ip_fw_chain *ch, ipfw_range_tlv *rt,
|
||||
uint32_t new_set)
|
||||
{
|
||||
struct ip_fw *rule;
|
||||
struct table_config *tc;
|
||||
struct named_object *no;
|
||||
struct namedobj_instance *ni;
|
||||
int bad, i, l, cmdlen;
|
||||
uint16_t kidx;
|
||||
ipfw_insn *cmd;
|
||||
|
||||
IPFW_UH_WLOCK_ASSERT(ch);
|
||||
|
||||
ni = CHAIN_TO_NI(ch);
|
||||
|
||||
/* Stage 1: count number of references by given rules */
|
||||
for (i = 0; i < ch->n_rules - 1; i++) {
|
||||
rule = ch->map[i];
|
||||
if (ipfw_match_range(rule, rt) == 0)
|
||||
continue;
|
||||
|
||||
l = rule->cmd_len;
|
||||
cmd = rule->cmd;
|
||||
cmdlen = 0;
|
||||
for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) {
|
||||
cmdlen = F_LEN(cmd);
|
||||
if (classify_opcode_kidx(cmd, &kidx) != 0)
|
||||
continue;
|
||||
no = ipfw_objhash_lookup_kidx(ni, kidx);
|
||||
KASSERT(no != NULL,
|
||||
("objhash lookup failed on index %d", kidx));
|
||||
tc = (struct table_config *)no;
|
||||
tc->ocount++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Stage 2: verify "ownership" */
|
||||
bad = 0;
|
||||
for (i = 0; i < ch->n_rules - 1; i++) {
|
||||
rule = ch->map[i];
|
||||
if (ipfw_match_range(rule, rt) == 0)
|
||||
continue;
|
||||
|
||||
l = rule->cmd_len;
|
||||
cmd = rule->cmd;
|
||||
cmdlen = 0;
|
||||
for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) {
|
||||
cmdlen = F_LEN(cmd);
|
||||
if (classify_opcode_kidx(cmd, &kidx) != 0)
|
||||
continue;
|
||||
no = ipfw_objhash_lookup_kidx(ni, kidx);
|
||||
KASSERT(no != NULL,
|
||||
("objhash lookup failed on index %d", kidx));
|
||||
tc = (struct table_config *)no;
|
||||
if (tc->no.refcnt != tc->ocount) {
|
||||
|
||||
/*
|
||||
* Number of references differ:
|
||||
* Other rule(s) are holding reference to given
|
||||
* table, so it is not possible to change its set.
|
||||
*
|
||||
* Note that refcnt may account
|
||||
* references to some going-to-be-added rules.
|
||||
* Since we don't know their numbers (and event
|
||||
* if they will be added) it is perfectly OK
|
||||
* to return error here.
|
||||
*/
|
||||
bad = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bad != 0)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Stage 3: change set or cleanup */
|
||||
for (i = 0; i < ch->n_rules - 1; i++) {
|
||||
rule = ch->map[i];
|
||||
if (ipfw_match_range(rule, rt) == 0)
|
||||
continue;
|
||||
|
||||
l = rule->cmd_len;
|
||||
cmd = rule->cmd;
|
||||
cmdlen = 0;
|
||||
for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) {
|
||||
cmdlen = F_LEN(cmd);
|
||||
if (classify_opcode_kidx(cmd, &kidx) != 0)
|
||||
continue;
|
||||
no = ipfw_objhash_lookup_kidx(ni, kidx);
|
||||
KASSERT(no != NULL,
|
||||
("objhash lookup failed on index %d", kidx));
|
||||
tc = (struct table_config *)no;
|
||||
|
||||
tc->ocount = 0;
|
||||
if (bad != 0)
|
||||
continue;
|
||||
|
||||
/* Actually change set. */
|
||||
ipfw_objhash_del(ni, no);
|
||||
no->set = new_set;
|
||||
ipfw_objhash_add(ni, no);
|
||||
}
|
||||
}
|
||||
|
||||
return (bad);
|
||||
}
|
||||
|
||||
static struct ipfw_sopt_handler scodes[] = {
|
||||
{ IP_FW_TABLE_XCREATE, 0, HDIR_SET, create_table },
|
||||
{ IP_FW_TABLE_XDESTROY, 0, HDIR_SET, flush_table_v0 },
|
||||
|
Loading…
Reference in New Issue
Block a user