git: 42ec75f83aa3 - main - pf: Optionally attempt to preserve rule counter values across ruleset updates

Kristof Provost kp at FreeBSD.org
Mon Apr 19 13:44:16 UTC 2021


The branch main has been updated by kp:

URL: https://cgit.FreeBSD.org/src/commit/?id=42ec75f83aa321fcea8d2eddc4e9099724f0669e

commit 42ec75f83aa321fcea8d2eddc4e9099724f0669e
Author:     Kristof Provost <kp at FreeBSD.org>
AuthorDate: 2021-04-15 14:12:11 +0000
Commit:     Kristof Provost <kp at FreeBSD.org>
CommitDate: 2021-04-19 12:31:47 +0000

    pf: Optionally attempt to preserve rule counter values across ruleset updates
    
    Usually rule counters are reset to zero on every update of the ruleset.
    With keepcounters set pf will attempt to find matching rules between old
    and new rulesets and preserve the rule counters.
    
    MFC after:      4 weeks
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
    Differential Revision:  https://reviews.freebsd.org/D29780
---
 lib/libpfctl/libpfctl.c   | 22 +++++++++++++
 lib/libpfctl/libpfctl.h   |  1 +
 sbin/pfctl/parse.y        |  6 +++-
 sbin/pfctl/pfctl.c        |  4 +++
 sbin/pfctl/pfctl_parser.h |  1 +
 share/man/man5/pf.conf.5  | 12 +++++--
 sys/net/pfvar.h           |  3 ++
 sys/netpfil/pf/pf_ioctl.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++-
 8 files changed, 126 insertions(+), 4 deletions(-)

diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c
index 8d81248e0229..86117268a9d0 100644
--- a/lib/libpfctl/libpfctl.c
+++ b/lib/libpfctl/libpfctl.c
@@ -569,3 +569,25 @@ int	pfctl_get_clear_rule(int dev, u_int32_t nr, u_int32_t ticket,
 
 	return (0);
 }
+
+int
+pfctl_set_keepcounters(int dev, bool keep)
+{
+	struct pfioc_nv	 nv;
+	nvlist_t	*nvl;
+	int		 ret;
+
+	nvl = nvlist_create(0);
+
+	nvlist_add_bool(nvl, "keep_counters", keep);
+
+	nv.data = nvlist_pack(nvl, &nv.len);
+	nv.size = nv.len;
+
+	nvlist_destroy(nvl);
+
+	ret = ioctl(dev, DIOCKEEPCOUNTERS, &nv);
+
+	free(nv.data);
+	return (ret);
+}
diff --git a/lib/libpfctl/libpfctl.h b/lib/libpfctl/libpfctl.h
index 32d0184f42ed..2c498189a6e4 100644
--- a/lib/libpfctl/libpfctl.h
+++ b/lib/libpfctl/libpfctl.h
@@ -188,5 +188,6 @@ int	pfctl_get_clear_rule(int dev, u_int32_t nr, u_int32_t ticket,
 int	pfctl_add_rule(int dev, const struct pfctl_rule *r,
 	    const char *anchor, const char *anchor_call, u_int32_t ticket,
 	    u_int32_t pool_ticket);
+int	pfctl_set_keepcounters(int dev, bool keep);
 
 #endif
diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y
index 9eac41fbf66f..e0314241eec3 100644
--- a/sbin/pfctl/parse.y
+++ b/sbin/pfctl/parse.y
@@ -461,7 +461,7 @@ int	parseport(char *, struct range *r, int);
 %token	REASSEMBLE FRAGDROP FRAGCROP ANCHOR NATANCHOR RDRANCHOR BINATANCHOR
 %token	SET OPTIMIZATION TIMEOUT LIMIT LOGINTERFACE BLOCKPOLICY FAILPOLICY
 %token	RANDOMID REQUIREORDER SYNPROXY FINGERPRINTS NOSYNC DEBUG SKIP HOSTID
-%token	ANTISPOOF FOR INCLUDE
+%token	ANTISPOOF FOR INCLUDE KEEPCOUNTERS
 %token	BITMASK RANDOM SOURCEHASH ROUNDROBIN STATICPORT PROBABILITY MAPEPORTSET
 %token	ALTQ CBQ CODEL PRIQ HFSC FAIRQ BANDWIDTH TBRSIZE LINKSHARE REALTIME
 %token	UPPERLIMIT QUEUE PRIORITY QLIMIT HOGS BUCKETS RTABLE TARGET INTERVAL
@@ -719,6 +719,9 @@ option		: SET OPTIMIZATION STRING		{
 			}
 			keep_state_defaults = $3;
 		}
