The branch main has been updated by ae: URL: https://cgit.FreeBSD.org/src/commit/?id=877e70e6087f9937e41da82f53bcbb4e04432428
commit 877e70e6087f9937e41da82f53bcbb4e04432428 Author: Andrey V. Elsukov <a...@freebsd.org> AuthorDate: 2025-07-22 08:20:13 +0000 Commit: Andrey V. Elsukov <a...@freebsd.org> CommitDate: 2025-08-03 10:07:33 +0000 ipfw: add protected rule for orphaned dynamic states When we have enabled V_dyn_keep_states, states that become ORPHANED will keep pointer to original rule. Then this rule pointer is used to apply rule action after ipfw_dyn_lookup_state(). Some rule actions use IPFW_INC_RULE_COUNTER() directly to this rule pointer to increment rule counters, but other rule actions use chain->map[f_pos] instead. The last case leads to incrementing counters on the wrong rule, because ORPHANED states have not parent rule in chain->map[]. To solve this we add protected rule, that will be matched only by packets that are handled by ORPHANED states. This is `count' rule that is prior to the default rule: 65535 count ip from any to any not // orphaned dynamic states counter Obtained from: Yandex LLC Sponsored by: Yandex LLC Differential Revision: https://reviews.freebsd.org/D51460 --- sys/netpfil/ipfw/ip_fw2.c | 2 +- sys/netpfil/ipfw/ip_fw_dynamic.c | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/sys/netpfil/ipfw/ip_fw2.c b/sys/netpfil/ipfw/ip_fw2.c index c129c8c49921..3f810533b7fc 100644 --- a/sys/netpfil/ipfw/ip_fw2.c +++ b/sys/netpfil/ipfw/ip_fw2.c @@ -3680,6 +3680,7 @@ vnet_ipfw_init(const void *unused) IPFW_LOCK_INIT(chain); + ipfw_dyn_init(chain); /* fill and insert the default rule */ rule = ipfw_alloc_rule(chain, sizeof(struct ip_fw)); rule->flags |= IPFW_RULE_NOOPT; @@ -3689,7 +3690,6 @@ vnet_ipfw_init(const void *unused) chain->default_rule = rule; ipfw_add_protected_rule(chain, rule, 0); - ipfw_dyn_init(chain); ipfw_eaction_init(chain, first); ipfw_init_skipto_cache(chain); ipfw_bpf_init(first); diff --git a/sys/netpfil/ipfw/ip_fw_dynamic.c b/sys/netpfil/ipfw/ip_fw_dynamic.c index 9694c145e112..cfb686594c7c 100644 --- a/sys/netpfil/ipfw/ip_fw_dynamic.c +++ b/sys/netpfil/ipfw/ip_fw_dynamic.c @@ -3141,6 +3141,43 @@ ipfw_dump_states(struct ip_fw_chain *chain, struct sockopt_data *sd) #undef DYN_EXPORT_STATES } +/* + * When we have enabled V_dyn_keep_states, states that become ORPHANED + * will keep pointer to original rule. Then this rule pointer is used + * to apply rule action after ipfw_dyn_lookup_state(). + * Some rule actions use IPFW_INC_RULE_COUNTER() directly to this rule + * pointer, but other actions use chain->map[f_pos] instead. The last + * case leads to incrementing counters on the wrong rule, because + * ORPHANED states have not parent rule in chain->map[]. + * To solve this we add protected rule: + * count ip from any to any not // comment + * It will be matched only by packets that are handled by ORPHANED states. + */ +static void +dyn_add_protected_rule(struct ip_fw_chain *chain) +{ + static const char *comment = + "orphaned dynamic states counter"; + struct ip_fw *rule; + ipfw_insn *cmd; + size_t l; + + l = roundup(strlen(comment) + 1, sizeof(uint32_t)); + rule = ipfw_alloc_rule(chain, sizeof(*rule) + sizeof(ipfw_insn) + l); + cmd = rule->cmd; + cmd->opcode = O_NOP; + cmd->len = 1 + l/sizeof(uint32_t); + cmd->len |= F_NOT; /* make rule to be not matched */ + strcpy((char *)(cmd + 1), comment); + cmd += F_LEN(cmd); + + cmd->len = 1; + cmd->opcode = O_COUNT; + rule->act_ofs = cmd - rule->cmd; + rule->cmd_len = rule->act_ofs + 1; + ipfw_add_protected_rule(chain, rule, 0); +} + void ipfw_dyn_init(struct ip_fw_chain *chain) { @@ -3203,6 +3240,8 @@ ipfw_dyn_init(struct ip_fw_chain *chain) callout_init(&V_dyn_timeout, 1); callout_reset(&V_dyn_timeout, hz, dyn_tick, curvnet); IPFW_ADD_OBJ_REWRITER(IS_DEFAULT_VNET(curvnet), dyn_opcodes); + + dyn_add_protected_rule(chain); } void