git: a7aad5bbcdf7 - main - pf: convert DIOCXBEGIN, DIOCXROLLBACK, DIOCXCOMMIT to netlink
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 30 Oct 2026 12:21:37 UTC
The branch main has been updated by kp:
URL: https://cgit.FreeBSD.org/src/commit/?id=a7aad5bbcdf7f82b4db8557adfa1a0267d43d06a
commit a7aad5bbcdf7f82b4db8557adfa1a0267d43d06a
Author: Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2026-10-20 07:34:39 +0000
Commit: Kristof Provost <kp@FreeBSD.org>
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 ***