summaryrefslogtreecommitdiffstats
path: root/sys/netpfil/ipfw/ip_fw_table.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netpfil/ipfw/ip_fw_table.c')
-rw-r--r--sys/netpfil/ipfw/ip_fw_table.c437
1 files changed, 170 insertions, 267 deletions
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