git: 21449c5c1eee - stable/12 - pf: Support killing 'matching' states

Kristof Provost kp at FreeBSD.org
Fri May 14 13:06:57 UTC 2021


The branch stable/12 has been updated by kp:

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

commit 21449c5c1eee253dc5ce5a70632edba525f803e7
Author:     Kristof Provost <kp at FreeBSD.org>
AuthorDate: 2021-05-03 13:35:50 +0000
Commit:     Kristof Provost <kp at FreeBSD.org>
CommitDate: 2021-05-14 08:48:30 +0000

    pf: Support killing 'matching' states
    
    Optionally also kill states that match (i.e. are the NATed state or
    opposite direction state entry for) the state we're killing.
    
    See also https://redmine.pfsense.org/issues/8555
    
    Submitted by:   Steven Brown
    Reviewed by:    bcr (man page)
    Obtained from:  https://github.com/pfsense/FreeBSD-src/pull/11/
    MFC after:      1 week
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
    Differential Revision:  https://reviews.freebsd.org/D30092
    
    (cherry picked from commit 93abcf17e6cf3c1cd8511c8ff7a8bf20b2d76367)
---
 lib/libpfctl/libpfctl.c   |   1 +
 lib/libpfctl/libpfctl.h   |   1 +
 sbin/pfctl/pfctl.8        |  13 +++++-
 sbin/pfctl/pfctl.c        |  23 +++++++++-
 sbin/pfctl/pfctl_parser.h |   1 +
 sys/net/pfvar.h           |   1 +
 sys/netpfil/pf/pf_ioctl.c | 108 ++++++++++++++++++++++++++++++++++++++++------
 7 files changed, 131 insertions(+), 17 deletions(-)

diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c
index f50afa7c78ef..8271d9bab3df 100644
--- a/lib/libpfctl/libpfctl.c
+++ b/lib/libpfctl/libpfctl.c
@@ -645,6 +645,7 @@ _pfctl_clear_states(int dev, const struct pfctl_kill *kill,
 	pfctl_nv_add_rule_addr(nvl, "rt_addr", &kill->rt_addr);
 	nvlist_add_string(nvl, "ifname", kill->ifname);
 	nvlist_add_string(nvl, "label", kill->label);
+	nvlist_add_bool(nvl, "kill_match", kill->kill_match);
 
 	nv.data = nvlist_pack(nvl, &nv.len);
 	nv.size = nv.len;
diff --git a/lib/libpfctl/libpfctl.h b/lib/libpfctl/libpfctl.h
index 5c8b2108d937..7a1e02f3d01b 100644
--- a/lib/libpfctl/libpfctl.h
+++ b/lib/libpfctl/libpfctl.h
@@ -194,6 +194,7 @@ struct pfctl_kill {
 	struct pf_rule_addr	rt_addr;
 	char			ifname[IFNAMSIZ];
 	char			label[PF_RULE_LABEL_SIZE];
+	bool			kill_match;
 };
 
 int	pfctl_get_rule(int dev, u_int32_t nr, u_int32_t ticket,
diff --git a/sbin/pfctl/pfctl.8 b/sbin/pfctl/pfctl.8
index f3905f850c5a..ad0d1ba34769 100644
--- a/sbin/pfctl/pfctl.8
+++ b/sbin/pfctl/pfctl.8
@@ -35,7 +35,7 @@
 .Sh SYNOPSIS
 .Nm pfctl
 .Bk -words
-.Op Fl AdeghmNnOPqRrvz
+.Op Fl AdeghMmNnOPqRrvz
 .Op Fl a Ar anchor
 .Oo Fl D Ar macro Ns =
 .Ar value Oc
@@ -331,6 +331,17 @@ A network prefix length can also be specified.
 To kill all states using a gateway in 192.168.0.0/24:
 .Pp
 .Dl # pfctl -k gateway -k 192.168.0.0/24
+.Pp
+.It Fl M
+Kill matching states in the opposite direction (on other interfaces) when
+killing states.
+This applies to states killed using the -k option and also will apply to the
+flush command when flushing states.
+This is useful when an interface is specified when flushing states.
+Example:
+.Pp
+.Dl # pfctl -M -i interface -Fs
+.Pp
 .It Fl m
 Merge in explicitly given options without resetting those
 which are omitted.
diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
index 96f3b4740d90..fd937cac9f63 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -245,7 +245,7 @@ usage(void)
 	extern char *__progname;
 
 	fprintf(stderr,
-"usage: %s [-AdeghmNnOPqRrvz] [-a anchor] [-D macro=value] [-F modifier]\n"
+"usage: %s [-AdeghMmNnOPqRrvz] [-a anchor] [-D macro=value] [-F modifier]\n"
 	"\t[-f file] [-i interface] [-K host | network]\n"
 	"\t[-k host | network | gateway | label | id] [-o level] [-p device]\n"
 	"\t[-s modifier] [-t table -T command [address ...]] [-x level]\n",
@@ -478,6 +478,9 @@ pfctl_clear_iface_states(int dev, const char *iface, int opts)
 	    sizeof(kill.ifname)) >= sizeof(kill.ifname))
 		errx(1, "invalid interface: %s", iface);
 
+	if (opts & PF_OPT_KILLMATCH)
+		kill.kill_match = true;
+
 	if (pfctl_clear_states(dev, &kill, &killed))
 		err(1, "DIOCCLRSTATES");
 	if ((opts & PF_OPT_QUIET) == 0)
@@ -661,6 +664,9 @@ pfctl_net_kill_states(int dev, const char *iface, int opts)
 
 	pfctl_addrprefix(state_kill[0], &kill.src.addr.v.a.mask);
 
+	if (opts & PF_OPT_KILLMATCH)
+		kill.kill_match = true;
+
 	if ((ret_ga = getaddrinfo(state_kill[0], NULL, NULL, &res[0]))) {
 		errx(1, "getaddrinfo: %s", gai_strerror(ret_ga));
 		/* NOTREACHED */
@@ -768,6 +774,9 @@ pfctl_gateway_kill_states(int dev, const char *iface, int opts)
 	    sizeof(kill.ifname)) >= sizeof(kill.ifname))
 		errx(1, "invalid interface: %s", iface);
 
