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;
 

Reply via email to