git: 877e70e6087f - main - ipfw: add protected rule for orphaned dynamic states

From: Andrey V. Elsukov <ae_at_FreeBSD.org>
Date: Sun, 03 Aug 2025 10:07:54 UTC
The branch main has been updated by ae:

URL: https://cgit.FreeBSD.org/src/commit/?id=877e70e6087f9937e41da82f53bcbb4e04432428

commit 877e70e6087f9937e41da82f53bcbb4e04432428
Author:     Andrey V. Elsukov <ae@FreeBSD.org>
AuthorDate: 2025-07-22 08:20:13 +0000
Commit:     Andrey V. Elsukov <ae@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