Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 11 May 2021 15:06:45 GMT
From:      Kristof Provost <kp@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org
Subject:   git: a5a03901798c - stable/12 - pf: Optionally attempt to preserve rule counter values across ruleset updates
Message-ID:  <202105111506.14BF6j60049425@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch stable/12 has been updated by kp:

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

commit a5a03901798c76f1f7c77535a2282a60f54b0ec2
Author:     Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2021-04-15 14:12:11 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2021-05-11 08:28:22 +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
    
    (cherry picked from commit 42ec75f83aa321fcea8d2eddc4e9099724f0669e)
---
 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 c2147e1b04c1..b07fcda9bd5a 100644
--- a/lib/libpfctl/libpfctl.c
+++ b/lib/libpfctl/libpfctl.c
@@ -586,3 +586,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 0f4f5c908738..e19187fc2526 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 0d0f6fea76be..903bbd47d660 100644
--- a/sbin/pfctl/parse.y
+++ b/sbin/pfctl/parse.y
@@ -464,7 +464,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
@@ -722,6 +722,9 @@ option		: SET OPTIMIZATION STRING		{
 			}
 			keep_state_defaults = $3;
 		}
+		| SET KEEPCOUNTERS {
+			pf->keep_counters = true;
+		}
 		;
 
 stringall	: STRING	{ $$ = $1; }
@@ -5591,6 +5594,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 8fa8ef44813a..af2ae6fe3bf0 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -1753,6 +1753,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 86870319df2a..ca0f0da02047 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 March 10, 2019
+.Dd April 19, 2021
 .Dt PF.CONF 5
 .Os
 .Sh NAME
@@ -619,6 +619,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
@@ -2889,7 +2896,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 6101f5f0ff54..dd69713d8574 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -994,6 +994,7 @@ struct pf_kstatus {
 	uint32_t	hostid;
 	char		ifname[IFNAMSIZ];
 	uint8_t		pf_chksum[PF_MD5_DIGEST_LENGTH];
+	bool		keep_counters;
 };
 
 struct pf_divert {
@@ -1303,6 +1304,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 dd57d4ac5c0f..4cedaba60504 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 *);
 
 /*
@@ -1023,11 +1024,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;
@@ -1059,6 +1076,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;
@@ -5022,6 +5062,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;
 
@@ -5301,6 +5345,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!!!
  */



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202105111506.14BF6j60049425>