pf: convert kill/clear state to use netlink

Sponsored by:	Rubicon Communications, LLC ("Netgate")
Differential Revision:	https://reviews.freebsd.org/D44090
This commit is contained in:
Kristof Provost 2024-02-26 11:20:29 +01:00
parent dfed87b5ce
commit 706d465dae
8 changed files with 220 additions and 55 deletions

View File

@ -57,6 +57,7 @@ static int change_table(int, const char *);
static void authpf_kill_states(void);
int dev; /* pf device */
struct pfctl_handle *pfh;
char anchorname[PF_ANCHOR_NAME_SIZE] = "authpf";
char rulesetname[MAXPATHLEN - PF_ANCHOR_NAME_SIZE - 2];
char tablename[PF_TABLE_NAME_SIZE] = "authpf_users";
@ -135,7 +136,8 @@ main(void)
}
/* open the pf device */
dev = open(PATH_DEVFILE, O_RDWR);
if (dev == -1) {
pfh = pfctl_open(PATH_DEVFILE);
if (dev == -1 || pfh == NULL) {
syslog(LOG_ERR, "cannot open packet filter device (%m)");
goto die;
}
@ -906,7 +908,7 @@ authpf_kill_states(void)
sizeof(kill.src.addr.v.a.addr));
memset(&kill.src.addr.v.a.mask, 0xff,
sizeof(kill.src.addr.v.a.mask));
if (pfctl_kill_states(dev, &kill, NULL))
if (pfctl_kill_states_h(pfh, &kill, NULL))
syslog(LOG_ERR, "pfctl_kill_states() failed (%m)");
/* Kill all states to ipsrc */
@ -915,7 +917,7 @@ authpf_kill_states(void)
sizeof(kill.dst.addr.v.a.addr));
memset(&kill.dst.addr.v.a.mask, 0xff,
sizeof(kill.dst.addr.v.a.mask));
if (pfctl_kill_states(dev, &kill, NULL))
if (pfctl_kill_states_h(pfh, &kill, NULL))
syslog(LOG_ERR, "pfctl_kill_states() failed (%m)");
}

View File

