svn commit: r334039 - head/sys/netpfil/ipfw

Andrey V. Elsukov ae at FreeBSD.org
Tue May 22 13:28:06 UTC 2018


Author: ae
Date: Tue May 22 13:28:05 2018
New Revision: 334039
URL: https://svnweb.freebsd.org/changeset/base/334039

Log:
  Restore the ability to keep states after parent rule deletion.
  
  This feature is disabled by default and was removed when dynamic states
  implementation changed to be lockless. Now it is reimplemented with small
  differences - when dyn_keep_states sysctl variable is enabled,
  dyn_match_ipv[46]_state() function doesn't match child states of deleted
  rule. And thus they are keept alive until expired. ipfw_dyn_lookup_state()
  function does check that state was not orphaned, and if so, it returns
  pointer to default_rule and its position in the rules map. The main visible
  difference is that orphaned states still have the same rule number that
  they have before parent rule deleted, because now a state has many fields
  related to rule and changing them all atomically to point to default_rule
  seems hard enough.
  
  Reported by:	<lantw44 at gmail.com>
  MFC after:	2 days

Modified:
  head/sys/netpfil/ipfw/ip_fw_dynamic.c

Modified: head/sys/netpfil/ipfw/ip_fw_dynamic.c
==============================================================================
--- head/sys/netpfil/ipfw/ip_fw_dynamic.c	Tue May 22 13:25:15 2018	(r334038)
+++ head/sys/netpfil/ipfw/ip_fw_dynamic.c	Tue May 22 13:28:05 2018	(r334039)
@@ -311,6 +311,9 @@ static VNET_DEFINE(struct callout, dyn_timeout);
 static VNET_DEFINE(uint32_t, curr_max_length);
 #define	V_curr_max_length		VNET(curr_max_length)
 
+static VNET_DEFINE(uint32_t, dyn_keep_states);
+#define	V_dyn_keep_states		VNET(dyn_keep_states)
+
 static VNET_DEFINE(uma_zone_t, dyn_data_zone);
 static VNET_DEFINE(uma_zone_t, dyn_parent_zone);
 static VNET_DEFINE(uma_zone_t, dyn_ipv4_zone);
@@ -361,6 +364,7 @@ static VNET_DEFINE(uint32_t, dyn_max);		/* max # of dy
 static VNET_DEFINE(uint32_t, dyn_count);	/* number of states */
 static VNET_DEFINE(uint32_t, dyn_parent_max);	/* max # of parent states */
 static VNET_DEFINE(uint32_t, dyn_parent_count);	/* number of parent states */
+
 #define	V_dyn_max			VNET(dyn_max)
 #define	V_dyn_count			VNET(dyn_count)
 #define	V_dyn_parent_max		VNET(dyn_parent_max)
@@ -475,7 +479,11 @@ SYSCTL_U32(_net_inet_ip_fw, OID_AUTO, dyn_short_lifeti
 SYSCTL_U32(_net_inet_ip_fw, OID_AUTO, dyn_keepalive,
     CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(dyn_keepalive), 0,
     "Enable keepalives for dynamic states.");
+SYSCTL_U32(_net_inet_ip_fw, OID_AUTO, dyn_keep_states,
+    CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(dyn_keep_states), 0,
+    "Do not flush dynamic states on rule deletion");
 
+
 #ifdef IPFIREWALL_DYNDEBUG
 #define	DYN_DEBUG(fmt, ...)	do {			\
 	printf("%s: " fmt "\n", __func__, __VA_ARGS__);	\
@@ -1387,18 +1395,32 @@ ipfw_dyn_lookup_state(const struct ip_fw_args *args, c
 			 * that will be added into head of this bucket.
 			 * And the state that we currently have matched
 			 * should be deleted by dyn_expire_states().
+			 *
+			 * In case when dyn_keep_states is enabled, return
+			 * pointer to default rule and corresponding f_pos
+			 * value.
+			 * XXX: In this case we lose the cache efficiency,
+			 *      since f_pos is not cached, because it seems
+			 *      there is no easy way to atomically switch
+			 *      all fields related to parent rule of given
+			 *      state.
 			 */
-			if (V_layer3_chain.map[data->f_pos] == rule)
+			if (V_layer3_chain.map[data->f_pos] == rule) {
 				data->chain_id = V_layer3_chain.id;
-			else {
+				info->f_pos = data->f_pos;
+			} else if (V_dyn_keep_states != 0) {
+				rule = V_layer3_chain.default_rule;
+				info->f_pos = V_layer3_chain.n_rules - 1;
+			} else {
 				rule = NULL;
 				info->direction = MATCH_NONE;
 				DYN_DEBUG("rule %p  [%u, %u] is considered "
 				    "invalid in data %p", rule, data->ruleid,
 				    data->rulenum, data);
+				/* info->f_pos doesn't matter here. */
 			}
-		}
-		info->f_pos = data->f_pos;
+		} else
+			info->f_pos = data->f_pos;
 	}
 	DYNSTATE_CRITICAL_EXIT();
 #if 0
@@ -2099,7 +2121,8 @@ dyn_match_ipv4_state(struct dyn_ipv4_state *s, const i
 	if (s->type == O_LIMIT)
 		return (dyn_match_range(s->data->rulenum, s->data->set, rt));
 
-	if (dyn_match_range(s->data->rulenum, s->data->set, rt))
+	if (V_dyn_keep_states == 0 &&
+	    dyn_match_range(s->data->rulenum, s->data->set, rt))
 		return (1);
 
 	return (0);
@@ -2117,7 +2140,8 @@ dyn_match_ipv6_state(struct dyn_ipv6_state *s, const i
 	if (s->type == O_LIMIT)
 		return (dyn_match_range(s->data->rulenum, s->data->set, rt));
 
-	if (dyn_match_range(s->data->rulenum, s->data->set, rt))
+	if (V_dyn_keep_states == 0 &&
+	    dyn_match_range(s->data->rulenum, s->data->set, rt))
 		return (1);
 
 	return (0);


More information about the svn-src-all mailing list