Hello,

change introduces a pfctl_recurse() function we use to implement
recursive flush operation. The pfctl_recurse() is built from
two pieces:
    - pfctl_walk_get(), which itself is a new callback for pfctl_walk()
      we introduced in earlier patch. The pfctl_walk_get() puts every
      anchor found in tree to single linked list

    - pfctl_get_anchors() uses pfctl_walk() to collect all anchors
      found in PF driver to list

    - once there is a list of anchors pfctl_recurse() consumes
      the list of anchors applying a nuke() function. The nuke()
      function is an argument to pfctl_recurse(). The actual
      implementation is subject of follow up diff.

I've done some expirements with making pfctl_recurse() to pass
a nuke() callback directly to pfctl_walk() function, but the results
where 'disappointing'. We soon were tripping a panics on dangling
pointers. I think we need to revisit pf_ruleset.c before we will
be able to get rid of the extra list here. I'd like to stay pragmatic
and go ahead with list and makes things nice and tidy later.

thanks and
regards
sashan

--------8<---------------8<---------------8<------------------8<--------

diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
index ba3610a6a7b..2c5835df7c6 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -107,8 +107,12 @@ void       pfctl_state_store(int, const char *);
 void   pfctl_state_load(int, const char *);
 void   pfctl_reset(int, int);
 int    pfctl_walk_show(int, struct pfioc_ruleset *, void *);
+int    pfctl_walk_get(int, struct pfioc_ruleset *, void *);
 int    pfctl_walk_anchors(int, int, char *, void *,
     int(*)(int, struct pfioc_ruleset *, void *));
+struct pfr_anchors *
+       pfctl_get_anchors(int, int);
+int    pfctl_recurse(int, int, int(*nuke)(int, int, struct pfr_anchoritem *));
 
 const char     *clearopt;
 char           *rulesopt;
@@ -2123,6 +2127,39 @@ pfctl_walk_show(int opts, struct pfioc_ruleset *pr, void 
*warg)
        return (0);
 }
 
+int
+pfctl_walk_get(int opts, struct pfioc_ruleset *pr, void *warg)
+{
+       struct pfr_anchoritem *pfra;
+       unsigned int len;
+       struct {
+               struct pfr_anchoritem *pfra;
+       } *tail_pfra = warg;
+
+       pfra = malloc(sizeof(*pfra));
+       if (pfra == NULL)
+               err(1, "%s", __func__);
+
+       len = strlen(pr->path) + 1;
+       len += strlen(pr->name) + 1;
+       pfra->pfra_anchorname = malloc(len);
+       if (pfra->pfra_anchorname == NULL)
+               err(1, "%s", __func__);
+
+       if (pr->path[0])
+               snprintf(pfra->pfra_anchorname, len, "%s/%s",
+                   pr->path, pr->name);
+       else
+               snprintf(pfra->pfra_anchorname, len, "%s", pr->name);
+
+       if (tail_pfra->pfra != NULL)
+               SLIST_INSERT_AFTER(tail_pfra->pfra, pfra, pfra_sle);
+
+       tail_pfra->pfra = pfra;
+
+       return (0);
+}
+
 int
 pfctl_walk_anchors(int dev, int opts, char *anchorname, void *warg,
     int(walkf)(int, struct pfioc_ruleset *, void *))
@@ -2131,7 +2168,7 @@ pfctl_walk_anchors(int dev, int opts, char *anchorname, 
void *warg,
        u_int32_t                mnr, nr;
 
        memset(&pr, 0, sizeof(pr));
-       memcpy(pr.path, anchorname, sizeof(pr.path));
+       strlcpy(pr.path, anchorname, sizeof(pr.path));
        if (ioctl(dev, DIOCGETRULESETS, &pr) == -1) {
                if (errno == EINVAL)
                        fprintf(stderr, "Anchor '%s' not found.\n",
@@ -2179,6 +2216,56 @@ pfctl_show_anchors(int dev, int opts, char *anchorname)
        return (rv);
 }
 
+struct pfr_anchors *
+pfctl_get_anchors(int dev, int opts)
+{
+       struct pfioc_ruleset    pr;
+       struct {
+               struct pfr_anchoritem *pfra;
+       } pfra;
+       static struct pfr_anchors anchors;
+
+       SLIST_INIT(&anchors);
+
+       memset(&pr, 0, sizeof(pr));
+       pfra.pfra = NULL;
+       pfctl_walk_get(opts, &pr, &pfra);
+       if (pfra.pfra == NULL) {
+               fprintf(stderr, "%s failed to allocate main anchor\n",
+                   __func__);
+               exit(1);
+       }
+       SLIST_INSERT_HEAD(&anchors, pfra.pfra, pfra_sle);
+
+       opts |= PF_OPT_VERBOSE;
+       if (pfctl_walk_anchors(dev, opts, "", &pfra, pfctl_walk_get)) {
+               fprintf(stderr,
+                   "%s failed to retreive list of anchors, can't continue\n",
+                   __func__);
+               exit(1);
+       }
+
+       return (&anchors);
+}
+
+int
+pfctl_recurse(int dev, int opts, int(*nuke)(int, int, struct pfr_anchoritem *))
+{
+       int                      rv = 0;
+       struct pfr_anchors      *anchors;
+       struct pfr_anchoritem   *pfra, *pfra_save;
+
+       anchors = pfctl_get_anchors(dev, opts);
+       SLIST_FOREACH_SAFE(pfra, anchors, pfra_sle, pfra_save) {
+               rv |= nuke(dev, opts, pfra);
+               SLIST_REMOVE(anchors, pfra, pfr_anchoritem, pfra_sle);
+               free(pfra->pfra_anchorname);
+               free(pfra);
+       }
+
+       return (rv);
+}
+
 const char *
 pfctl_lookup_option(char *cmd, const char **list)
 {
diff --git a/sbin/pfctl/pfctl.h b/sbin/pfctl/pfctl.h
index 7981cf66fdb..9e9552e62ae 100644
--- a/sbin/pfctl/pfctl.h
+++ b/sbin/pfctl/pfctl.h
@@ -48,6 +48,14 @@ struct pfr_buffer {
            (var) != NULL;                              \
            (var) = pfr_buf_next((buf), (var)))
 
+
+struct pfr_anchoritem {
+       SLIST_ENTRY(pfr_anchoritem)     pfra_sle;
+       char    *pfra_anchorname;
+};
+
+SLIST_HEAD(pfr_anchors, pfr_anchoritem);
+
 int     pfr_get_fd(void);
 int     pfr_clr_tables(struct pfr_table *, int *, int);
 int     pfr_add_tables(struct pfr_table *, int, int *, int);

Reply via email to