@ -1630,22 +1630,6 @@ pfctl_get_creatorids(struct pfctl_handle *h, uint32_t *creators, size_t *len)
return (error);
}
static void
pfctl_nv_add_state_cmp(nvlist_t *nvl, const char *name,
const struct pfctl_state_cmp *cmp)
{
nvlist_t *nv;
nv = nvlist_create(0);
nvlist_add_number(nv, "id", cmp->id);
nvlist_add_number(nv, "creatorid", htonl(cmp->creatorid));
nvlist_add_number(nv, "direction", cmp->direction);
nvlist_add_nvlist(nvl, name, nv);
nvlist_destroy(nv);
}
static inline bool
snl_attr_get_pfaddr(struct snl_state *ss __unused, struct nlattr *nla,
const void *arg __unused, void *target)
@ -1848,48 +1832,110 @@ pfctl_free_states(struct pfctl_states *states)
bzero(states, sizeof(*states));
}
struct pfctl_nl_clear_states {
uint32_t killed;
};
#define _OUT(_field) offsetof(struct pfctl_nl_clear_states, _field)
static struct snl_attr_parser ap_clear_states[] = {
{ .type = PF_CS_KILLED, .off = _OUT(killed), .cb = snl_attr_get_uint32 },
};
static struct snl_field_parser fp_clear_states[] = {};
#undef _OUT
SNL_DECLARE_PARSER(clear_states_parser, struct genlmsghdr, fp_clear_states, ap_clear_states);
static int
_pfctl_clear_states(int dev, const struct pfctl_kill *kill,
unsigned int *killed, uint64_t ioctlval)
_pfctl_clear_states_h(struct pfctl_handle *h, const struct pfctl_kill *kill,
unsigned int *killed, int cmd)
{
nvlist_t *nvl;
int ret;
struct snl_writer nw;
struct snl_errmsg_data e = {};
struct pfctl_nl_clear_states attrs = {};
struct nlmsghdr *hdr;
uint32_t seq_id;
int family_id;
nvl = nvlist_create(0);
family_id = snl_get_genl_family(&h->ss, PFNL_FAMILY_NAME);
if (family_id == 0)
return (ENOTSUP);
pfctl_nv_add_state_cmp(nvl, "cmp", &kill->cmp);
nvlist_add_number(nvl, "af", kill->af);
nvlist_add_number(nvl, "proto", kill->proto);
pfctl_nv_add_rule_addr(nvl, "src", &kill->src);
pfctl_nv_add_rule_addr(nvl, "dst", &kill->dst);
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);
nvlist_add_bool(nvl, "nat", kill->nat);
snl_init_writer(&h->ss, &nw);
hdr = snl_create_genl_msg_request(&nw, family_id, cmd);
hdr->nlmsg_flags |= NLM_F_DUMP;
if ((ret = pfctl_do_ioctl(dev, ioctlval, 1024, &nvl)) != 0)
goto out;
snl_add_msg_attr_u64(&nw, PF_CS_CMP_ID, kill->cmp.id);
snl_add_msg_attr_u32(&nw, PF_CS_CMP_CREATORID, htonl(kill->cmp.creatorid));
snl_add_msg_attr_u8(&nw, PF_CS_CMP_DIR, kill->cmp.direction);
snl_add_msg_attr_u8(&nw, PF_CS_AF, kill->af);
snl_add_msg_attr_u8(&nw, PF_CS_PROTO, kill->proto);
snl_add_msg_attr_rule_addr(&nw, PF_CS_SRC, &kill->src);
snl_add_msg_attr_rule_addr(&nw, PF_CS_DST, &kill->dst);
snl_add_msg_attr_rule_addr(&nw, PF_CS_RT_ADDR, &kill->rt_addr);
snl_add_msg_attr_string(&nw, PF_CS_IFNAME, kill->ifname);
snl_add_msg_attr_string(&nw, PF_CS_LABEL, kill->label);
snl_add_msg_attr_bool(&nw, PF_CS_KILL_MATCH, kill->kill_match);
snl_add_msg_attr_bool(&nw, PF_CS_NAT, kill->nat);
if ((hdr = snl_finalize_msg(&nw)) == NULL)
return (ENXIO);
seq_id = hdr->nlmsg_seq;
if (! snl_send_message(&h->ss, hdr))
return (ENXIO);
while ((hdr = snl_read_reply_multi(&h->ss, seq_id, &e)) != NULL) {
if (! snl_parse_nlmsg(&h->ss, hdr, &clear_states_parser, &attrs))
continue;
}
if (killed)
*killed = nvlist_get_number(nvl, "killed");
*killed = attrs.killed;
return (e.error);
}
int
pfctl_clear_states_h(struct pfctl_handle *h, const struct pfctl_kill *kill,
unsigned int *killed)
{
return(_pfctl_clear_states_h(h, kill, killed, PFNL_CMD_CLRSTATES));
}
int
pfctl_kill_states_h(struct pfctl_handle *h, const struct pfctl_kill *kill,
unsigned int *killed)
{
return(_pfctl_clear_states_h(h, kill, killed, PFNL_CMD_KILLSTATES));
}
static int
_pfctl_clear_states(int dev __unused, const struct pfctl_kill *kill,
unsigned int *killed, uint64_t cmd)
{
struct pfctl_handle *h;
int ret;
h = pfctl_open(PF_DEVICE);
if (h == NULL)
return (ENODEV);
ret = _pfctl_clear_states_h(h, kill, killed, cmd);
pfctl_close(h);
out:
nvlist_destroy(nvl);
return (ret);
}
int
pfctl_clear_states(int dev, const struct pfctl_kill *kill,
pfctl_clear_states(int dev __unused, const struct pfctl_kill *kill,
unsigned int *killed)
{
return (_pfctl_clear_states(dev, kill, killed, DIOCCLRSTATESNV));
return (_pfctl_clear_states(dev, kill, killed, PFNL_CMD_CLRSTATES));
}
int
pfctl_kill_states(int dev, const struct pfctl_kill *kill, unsigned int *killed)
pfctl_kill_states(int dev __unused, const struct pfctl_kill *kill, unsigned int *killed)
{
return (_pfctl_clear_states(dev, kill, killed, DIOCKILLSTATESNV));
return (_pfctl_clear_states(dev, kill, killed, PFNL_CMD_KILLSTATES));
}
int

View File

@ -446,6 +446,10 @@ int pfctl_clear_states(int dev, const struct pfctl_kill *kill,
unsigned int *killed);
int pfctl_kill_states(int dev, const struct pfctl_kill *kill,
unsigned int *killed);
int pfctl_clear_states_h(struct pfctl_handle *h, const struct pfctl_kill *kill,
unsigned int *killed);
int pfctl_kill_states_h(struct pfctl_handle *h, const struct pfctl_kill *kill,
unsigned int *killed);
int pfctl_clear_rules(int dev, const char *anchorname);
int pfctl_clear_nat(int dev, const char *anchorname);
int pfctl_clear_eth_rules(int dev, const char *anchorname);

View File

@ -545,7 +545,7 @@ pfctl_clear_iface_states(int dev, const char *iface, int opts)
if (opts & PF_OPT_KILLMATCH)
kill.kill_match = true;
if (pfctl_clear_states(dev, &kill, &killed))
if (pfctl_clear_states_h(pfh, &kill, &killed))
err(1, "DIOCCLRSTATES");
if ((opts & PF_OPT_QUIET) == 0)
fprintf(stderr, "%d states cleared\n", killed);
@ -801,13 +801,13 @@ pfctl_net_kill_states(int dev, const char *iface, int opts)
errx(1, "Unknown address family %d",
kill.af);
if (pfctl_kill_states(dev, &kill, &newkilled))
if (pfctl_kill_states_h(pfh, &kill, &newkilled))
err(1, "DIOCKILLSTATES");
killed += newkilled;
}
freeaddrinfo(res[1]);
} else {
if (pfctl_kill_states(dev, &kill, &newkilled))
if (pfctl_kill_states_h(pfh, &kill, &newkilled))
err(1, "DIOCKILLSTATES");
killed += newkilled;
}
@ -873,7 +873,7 @@ pfctl_gateway_kill_states(int dev, const char *iface, int opts)
else
errx(1, "Unknown address family %d", kill.af);
if (pfctl_kill_states(dev, &kill, &newkilled))
if (pfctl_kill_states_h(pfh, &kill, &newkilled))
err(1, "DIOCKILLSTATES");
killed += newkilled;
}
@ -907,7 +907,7 @@ pfctl_label_kill_states(int dev, const char *iface, int opts)
sizeof(kill.label))
errx(1, "label too long: %s", state_kill[1]);
if (pfctl_kill_states(dev, &kill, &killed))
if (pfctl_kill_states_h(pfh, &kill, &killed))
err(1, "DIOCKILLSTATES");
if ((opts & PF_OPT_QUIET) == 0)
@ -946,7 +946,7 @@ pfctl_id_kill_states(int dev, const char *iface, int opts)
usage();
}
if (pfctl_kill_states(dev, &kill, &killed))
if (pfctl_kill_states_h(pfh, &kill, &killed))
err(1, "DIOCKILLSTATES");
if ((opts & PF_OPT_QUIET) == 0)