+		| SET KEEPCOUNTERS {
+			pf->keep_counters = true;
+		}
 		;
 
 stringall	: STRING	{ $$ = $1; }
@@ -5593,6 +5596,7 @@ lookup(char *s)
 		{ "inet6",		INET6},
 		{ "interval",		INTERVAL},
 		{ "keep",		KEEP},
+		{ "keepcounters",	KEEPCOUNTERS},
 		{ "label",		LABEL},
 		{ "limit",		LIMIT},
 		{ "linkshare",		LINKSHARE},
diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
index 9f6b3d2e36ea..82af047e7571 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -1745,6 +1745,10 @@ pfctl_load_options(struct pfctl *pf)
 		if (pfctl_load_hostid(pf, pf->hostid))
 			error = 1;
 
+	/* load keepcounters */
+	if (pfctl_set_keepcounters(pf->dev, pf->keep_counters))
+		error = 1;
+
 	return (error);
 }
 
diff --git a/sbin/pfctl/pfctl_parser.h b/sbin/pfctl/pfctl_parser.h
index 43d8488dcab8..0c66d5dda97a 100644
--- a/sbin/pfctl/pfctl_parser.h
+++ b/sbin/pfctl/pfctl_parser.h
@@ -98,6 +98,7 @@ struct pfctl {
 	u_int32_t	 debug;
 	u_int32_t	 hostid;
 	char		*ifname;
+	bool		 keep_counters;
 
 	u_int8_t	 timeout_set[PFTM_MAX];
 	u_int8_t	 limit_set[PF_LIMIT_MAX];
diff --git a/share/man/man5/pf.conf.5 b/share/man/man5/pf.conf.5
index 52a7331c7cb2..00fbd4421e7f 100644
--- a/share/man/man5/pf.conf.5
+++ b/share/man/man5/pf.conf.5
@@ -28,7 +28,7 @@
 .\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 .\" POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd December 7, 2019
+.Dd April 19, 2021
 .Dt PF.CONF 5
 .Os
 .Sh NAME
@@ -618,6 +618,13 @@ Generate debug messages for various errors.
 .It Ar loud
 Generate debug messages for common conditions.
 .El
+.It Ar set keepcounters
+Preserve rule counters across rule updates.
+Usually rule counters are reset to zero on every update of the ruleset.
+With
+.Ar keepcounters
+set pf will attempt to find matching rules between old and new rulesets
+and preserve the rule counters.
 .El
 .Sh TRAFFIC NORMALIZATION
 Traffic normalization is used to sanitize packet content in such
@@ -2888,7 +2895,8 @@ option         = "set" ( [ "timeout" ( timeout | "{" timeout-list "}" ) ] |
                  [ "require-order" ( "yes" | "no" ) ]
                  [ "fingerprints" filename ] |
                  [ "skip on" ifspec ] |
-                 [ "debug" ( "none" | "urgent" | "misc" | "loud" ) ] )
+                 [ "debug" ( "none" | "urgent" | "misc" | "loud" ) ]
+                 [ "keepcounters" ] )
 
 pf-rule        = action [ ( "in" | "out" ) ]
                  [ "log" [ "(" logopts ")"] ] [ "quick" ]
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index efb9a382511e..3a79a281f916 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -996,6 +996,7 @@ struct pf_kstatus {
 	uint32_t	hostid;
 	char		ifname[IFNAMSIZ];
 	uint8_t		pf_chksum[PF_MD5_DIGEST_LENGTH];
+	bool		keep_counters;
 };
 
 struct pf_divert {
@@ -1304,6 +1305,8 @@ struct pfioc_iface {
 #define	DIOCSETIFFLAG	_IOWR('D', 89, struct pfioc_iface)
 #define	DIOCCLRIFFLAG	_IOWR('D', 90, struct pfioc_iface)
 #define	DIOCKILLSRCNODES	_IOWR('D', 91, struct pfioc_src_node_kill)
+#define	DIOCKEEPCOUNTERS	_IOWR('D', 92, struct pfioc_nv)
+
 struct pf_ifspeed_v0 {
 	char			ifname[IFNAMSIZ];
 	u_int32_t		baudrate;
diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c
index e7965b7dcecd..917501163264 100644
--- a/sys/netpfil/pf/pf_ioctl.c
+++ b/sys/netpfil/pf/pf_ioctl.c
@@ -197,6 +197,7 @@ static void		 pf_clear_states(void);
 static int		 pf_clear_tables(void);
 static void		 pf_clear_srcnodes(struct pf_ksrc_node *);
 static void		 pf_kill_srcnodes(struct pfioc_src_node_kill *);
+static int		 pf_keepcounters(struct pfioc_nv *);
 static void		 pf_tbladdr_copyout(struct pf_addr_wrap *);
 
 /*
@@ -1022,11 +1023,27 @@ pf_hash_rule(MD5_CTX *ctx, struct pf_krule *rule)
 	PF_MD5_UPD(rule, tos);
 }
 
+static bool
+pf_krule_compare(struct pf_krule *a, struct pf_krule *b)
+{
+	MD5_CTX		ctx[2];
+	u_int8_t	digest[2][PF_MD5_DIGEST_LENGTH];
+
+	MD5Init(&ctx[0]);
+	MD5Init(&ctx[1]);
+	pf_hash_rule(&ctx[0], a);
+	pf_hash_rule(&ctx[1], b);
+	MD5Final(digest[0], &ctx[0]);
+	MD5Final(digest[1], &ctx[1]);
+
+	return (memcmp(digest[0], digest[1], PF_MD5_DIGEST_LENGTH) == 0);
+}
+
 static int
 pf_commit_rules(u_int32_t ticket, int rs_num, char *anchor)
 {
 	struct pf_kruleset	*rs;
-	struct pf_krule		*rule, **old_array;
+	struct pf_krule		*rule, **old_array, *tail;
 	struct pf_krulequeue	*old_rules;
 	int			 error;
 	u_int32_t		 old_rcount;
@@ -1058,6 +1075,29 @@ pf_commit_rules(u_int32_t ticket, int rs_num, char *anchor)
 	    rs->rules[rs_num].inactive.ptr_array;
 	rs->rules[rs_num].active.rcount =
 	    rs->rules[rs_num].inactive.rcount;
+
+	/* Attempt to preserve counter information. */
+	if (V_pf_status.keep_counters) {
+		TAILQ_FOREACH(rule, rs->rules[rs_num].active.ptr,
+		    entries) {
+			tail = TAILQ_FIRST(old_rules);
+			while ((tail != NULL) && ! pf_krule_compare(tail, rule))
+				tail = TAILQ_NEXT(tail, entries);
+			if (tail != NULL) {
+				counter_u64_add(rule->evaluations,
+				    counter_u64_fetch(tail->evaluations));
+				counter_u64_add(rule->packets[0],
+				    counter_u64_fetch(tail->packets[0]));
+				counter_u64_add(rule->packets[1],
+				    counter_u64_fetch(tail->packets[1]));
+				counter_u64_add(rule->bytes[0],
+				    counter_u64_fetch(tail->bytes[0]));
+				counter_u64_add(rule->bytes[1],
+				    counter_u64_fetch(tail->bytes[1]));
+			}
+		}
+	}
+
 	rs->rules[rs_num].inactive.ptr = old_rules;
 	rs->rules[rs_num].inactive.ptr_array = old_array;
 	rs->rules[rs_num].inactive.rcount = old_rcount;
@@ -4948,6 +4988,10 @@ DIOCCHANGEADDR_error:
 		pf_kill_srcnodes((struct pfioc_src_node_kill *)addr);
 		break;
 
+	case DIOCKEEPCOUNTERS:
+		error = pf_keepcounters((struct pfioc_nv *)addr);
+		break;
+
 	case DIOCSETHOSTID: {
 		u_int32_t	*hostid = (u_int32_t *)addr;
 
@@ -5227,6 +5271,41 @@ pf_kill_srcnodes(struct pfioc_src_node_kill *psnk)
 	psnk->psnk_killed = pf_free_src_nodes(&kill);
 }
 
+static int
+pf_keepcounters(struct pfioc_nv *nv)
+{
+	nvlist_t	*nvl = NULL;
+	void		*nvlpacked = NULL;
+	int		 error = 0;
+
+#define	ERROUT(x)	do { error = (x); goto on_error; } while (0)
+
+	if (nv->len > pf_ioctl_maxcount)
+		ERROUT(ENOMEM);
+
+	nvlpacked = malloc(nv->len, M_TEMP, M_WAITOK);
+	if (nvlpacked == NULL)
+		ERROUT(ENOMEM);
+
+	error = copyin(nv->data, nvlpacked, nv->len);
+	if (error)
+		ERROUT(error);
+
+	nvl = nvlist_unpack(nvlpacked, nv->len, 0);
+	if (nvl == NULL)
+		ERROUT(EBADMSG);
+
+	if (! nvlist_exists_bool(nvl, "keep_counters"))
+		ERROUT(EBADMSG);
+
+	V_pf_status.keep_counters = nvlist_get_bool(nvl, "keep_counters");
+
+on_error:
+	nvlist_destroy(nvl);
+	free(nvlpacked, M_TEMP);
+	return (error);
+}
+
 /*
  * XXX - Check for version missmatch!!!
  */


More information about the dev-commits-src-all mailing list