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
This commit is contained in:
parent
c2e11d81d2
commit
93abcf17e6
@ -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);
|
pfctl_nv_add_rule_addr(nvl, "rt_addr", &kill->rt_addr);
|
||||||
nvlist_add_string(nvl, "ifname", kill->ifname);
|
nvlist_add_string(nvl, "ifname", kill->ifname);
|
||||||
nvlist_add_string(nvl, "label", kill->label);
|
nvlist_add_string(nvl, "label", kill->label);
|
||||||
|
nvlist_add_bool(nvl, "kill_match", kill->kill_match);
|
||||||
|
|
||||||
nv.data = nvlist_pack(nvl, &nv.len);
|
nv.data = nvlist_pack(nvl, &nv.len);
|
||||||
nv.size = nv.len;
|
nv.size = nv.len;
|
||||||
|
@ -194,6 +194,7 @@ struct pfctl_kill {
|
|||||||
struct pf_rule_addr rt_addr;
|
struct pf_rule_addr rt_addr;
|
||||||
char ifname[IFNAMSIZ];
|
char ifname[IFNAMSIZ];
|
||||||
char label[PF_RULE_LABEL_SIZE];
|
char label[PF_RULE_LABEL_SIZE];
|
||||||
|
bool kill_match;
|
||||||
};
|
};
|
||||||
|
|
||||||
int pfctl_get_rule(int dev, u_int32_t nr, u_int32_t ticket,
|
int pfctl_get_rule(int dev, u_int32_t nr, u_int32_t ticket,
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
.Sh SYNOPSIS
|
.Sh SYNOPSIS
|
||||||
.Nm pfctl
|
.Nm pfctl
|
||||||
.Bk -words
|
.Bk -words
|
||||||
.Op Fl AdeghmNnOPqRrvz
|
.Op Fl AdeghMmNnOPqRrvz
|
||||||
.Op Fl a Ar anchor
|
.Op Fl a Ar anchor
|
||||||
.Oo Fl D Ar macro Ns =
|
.Oo Fl D Ar macro Ns =
|
||||||
.Ar value Oc
|
.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:
|
To kill all states using a gateway in 192.168.0.0/24:
|
||||||
.Pp
|
.Pp
|
||||||
.Dl # pfctl -k gateway -k 192.168.0.0/24
|
.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
|
.It Fl m
|
||||||
Merge in explicitly given options without resetting those
|
Merge in explicitly given options without resetting those
|
||||||
which are omitted.
|
which are omitted.
|
||||||
|
@ -245,7 +245,7 @@ usage(void)
|
|||||||
extern char *__progname;
|
extern char *__progname;
|
||||||
|
|
||||||
fprintf(stderr,
|
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[-f file] [-i interface] [-K host | network]\n"
|
||||||
"\t[-k host | network | gateway | label | id] [-o level] [-p device]\n"
|
"\t[-k host | network | gateway | label | id] [-o level] [-p device]\n"
|
||||||
"\t[-s modifier] [-t table -T command [address ...]] [-x level]\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))
|
sizeof(kill.ifname)) >= sizeof(kill.ifname))
|
||||||
errx(1, "invalid interface: %s", iface);
|
errx(1, "invalid interface: %s", iface);
|
||||||
|
|
||||||
|
if (opts & PF_OPT_KILLMATCH)
|
||||||
|
kill.kill_match = true;
|
||||||
|
|
||||||
if (pfctl_clear_states(dev, &kill, &killed))
|
if (pfctl_clear_states(dev, &kill, &killed))
|
||||||
err(1, "DIOCCLRSTATES");
|
err(1, "DIOCCLRSTATES");
|
||||||
if ((opts & PF_OPT_QUIET) == 0)
|
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);
|
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]))) {
|
if ((ret_ga = getaddrinfo(state_kill[0], NULL, NULL, &res[0]))) {
|
||||||
errx(1, "getaddrinfo: %s", gai_strerror(ret_ga));
|
errx(1, "getaddrinfo: %s", gai_strerror(ret_ga));
|
||||||
/* NOTREACHED */
|
/* NOTREACHED */
|
||||||
@ -768,6 +774,9 @@ pfctl_gateway_kill_states(int dev, const char *iface, int opts)
|
|||||||
sizeof(kill.ifname)) >= sizeof(kill.ifname))
|
sizeof(kill.ifname)) >= sizeof(kill.ifname))
|
||||||
errx(1, "invalid interface: %s", iface);
|
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);
|
pfctl_addrprefix(state_kill[1], &kill.rt_addr.addr.v.a.mask);
|
||||||
|
|
||||||
if ((ret_ga = getaddrinfo(state_kill[1], NULL, NULL, &res))) {
|
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))
|
sizeof(kill.ifname)) >= sizeof(kill.ifname))
|
||||||
errx(1, "invalid interface: %s", iface);
|
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)) >=
|
if (strlcpy(kill.label, state_kill[1], sizeof(kill.label)) >=
|
||||||
sizeof(kill.label))
|
sizeof(kill.label))
|
||||||
errx(1, "label too long: %s", state_kill[1]);
|
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));
|
memset(&kill, 0, sizeof(kill));
|
||||||
|
|
||||||
|
if (opts & PF_OPT_KILLMATCH)
|
||||||
|
kill.kill_match = true;
|
||||||
|
|
||||||
if ((sscanf(state_kill[1], "%jx/%x",
|
if ((sscanf(state_kill[1], "%jx/%x",
|
||||||
&kill.cmp.id, &kill.cmp.creatorid)) == 2)
|
&kill.cmp.id, &kill.cmp.creatorid)) == 2)
|
||||||
HTONL(kill.cmp.creatorid);
|
HTONL(kill.cmp.creatorid);
|
||||||
@ -2199,7 +2215,7 @@ main(int argc, char *argv[])
|
|||||||
usage();
|
usage();
|
||||||
|
|
||||||
while ((ch = getopt(argc, argv,
|
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) {
|
switch (ch) {
|
||||||
case 'a':
|
case 'a':
|
||||||
anchoropt = optarg;
|
anchoropt = optarg;
|
||||||
@ -2252,6 +2268,9 @@ main(int argc, char *argv[])
|
|||||||
case 'm':
|
case 'm':
|
||||||
opts |= PF_OPT_MERGE;
|
opts |= PF_OPT_MERGE;
|
||||||
break;
|
break;
|
||||||
|
case 'M':
|
||||||
|
opts |= PF_OPT_KILLMATCH;
|
||||||
|
break;
|
||||||
case 'n':
|
case 'n':
|
||||||
opts |= PF_OPT_NOACTION;
|
opts |= PF_OPT_NOACTION;
|
||||||
break;
|
break;
|
||||||
|
@ -55,6 +55,7 @@
|
|||||||
#define PF_OPT_NUMERIC 0x1000
|
#define PF_OPT_NUMERIC 0x1000
|
||||||
#define PF_OPT_MERGE 0x2000
|
#define PF_OPT_MERGE 0x2000
|
||||||
#define PF_OPT_RECURSE 0x4000
|
#define PF_OPT_RECURSE 0x4000
|
||||||
|
#define PF_OPT_KILLMATCH 0x8000
|
||||||
|
|
||||||
#define PF_TH_ALL 0xFF
|
#define PF_TH_ALL 0xFF
|
||||||
|
|
||||||
|
@ -1085,6 +1085,7 @@ struct pf_kstate_kill {
|
|||||||
char psk_ifname[IFNAMSIZ];
|
char psk_ifname[IFNAMSIZ];
|
||||||
char psk_label[PF_RULE_LABEL_SIZE];
|
char psk_label[PF_RULE_LABEL_SIZE];
|
||||||
u_int psk_killed;
|
u_int psk_killed;
|
||||||
|
bool psk_kill_match;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -2467,6 +2467,8 @@ pf_nvstate_kill_to_kstate_kill(const nvlist_t *nvl,
|
|||||||
sizeof(kill->psk_ifname)));
|
sizeof(kill->psk_ifname)));
|
||||||
PFNV_CHK(pf_nvstring(nvl, "label", kill->psk_label,
|
PFNV_CHK(pf_nvstring(nvl, "label", kill->psk_label,
|
||||||
sizeof(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:
|
errout:
|
||||||
return (error);
|
return (error);
|
||||||
@ -2644,13 +2646,33 @@ pf_label_match(const struct pf_krule *rule, const char *label)
|
|||||||
return (false);
|
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
|
static int
|
||||||
pf_killstates_row(struct pf_kstate_kill *psk, struct pf_idhash *ih)
|
pf_killstates_row(struct pf_kstate_kill *psk, struct pf_idhash *ih)
|
||||||
{
|
{
|
||||||
struct pf_state *s;
|
struct pf_state *s;
|
||||||
struct pf_state_key *sk;
|
struct pf_state_key *sk;
|
||||||
struct pf_addr *srcaddr, *dstaddr;
|
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;
|
u_int16_t srcport, dstport;
|
||||||
|
|
||||||
relock_DIOCKILLSTATES:
|
relock_DIOCKILLSTATES:
|
||||||
@ -2707,8 +2729,36 @@ pf_killstates_row(struct pf_kstate_kill *psk, struct pf_idhash *ih)
|
|||||||
s->kif->pfik_name))
|
s->kif->pfik_name))
|
||||||
continue;
|
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);
|
pf_unlink_state(s, PF_ENTER_LOCKED);
|
||||||
killed++;
|
killed++;
|
||||||
|
|
||||||
|
if (psk->psk_kill_match)
|
||||||
|
killed += pf_kill_matching_state(&match_key, dir);
|
||||||
|
|
||||||
goto relock_DIOCKILLSTATES;
|
goto relock_DIOCKILLSTATES;
|
||||||
}
|
}
|
||||||
PF_HASHROW_UNLOCK(ih);
|
PF_HASHROW_UNLOCK(ih);
|
||||||
@ -5442,27 +5492,57 @@ pf_keepcounters(struct pfioc_nv *nv)
|
|||||||
static unsigned int
|
static unsigned int
|
||||||
pf_clear_states(const struct pf_kstate_kill *kill)
|
pf_clear_states(const struct pf_kstate_kill *kill)
|
||||||
{
|
{
|
||||||
|
struct pf_state_key_cmp match_key;
|
||||||
struct pf_state *s;
|
struct pf_state *s;
|
||||||
unsigned int killed = 0;
|
int idx;
|
||||||
|
unsigned int killed = 0, dir;
|
||||||
|
|
||||||
for (unsigned int i = 0; i <= pf_hashmask; i++) {
|
for (unsigned int i = 0; i <= pf_hashmask; i++) {
|
||||||
struct pf_idhash *ih = &V_pf_idhash[i];
|
struct pf_idhash *ih = &V_pf_idhash[i];
|
||||||
|
|
||||||
relock_DIOCCLRSTATES:
|
relock_DIOCCLRSTATES:
|
||||||
PF_HASHROW_LOCK(ih);
|
PF_HASHROW_LOCK(ih);
|
||||||
LIST_FOREACH(s, &ih->states, entry)
|
LIST_FOREACH(s, &ih->states, entry) {
|
||||||
if (!kill->psk_ifname[0] ||
|
if (kill->psk_ifname[0] &&
|
||||||
!strcmp(kill->psk_ifname,
|
strcmp(kill->psk_ifname,
|
||||||
s->kif->pfik_name)) {
|
s->kif->pfik_name))
|
||||||
/*
|
continue;
|
||||||
* Don't send out individual
|
|
||||||
* delete messages.
|
if (kill->psk_kill_match) {
|
||||||
*/
|
bzero(&match_key, sizeof(match_key));
|
||||||
s->state_flags |= PFSTATE_NOSYNC;
|
|
||||||
pf_unlink_state(s, PF_ENTER_LOCKED);
|
if (s->direction == PF_OUT) {
|
||||||
killed++;
|
dir = PF_IN;
|
||||||
goto relock_DIOCCLRSTATES;
|
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);
|
PF_HASHROW_UNLOCK(ih);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user