View File

@ -2208,6 +2208,9 @@ extern int pf_state_insert(struct pfi_kkif *,
struct pf_kstate *);
extern struct pf_kstate *pf_alloc_state(int);
extern void pf_free_state(struct pf_kstate *);
extern void pf_killstates(struct pf_kstate_kill *,
unsigned int *);
extern unsigned int pf_clear_states(const struct pf_kstate_kill *);
static __inline void
pf_ref_state(struct pf_kstate *s)

View File

@ -227,9 +227,6 @@ struct cdev *pf_dev;
* XXX - These are new and need to be checked when moveing to a new version
*/
static void pf_clear_all_states(void);
static unsigned int pf_clear_states(const struct pf_kstate_kill *);
static void pf_killstates(struct pf_kstate_kill *,
unsigned int *);
static int pf_killstates_row(struct pf_kstate_kill *,
struct pf_idhash *);
static int pf_killstates_nv(struct pfioc_nv *);
@ -5944,7 +5941,7 @@ pf_keepcounters(struct pfioc_nv *nv)
return (error);
}
static unsigned int
unsigned int
pf_clear_states(const struct pf_kstate_kill *kill)
{
struct pf_state_key_cmp match_key;
@ -6013,7 +6010,7 @@ pf_clear_states(const struct pf_kstate_kill *kill)
return (killed);
}
static void
void
pf_killstates(struct pf_kstate_kill *kill, unsigned int *killed)
{
struct pf_kstate *s;

View File

@ -782,6 +782,7 @@ static const struct nlattr_parser nla_p_getrules[] = {
};
static const struct nlfield_parser nlf_p_getrules[] = {
};
#undef _IN
#undef _OUT
NL_DECLARE_PARSER(getrules_parser, struct genlmsghdr, nlf_p_getrules, nla_p_getrules);
@ -842,6 +843,8 @@ static const struct nlattr_parser nla_p_getrule[] = {
};
static const struct nlfield_parser nlf_p_getrule[] = {
};
#undef _IN
#undef _OUT
NL_DECLARE_PARSER(getrule_parser, struct genlmsghdr, nlf_p_getrule, nla_p_getrule);
static int
@ -1000,10 +1003,87 @@ pf_handle_getrule(struct nlmsghdr *hdr, struct nl_pstate *npt)
return (error);
}
#define _IN(_field) offsetof(struct genlmsghdr, _field)
#define _OUT(_field) offsetof(struct pf_kstate_kill, _field)
static const struct nlattr_parser nla_p_clear_states[] = {
{ .type = PF_CS_CMP_ID, .off = _OUT(psk_pfcmp.id), .cb = nlattr_get_uint64 },
{ .type = PF_CS_CMP_CREATORID, .off = _OUT(psk_pfcmp.creatorid), .cb = nlattr_get_uint32 },
{ .type = PF_CS_CMP_DIR, .off = _OUT(psk_pfcmp.direction), .cb = nlattr_get_uint8 },
{ .type = PF_CS_AF, .off = _OUT(psk_af), .cb = nlattr_get_uint8 },
{ .type = PF_CS_PROTO, .off = _OUT(psk_proto), .cb = nlattr_get_uint8 },
{ .type = PF_CS_SRC, .off = _OUT(psk_src), .arg = &rule_addr_parser, .cb = nlattr_get_nested },
{ .type = PF_CS_DST, .off = _OUT(psk_dst), .arg = &rule_addr_parser, .cb = nlattr_get_nested },
{ .type = PF_CS_RT_ADDR, .off = _OUT(psk_rt_addr), .arg = &rule_addr_parser, .cb = nlattr_get_nested },
{ .type = PF_CS_IFNAME, .off = _OUT(psk_ifname), .arg = (void *)IFNAMSIZ, .cb = nlattr_get_chara },
{ .type = PF_CS_LABEL, .off = _OUT(psk_label), .arg = (void *)PF_RULE_LABEL_SIZE, .cb = nlattr_get_chara },
{ .type = PF_CS_KILL_MATCH, .off = _OUT(psk_kill_match), .cb = nlattr_get_bool },
{ .type = PF_CS_NAT, .off = _OUT(psk_nat), .cb = nlattr_get_bool },
};
static const struct nlfield_parser nlf_p_clear_states[] = {};
#undef _IN
#undef _OUT
NL_DECLARE_PARSER(clear_states_parser, struct genlmsghdr, nlf_p_clear_states, nla_p_clear_states);
static int
pf_handle_killclear_states(struct nlmsghdr *hdr, struct nl_pstate *npt, int cmd)
{
struct pf_kstate_kill kill = {};
struct epoch_tracker et;
struct nl_writer *nw = npt->nw;
struct genlmsghdr *ghdr_new;
int error;
unsigned int killed = 0;
error = nl_parse_nlmsg(hdr, &clear_states_parser, npt, &kill);
if (error != 0)
return (error);
if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
return (ENOMEM);
ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
ghdr_new->cmd = cmd;
ghdr_new->version = 0;
ghdr_new->reserved = 0;
NET_EPOCH_ENTER(et);
if (cmd == PFNL_CMD_KILLSTATES)
pf_killstates(&kill, &killed);
else
killed = pf_clear_states(&kill);
NET_EPOCH_EXIT(et);
nlattr_add_u32(nw, PF_CS_KILLED, killed);
if (! nlmsg_end(nw)) {
error = ENOMEM;
goto out;
}
return (0);
out:
nlmsg_abort(nw);
return (error);
}
static int
pf_handle_clear_states(struct nlmsghdr *hdr, struct nl_pstate *npt)
{
return (pf_handle_killclear_states(hdr, npt, PFNL_CMD_CLRSTATES));
}
static int
pf_handle_kill_states(struct nlmsghdr *hdr, struct nl_pstate *npt)
{
return (pf_handle_killclear_states(hdr, npt, PFNL_CMD_KILLSTATES));
}
static const struct nlhdr_parser *all_parsers[] = {
&state_parser,
&addrule_parser,
&getrules_parser
&getrules_parser,
&clear_states_parser,
};
static int family_id;
@ -1058,6 +1138,20 @@ static const struct genl_cmd pf_cmds[] = {
.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
.cmd_priv = PRIV_NETINET_PF,
},
{
.cmd_num = PFNL_CMD_CLRSTATES,
.cmd_name = "CLRSTATES",
.cmd_cb = pf_handle_clear_states,
.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
.cmd_priv = PRIV_NETINET_PF,
},
{
.cmd_num = PFNL_CMD_KILLSTATES,
.cmd_name = "KILLSTATES",
.cmd_cb = pf_handle_kill_states,
.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
.cmd_priv = PRIV_NETINET_PF,
},
};
void

