pf: support basic filters for state listing

Allow users(pace) to specify a protocol, interface, address family and/
or address and mask, allowing the state listing to be pre-filtered in
the kernel.

Sponsored by:	Rubicon Communications, LLC ("Netgate")
Differential Revision:	https://reviews.freebsd.org/D42280
This commit is contained in:
Kristof Provost 2023-10-16 16:47:22 +02:00
parent ffbf25951e
commit 044eef6ab9
5 changed files with 70 additions and 11 deletions

View file

@ -1342,7 +1342,7 @@ static const struct snl_hdr_parser *all_parsers[] = {
};
static int
pfctl_get_states_nl(struct snl_state *ss, pfctl_get_state_fn f, void *arg)
pfctl_get_states_nl(struct pfctl_state_filter *filter, struct snl_state *ss, pfctl_get_state_fn f, void *arg)
{
SNL_VERIFY_PARSERS(all_parsers);
int family_id = snl_get_genl_family(ss, PFNL_FAMILY_NAME);
@ -1354,7 +1354,14 @@ pfctl_get_states_nl(struct snl_state *ss, pfctl_get_state_fn f, void *arg)
snl_init_writer(ss, &nw);
hdr = snl_create_genl_msg_request(&nw, family_id, PFNL_CMD_GETSTATES);
hdr->nlmsg_flags |= NLM_F_DUMP;
snl_add_msg_attr_string(&nw, PF_ST_IFNAME, filter->ifname);
snl_add_msg_attr_u16(&nw, PF_ST_PROTO, filter->proto);
snl_add_msg_attr_u8(&nw, PF_ST_AF, filter->af);
snl_add_msg_attr_ip6(&nw, PF_ST_FILTER_ADDR, &filter->addr.v6);
snl_add_msg_attr_ip6(&nw, PF_ST_FILTER_MASK, &filter->mask.v6);
hdr = snl_finalize_msg(&nw);
uint32_t seq_id = hdr->nlmsg_seq;
snl_send_message(ss, hdr);
@ -1379,12 +1386,19 @@ pfctl_get_states_nl(struct snl_state *ss, pfctl_get_state_fn f, void *arg)
int
pfctl_get_states_iter(pfctl_get_state_fn f, void *arg)
{
struct pfctl_state_filter filter = {};
return (pfctl_get_filtered_states_iter(&filter, f, arg));
}
int
pfctl_get_filtered_states_iter(struct pfctl_state_filter *filter, pfctl_get_state_fn f, void *arg)
{
struct snl_state ss = {};
int error;
snl_init(&ss, NETLINK_GENERIC);
error = pfctl_get_states_nl(&ss, f, arg);
error = pfctl_get_states_nl(filter, &ss, f, arg);
snl_free(&ss);
return (error);

View file

@ -415,8 +415,17 @@ int pfctl_add_rule(int dev, const struct pfctl_rule *r,
uint32_t pool_ticket);
int pfctl_set_keepcounters(int dev, bool keep);
int pfctl_get_creatorids(uint32_t *creators, size_t *len);
struct pfctl_state_filter {
char ifname[IFNAMSIZ];
uint16_t proto;
sa_family_t af;
struct pf_addr addr;
struct pf_addr mask;
};
typedef int (*pfctl_get_state_fn)(struct pfctl_state *, void *);
int pfctl_get_states_iter(pfctl_get_state_fn f, void *arg);
int pfctl_get_filtered_states_iter(struct pfctl_state_filter *filter, pfctl_get_state_fn f, void *arg);
int pfctl_get_states(int dev, struct pfctl_states *states);
void pfctl_free_states(struct pfctl_states *states);
int pfctl_clear_states(int dev, const struct pfctl_kill *kill,

View file

@ -1529,9 +1529,6 @@ pfctl_show_state(struct pfctl_state *s, void *arg)
{
struct pfctl_show_state_arg *a = (struct pfctl_show_state_arg *)arg;
if (a->iface != NULL && strcmp(s->ifname, a->iface))
return (0);
if (a->dotitle) {
pfctl_print_title("STATES:");
a->dotitle = 0;
@ -1545,12 +1542,16 @@ int
pfctl_show_states(int dev, const char *iface, int opts)
{
struct pfctl_show_state_arg arg;
struct pfctl_state_filter filter = {};
if (iface != NULL)
strncpy(filter.ifname, iface, IFNAMSIZ);
arg.opts = opts;
arg.dotitle = opts & PF_OPT_SHOWALL;
arg.iface = iface;
if (pfctl_get_states_iter(pfctl_show_state, &arg))
if (pfctl_get_filtered_states_iter(&filter, pfctl_show_state, &arg))
return (-1);
return (0);

View file

@ -52,6 +52,11 @@ struct nl_parsed_state {
uint8_t version;
uint32_t id;
uint32_t creatorid;
char ifname[IFNAMSIZ];
uint16_t proto;
sa_family_t af;
struct pf_addr addr;
struct pf_addr mask;
};
#define _IN(_field) offsetof(struct genlmsghdr, _field)
@ -59,6 +64,11 @@ struct nl_parsed_state {
static const struct nlattr_parser nla_p_state[] = {
{ .type = PF_ST_ID, .off = _OUT(id), .cb = nlattr_get_uint32 },
{ .type = PF_ST_CREATORID, .off = _OUT(creatorid), .cb = nlattr_get_uint32 },
{ .type = PF_ST_IFNAME, .arg = (const void *)IFNAMSIZ, .off = _OUT(ifname), .cb = nlattr_get_chara },
{ .type = PF_ST_AF, .off = _OUT(proto), .cb = nlattr_get_uint8 },
{ .type = PF_ST_PROTO, .off = _OUT(proto), .cb = nlattr_get_uint16 },
{ .type = PF_ST_FILTER_ADDR, .off = _OUT(addr), .cb = nlattr_get_in6_addr },
{ .type = PF_ST_FILTER_MASK, .off = _OUT(mask), .cb = nlattr_get_in6_addr },
};
static const struct nlfield_parser nlf_p_generic[] = {
{ .off_in = _IN(version), .off_out = _OUT(version), .cb = nlf_get_u8 },
@ -217,11 +227,34 @@ handle_dumpstates(struct nlpcb *nlp, struct nl_parsed_state *attrs,
PF_HASHROW_LOCK(ih);
LIST_FOREACH(s, &ih->states, entry) {
if (s->timeout != PFTM_UNLINKED) {
error = dump_state(nlp, hdr, s, npt);
if (error != 0)
break;
}
sa_family_t af = s->key[PF_SK_WIRE]->af;
if (s->timeout == PFTM_UNLINKED)
continue;
/* Filter */
if (attrs->creatorid != 0 && s->creatorid != attrs->creatorid)
continue;
if (attrs->ifname[0] != 0 &&
strncmp(attrs->ifname, s->kif->pfik_name, IFNAMSIZ) != 0)
continue;
if (attrs->proto != 0 && s->key[PF_SK_WIRE]->proto != attrs->proto)
continue;
if (attrs->af != 0 && af != attrs->af)
continue;
if (pf_match_addr(1, &s->key[PF_SK_WIRE]->addr[0],
&attrs->mask, &attrs->addr, af) &&
pf_match_addr(1, &s->key[PF_SK_WIRE]->addr[1],
&attrs->mask, &attrs->addr, af) &&
pf_match_addr(1, &s->key[PF_SK_STACK]->addr[0],
&attrs->mask, &attrs->addr, af) &&
pf_match_addr(1, &s->key[PF_SK_STACK]->addr[1],
&attrs->mask, &attrs->addr, af))
continue;
error = dump_state(nlp, hdr, s, npt);
if (error != 0)
break;
}
PF_HASHROW_UNLOCK(ih);
}

View file

@ -97,6 +97,8 @@ enum pfstate_type_t {
PF_ST_SYNC_FLAGS = 26, /* u8 */
PF_ST_UPDATES = 27, /* u8 */
PF_ST_VERSION = 28, /* u64 */
PF_ST_FILTER_ADDR = 29, /* in6_addr */
PF_ST_FILTER_MASK = 30, /* in6_addr */
};
enum pf_addr_type_t {