Hello,
On Wed, Apr 26, 2023 at 01:25:45AM +0200, Alexandr Nedvedicky wrote:
> Hello,
>
> this is 3rd of three diffs to improve DIOCGETRULE ioctl operation.
> the summary of changes done here is as follows:
> - add new members to pf_trans structure so it can
> hold data for DIOCGETRULE operation
>
> - DIOCGETRULES opens transaction and returns ticket/transaction id
> to pfctl. pfctl uses ticket in DIOCGETRULE to identify transaction
> when it retrieves the rule
>
> - change introduces new reference counter to pf_anchor which is used
> by pf_trans to keep reference to pf_anchor object.
>
> - DIOCGETRULES opens transaction and stores a reference to anchor
> with current ruleset version and pointer to the first rule
>
> - DIOCGETRULE looks up transaction, verifies version number is
> same so it can safely access next rule.
>
> - pfctl when handling 'pfctl -sr -R n' must iterate to
> n-th rule one-by-one.
>
updating diff to accommodate changes in diff 2/3 [1] in the stack
of changes.
thanks and
regards
sashan
[1] https://marc.info/?l=openbsd-tech&m=168251129621089&w=2
--------8<---------------8<---------------8<------------------8<--------
diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
index 8cbd9d77b4f..3787e97a6b1 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -837,7 +837,7 @@ pfctl_show_rules(int dev, char *path, int opts, enum
pfctl_show format,
char *anchorname, int depth, int wildcard, long shownr)
{
struct pfioc_rule pr;
- u_int32_t nr, mnr, header = 0;
+ u_int32_t header = 0;
int len = strlen(path), ret = 0;
char *npath, *p;
@@ -893,24 +893,9 @@ pfctl_show_rules(int dev, char *path, int opts, enum
pfctl_show format,
goto error;
}
- if (shownr < 0) {
- mnr = pr.nr;
- nr = 0;
- } else if (shownr < pr.nr) {
- nr = shownr;
- mnr = shownr + 1;
- } else {
- warnx("rule %ld not found", shownr);
- ret = -1;
- goto error;
- }
- for (; nr < mnr; ++nr) {
- pr.nr = nr;
- if (ioctl(dev, DIOCGETRULE, &pr) == -1) {
- warn("DIOCGETRULE");
- ret = -1;
- goto error;
- }
+ while (ioctl(dev, DIOCGETRULE, &pr) != -1) {
+ if ((shownr != -1) && (shownr != pr.nr))
+ continue;
/* anchor is the same for all rules in it */
if (pr.rule.anchor_wildcard == 0)
@@ -968,6 +953,13 @@ pfctl_show_rules(int dev, char *path, int opts, enum
pfctl_show format,
case PFCTL_SHOW_NOTHING:
break;
}
+ errno = 0;
+ }
+
+ if ((errno != 0) && (errno != ENOENT)) {
+ warn("DIOCGETRULE");
+ ret = -1;
+ goto error;
}
/*
diff --git a/sys/net/pf_ioctl.c b/sys/net/pf_ioctl.c
index 8ce0af53a89..d294a8a3b3c 100644
--- a/sys/net/pf_ioctl.c
+++ b/sys/net/pf_ioctl.c
@@ -122,6 +122,10 @@ struct pf_trans *pf_find_trans(uint64_t);
void pf_free_trans(struct pf_trans *);
void pf_rollback_trans(struct pf_trans *);
+void pf_init_tgetrule(struct pf_trans *,
+ struct pf_anchor *, uint32_t, struct pf_rule *);
+void pf_cleanup_tgetrule(struct pf_trans *t);
+
struct pf_rule pf_default_rule, pf_default_rule_new;
struct {
@@ -1470,7 +1474,9 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags,
struct proc *p)
case DIOCGETRULES: {
struct pfioc_rule *pr = (struct pfioc_rule *)addr;
struct pf_ruleset *ruleset;
- struct pf_rule *tail;
+ struct pf_rule *rule;
+ struct pf_trans *t;
+ u_int32_t ruleset_version;
NET_LOCK();
PF_LOCK();
@@ -1482,14 +1488,21 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags,
struct proc *p)
NET_UNLOCK();
goto fail;
}
- tail = TAILQ_LAST(ruleset->rules.active.ptr, pf_rulequeue);
- if (tail)
- pr->nr = tail->nr + 1;
+ rule = TAILQ_LAST(ruleset->rules.active.ptr, pf_rulequeue);
+ if (rule)
+ pr->nr = rule->nr + 1;
else
pr->nr = 0;
- pr->ticket = ruleset->rules.active.version;
+ ruleset_version = ruleset->rules.active.version;
+ pf_anchor_take(ruleset->anchor);
+ rule = TAILQ_FIRST(ruleset->rules.active.ptr);
PF_UNLOCK();
NET_UNLOCK();
+
+ t = pf_open_trans(minor(dev));
+ pf_init_tgetrule(t, ruleset->anchor, ruleset_version, rule);
+ pr->ticket = t->pft_ticket;
+
break;
}
@@ -1497,29 +1510,29 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags,
struct proc *p)
struct pfioc_rule *pr = (struct pfioc_rule *)addr;
struct pf_ruleset *ruleset;
struct pf_rule *rule;
+ struct pf_trans *t;
int i;
+ t = pf_find_trans(pr->ticket);
+ if (t == NULL)
+ return (EINVAL);
+ KASSERT(t->pft_unit == minor(dev));
+ if (t->pft_type != PF_TRANS_GETRULE)
+ return (EINVAL);
+
NET_LOCK();
PF_LOCK();
- pr->anchor[sizeof(pr->anchor) - 1] = '\0';
- ruleset = pf_find_ruleset(pr->anchor);
- if (ruleset == NULL) {
- error = EINVAL;
- PF_UNLOCK();
- NET_UNLOCK();
- goto fail;
- }
- if (pr->ticket != ruleset->rules.active.version) {
+ KASSERT(t->pftgr_anchor != NULL);
+ ruleset = &t->pftgr_anchor->ruleset;
+ if (t->pftgr_version != ruleset->rules.active.version) {
error = EBUSY;
PF_UNLOCK();
NET_UNLOCK();
goto fail;
}
- rule = TAILQ_FIRST(ruleset->rules.active.ptr);
- while ((rule != NULL) && (rule->nr != pr->nr))
- rule = TAILQ_NEXT(rule, entries);
+ rule = t->pftgr_rule;
if (rule == NULL) {
- error = EBUSY;
+ error = ENOENT;
PF_UNLOCK();
NET_UNLOCK();
goto fail;
@@ -1558,6 +1571,8 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags,
struct proc *p)
rule->bytes[0] = rule->bytes[1] = 0;
rule->states_tot = 0;
}
+ pr->nr = rule->nr;
+ t->pftgr_rule = TAILQ_NEXT(rule, entries);
PF_UNLOCK();
NET_UNLOCK();
break;
@@ -3298,9 +3313,38 @@ pf_find_trans(uint64_t ticket)
return (t);
}
+void
+pf_init_tgetrule(struct pf_trans *t, struct pf_anchor *a,
+ uint32_t rs_version, struct pf_rule *r)
+{
+ t->pft_type = PF_TRANS_GETRULE;
+ if (a == NULL)
+ t->pftgr_anchor = &pf_main_anchor;
+ else
+ t->pftgr_anchor = a;
+
+ t->pftgr_version = rs_version;
+ t->pftgr_rule = r;
+}
+
+void
+pf_cleanup_tgetrule(struct pf_trans *t)
+{
+ KASSERT(t->pft_type == PF_TRANS_GETRULE);
+ pf_anchor_rele(t->pftgr_anchor);
+}
+
void
pf_free_trans(struct pf_trans *t)
{
+ switch (t->pft_type) {
+ case PF_TRANS_GETRULE:
+ pf_cleanup_tgetrule(t);
+ break;
+ default:
+ log(LOG_ERR, "%s unknown transaction type: %d\n",
+ __func__, t->pft_type);
+ }
free(t, M_TEMP, sizeof(*t));
}
diff --git a/sys/net/pf_ruleset.c b/sys/net/pf_ruleset.c
index 6131895751e..9d17d71e124 100644
--- a/sys/net/pf_ruleset.c
+++ b/sys/net/pf_ruleset.c
@@ -233,6 +233,9 @@ pf_create_anchor(struct pf_anchor *parent, const char
*aname)
pf_init_ruleset(&anchor->ruleset);
anchor->ruleset.anchor = anchor;
+#ifdef _KERNEL
+ refcnt_init(&anchor->ref);
+#endif
return (anchor);
}
@@ -308,7 +311,7 @@ pf_remove_if_empty_ruleset(struct pf_ruleset *ruleset)
if ((parent = ruleset->anchor->parent) != NULL)
RB_REMOVE(pf_anchor_node, &parent->children,
ruleset->anchor);
- rs_pool_put_anchor(ruleset->anchor);
+ pf_anchor_rele(ruleset->anchor);
if (parent == NULL)
return;
ruleset = &parent->ruleset;
@@ -431,3 +434,27 @@ pf_remove_anchor(struct pf_rule *r)
pf_remove_if_empty_ruleset(&r->anchor->ruleset);
r->anchor = NULL;
}
+
+void
+pf_anchor_rele(struct pf_anchor *anchor)
+{
+ if ((anchor == NULL) || (anchor == &pf_main_anchor))
+ return;
+
+#ifdef _KERNEL
+ if (refcnt_rele(&anchor->ref))
+ rs_pool_put_anchor(anchor);
+#else
+ rs_pool_put_anchor(anchor);
+#endif
+}
+
+struct pf_anchor *
+pf_anchor_take(struct pf_anchor *anchor)
+{
+#ifdef _KERNEL
+ if ((anchor != NULL) && (anchor != &pf_main_anchor))
+ refcnt_take(&anchor->ref);
+#endif
+ return (anchor);
+}
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index cf1e34c36b4..529fee3d322 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -844,6 +844,7 @@ struct pf_anchor {
struct pf_ruleset ruleset;
int refcnt; /* anchor rules */
int match;
+ struct refcnt ref; /* for transactions */
};
RB_PROTOTYPE(pf_anchor_global, pf_anchor, entry_global, pf_anchor_compare)
RB_PROTOTYPE(pf_anchor_node, pf_anchor, entry_node, pf_anchor_compare)
@@ -1823,6 +1824,8 @@ struct pf_ruleset *pf_get_leaf_ruleset(char *,
char **);
struct pf_anchor *pf_create_anchor(struct pf_anchor *, const char *);
struct pf_ruleset *pf_find_or_create_ruleset(const char *);
void pf_rs_initialize(void);
+void pf_anchor_rele(struct pf_anchor *);
+struct pf_anchor *pf_anchor_take(struct pf_anchor *);
/* The fingerprint functions can be linked into userland programs (tcpdump) */
int pf_osfp_add(struct pf_osfp_ioctl *);
diff --git a/sys/net/pfvar_priv.h b/sys/net/pfvar_priv.h
index 5af2027733a..011c8f083a0 100644
--- a/sys/net/pfvar_priv.h
+++ b/sys/net/pfvar_priv.h
@@ -324,6 +324,7 @@ extern struct cpumem *pf_anchor_stack;
enum pf_trans_type {
PF_TRANS_NONE,
+ PF_TRANS_GETRULE,
PF_TRANS_MAX
};
@@ -332,7 +333,19 @@ struct pf_trans {
uint32_t pft_unit; /* process id */
uint64_t pft_ticket;
enum pf_trans_type pft_type;
+ union {
+ struct {
+ u_int32_t gr_version;
+ struct pf_anchor *gr_anchor;
+ struct pf_rule *gr_rule;
+ } u_getrule;
+ } u;
};
+
+#define pftgr_version u.u_getrule.gr_version
+#define pftgr_anchor u.u_getrule.gr_anchor
+#define pftgr_rule u.u_getrule.gr_rule
+
extern struct task pf_purge_task;
extern struct timeout pf_purge_to;