git: baf9b6d04205 - main - pf: allow pflow to be activated per rule

From: Kristof Provost <kp_at_FreeBSD.org>
Date: Tue, 16 Jan 2024 08:51:54 UTC
The branch main has been updated by kp:

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

commit baf9b6d04205e02e6248377b1e13b33059a35fc9
Author:     Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2023-12-01 13:55:50 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2024-01-16 08:45:54 +0000

    pf: allow pflow to be activated per rule
    
    Only generate ipfix/netflow reports (through pflow) for the rules where
    this is enabled. Reports can also be enabled globally through 'set
    state-default pflow'.
    
    Obtained from:  OpenBSD
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
    Differential Revision:  https://reviews.freebsd.org/D43108
---
 sbin/pfctl/parse.y          | 22 ++++++++++++++++++++--
 sbin/pfctl/pf_print_state.c |  2 ++
 sbin/pfctl/pfctl_parser.c   |  8 ++++++++
 share/man/man5/pf.conf.5    |  9 +++++++--
 sys/net/pflow.h             |  1 -
 sys/net/pfvar.h             |  3 +++
 sys/netpfil/pf/pf.c         |  5 +++++
 sys/netpfil/pf/pf.h         |  3 ++-
 sys/netpfil/pf/pf_ioctl.c   |  1 +
 sys/netpfil/pf/pflow.c      | 35 +++++++++++++++++++----------------
 10 files changed, 67 insertions(+), 22 deletions(-)

diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y
index 17227b674814..94b7e241cd25 100644
--- a/sbin/pfctl/parse.y
+++ b/sbin/pfctl/parse.y
@@ -172,7 +172,8 @@ enum	{ PF_STATE_OPT_MAX, PF_STATE_OPT_NOSYNC, PF_STATE_OPT_SRCTRACK,
 	    PF_STATE_OPT_MAX_SRC_STATES, PF_STATE_OPT_MAX_SRC_CONN,
 	    PF_STATE_OPT_MAX_SRC_CONN_RATE, PF_STATE_OPT_MAX_SRC_NODES,
 	    PF_STATE_OPT_OVERLOAD, PF_STATE_OPT_STATELOCK,
-	    PF_STATE_OPT_TIMEOUT, PF_STATE_OPT_SLOPPY, };
+	    PF_STATE_OPT_TIMEOUT, PF_STATE_OPT_SLOPPY,
+	    PF_STATE_OPT_PFLOW };
 
 enum	{ PF_SRCTRACK_NONE, PF_SRCTRACK, PF_SRCTRACK_GLOBAL, PF_SRCTRACK_RULE };
 
@@ -512,7 +513,7 @@ int	parseport(char *, struct range *r, int);
 %token	DNPIPE DNQUEUE RIDENTIFIER
 %token	LOAD RULESET_OPTIMIZATION PRIO
 %token	STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE
-%token	MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY
+%token	MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY PFLOW
 %token	TAGGED TAG IFBOUND FLOATING STATEPOLICY STATEDEFAULTS ROUTE SETTOS
 %token	DIVERTTO DIVERTREPLY BRIDGE_TO
 %token	<v.string>		STRING
@@ -2615,6 +2616,14 @@ pfrule		: action dir logquick interface route af proto fromto
 					}
 					r.rule_flag |= PFRULE_STATESLOPPY;
 					break;