+	if (opts & PF_OPT_KILLMATCH)
+		kill.kill_match = true;
+
 	pfctl_addrprefix(state_kill[1], &kill.rt_addr.addr.v.a.mask);
 
 	if ((ret_ga = getaddrinfo(state_kill[1], NULL, NULL, &res))) {
@@ -821,6 +830,9 @@ pfctl_label_kill_states(int dev, const char *iface, int opts)
 	    sizeof(kill.ifname)) >= sizeof(kill.ifname))
 		errx(1, "invalid interface: %s", iface);
 
+	if (opts & PF_OPT_KILLMATCH)
+		kill.kill_match = true;
+
 	if (strlcpy(kill.label, state_kill[1], sizeof(kill.label)) >=
 	    sizeof(kill.label))
 		errx(1, "label too long: %s", state_kill[1]);
@@ -846,6 +858,10 @@ pfctl_id_kill_states(int dev, const char *iface, int opts)
 	}
 
 	memset(&kill, 0, sizeof(kill));
+
+	if (opts & PF_OPT_KILLMATCH)
+		kill.kill_match = true;
+
 	if ((sscanf(state_kill[1], "%jx/%x",
 	    &kill.cmp.id, &kill.cmp.creatorid)) == 2)
 		HTONL(kill.cmp.creatorid);
@@ -2199,7 +2215,7 @@ main(int argc, char *argv[])
 		usage();
 
 	while ((ch = getopt(argc, argv,
-	    "a:AdD:eqf:F:ghi:k:K:mnNOo:Pp:rRs:t:T:vx:z")) != -1) {
+	    "a:AdD:eqf:F:ghi:k:K:mMnNOo:Pp:rRs:t:T:vx:z")) != -1) {
 		switch (ch) {
 		case 'a':
 			anchoropt = optarg;
@@ -2252,6 +2268,9 @@ main(int argc, char *argv[])
 		case 'm':
 			opts |= PF_OPT_MERGE;
 			break;
+		case 'M':
+			opts |= PF_OPT_KILLMATCH;
+			break;
 		case 'n':
 			opts |= PF_OPT_NOACTION;
 			break;
diff --git a/sbin/pfctl/pfctl_parser.h b/sbin/pfctl/pfctl_parser.h
index 0c66d5dda97a..5353900b380a 100644
--- a/sbin/pfctl/pfctl_parser.h
+++ b/sbin/pfctl/pfctl_parser.h
@@ -55,6 +55,7 @@
 #define PF_OPT_NUMERIC		0x1000
 #define PF_OPT_MERGE		0x2000
 #define PF_OPT_RECURSE		0x4000
+#define PF_OPT_KILLMATCH	0x8000
 
 #define PF_TH_ALL		0xFF
 
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index 196a5edb874a..fda0cc57681d 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -1083,6 +1083,7 @@ struct pf_kstate_kill {
 	char			psk_ifname[IFNAMSIZ];
 	char			psk_label[PF_RULE_LABEL_SIZE];
 	u_int			psk_killed;
+	bool			psk_kill_match;
 };
 #endif
 
diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c
index 69d32b835a43..d676bc7ec9c1 100644
--- a/sys/netpfil/pf/pf_ioctl.c
+++ b/sys/netpfil/pf/pf_ioctl.c
@@ -2417,13 +2417,33 @@ pf_label_match(const struct pf_krule *rule, const char *label)
 	return (false);
 }
 
+static unsigned int
+pf_kill_matching_state(struct pf_state_key_cmp *key, int dir)
+{
+	struct pf_state *match;
+	int more = 0;
+	unsigned int killed = 0;
+
+	/* Call with unlocked hashrow */
+
+	match = pf_find_state_all(key, dir, &more);
+	if (match && !more) {
+		pf_unlink_state(match, 0);
+		killed++;
+	}
+
+	return (killed);
+}
+
 static int
 pf_killstates_row(struct pf_kstate_kill *psk, struct pf_idhash *ih)
 {
 	struct pf_state		*s;
 	struct pf_state_key	*sk;
 	struct pf_addr		*srcaddr, *dstaddr;
-	int			 killed = 0;
+	struct pf_state_key_cmp	 match_key;
+	int			 idx, killed = 0;
+	unsigned int		 dir;
 	u_int16_t		 srcport, dstport;
 
 relock_DIOCKILLSTATES:
@@ -2480,8 +2500,36 @@ relock_DIOCKILLSTATES:
 		    s->kif->pfik_name))
 			continue;
 
