Skip to content

Commit 93abcf1

Browse files
committed
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: pfsense/FreeBSD-src#11 MFC after: 1 week Sponsored by: Rubicon Communications, LLC ("Netgate") Differential Revision: https://reviews.freebsd.org/D30092
1 parent c2e11d8 commit 93abcf1

File tree

7 files changed

+131
-17
lines changed

7 files changed

+131
-17
lines changed

lib/libpfctl/libpfctl.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,7 @@ _pfctl_clear_states(int dev, const struct pfctl_kill *kill,
645645
pfctl_nv_add_rule_addr(nvl, "rt_addr", &kill->rt_addr);
646646
nvlist_add_string(nvl, "ifname", kill->ifname);
647647
nvlist_add_string(nvl, "label", kill->label);
648+
nvlist_add_bool(nvl, "kill_match", kill->kill_match);
648649

649650
nv.data = nvlist_pack(nvl, &nv.len);
650651
nv.size = nv.len;

lib/libpfctl/libpfctl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ struct pfctl_kill {
194194
struct pf_rule_addr rt_addr;
195195
char ifname[IFNAMSIZ];
196196
char label[PF_RULE_LABEL_SIZE];
197+
bool kill_match;
197198
};
198199

199200
int pfctl_get_rule(int dev, u_int32_t nr, u_int32_t ticket,

sbin/pfctl/pfctl.8

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
.Sh SYNOPSIS
3636
.Nm pfctl
3737
.Bk -words
38-
.Op Fl AdeghmNnOPqRrvz
38+
.Op Fl AdeghMmNnOPqRrvz
3939
.Op Fl a Ar anchor
4040
.Oo Fl D Ar macro Ns =
4141
.Ar value Oc
@@ -331,6 +331,17 @@ A network prefix length can also be specified.
331331
To kill all states using a gateway in 192.168.0.0/24:
332332
.Pp
333333
.Dl # pfctl -k gateway -k 192.168.0.0/24
334+
.Pp
335+
.It Fl M
336+
Kill matching states in the opposite direction (on other interfaces) when
337+
killing states.
338+
This applies to states killed using the -k option and also will apply to the
339+
flush command when flushing states.
340+
This is useful when an interface is specified when flushing states.
341+
Example:
342+
.Pp
343+
.Dl # pfctl -M -i interface -Fs
344+
.Pp
334345
.It Fl m
335346
Merge in explicitly given options without resetting those
336347
which are omitted.

sbin/pfctl/pfctl.c

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ usage(void)
245245
extern char *__progname;
246246

247247
fprintf(stderr,
248-
"usage: %s [-AdeghmNnOPqRrvz] [-a anchor] [-D macro=value] [-F modifier]\n"
248+
"usage: %s [-AdeghMmNnOPqRrvz] [-a anchor] [-D macro=value] [-F modifier]\n"
249249
"\t[-f file] [-i interface] [-K host | network]\n"
250250
"\t[-k host | network | gateway | label | id] [-o level] [-p device]\n"
251251
"\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)
478478
sizeof(kill.ifname)) >= sizeof(kill.ifname))
479479
errx(1, "invalid interface: %s", iface);
480480

481+
if (opts & PF_OPT_KILLMATCH)
482+
kill.kill_match = true;
483+
481484
if (pfctl_clear_states(dev, &kill, &killed))
482485
err(1, "DIOCCLRSTATES");
483486
if ((opts & PF_OPT_QUIET) == 0)
@@ -661,6 +664,9 @@ pfctl_net_kill_states(int dev, const char *iface, int opts)
661664

662665
pfctl_addrprefix(state_kill[0], &kill.src.addr.v.a.mask);
663666

667+
if (opts & PF_OPT_KILLMATCH)
668+
kill.kill_match = true;
669+
664670
if ((ret_ga = getaddrinfo(state_kill[0], NULL, NULL, &res[0]))) {
665671
errx(1, "getaddrinfo: %s", gai_strerror(ret_ga));
666672
/* NOTREACHED */
@@ -768,6 +774,9 @@ pfctl_gateway_kill_states(int dev, const char *iface, int opts)
768774
sizeof(kill.ifname)) >= sizeof(kill.ifname))
769775
errx(1, "invalid interface: %s", iface);
770776

777+
if (opts & PF_OPT_KILLMATCH)
778+
kill.kill_match = true;
779+
771780
pfctl_addrprefix(state_kill[1], &kill.rt_addr.addr.v.a.mask);
772781

