Date: Tue, 17 May 2016 07:47:23 +0000 (UTC) From: "Andrey V. Elsukov" <ae@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r300021 - in head: sbin/ipfw sys/netinet sys/netpfil/ipfw Message-ID: <201605170747.u4H7lNXI040509@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: ae Date: Tue May 17 07:47:23 2016 New Revision: 300021 URL: https://svnweb.freebsd.org/changeset/base/300021 Log: 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 Modified: head/sbin/ipfw/ipfw2.c head/sys/netinet/ip_fw.h head/sys/netpfil/ipfw/ip_fw_eaction.c head/sys/netpfil/ipfw/ip_fw_private.h head/sys/netpfil/ipfw/ip_fw_sockopt.c head/sys/netpfil/ipfw/ip_fw_table.c Modified: head/sbin/ipfw/ipfw2.c ============================================================================== --- head/sbin/ipfw/ipfw2.c Tue May 17 07:15:25 2016 (r300020) +++ head/sbin/ipfw/ipfw2.c Tue May 17 07:47:23 2016 (r300021) @@ -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; Modified: head/sys/netinet/ip_fw.h ============================================================================== --- head/sys/netinet/ip_fw.h Tue May 17 07:15:25 2016 (r300020) +++ head/sys/netinet/ip_fw.h Tue May 17 07:47:23 2016 (r300021) @@ -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; Modified: head/sys/netpfil/ipfw/ip_fw_eaction.c ============================================================================== --- head/sys/netpfil/ipfw/ip_fw_eaction.c Tue May 17 07:15:25 2016 (r300020) +++ head/sys/netpfil/ipfw/ip_fw_eaction.c Tue May 17 07:47:23 2016 (r300021) @@ -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 * Modified: head/sys/netpfil/ipfw/ip_fw_private.h ============================================================================== --- head/sys/netpfil/ipfw/ip_fw_private.h Tue May 17 07:15:25 2016 (r300020) +++ head/sys/netpfil/ipfw/ip_fw_private.h Tue May 17 07:47:23 2016 (r300021) @@ -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 */ 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 namedo 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, 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, Modified: head/sys/netpfil/ipfw/ip_fw_sockopt.c ============================================================================== --- head/sys/netpfil/ipfw/ip_fw_sockopt.c Tue May 17 07:15:25 2016 (r300020) +++ head/sys/netpfil/ipfw/ip_fw_sockopt.c Tue May 17 07:47:23 2016 (r300021) @@ -851,6 +851,113 @@ ipfw_match_range(struct ip_fw *rule, ipf 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, 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, ip * 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, i 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, i 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, i 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, i } IPFW_UH_WUNLOCK(chain); - return (0); + return (ret); } /** @@ -1280,14 +1497,14 @@ del_entry(struct ip_fw_chain *chain, uin 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 *ch 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_insta 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_ins } /* + * 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. */ Modified: head/sys/netpfil/ipfw/ip_fw_table.c ============================================================================== --- head/sys/netpfil/ipfw/ip_fw_table.c Tue May 17 07:15:25 2016 (r300020) +++ head/sys/netpfil/ipfw/ip_fw_table.c Tue May 17 07:47:23 2016 (r300021) @@ -1602,64 +1602,6 @@ ipfw_resize_tables(struct ip_fw_chain *c } /* - * 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, 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 * 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, str 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); *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201605170747.u4H7lNXI040509>