summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sbin/ipfw/ipfw2.c3
-rw-r--r--sys/netinet/ip_fw.h4
-rw-r--r--sys/netpfil/ipfw/ip_fw_eaction.c24
-rw-r--r--sys/netpfil/ipfw/ip_fw_private.h26
-rw-r--r--sys/netpfil/ipfw/ip_fw_sockopt.c302
-rw-r--r--sys/netpfil/ipfw/ip_fw_table.c437
6 files changed, 498 insertions, 298 deletions
diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c
index 5389268..42d321d 100644
--- a/sbin/ipfw/ipfw2.c
+++ b/sbin/ipfw/ipfw2.c
@@ -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;
diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h
index e46abf8..640025d 100644
--- a/sys/netinet/ip_fw.h
+++ b/sys/netinet/ip_fw.h
@@ -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;
diff --git a/sys/netpfil/ipfw/ip_fw_eaction.c b/sys/netpfil/ipfw/ip_fw_eaction.c
index 09e0310..a9de696 100644
--- a/sys/netpfil/ipfw/ip_fw_eaction.c
+++ b/sys/netpfil/ipfw/ip_fw_eaction.c
@@ -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 *
diff --git a/sys/netpfil/ipfw/ip_fw_private.h b/sys/netpfil/ipfw/ip_fw_private.h
index 52e8a76..e90781a 100644
--- a/sys/netpfil/ipfw/ip_fw_private.h
+++ b/sys/netpfil/ipfw/ip_fw_private.h
@@ -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,
diff --git a/sys/netpfil/ipfw/ip_fw_sockopt.c b/sys/netpfil/ipfw/ip_fw_sockopt.c
index c183dfc..d186ba5 100644
--- a/sys/netpfil/ipfw/ip_fw_sockopt.c
+++ b/sys/netpfil/ipfw/ip_fw_sockopt.c
@@ -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;
+ }
+ 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);
}
- if (V_fw_tables_sets != 0)
- ipfw_swap_tables_sets(chain, rt->set, rt->new_set, mv);
+ 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
@@ -4269,6 +4500,29 @@ ipfw_objhash_foreach(struct namedobj_instance *ni, objhash_cb_t *f, void *arg)
}
/*
+ * 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.
*/
diff --git a/sys/netpfil/ipfw/ip_fw_table.c b/sys/netpfil/ipfw/ip_fw_table.c
index 194be08..68225b3 100644
--- a/sys/netpfil/ipfw/ip_fw_table.c
+++ b/sys/netpfil/ipfw/ip_fw_table.c
@@ -1602,64 +1602,6 @@ ipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables)
}
/*
- * 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 },
OpenPOWER on IntegriCloud