+				case PF_STATE_OPT_PFLOW:
+					if (r.rule_flag & PFRULE_PFLOW) {
+						yyerror("state pflow option: "
+						    "multiple definitions");
+						YYERROR;
+					}
+					r.rule_flag |= PFRULE_PFLOW;
+					break;
 				case PF_STATE_OPT_TIMEOUT:
 					if (o->data.timeout.number ==
 					    PFTM_ADAPTIVE_START ||
@@ -4368,6 +4377,14 @@ state_opt_item	: MAXIMUM NUMBER		{
 			$$->next = NULL;
 			$$->tail = $$;
 		}
+		| PFLOW {
+			$$ = calloc(1, sizeof(struct node_state_opt));
+			if ($$ == NULL)
+				err(1, "state_opt_item: calloc");
+			$$->type = PF_STATE_OPT_PFLOW;
+			$$->next = NULL;
+			$$->tail = $$;
+		}
 		| STRING NUMBER			{
 			int	i;
 
@@ -6318,6 +6335,7 @@ lookup(char *s)
 		{ "out",		OUT},
 		{ "overload",		OVERLOAD},
 		{ "pass",		PASS},
+		{ "pflow",		PFLOW},
 		{ "port",		PORT},
 		{ "prio",		PRIO},
 		{ "priority",		PRIORITY},
diff --git a/sbin/pfctl/pf_print_state.c b/sbin/pfctl/pf_print_state.c
index 36c2103a2142..0b862273fab3 100644
--- a/sbin/pfctl/pf_print_state.c
+++ b/sbin/pfctl/pf_print_state.c
@@ -376,6 +376,8 @@ print_state(struct pfctl_state *s, int opts)
 			printf(", sloppy");
 		if (s->state_flags & PFSTATE_NOSYNC)
 			printf(", no-sync");
+		if (s->state_flags & PFSTATE_PFLOW)
+			printf(", pflow");
 		if (s->state_flags & PFSTATE_ACK)
 			printf(", psync-ack");
 		if (s->state_flags & PFSTATE_NODF)
diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c
index 7dc0c3ae0cf2..e71b7b160495 100644
--- a/sbin/pfctl/pfctl_parser.c
+++ b/sbin/pfctl/pfctl_parser.c
@@ -1051,6 +1051,8 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numer
 		opts = 1;
 	if (r->rule_flag & PFRULE_STATESLOPPY)
 		opts = 1;
+	if (r->rule_flag & PFRULE_PFLOW)
+		opts = 1;
 	for (i = 0; !opts && i < PFTM_MAX; ++i)
 		if (r->timeout[i])
 			opts = 1;
@@ -1123,6 +1125,12 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numer
 			printf("sloppy");
 			opts = 0;
 		}
+		if (r->rule_flag & PFRULE_PFLOW) {
+			if (!opts)
+				printf(", ");
+			printf("pflow");
+			opts = 0;
+		}
 		for (i = 0; i < PFTM_MAX; ++i)
 			if (r->timeout[i]) {
 				int j;
diff --git a/share/man/man5/pf.conf.5 b/share/man/man5/pf.conf.5
index 3193c18760c8..9803d96bc5d9 100644
--- a/share/man/man5/pf.conf.5
+++ b/share/man/man5/pf.conf.5
@@ -27,7 +27,7 @@
 .\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 .\" POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd November 17, 2023
+.Dd December 6, 2023
 .Dt PF.CONF 5
 .Os
 .Sh NAME
@@ -2429,6 +2429,10 @@ easier.
 This is intended to be used in situations where one does not see all
 packets of a connection, e.g. in asymmetric routing situations.
 Cannot be used with modulate or synproxy state.
+.It Ar pflow
+States created by this rule are exported on the
+.Xr pflow 4
+interface.
 .El
 .Pp
 Multiple options can be specified, separated by commas:
@@ -3345,7 +3349,7 @@ state-opt      = ( "max" number | "no-sync" | timeout | "sloppy" |
                  "max-src-conn" number |
                  "max-src-conn-rate" number "/" number |
                  "overload" "\*(Lt" string "\*(Gt" [ "flush" ] |
-                 "if-bound" | "floating" )
+                 "if-bound" | "floating" | "pflow" )
 
 fragmentation  = [ "fragment reassemble" ]
 
@@ -3406,6 +3410,7 @@ Service name database.
 .Xr ip 4 ,
 .Xr ip6 4 ,
 .Xr pf 4 ,
+.Xr pflow 4 ,
 .Xr pfsync 4 ,
 .Xr tcp 4 ,
 .Xr sctp 4 ,
diff --git a/sys/net/pflow.h b/sys/net/pflow.h
index fcf24e091b57..2b7dfe24b5fc 100644
--- a/sys/net/pflow.h
+++ b/sys/net/pflow.h
@@ -326,7 +326,6 @@ enum pflow_set_type_t {
 };
 
 #ifdef _KERNEL
-int export_pflow(struct pf_kstate *);
 int pflow_sysctl(int *, u_int,  void *, size_t *, void *, size_t);
 #endif /* _KERNEL */
 
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index d6852244ce9b..037286a756be 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -1186,6 +1186,7 @@ typedef	void		pfsync_delete_state_t(struct pf_kstate *);
 typedef void		pfsync_clear_states_t(u_int32_t, const char *);
 typedef int		pfsync_defer_t(struct pf_kstate *, struct mbuf *);
 typedef void		pfsync_detach_ifnet_t(struct ifnet *);
+typedef void		pflow_export_state_t(const struct pf_kstate *);
 
 VNET_DECLARE(pfsync_state_import_t *, pfsync_state_import_ptr);
 #define V_pfsync_state_import_ptr	VNET(pfsync_state_import_ptr)
@@ -1199,6 +1200,8 @@ VNET_DECLARE(pfsync_clear_states_t *, pfsync_clear_states_ptr);
 #define V_pfsync_clear_states_ptr	VNET(pfsync_clear_states_ptr)
 VNET_DECLARE(pfsync_defer_t *, pfsync_defer_ptr);
 #define V_pfsync_defer_ptr		VNET(pfsync_defer_ptr)
+VNET_DECLARE(pflow_export_state_t *,	pflow_export_state_ptr);
+#define V_pflow_export_state_ptr	VNET(pflow_export_state_ptr)
 extern pfsync_detach_ifnet_t	*pfsync_detach_ifnet_ptr;
 
 void			pfsync_state_export(union pfsync_state_union *,
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index df93cc1bebc3..7eaac809e487 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -1397,6 +1397,9 @@ pf_detach_state(struct pf_kstate *s)
 
 	pf_sctp_multihome_detach_addr(s);
 
+	if ((s->state_flags & PFSTATE_PFLOW) && V_pflow_export_state_ptr)
+		V_pflow_export_state_ptr(s);
+
 	if (sks != NULL) {
 		kh = &V_pf_keyhash[pf_hashkey(sks)];
 		PF_HASHROW_LOCK(kh);
@@ -4872,6 +4875,8 @@ pf_create_state(struct pf_krule *r, struct pf_krule *nr, struct pf_krule *a,
 		s->state_flags |= PFSTATE_SLOPPY;
 	if (pd->flags & PFDESC_TCP_NORM) /* Set by old-style scrub rules */
 		s->state_flags |= PFSTATE_SCRUB_TCP;
+	if (r->rule_flag & PFRULE_PFLOW)
+		s->state_flags |= PFSTATE_PFLOW;
 
 	s->act.log = pd->act.log & PF_LOG_ALL;
 	s->sync_state = PFSYNC_S_NONE;
diff --git a/sys/netpfil/pf/pf.h b/sys/netpfil/pf/pf.h
index dd9796b59ce9..d5ab4f03a96d 100644
--- a/sys/netpfil/pf/pf.h
+++ b/sys/netpfil/pf/pf.h
@@ -614,6 +614,7 @@ struct pf_rule {
 #define	PFRULE_SET_TOS		0x00002000
 #define	PFRULE_IFBOUND		0x00010000 /* if-bound */
 #define	PFRULE_STATESLOPPY	0x00020000 /* sloppy state tracking */
+#define	PFRULE_PFLOW		0x00040000
 
 #ifdef _KERNEL
 #define	PFRULE_REFS		0x0080	/* rule has references */
@@ -626,7 +627,7 @@ struct pf_rule {
 /* pf_state->state_flags, pf_rule_actions->flags, pf_krule->scrub_flags */
 #define	PFSTATE_ALLOWOPTS	0x0001
 #define	PFSTATE_SLOPPY		0x0002
-/*  was	PFSTATE_PFLOW		0x0004 */
+#define	PFSTATE_PFLOW		0x0004
 #define	PFSTATE_NOSYNC		0x0008
 #define	PFSTATE_ACK		0x0010
 #define	PFSTATE_NODF		0x0020
diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c
index e09b7b71920e..956d954d652c 100644
--- a/sys/netpfil/pf/pf_ioctl.c
+++ b/sys/netpfil/pf/pf_ioctl.c
@@ -304,6 +304,7 @@ VNET_DEFINE(pfsync_update_state_t *, pfsync_update_state_ptr);
 VNET_DEFINE(pfsync_delete_state_t *, pfsync_delete_state_ptr);
 VNET_DEFINE(pfsync_clear_states_t *, pfsync_clear_states_ptr);
 VNET_DEFINE(pfsync_defer_t *, pfsync_defer_ptr);
+VNET_DEFINE(pflow_export_state_t *, pflow_export_state_ptr);
 pfsync_detach_ifnet_t *pfsync_detach_ifnet_ptr;
 
 /* pflog */
diff --git a/sys/netpfil/pf/pflow.c b/sys/netpfil/pf/pflow.c
index 2dc612bd9e07..a2699240fbc8 100644
--- a/sys/netpfil/pf/pflow.c
+++ b/sys/netpfil/pf/pflow.c
@@ -87,18 +87,19 @@ static void	pflow_timeout(void *);
 static void	pflow_timeout6(void *);
 static void	pflow_timeout_tmpl(void *);
 static void	copy_flow_data(struct pflow_flow *, struct pflow_flow *,
-	struct pf_kstate *, struct pf_state_key *, int, int);
+	const struct pf_kstate *, struct pf_state_key *, int, int);
 static void	copy_flow_ipfix_4_data(struct pflow_ipfix_flow4 *,
-	struct pflow_ipfix_flow4 *, struct pf_kstate *, struct pf_state_key *,
+	struct pflow_ipfix_flow4 *, const struct pf_kstate *, struct pf_state_key *,
 	struct pflow_softc *, int, int);
 static void	copy_flow_ipfix_6_data(struct pflow_ipfix_flow6 *,
-	struct pflow_ipfix_flow6 *, struct pf_kstate *, struct pf_state_key *,
+	struct pflow_ipfix_flow6 *, const struct pf_kstate *, struct pf_state_key *,
 	struct pflow_softc *, int, int);
-static int	pflow_pack_flow(struct pf_kstate *, struct pf_state_key *,
+static int	pflow_pack_flow(const struct pf_kstate *, struct pf_state_key *,
 	struct pflow_softc *);
-static int	pflow_pack_flow_ipfix(struct pf_kstate *, struct pf_state_key *,
+static int	pflow_pack_flow_ipfix(const struct pf_kstate *, struct pf_state_key *,
 	struct pflow_softc *);
-static int	export_pflow_if(struct pf_kstate*, struct pf_state_key *,
+static void	export_pflow(const struct pf_kstate *);
+static int	export_pflow_if(const struct pf_kstate*, struct pf_state_key *,
 	struct pflow_softc *);
 static int	copy_flow_to_m(struct pflow_flow *flow, struct pflow_softc *sc);
 static int	copy_flow_ipfix_4_to_m(struct pflow_ipfix_flow4 *flow,
@@ -323,6 +324,8 @@ pflow_create(int unit)
 	CK_LIST_INSERT_HEAD(&V_pflowif_list, pflowif, sc_next);
 	mtx_unlock(&V_pflowif_list_mtx);
 
+	V_pflow_export_state_ptr = export_pflow;
+
 	return (0);
 }
 
@@ -352,6 +355,8 @@ pflow_destroy(int unit, bool drain)
 		return (ENOENT);
 	}
 	CK_LIST_REMOVE(sc, sc_next);
+	if (CK_LIST_EMPTY(&V_pflowif_list))
+		V_pflow_export_state_ptr = NULL;
 	mtx_unlock(&V_pflowif_list_mtx);
 
 	sc->sc_dying = 1;
@@ -511,7 +516,7 @@ pflow_get_mbuf(struct pflow_softc *sc, u_int16_t set_id)
 
 static void
 copy_flow_data(struct pflow_flow *flow1, struct pflow_flow *flow2,
-    struct pf_kstate *st, struct pf_state_key *sk, int src, int dst)
+    const struct pf_kstate *st, struct pf_state_key *sk, int src, int dst)
 {
 	flow1->src_ip = flow2->dest_ip = sk->addr[src].v4.s_addr;
 	flow1->src_port = flow2->dest_port = sk->port[src];
@@ -548,7 +553,7 @@ copy_flow_data(struct pflow_flow *flow1, struct pflow_flow *flow2,
 
 static void
 copy_flow_ipfix_4_data(struct pflow_ipfix_flow4 *flow1,
-    struct pflow_ipfix_flow4 *flow2, struct pf_kstate *st,
+    struct pflow_ipfix_flow4 *flow2, const struct pf_kstate *st,
     struct pf_state_key *sk, struct pflow_softc *sc, int src, int dst)
 {
 	flow1->src_ip = flow2->dest_ip = sk->addr[src].v4.s_addr;
@@ -585,7 +590,7 @@ copy_flow_ipfix_4_data(struct pflow_ipfix_flow4 *flow1,
 
 static void
 copy_flow_ipfix_6_data(struct pflow_ipfix_flow6 *flow1,
-    struct pflow_ipfix_flow6 *flow2, struct pf_kstate *st,
+    struct pflow_ipfix_flow6 *flow2, const struct pf_kstate *st,
     struct pf_state_key *sk, struct pflow_softc *sc, int src, int dst)
 {
 	bcopy(&sk->addr[src].v6, &flow1->src_ip, sizeof(flow1->src_ip));
@@ -622,8 +627,8 @@ copy_flow_ipfix_6_data(struct pflow_ipfix_flow6 *flow1,
 	flow1->tos = flow2->tos = st->rule.ptr->tos;
 }
 
-int
-export_pflow(struct pf_kstate *st)
+static void
+export_pflow(const struct pf_kstate *st)
 {
 	struct pflow_softc	*sc = NULL;
 	struct pf_state_key	*sk;
@@ -648,12 +653,10 @@ export_pflow(struct pf_kstate *st)
 		}
 		PFLOW_UNLOCK(sc);
 	}
-
-	return (0);
 }
 
 static int
-export_pflow_if(struct pf_kstate *st, struct pf_state_key *sk,
+export_pflow_if(const struct pf_kstate *st, struct pf_state_key *sk,
     struct pflow_softc *sc)
 {
 	struct pf_kstate	 pfs_copy;
@@ -787,7 +790,7 @@ copy_flow_ipfix_6_to_m(struct pflow_ipfix_flow6 *flow, struct pflow_softc *sc)
 }
 
 static int
-pflow_pack_flow(struct pf_kstate *st, struct pf_state_key *sk,
+pflow_pack_flow(const struct pf_kstate *st, struct pf_state_key *sk,
     struct pflow_softc *sc)
 {
 	struct pflow_flow	 flow1;
@@ -812,7 +815,7 @@ pflow_pack_flow(struct pf_kstate *st, struct pf_state_key *sk,
 }
 
 static int
-pflow_pack_flow_ipfix(struct pf_kstate *st, struct pf_state_key *sk,
+pflow_pack_flow_ipfix(const struct pf_kstate *st, struct pf_state_key *sk,
     struct pflow_softc *sc)
 {
 	struct pflow_ipfix_flow4	 flow4_1, flow4_2;