git: 706d465dae6a - main - pf: convert kill/clear state to use netlink
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 28 Feb 2024 22:27:14 UTC
The branch main has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=706d465dae6aa3e1b567299b9e80eb574b6c5abf commit 706d465dae6aa3e1b567299b9e80eb574b6c5abf Author: Kristof Provost <kp@FreeBSD.org> AuthorDate: 2024-02-26 10:20:29 +0000 Commit: Kristof Provost <kp@FreeBSD.org> CommitDate: 2024-02-28 22:26:18 +0000 pf: convert kill/clear state to use netlink Sponsored by: Rubicon Communications, LLC ("Netgate") Differential Revision: https://reviews.freebsd.org/D44090 --- contrib/pf/authpf/authpf.c | 8 +-- lib/libpfctl/libpfctl.c | 128 ++++++++++++++++++++++++++++++--------------- lib/libpfctl/libpfctl.h | 4 ++ sbin/pfctl/pfctl.c | 12 ++--- sys/net/pfvar.h | 3 ++ sys/netpfil/pf/pf_ioctl.c | 7 +-- sys/netpfil/pf/pf_nl.c | 96 +++++++++++++++++++++++++++++++++- sys/netpfil/pf/pf_nl.h | 19 +++++++ 8 files changed, 221 insertions(+), 56 deletions(-) diff --git a/contrib/pf/authpf/authpf.c b/contrib/pf/authpf/authpf.c index 9858c1c50ced..81dbcb747f5f 100644 --- a/contrib/pf/authpf/authpf.c +++ b/contrib/pf/authpf/authpf.c @@ -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)"); } diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c index 71546c4709c2..f12e45291fc1 100644 --- a/lib/libpfctl/libpfctl.c +++ b/lib/libpfctl/libpfctl.c @@ -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); - - if ((ret = pfctl_do_ioctl(dev, ioctlval, 1024, &nvl)) != 0) - goto out; + snl_init_writer(&h->ss, &nw); + hdr = snl_create_genl_msg_request(&nw, family_id, cmd); + hdr->nlmsg_flags |= NLM_F_DUMP; + + 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 diff --git a/lib/libpfctl/libpfctl.h b/lib/libpfctl/libpfctl.h index f05044a9a985..b16caf1f6480 100644 --- a/lib/libpfctl/libpfctl.h +++ b/lib/libpfctl/libpfctl.h @@ -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); diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c index c583279750f1..f8ecded066d2 100644 --- a/sbin/pfctl/pfctl.c +++ b/sbin/pfctl/pfctl.c @@ -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) diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h index f3a808f8ad26..88fb99ead84e 100644 --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -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) diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c index d83933cd293f..1b22d49a6255 100644 --- a/sys/netpfil/pf/pf_ioctl.c +++ b/sys/netpfil/pf/pf_ioctl.c @@ -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 @@ on_error: 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 @@ relock_DIOCCLRSTATES: return (killed); } -static void +void pf_killstates(struct pf_kstate_kill *kill, unsigned int *killed) { struct pf_kstate *s; diff --git a/sys/netpfil/pf/pf_nl.c b/sys/netpfil/pf/pf_nl.c index 120ce88f8720..e4558c156aef 100644 --- a/sys/netpfil/pf/pf_nl.c +++ b/sys/netpfil/pf/pf_nl.c @@ -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 @@ out: 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 diff --git a/sys/netpfil/pf/pf_nl.h b/sys/netpfil/pf/pf_nl.h index 51df8b7aece9..0ae4a9283072 100644 --- a/sys/netpfil/pf/pf_nl.h +++ b/sys/netpfil/pf/pf_nl.h @@ -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);