773782
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)
821830
sizeof(kill.ifname)) >= sizeof(kill.ifname))
822831
errx(1, "invalid interface: %s", iface);
823832

833+
if (opts & PF_OPT_KILLMATCH)
834+
kill.kill_match = true;
835+
824836
if (strlcpy(kill.label, state_kill[1], sizeof(kill.label)) >=
825837
sizeof(kill.label))
826838
errx(1, "label too long: %s", state_kill[1]);
@@ -846,6 +858,10 @@ pfctl_id_kill_states(int dev, const char *iface, int opts)
846858
}
847859

848860
memset(&kill, 0, sizeof(kill));
861+
862+
if (opts & PF_OPT_KILLMATCH)
863+
kill.kill_match = true;
864+
849865
if ((sscanf(state_kill[1], "%jx/%x",
850866
&kill.cmp.id, &kill.cmp.creatorid)) == 2)
851867
HTONL(kill.cmp.creatorid);
@@ -2199,7 +2215,7 @@ main(int argc, char *argv[])
21992215
usage();
22002216

22012217
while ((ch = getopt(argc, argv,
2202-
"a:AdD:eqf:F:ghi:k:K:mnNOo:Pp:rRs:t:T:vx:z")) != -1) {
2218+
"a:AdD:eqf:F:ghi:k:K:mMnNOo:Pp:rRs:t:T:vx:z")) != -1) {
22032219
switch (ch) {
22042220
case 'a':
22052221
anchoropt = optarg;
@@ -2252,6 +2268,9 @@ main(int argc, char *argv[])
22522268
case 'm':
22532269
opts |= PF_OPT_MERGE;
22542270
break;
2271+
case 'M':
2272+
opts |= PF_OPT_KILLMATCH;
2273+
break;
22552274
case 'n':
22562275
opts |= PF_OPT_NOACTION;
22572276
break;

sbin/pfctl/pfctl_parser.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
#define PF_OPT_NUMERIC 0x1000
5656
#define PF_OPT_MERGE 0x2000
5757
#define PF_OPT_RECURSE 0x4000
58+
#define PF_OPT_KILLMATCH 0x8000
5859

5960
#define PF_TH_ALL 0xFF
6061

sys/net/pfvar.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,6 +1085,7 @@ struct pf_kstate_kill {
10851085
char psk_ifname[IFNAMSIZ];
10861086
char psk_label[PF_RULE_LABEL_SIZE];
10871087
u_int psk_killed;
1088+
bool psk_kill_match;
10881089
};
10891090
#endif
10901091

sys/netpfil/pf/pf_ioctl.c

Lines changed: 94 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2467,6 +2467,8 @@ pf_nvstate_kill_to_kstate_kill(const nvlist_t *nvl,
24672467
sizeof(kill->psk_ifname)));
24682468
PFNV_CHK(pf_nvstring(nvl, "label", kill->psk_label,
24692469
sizeof(kill->psk_label)));
2470+
if (nvlist_exists_bool(nvl, "kill_match"))
2471+
kill->psk_kill_match = nvlist_get_bool(nvl, "kill_match");
24702472

24712473
errout:
24722474
return (error);
@@ -2644,13 +2646,33 @@ pf_label_match(const struct pf_krule *rule, const char *label)
26442646
return (false);
26452647
}
26462648

2649+
static unsigned int
2650+
pf_kill_matching_state(struct pf_state_key_cmp *key, int dir)
2651+
{
2652+
struct pf_state *match;
2653+
int more = 0;
2654+
unsigned int killed = 0;
2655+
2656+
/* Call with unlocked hashrow */
2657+
2658+
match = pf_find_state_all(key, dir, &more);
2659+
if (match && !more) {
2660+
pf_unlink_state(match, 0);
2661+
killed++;
2662+
}
2663+
2664+
return (killed);
2665+
}
2666+
26472667
static int
26482668
pf_killstates_row(struct pf_kstate_kill *psk, struct pf_idhash *ih)
26492669
{
26502670
struct pf_state *s;
26512671
struct pf_state_key *sk;
26522672
struct pf_addr *srcaddr, *dstaddr;
2653-
int killed = 0;
2673+
struct pf_state_key_cmp match_key;
2674+
int idx, killed = 0;
2675+
unsigned int dir;
26542676
u_int16_t srcport, dstport;
26552677

26562678
relock_DIOCKILLSTATES:
@@ -2707,8 +2729,36 @@ pf_killstates_row(struct pf_kstate_kill *psk, struct pf_idhash *ih)
27072729
s->kif->pfik_name))
27082730
continue;
27092731