View File

@ -43,6 +43,8 @@ enum {
PFNL_CMD_ADDRULE = 5,
PFNL_CMD_GETRULES = 6,
PFNL_CMD_GETRULE = 7,
PFNL_CMD_CLRSTATES = 8,
PFNL_CMD_KILLSTATES = 9,
__PFNL_CMD_MAX,
};
#define PFNL_CMD_MAX (__PFNL_CMD_MAX -1)
@ -262,6 +264,23 @@ enum pf_getrules_type_t {
PF_GR_CLEAR = 5, /* u8 */
};
enum pf_clear_states_type_t {
PF_CS_UNSPEC,
PF_CS_CMP_ID = 1, /* u64 */
PF_CS_CMP_CREATORID = 2, /* u32 */
PF_CS_CMP_DIR = 3, /* u8 */
PF_CS_AF = 4, /* u8 */
PF_CS_PROTO = 5, /* u8 */
PF_CS_SRC = 6, /* nested, pf_addr_wrap */
PF_CS_DST = 7, /* nested, pf_addr_wrap */
PF_CS_RT_ADDR = 8, /* nested, pf_addr_wrap */
PF_CS_IFNAME = 9, /* string */
PF_CS_LABEL = 10, /* string */
PF_CS_KILL_MATCH = 11, /* bool */
PF_CS_NAT = 12, /* bool */
PF_CS_KILLED = 13, /* u32 */
};
#ifdef _KERNEL
void pf_nl_register(void);