+		if (psk->psk_kill_match) {
+			/* Create the key to find matching states, with lock
+			 * held. */
+
+			bzero(&match_key, sizeof(match_key));
+
+			if (s->direction == PF_OUT) {
+				dir = PF_IN;
+				idx = PF_SK_STACK;
+			} else {
+				dir = PF_OUT;
+				idx = PF_SK_WIRE;
+			}
+
+			match_key.af = s->key[idx]->af;
+			match_key.proto = s->key[idx]->proto;
+			PF_ACPY(&match_key.addr[0],
+			    &s->key[idx]->addr[1], match_key.af);
+			match_key.port[0] = s->key[idx]->port[1];
+			PF_ACPY(&match_key.addr[1],
+			    &s->key[idx]->addr[0], match_key.af);
+			match_key.port[1] = s->key[idx]->port[0];
+		}
+
 		pf_unlink_state(s, PF_ENTER_LOCKED);
 		killed++;
+
+		if (psk->psk_kill_match)
+			killed += pf_kill_matching_state(&match_key, dir);
+
 		goto relock_DIOCKILLSTATES;
 	}
 	PF_HASHROW_UNLOCK(ih);
@@ -2554,6 +2602,8 @@ pf_nvstate_kill_to_kstate_kill(const nvlist_t *nvl,
 	    sizeof(kill->psk_ifname)));
 	PFNV_CHK(pf_nvstring(nvl, "label", kill->psk_label,
 	    sizeof(kill->psk_label)));
+	if (nvlist_exists_bool(nvl, "kill_match"))
+		kill->psk_kill_match = nvlist_get_bool(nvl, "kill_match");
 
 errout:
 	return (error);
@@ -5457,27 +5507,57 @@ on_error:
 static unsigned int
 pf_clear_states(const struct pf_kstate_kill *kill)
 {
+	struct pf_state_key_cmp	 match_key;
 	struct pf_state	*s;
-	unsigned int	 killed = 0;
+	int		 idx;
+	unsigned int	 killed = 0, dir;
 
 	for (unsigned int i = 0; i <= pf_hashmask; i++) {
 		struct pf_idhash *ih = &V_pf_idhash[i];
 
 relock_DIOCCLRSTATES:
 		PF_HASHROW_LOCK(ih);
-		LIST_FOREACH(s, &ih->states, entry)
-			if (!kill->psk_ifname[0] ||
-			    !strcmp(kill->psk_ifname,
-			    s->kif->pfik_name)) {
-				/*
-				 * Don't send out individual
-				 * delete messages.
-				 */
-				s->state_flags |= PFSTATE_NOSYNC;
-				pf_unlink_state(s, PF_ENTER_LOCKED);
-				killed++;
-				goto relock_DIOCCLRSTATES;
+		LIST_FOREACH(s, &ih->states, entry) {
+			if (kill->psk_ifname[0] &&
+			    strcmp(kill->psk_ifname,
+			    s->kif->pfik_name))
+				continue;
+
+			if (kill->psk_kill_match) {
+				bzero(&match_key, sizeof(match_key));
+
+				if (s->direction == PF_OUT) {
+					dir = PF_IN;
+					idx = PF_SK_STACK;
+				} else {
+					dir = PF_OUT;
+					idx = PF_SK_WIRE;
+				}
+
+				match_key.af = s->key[idx]->af;
+				match_key.proto = s->key[idx]->proto;
+				PF_ACPY(&match_key.addr[0],
+				    &s->key[idx]->addr[1], match_key.af);
+				match_key.port[0] = s->key[idx]->port[1];
+				PF_ACPY(&match_key.addr[1],
+				    &s->key[idx]->addr[0], match_key.af);
+				match_key.port[1] = s->key[idx]->port[0];
 			}
+
+			/*
+			 * Don't send out individual
+			 * delete messages.
+			 */
+			s->state_flags |= PFSTATE_NOSYNC;
+			pf_unlink_state(s, PF_ENTER_LOCKED);
+			killed++;
+
+			if (kill->psk_kill_match)
+				killed += pf_kill_matching_state(&match_key,
+				    dir);
+
+			goto relock_DIOCCLRSTATES;
+		}
 		PF_HASHROW_UNLOCK(ih);
 	}
 


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