2732+
if (psk->psk_kill_match) {
2733+
/* Create the key to find matching states, with lock
2734+
* held. */
2735+
2736+
bzero(&match_key, sizeof(match_key));
2737+
2738+
if (s->direction == PF_OUT) {
2739+
dir = PF_IN;
2740+
idx = PF_SK_STACK;
2741+
} else {
2742+
dir = PF_OUT;
2743+
idx = PF_SK_WIRE;
2744+
}
2745+
2746+
match_key.af = s->key[idx]->af;
2747+
match_key.proto = s->key[idx]->proto;
2748+
PF_ACPY(&match_key.addr[0],
2749+
&s->key[idx]->addr[1], match_key.af);
2750+
match_key.port[0] = s->key[idx]->port[1];
2751+
PF_ACPY(&match_key.addr[1],
2752+
&s->key[idx]->addr[0], match_key.af);
2753+
match_key.port[1] = s->key[idx]->port[0];
2754+
}
2755+
27102756
pf_unlink_state(s, PF_ENTER_LOCKED);
27112757
killed++;
2758+
2759+
if (psk->psk_kill_match)
2760+
killed += pf_kill_matching_state(&match_key, dir);
2761+
27122762
goto relock_DIOCKILLSTATES;
27132763
}
27142764
PF_HASHROW_UNLOCK(ih);
@@ -5442,27 +5492,57 @@ pf_keepcounters(struct pfioc_nv *nv)
54425492
static unsigned int
54435493
pf_clear_states(const struct pf_kstate_kill *kill)
54445494
{
5495+
struct pf_state_key_cmp match_key;
54455496
struct pf_state *s;
5446-
unsigned int killed = 0;
5497+
int idx;
5498+
unsigned int killed = 0, dir;
54475499

54485500
for (unsigned int i = 0; i <= pf_hashmask; i++) {
54495501
struct pf_idhash *ih = &V_pf_idhash[i];
54505502

54515503
relock_DIOCCLRSTATES:
54525504
PF_HASHROW_LOCK(ih);
5453-
LIST_FOREACH(s, &ih->states, entry)
5454-
if (!kill->psk_ifname[0] ||
5455-
!strcmp(kill->psk_ifname,
5456-
s->kif->pfik_name)) {
5457-
/*
5458-
* Don't send out individual
5459-
* delete messages.
5460-
*/
5461-
s->state_flags |= PFSTATE_NOSYNC;
5462-
pf_unlink_state(s, PF_ENTER_LOCKED);
5463-
killed++;
5464-
goto relock_DIOCCLRSTATES;
5505+
LIST_FOREACH(s, &ih->states, entry) {
5506+
if (kill->psk_ifname[0] &&
5507+
strcmp(kill->psk_ifname,
5508+
s->kif->pfik_name))
5509+
continue;
5510+
5511+
if (kill->psk_kill_match) {
5512+
bzero(&match_key, sizeof(match_key));
5513+
5514+
if (s->direction == PF_OUT) {
5515+
dir = PF_IN;
5516+
idx = PF_SK_STACK;
5517+
} else {
5518+
dir = PF_OUT;
5519+
idx = PF_SK_WIRE;
5520+
}
5521+
5522+
match_key.af = s->key[idx]->af;
5523+
match_key.proto = s->key[idx]->proto;
5524+
PF_ACPY(&match_key.addr[0],
5525+
&s->key[idx]->addr[1], match_key.af);
5526+
match_key.port[0] = s->key[idx]->port[1];
5527+
PF_ACPY(&match_key.addr[1],
5528+
&s->key[idx]->addr[0], match_key.af);
5529+
match_key.port[1] = s->key[idx]->port[0];
54655530
}
5531+
5532+
/*
5533+
* Don't send out individual
5534+
* delete messages.
5535+
*/
5536+
s->state_flags |= PFSTATE_NOSYNC;
5537+
pf_unlink_state(s, PF_ENTER_LOCKED);
5538+
killed++;
5539+
5540+
if (kill->psk_kill_match)
5541+
killed += pf_kill_matching_state(&match_key,
5542+
dir);
5543+
5544+
goto relock_DIOCCLRSTATES;
5545+
}
54665546
PF_HASHROW_UNLOCK(ih);
54675547
}
54685548

0 commit comments

Comments
 (0)