From nobody Fri Oct 30 12:21:37 2026 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4jGKw71Krlz6wm8c for ; Fri, 30 Oct 2026 12:21:43 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id 4jGKw70T6nz4VFy for ; Fri, 30 Oct 2026 12:21:43 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1793362903; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=YvF0ofiuG/bVrjzlcO2Az7DJYY3I24dv7LsvX++XAi0=; b=c+9Ih+bYI+nhEX0MZoUIFhZ6kOqoHnj8wwk8dkUqPGuWx8LmngRMUvuo8bhdJ0KGZmSTvF mJ4eSLyY7QYoKN6baskINHX4s4Ms4pBwyJfglzhDCt0/AqE69zqzXkyCqCXJqJc0NeZqKs 4JNuy474Qk5ElKMEznz+zL18o97U59dFbUh4wp6zeknyLEADyToClqEn+N9r0P3aHOInMh /wWi6p6wdvo+zH5NUtir3cKH/hGtfzK+3r9d3Bge+I8eUbU6afF+EkLb/l0LI5Lna/L+k4 mypyyQmjnOY9PgNa8zEgOQYek3vTquatiLCNhRdzAd4JuP+swpMhGDu2c6FpJg== ARC-Seal: i=1; a=rsa-sha256; d=freebsd.org; s=dkim; cv=none; t=1793362903; b=fARvQD6eWYcEBEW+XJqhbFWq5Y+lcb6Jee6fdsEWdUd3eAS4oZ25M5ViHhNbjIHAEzkgfY elDfLTtbeRwvyu7b1wdS8q1xS0wJojPgWGqWF5OhlhLN7AjPK0HFlTDS/VQEER/w0tW55/ sEjpxwggQ9/vYE/hTCze8V0i3asFp0OAu1oQ9VtpqFr9X5fSBuFfVT3VW2Cg8RPGW41IuH tCzVBHNAs6HCZDH3gSzdlC9+bSavQWmdf9Vu0vXrIRb+LUoAx+36lwVuuAHGXVb+uFCB11 cAeHlmTqqQlAIBN/0I4FW1OVdhsBGH7iCd8nEqpbYH8b8pIFDPvRNbuSmQdeYA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1793362903; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=YvF0ofiuG/bVrjzlcO2Az7DJYY3I24dv7LsvX++XAi0=; b=wBkH+CwxKFvc1BghPBpk9ZZTWNkxh8N2pd2bGoRVjcHEYcPQw5TPdccRnKrXPzfYoNPvPo DwLXPyLpaeDiB8uW/e7ypqIGrwYuMaUWDqu/KkFbrJwnY5JiLjfKqJZwUSAN6zPXWU79Jj 87k3G+ToeHzYn4I4QYOMt+9rRXp1EdPY9v4WtlYapGrC/BRuv61BGUWCFbQK7EaVGCYzAJ HOnj8w+2MD16hFNp5+zvlbb+SCATDZ61aZuBL/e+t0byUQnV8v9hRUw2j+oflpkWsGOJMJ oUlHfJeWNyX++GVKwRju1MdpBOgZEHBej+Ht12NmD+f2i6PPOGWCYbepEy4+aA== ARC-Authentication-Results: i=1; mx1.freebsd.org; none Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4jGKw706s4zQ4q for ; Fri, 30 Oct 2026 12:21:43 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 46b84 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Fri, 30 Oct 2026 12:21:37 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Kristof Provost Subject: git: a7aad5bbcdf7 - main - pf: convert DIOCXBEGIN, DIOCXROLLBACK, DIOCXCOMMIT to netlink List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org List-Id: Commit messages for all branches of the src repository List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: kp X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: a7aad5bbcdf7f82b4db8557adfa1a0267d43d06a Auto-Submitted: auto-generated Date: Fri, 30 Oct 2026 12:21:37 +0000 Message-Id: <6ae48bd1.46b84.c901e40@gitrepo.freebsd.org> The branch main has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=a7aad5bbcdf7f82b4db8557adfa1a0267d43d06a commit a7aad5bbcdf7f82b4db8557adfa1a0267d43d06a Author: Kristof Provost AuthorDate: 2026-10-20 07:34:39 +0000 Commit: Kristof Provost CommitDate: 2026-10-30 12:21:10 +0000 pf: convert DIOCXBEGIN, DIOCXROLLBACK, DIOCXCOMMIT to netlink Sponsored by: Rubicon Communications, LLC ("Netgate") --- lib/libpfctl/libpfctl.c | 200 ++++++++++++++++++++++++++++++++++++++++ lib/libpfctl/libpfctl.h | 12 +++ lib/libsysdecode/netlink.c | 16 ++++ sbin/pfctl/pfctl.c | 74 ++++++++------- sbin/pfctl/pfctl.h | 4 - sbin/pfctl/pfctl_parser.c | 37 -------- sbin/pfctl/pfctl_parser.h | 4 +- sys/net/pfvar.h | 18 ++++ sys/netpfil/pf/pf_ioctl.c | 224 +++++++++++++++++++++++++++++++++++++++++++++ sys/netpfil/pf/pf_nl.c | 145 +++++++++++++++++++++++++++++ sys/netpfil/pf/pf_nl.h | 15 +++ 11 files changed, 673 insertions(+), 76 deletions(-) diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c index f8ce51e2df18..1522fd1554ab 100644 --- a/lib/libpfctl/libpfctl.c +++ b/lib/libpfctl/libpfctl.c @@ -4594,6 +4594,206 @@ out: return (e.error); } +struct pfctl_trans_e { + int rs_num; + char anchor[MAXPATHLEN]; + uint32_t ticket; + TAILQ_ENTRY(pfctl_trans_e) entry; +}; + +struct pfctl_trans { + struct pfctl_trans_e tmp; + TAILQ_HEAD(, pfctl_trans_e) elms; +}; + +struct pfctl_trans * +pfctl_trans_alloc(void) +{ + struct pfctl_trans *t = malloc(sizeof(struct pfctl_trans)); + + bzero(t, sizeof(*t)); + + TAILQ_INIT(&t->elms); + + return (t); +} + +void +pfctl_trans_free(struct pfctl_trans *t) +{ + struct pfctl_trans_e *e, *et; + + TAILQ_FOREACH_SAFE(e, &t->elms, entry, et) + free(e); + + free(t); +} + +size_t +pfctl_trans_size(struct pfctl_trans *t) +{ + struct pfctl_trans_e *e; + size_t s = 0; + + TAILQ_FOREACH(e, &t->elms, entry) + s++; + + return (s); +} + +int +pfctl_trans_add(struct pfctl_trans *t, int rs_num, const char *anchor) +{ + struct pfctl_trans_e *e; + + e = malloc(sizeof(*e)); + if (e == NULL) + return (ENOMEM); + + e->rs_num = rs_num; + e->ticket = 0; + strlcpy(e->anchor, anchor, sizeof(e->anchor)); + + TAILQ_INSERT_TAIL(&t->elms, e, entry); + + return (0); +} + +uint32_t +pfctl_get_ticket(struct pfctl_trans *t, int rs_num, const char *anchor) +{ + struct pfctl_trans_e *e; + + TAILQ_FOREACH(e, &t->elms, entry) { + if (e->rs_num != rs_num) + continue; + if (strcmp(e->anchor, anchor) != 0) + continue; + + return (e->ticket); + } + + return (-1); +} + +static void +snl_add_msg_attr_trans_e(struct snl_writer *nw, uint32_t type, + const struct pfctl_trans_e *e) +{ + int off = snl_add_msg_attr_nested(nw, type); + + snl_add_msg_attr_u32(nw, PF_TE_RULESET_NUM, e->rs_num); + snl_add_msg_attr_string(nw, PF_TE_ANCHOR, e->anchor); + snl_add_msg_attr_u32(nw, PF_TE_TICKET, e->ticket); + + snl_end_attr_nested(nw, off); +} + +#define _OUT(_field) offsetof(struct pfctl_trans_e, _field) +static const struct snl_attr_parser ap_trans_e[] = { + { .type = PF_TE_RULESET_NUM, .off = _OUT(rs_num), .cb = snl_attr_get_uint32 }, + { .type = PF_TE_ANCHOR, .off = _OUT(anchor), .arg_u32 = MAXPATHLEN, .cb = snl_attr_copy_string }, + { .type = PF_TE_TICKET, .off = _OUT(ticket), .cb = snl_attr_get_uint32 }, +}; +#undef _OUT + +static bool +_pfctl_trans_e_post(struct snl_state *ss, void *target) +{ + struct pfctl_trans *t = (struct pfctl_trans *)target; + struct pfctl_trans_e *e; + + (void)ss; + + TAILQ_FOREACH(e, &t->elms, entry) { + if (e->rs_num != t->tmp.rs_num) + continue; + if (strcmp(e->anchor, t->tmp.anchor) != 0) + continue; + e->ticket = t->tmp.ticket; + return (true); + } + + e = malloc(sizeof(*e)); + if (! e) + return (false); + + memcpy(e, &t->tmp, sizeof(*e)); + + TAILQ_INSERT_TAIL(&t->elms, e, entry); + + return (true); +} + +SNL_DECLARE_ATTR_PARSER_EXT(trans_e_parser, 0, ap_trans_e, _pfctl_trans_e_post); + +static struct snl_attr_parser ap_trans[] = { + { .type = PF_TR_TRANS, .arg = &trans_e_parser, .cb = snl_attr_get_nested }, +}; +SNL_DECLARE_PARSER(trans_parser, struct genlmsghdr, snl_f_p_empty, ap_trans); + +static int +_pfctl_trans(struct pfctl_handle *h, struct pfctl_trans *t, size_t off, int cmd) +{ + struct snl_writer nw; + struct snl_errmsg_data e = {}; + struct nlmsghdr *hdr; + struct pfctl_trans_e *te; + uint32_t seq_id; + + snl_init_writer(&h->ss, &nw); + hdr = snl_create_genl_msg_request(&nw, h->family_id, + cmd); + + snl_add_msg_attr_u32(&nw, PF_TR_UNSPEC, 0); + TAILQ_FOREACH(te, &t->elms, entry) { + if (off) { + off--; + continue; + } + + snl_add_msg_attr_trans_e(&nw, PF_TR_TRANS, te); + } + + if ((hdr = snl_finalize_msg(&nw)) == NULL) { + e.error = ENXIO; + goto out; + } + seq_id = hdr->nlmsg_seq; + + if (! snl_send_message(&h->ss, hdr)) { + e.error = ENXIO; + goto out; + } + + while ((hdr = snl_read_reply_multi(&h->ss, seq_id, &e)) != NULL) { + if (! snl_parse_nlmsg(&h->ss, hdr, &trans_parser, t)) + continue; + } + +out: + snl_clear_lb(&h->ss); + return (e.error); +} + +int +pfctl_trans_begin(struct pfctl_handle *h, struct pfctl_trans *t, size_t off) +{ + return (_pfctl_trans(h, t, off, PFNL_CMD_BEGIN)); +} + +int +pfctl_trans_commit(struct pfctl_handle *h, struct pfctl_trans *t, size_t off) +{ + return (_pfctl_trans(h, t, off, PFNL_CMD_COMMIT)); +} + +int +pfctl_trans_rollback(struct pfctl_handle *h, struct pfctl_trans *t, size_t off) +{ + return (_pfctl_trans(h, t, off, PFNL_CMD_ROLLBACK)); +} + static const struct snl_hdr_parser *all_parsers[] = { &begin_addrs_parser, &clear_states_parser, diff --git a/lib/libpfctl/libpfctl.h b/lib/libpfctl/libpfctl.h index 0484024bec17..0e56d081e9d7 100644 --- a/lib/libpfctl/libpfctl.h +++ b/lib/libpfctl/libpfctl.h @@ -699,4 +699,16 @@ int pfctl_flush_fingerprints(struct pfctl_handle *h); int pfctl_get_fingerprint(struct pfctl_handle *h, int idx, struct pf_osfp_ioctl *fp); int pfctl_add_fingerprint(struct pfctl_handle *h, struct pf_osfp_ioctl *fp); +struct pfctl_trans; + +struct pfctl_trans* pfctl_trans_alloc(void); +void pfctl_trans_free(struct pfctl_trans *t); +size_t pfctl_trans_size(struct pfctl_trans *t); +int pfctl_trans_add(struct pfctl_trans *t, int rs_num, const char *anchor); +uint32_t pfctl_get_ticket(struct pfctl_trans *t, int rs_num, const char *anchor); + +int pfctl_trans_begin(struct pfctl_handle *h, struct pfctl_trans *t, size_t off); +int pfctl_trans_commit(struct pfctl_handle *h, struct pfctl_trans *t, size_t off); +int pfctl_trans_rollback(struct pfctl_handle *h, struct pfctl_trans *t, size_t off); + #endif diff --git a/lib/libsysdecode/netlink.c b/lib/libsysdecode/netlink.c index 5a7021b7b22b..ef13e213dcca 100644 --- a/lib/libsysdecode/netlink.c +++ b/lib/libsysdecode/netlink.c @@ -347,6 +347,18 @@ static const struct nlattr_decoder nla_d_ruleset[] = { }; NL_DECLARE_ATTR_DECODER(ruleset_decoder, nla_d_ruleset); +static const struct nlattr_decoder nla_d_trans_e[] = { + { .type = PF_TE_RULESET_NUM, .attr_name = "rs_num", .cb = nlattr_decode_uint32 }, + { .type = PF_TE_ANCHOR, .attr_name="anchor", .cb = nlattr_decode_string }, + { .type = PF_TE_TICKET, .attr_name = "ticket", .cb = nlattr_decode_uint32 }, +}; +NL_DECLARE_ATTR_DECODER(trans_e_decoder, nla_d_trans_e); + +static const struct nlattr_decoder nla_d_trans[] = { + { .type = PF_TR_TRANS, .attr_name = "trans", .cb = nlattr_decode_nested, .args = &trans_e_decoder }, +}; +NL_DECLARE_ATTR_DECODER(trans_decoder, nla_d_trans); + static inline void nl_verify_decoders(const struct nlattr_decoder_set **decoder, size_t count) { @@ -368,6 +380,7 @@ static const struct nlattr_decoder_set *all_decoders[] = { &rule_addr_decoder, &killclear_states_decoder, &ruleset_decoder, + &trans_decoder, }; static const struct pfnl_cmd_decoder cmd_decoder[] = { @@ -378,6 +391,9 @@ static const struct pfnl_cmd_decoder cmd_decoder[] = { { .cmd_num = PFNL_CMD_GET_ADDR, .ds = &addr_decoder }, { .cmd_num = PFNL_CMD_GET_RULESETS, .ds = &ruleset_decoder }, { .cmd_num = PFNL_CMD_GET_RULESET, .ds = &ruleset_decoder }, + { .cmd_num = PFNL_CMD_BEGIN, .ds = &trans_decoder }, + { .cmd_num = PFNL_CMD_COMMIT, .ds = &trans_decoder }, + { .cmd_num = PFNL_CMD_ROLLBACK, .ds = &trans_decoder }, }; static inline void diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c index 9ffdea03fea3..a8a870a740a2 100644 --- a/sbin/pfctl/pfctl.c +++ b/sbin/pfctl/pfctl.c @@ -620,18 +620,24 @@ pfctl_flush_nat(int dev, int opts, char *anchorname) int pfctl_clear_altq(int dev, int opts) { - struct pfr_buffer t; + struct pfctl_trans *t; if (!altqsupport) return (-1); - memset(&t, 0, sizeof(t)); - t.pfrb_type = PFRB_TRANS; - if (pfctl_add_trans(&t, PF_RULESET_ALTQ, "") || - pfctl_trans(dev, &t, DIOCXBEGIN, 0) || - pfctl_trans(dev, &t, DIOCXCOMMIT, 0)) + + t = pfctl_trans_alloc(); + if (t == NULL) + return (ENOMEM); + + if (pfctl_trans_add(t, PF_RULESET_ALTQ, "") || + pfctl_trans_begin(pfh, t, 0) || + pfctl_trans_commit(pfh, t, 0)) err(1, "pfctl_clear_altq"); if ((opts & PF_OPT_QUIET) == 0) fprintf(stderr, "altq cleared\n"); + + pfctl_trans_free(t); + return (0); } @@ -2234,13 +2240,13 @@ int pfctl_eth_ruleset_trans(struct pfctl *pf, char *path, struct pfctl_eth_anchor *a) { - int osize = pf->trans->pfrb_size; + int osize = pfctl_trans_size(pf->trans); if ((pf->loadopt & PFCTL_FLAG_ETH) != 0) { - if (pfctl_add_trans(pf->trans, PF_RULESET_ETH, path)) + if (pfctl_trans_add(pf->trans, PF_RULESET_ETH, path)) return (1); } - if (pfctl_trans(pf->dev, pf->trans, DIOCXBEGIN, osize)) + if (pfctl_trans_begin(pf->h, pf->trans, osize)) return (5); return (0); @@ -2249,32 +2255,32 @@ pfctl_eth_ruleset_trans(struct pfctl *pf, char *path, int pfctl_ruleset_trans(struct pfctl *pf, char *path, struct pfctl_anchor *a, bool do_eth) { - int osize = pf->trans->pfrb_size; + int osize = pfctl_trans_size(pf->trans); if ((pf->loadopt & PFCTL_FLAG_ETH) != 0 && do_eth) { - if (pfctl_add_trans(pf->trans, PF_RULESET_ETH, path)) + if (pfctl_trans_add(pf->trans, PF_RULESET_ETH, path)) return (1); } if ((pf->loadopt & PFCTL_FLAG_NAT) != 0) { - if (pfctl_add_trans(pf->trans, PF_RULESET_NAT, path) || - pfctl_add_trans(pf->trans, PF_RULESET_BINAT, path) || - pfctl_add_trans(pf->trans, PF_RULESET_RDR, path)) + if (pfctl_trans_add(pf->trans, PF_RULESET_NAT, path) || + pfctl_trans_add(pf->trans, PF_RULESET_BINAT, path) || + pfctl_trans_add(pf->trans, PF_RULESET_RDR, path)) return (1); } if (a == pf->astack[0] && ((altqsupport && (pf->loadopt & PFCTL_FLAG_ALTQ) != 0))) { - if (pfctl_add_trans(pf->trans, PF_RULESET_ALTQ, path)) + if (pfctl_trans_add(pf->trans, PF_RULESET_ALTQ, path)) return (2); } if ((pf->loadopt & PFCTL_FLAG_FILTER) != 0) { - if (pfctl_add_trans(pf->trans, PF_RULESET_SCRUB, path) || - pfctl_add_trans(pf->trans, PF_RULESET_FILTER, path)) + if (pfctl_trans_add(pf->trans, PF_RULESET_SCRUB, path) || + pfctl_trans_add(pf->trans, PF_RULESET_FILTER, path)) return (3); } if (pf->loadopt & PFCTL_FLAG_TABLE) - if (pfctl_add_trans(pf->trans, PF_RULESET_TABLE, path)) + if (pfctl_trans_add(pf->trans, PF_RULESET_TABLE, path)) return (4); - if (pfctl_trans(pf->dev, pf->trans, DIOCXBEGIN, osize)) + if (pfctl_trans_begin(pf->h, pf->trans, osize)) return (5); return (0); @@ -2657,12 +2663,12 @@ pfctl_add_altq(struct pfctl *pf, struct pf_altq *a) int pfctl_rules(int dev, char *filename, int opts, int optimize, - char *anchorname, struct pfr_buffer *trans) + char *anchorname, struct pfctl_trans *trans) { #define ERR(...) do { warn(__VA_ARGS__); goto _error; } while(0) #define ERRX(...) do { warnx(__VA_ARGS__); goto _error; } while(0) - struct pfr_buffer *t, buf; + struct pfctl_trans *t; struct pfioc_altq pa; struct pfctl pf; struct pfctl_ruleset *rs; @@ -2683,14 +2689,11 @@ pfctl_rules(int dev, char *filename, int opts, int optimize, pf_eth_main_anchor.ruleset.anchor = &pf_eth_main_anchor; if (trans == NULL) { - bzero(&buf, sizeof(buf)); - buf.pfrb_type = PFRB_TRANS; - pf.trans = &buf; - t = &buf; + t = pfctl_trans_alloc(); osize = 0; } else { t = trans; - osize = t->pfrb_size; + osize = pfctl_trans_size(t); } memset(&pa, 0, sizeof(pa)); @@ -2812,9 +2815,10 @@ pfctl_rules(int dev, char *filename, int opts, int optimize, if ((opts & PF_OPT_NOACTION) == 0) { if (!anchorname[0] && pfctl_load_options(&pf)) goto _error; - if (pfctl_trans(dev, t, DIOCXCOMMIT, osize)) + if (pfctl_trans_commit(pfh, t, osize)) ERR("DIOCXCOMMIT"); } + pfctl_trans_free(t); } free(path); return (0); @@ -2822,7 +2826,7 @@ pfctl_rules(int dev, char *filename, int opts, int optimize, _error: if (trans == NULL) { /* main ruleset */ if ((opts & PF_OPT_NOACTION) == 0) - if (pfctl_trans(dev, t, DIOCXROLLBACK, osize)) + if (pfctl_trans_rollback(pfh, t, osize)) err(1, "DIOCXROLLBACK"); exit(1); } else { /* sub ruleset */ @@ -3618,7 +3622,7 @@ void pfctl_reset(int dev, int opts) { struct pfctl pf; - struct pfr_buffer t; + struct pfctl_trans *t; int i; memset(&pf, 0, sizeof(pf)); @@ -3635,9 +3639,11 @@ pfctl_reset(int dev, int opts) err(1, "%s: strdup", __func__); pf.ifname_set = 1; - memset(&t, 0, sizeof(t)); - t.pfrb_type = PFRB_TRANS; - if (pfctl_trans(dev, &t, DIOCXBEGIN, 0)) + t = pfctl_trans_alloc(); + if (t == NULL) + err(1, "%s: pfctl_trans_alloc()", __func__); + + if (pfctl_trans_begin(pfh, t, 0)) err(1, "%s: DIOCXBEGIN", __func__); for (i = 0; pf_limits[i].name; i++) @@ -3648,9 +3654,11 @@ pfctl_reset(int dev, int opts) pfctl_load_options(&pf); - if (pfctl_trans(dev, &t, DIOCXCOMMIT, 0)) + if (pfctl_trans_commit(pfh, t, 0)) err(1, "%s: DIOCXCOMMIT", __func__); + pfctl_trans_free(t); + pfctl_clear_interface_flags(dev, opts); } diff --git a/sbin/pfctl/pfctl.h b/sbin/pfctl/pfctl.h index bcec429cdf78..48d8f096a0aa 100644 --- a/sbin/pfctl/pfctl.h +++ b/sbin/pfctl/pfctl.h @@ -170,10 +170,6 @@ void print_seq(struct pfctl_state_peer *); void print_state(struct pfctl_state *, int); int pfctl_cmdline_symset(char *); -int pfctl_add_trans(struct pfr_buffer *, int, const char *); -u_int32_t - pfctl_get_ticket(struct pfr_buffer *, int, const char *); -int pfctl_trans(int, struct pfr_buffer *, u_long, int); int pf_get_ruleset_number(u_int8_t); void pf_init_ruleset(struct pfctl_ruleset *); diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c index 46e8f81d0693..3748b25669aa 100644 --- a/sbin/pfctl/pfctl_parser.c +++ b/sbin/pfctl/pfctl_parser.c @@ -2131,40 +2131,3 @@ append_addr_host(struct pfr_buffer *b, struct node_host *n, int test, int not) return (0); } - -int -pfctl_add_trans(struct pfr_buffer *buf, int rs_num, const char *anchor) -{ - struct pfioc_trans_e trans; - - bzero(&trans, sizeof(trans)); - trans.rs_num = rs_num; - if (strlcpy(trans.anchor, anchor, - sizeof(trans.anchor)) >= sizeof(trans.anchor)) - errx(1, "pfctl_add_trans: strlcpy"); - - return pfr_buf_add(buf, &trans); -} - -u_int32_t -pfctl_get_ticket(struct pfr_buffer *buf, int rs_num, const char *anchor) -{ - struct pfioc_trans_e *p; - - PFRB_FOREACH(p, buf) - if (rs_num == p->rs_num && !strcmp(anchor, p->anchor)) - return (p->ticket); - errx(1, "pfctl_get_ticket: assertion failed"); -} - -int -pfctl_trans(int dev, struct pfr_buffer *buf, u_long cmd, int from) -{ - struct pfioc_trans trans; - - bzero(&trans, sizeof(trans)); - trans.size = buf->pfrb_size - from; - trans.esize = sizeof(struct pfioc_trans_e); - trans.array = ((struct pfioc_trans_e *)buf->pfrb_caddr) + from; - return ioctl(dev, cmd, &trans); -} diff --git a/sbin/pfctl/pfctl_parser.h b/sbin/pfctl/pfctl_parser.h index 5b2ff95f88c9..6c01d72d78c9 100644 --- a/sbin/pfctl/pfctl_parser.h +++ b/sbin/pfctl/pfctl_parser.h @@ -107,7 +107,7 @@ struct pfctl { struct pfioc_pooladdr paddr; struct pfioc_altq *paltq; struct pfioc_queue *pqueue; - struct pfr_buffer *trans; + struct pfctl_trans *trans; struct pfctl_anchor *anchor, *alast; struct pfr_ktablehead pfr_ktlast; int eth_nr; @@ -306,7 +306,7 @@ struct pfr_uktable; void copy_satopfaddr(struct pf_addr *, struct sockaddr *); -int pfctl_rules(int, char *, int, int, char *, struct pfr_buffer *); +int pfctl_rules(int, char *, int, int, char *, struct pfctl_trans *); int pfctl_optimize_ruleset(struct pfctl *, struct pfctl_ruleset *); void pfctl_init_rule(struct pfctl_rule *r); diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h index 5b4d87a22301..ba2bb8bf5bce 100644 --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -3186,6 +3186,24 @@ int inet_nat46(int, const void *, void *, const void *, u_int8_t); int inet_nat46_inet(const void *, void *, const void *, u_int8_t); int inet_nat46_inet6(const void *, void *, const void *, u_int8_t); +struct pf_trans_e { + int rs_num; + char anchor[MAXPATHLEN]; + uint32_t ticket; + TAILQ_ENTRY(pf_trans_e) entry; +}; + +struct pf_trans { + struct pf_trans_e tmp; + TAILQ_HEAD(, pf_trans_e) elms; + size_t count; +}; + +int pf_begin(struct pf_trans *); +int pf_commit(struct pf_trans *); +int pf_rollback(struct pf_trans *); +void pf_trans_free(struct pf_trans *); + #endif /* _KERNEL */ #endif /* _NET_PFVAR_H_ */ diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c index 55a5c38e1301..464a89a40b9a 100644 --- a/sys/netpfil/pf/pf_ioctl.c +++ b/sys/netpfil/pf/pf_ioctl.c @@ -3840,6 +3840,230 @@ pf_ioctl_natlook(struct pfioc_natlook *pnl) return (0); } +int +pf_begin(struct pf_trans *t) +{ + struct pf_trans_e *e; + int error = 0; + + PF_RULES_WLOCK(); + + TAILQ_FOREACH(e, &t->elms, entry) { + switch (e->rs_num) { + case PF_RULESET_ETH: + error = pf_begin_eth(&e->ticket, e->anchor); + if (error) + goto out; + break; +#ifdef ALTQ + case PF_RULESET_ALTQ: + if (e->anchor[0]) { + error = EINVAL; + goto out; + } + if ((error = pf_begin_altq(&e->ticket))) + goto out; + break; +#endif + case PF_RULESET_TABLE: { + struct pfr_table table; + + bzero(&table, sizeof(table)); + strlcpy(table.pfrt_anchor, e->anchor, + sizeof(table.pfrt_anchor)); + if ((error = pfr_ina_begin(&table, + &e->ticket, NULL, 0))) + goto out; + break; + } + default: + if ((error = pf_begin_rules(&e->ticket, e->rs_num, + e->anchor))) + goto out; + break; + } + } + +out: + PF_RULES_WUNLOCK(); + + return (error); +} + +int +pf_commit(struct pf_trans *t) +{ + struct pf_trans_e *e; + struct pf_kruleset *rs; + struct pf_keth_ruleset *ers; + int error = 0; + + PF_RULES_WLOCK(); + + /* First makes sure everything will succeed. */ + TAILQ_FOREACH(e, &t->elms, entry) { + switch (e->rs_num) { + case PF_RULESET_ETH: + ers = pf_find_keth_ruleset(e->anchor); + if (ers == NULL || e->ticket == 0 || + e->ticket != ers->inactive.ticket) { + error = EINVAL; + goto out; + } + break; +#ifdef ALTQ + case PF_RULESET_ALTQ: + if (e->anchor[0]) { + error = EINVAL; + goto out; + } + if (!V_altqs_inactive_open || e->ticket != + V_ticket_altqs_inactive) { + error = EBUSY; + goto out; + } + break; +#endif /* ALTQ */ + case PF_RULESET_TABLE: + rs = pf_find_kruleset(e->anchor); + if (rs == NULL || !rs->topen || e->ticket != + rs->tticket) { + error = EBUSY; + goto out; + } + break; + default: + if (e->rs_num < 0 || e->rs_num >= + PF_RULESET_MAX) { + error = EINVAL; + goto out; + } + rs = pf_find_kruleset(e->anchor); + if (rs == NULL || + !rs->rules[e->rs_num].inactive.open || + rs->rules[e->rs_num].inactive.ticket != + e->ticket) { + error = EBUSY; + goto out; + } + break; + } + } + + /* Now do the commit - no errors should happen here. */ + TAILQ_FOREACH(e, &t->elms, entry) { + switch (e->rs_num) { + case PF_RULESET_ETH: + if ((error = pf_commit_eth(e->ticket, e->anchor))) { + goto out; /* really bad */ + } + break; +#ifdef ALTQ + case PF_RULESET_ALTQ: + if ((error = pf_commit_altq(e->ticket))) { + goto out; /* really bad */ + } + break; +#endif /* ALTQ */ + case PF_RULESET_TABLE: + { + struct pfr_table table; + + bzero(&table, sizeof(table)); + (void)strlcpy(table.pfrt_anchor, e->anchor, + sizeof(table.pfrt_anchor)); + if ((error = pfr_ina_commit(&table, + e->ticket, NULL, NULL, 0))) { + goto out; /* really bad */ + } + break; + } + default: + if ((error = pf_commit_rules(e->ticket, + e->rs_num, e->anchor))) { + goto out; /* really bad */ + } + break; + } + } + +out: + PF_RULES_WUNLOCK(); + + /* Only hook into EtherNet taffic if we've got rules for it. */ + if (! TAILQ_EMPTY(V_pf_keth->active.rules)) + hook_pf_eth(); + else + dehook_pf_eth(); + + return (error); +} + +int +pf_rollback(struct pf_trans *t) +{ + struct pf_trans_e *e; + int error = 0; + + PF_RULES_WLOCK(); + TAILQ_FOREACH(e, &t->elms, entry) { + e->anchor[sizeof(e->anchor) - 1] = '\0'; + switch (e->rs_num) { + case PF_RULESET_ETH: + if ((error = pf_rollback_eth(e->ticket, + e->anchor))) { + goto out; /* really bad */ + } + break; +#ifdef ALTQ + case PF_RULESET_ALTQ: + if (e->anchor[0]) { + error = EINVAL; + goto out; + } + if ((error = pf_rollback_altq(e->ticket))) { + goto out; /* really bad */ + } + break; +#endif /* ALTQ */ + case PF_RULESET_TABLE: + { + struct pfr_table table; + + bzero(&table, sizeof(table)); + strlcpy(table.pfrt_anchor, e->anchor, + sizeof(table.pfrt_anchor)); + if ((error = pfr_ina_rollback(&table, + e->ticket, NULL, 0))) { + goto out; /* really bad */ + } + break; + } + default: + if ((error = pf_rollback_rules(e->ticket, + e->rs_num, e->anchor))) { + goto out; /* really bad */ + } + break; + } + } + +out: + PF_RULES_WUNLOCK(); + + return (error); +} + +void +pf_trans_free(struct pf_trans *t) +{ + struct pf_trans_e *e, *tmp; + + TAILQ_FOREACH_SAFE(e, &t->elms, entry, tmp) { + free(e, M_PF); + } +} + static int pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td) { diff --git a/sys/netpfil/pf/pf_nl.c b/sys/netpfil/pf/pf_nl.c index de7994b314c4..040d85e3c1cb 100644 --- a/sys/netpfil/pf/pf_nl.c +++ b/sys/netpfil/pf/pf_nl.c @@ -3088,6 +3088,126 @@ pf_handle_osfp_add(struct nlmsghdr *hdr, struct nl_pstate *npt) return (0); } +#define _OUT(_field) offsetof(struct pf_trans_e, _field) +static const struct nlattr_parser nla_p_trans_e[] = { + { .type = PF_TE_RULESET_NUM, .off = _OUT(rs_num), .cb = nlattr_get_uint32 }, + { .type = PF_TE_ANCHOR, .off = _OUT(anchor), .arg = (void *)MAXPATHLEN, .cb = nlattr_get_chara }, + { .type = PF_TE_TICKET, .off = _OUT(ticket), .cb = nlattr_get_uint32 }, +}; +#undef _OUT +static bool +nl_trans_e_post_parse(void *parsed_attrs, struct nl_pstate *npt) +{ + struct pf_trans *attrs = (struct pf_trans *)parsed_attrs; + struct pf_trans_e *e; + + e = malloc(sizeof (*e), M_PF, M_WAITOK); + + memcpy(e, &attrs->tmp, sizeof(*e)); + + TAILQ_INSERT_TAIL(&attrs->elms, e, entry); + + /* Limit number of entries we accept. */ + attrs->count++; + if (attrs->count > pf_ioctl_maxcount) { + return (false); + } + + return (true); +} + +NL_DECLARE_ATTR_PARSER_EXT(trans_e_parser, nla_p_trans_e, nl_trans_e_post_parse); + +static const struct nlattr_parser nla_p_trans[] = { + { .type = PF_TR_TRANS, .arg = &trans_e_parser, .cb = nlattr_get_nested }, +}; +NL_DECLARE_PARSER(trans_parser, struct genlmsghdr, nlf_p_empty, nla_p_trans); + +static bool +nlattr_add_trans(struct nl_writer *nw, int attr, + const struct pf_trans_e *e) +{ + int off = nlattr_add_nested(nw, attr); + if (off == 0) + return (false); + + nlattr_add_u32(nw, PF_TE_RULESET_NUM, e->rs_num); + nlattr_add_string(nw, PF_TE_ANCHOR, e->anchor); + nlattr_add_u32(nw, PF_TE_TICKET, e->ticket); + + nlattr_set_len(nw, off); + + return (true); +} + +static int +pf_handle_trans(struct nlmsghdr *hdr, struct nl_pstate *npt, int cmd) +{ + struct pf_trans attrs = { 0 }; + struct pf_trans_e *e; + struct nl_writer *nw = npt->nw; + struct genlmsghdr *ghdr_new; + int error = 0; + + TAILQ_INIT(&attrs.elms); + + error = nl_parse_nlmsg(hdr, &trans_parser, npt, &attrs); + if (error != 0) + goto out; + + switch (cmd) { + case PFNL_CMD_BEGIN: + error = pf_begin(&attrs); + break; + case PFNL_CMD_COMMIT: + error = pf_commit(&attrs); + break; + case PFNL_CMD_ROLLBACK: + error = pf_rollback(&attrs); + break; + default: + panic("Unexpected command %d", cmd); + } + + if (! nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) + return (ENOMEM); + + ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr); + ghdr_new->cmd = cmd; + + // Send the transaction info back, plus ticket info + nlattr_add_u32(nw, PF_TR_UNSPEC, 0); + TAILQ_FOREACH(e, &attrs.elms, entry) { + nlattr_add_trans(nw, PF_TR_TRANS, e); + } + + if (! nlmsg_end(nw)) + return (ENOMEM); + +out: + pf_trans_free(&attrs); + + return (error); +} + +static int +pf_handle_begin(struct nlmsghdr *hdr, struct nl_pstate *npt) +{ + return (pf_handle_trans(hdr, npt, PFNL_CMD_BEGIN)); +} + +static int +pf_handle_rollback(struct nlmsghdr *hdr, struct nl_pstate *npt) +{ + return (pf_handle_trans(hdr, npt, PFNL_CMD_ROLLBACK)); +} + +static int +pf_handle_commit(struct nlmsghdr *hdr, struct nl_pstate *npt) +{ + return (pf_handle_trans(hdr, npt, PFNL_CMD_COMMIT)); +} + static const struct nlhdr_parser *all_parsers[] = { &state_parser, &addrule_parser, @@ -3104,6 +3224,7 @@ static const struct nlhdr_parser *all_parsers[] = { &table_parser, &table_addr_parser, &table_astats_parser, + &trans_parser, &state_limiter_parser, &source_limiter_parser, &source_parser, @@ -3523,6 +3644,30 @@ static const struct genl_cmd pf_cmds[] = { .cmd_priv = PRIV_NETINET_PF, .cmd_securelevel = 3, }, + { + .cmd_num = PFNL_CMD_BEGIN, + .cmd_name = "BEGIN", + .cmd_cb = pf_handle_begin, + .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL, + .cmd_priv = PRIV_NETINET_PF, + .cmd_securelevel = 3, + }, + { + .cmd_num = PFNL_CMD_ROLLBACK, + .cmd_name = "ROLLBACK", + .cmd_cb = pf_handle_rollback, + .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL, *** 47 LINES SKIPPED ***