diff --git a/lib/libpfctl/libpfctl.h b/lib/libpfctl/libpfctl.h --- a/lib/libpfctl/libpfctl.h +++ b/lib/libpfctl/libpfctl.h @@ -415,8 +415,17 @@ 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, diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c --- a/lib/libpfctl/libpfctl.c +++ b/lib/libpfctl/libpfctl.c @@ -1342,7 +1342,7 @@ }; 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 @@ 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 @@ 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); diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c --- a/sbin/pfctl/pfctl.c +++ b/sbin/pfctl/pfctl.c @@ -1529,9 +1529,6 @@ { 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 @@ 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); diff --git a/sys/netpfil/pf/pf_nl.h b/sys/netpfil/pf/pf_nl.h --- a/sys/netpfil/pf/pf_nl.h +++ b/sys/netpfil/pf/pf_nl.h @@ -97,6 +97,8 @@ 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 { diff --git a/sys/netpfil/pf/pf_nl.c b/sys/netpfil/pf/pf_nl.c --- a/sys/netpfil/pf/pf_nl.c +++ b/sys/netpfil/pf/pf_nl.c @@ -52,6 +52,11 @@ 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 @@ 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 @@ 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); }