On Mon, Jul 20, 2020 at 05:07:03PM +0200, Klemens Nanni wrote:
> On Mon, Jul 20, 2020 at 01:14:00PM +0200, Alexandr Nedvedicky wrote:
> > I took a closer look at your change and related area.  Below is an alternate
> > way to fix the bug you've found.
> Thanks for bringing it up again, I forgot to reply earlier.
> 
> > there are few details worth to note:
> > 
> >     ptr_array matters to main ruleset only, because it is the only 
> >     ruleset covered by MD5 sum for pfsync.
> Correct, as is directly seen by the only caller pf_commit_rules():
> 
> 819         /* Calculate checksum for the main ruleset */
> 820         if (rs == &pf_main_ruleset) {
> 821                 error = pf_setup_pfsync_matching(rs);
> 822                 if (error != 0)
> 823                         return (error);
> 824         }
> 
> >     it looks like we should also recompute the MD5sum if we remove the rule
> >     from the main ruleset 
> Yes, but that is a separate bug, regardless of how we handle checksum
> calculation and rule lookups.
> 
> My plan was to tackle this next, but reusing existing code instead:
> 
> After my initial diff, pf_setup_pfsync_matching() would merely update
> the checksum, so we could rename the function to something more
> appropiate and call it from pf_purge_rule() instead of duplicating it
> there.
Here is a new diff that amends the previous with the following:

        - rename pf_setup_pfsync_matching() to pf_calc_chksum()
        - make it void and simplify callees
        - rehash in pf_purge_rule() to account for removed `once' rules

> > my diff introduces a new member of pr_ruleset structure, which keeps the
> > array size so we can pass it to free(9f) later.
> I persued this first (as done in other free() size diffs) but eventually
> opted for the removal of ptr_array for the sake of simplicity.
> 
> > I have no pfsync deployment readily available for testing. Will be glad
> > if you can give my diff a try.
> Works as expected in both regards: pfsync picks the right rule and the
> checksum updates after rules expired.
I have tested this with my usual setup where states keep syncing and
`once' rules disappear from the main ruleset on the machine I hit,
compared to -CURRENT however watching `pfctl -vsi | grep Check' no shows
the checksum updating accordingly.

Admins using `once' rules are hopefully aware of this caveat already,
but now the checksum actually indicates out-of-sync rulesets and does
no longer present the same checksum for different rulesets.

Feedback? OK?


Index: if_pfsync.c
===================================================================
RCS file: /cvs/src/sys/net/if_pfsync.c,v
retrieving revision 1.277
diff -u -p -r1.277 if_pfsync.c
--- if_pfsync.c 21 Aug 2020 22:59:27 -0000      1.277
+++ if_pfsync.c 23 Aug 2020 12:09:39 -0000
@@ -500,6 +500,7 @@ pfsync_state_import(struct pfsync_state 
        struct pfi_kif  *kif;
        int pool_flags;
        int error = ENOMEM;
+       int n = 0;
 
        if (sp->creatorid == 0) {
                DPFPRINTF(LOG_NOTICE, "pfsync_state_import: "
@@ -524,9 +525,11 @@ pfsync_state_import(struct pfsync_state 
         */
        if (sp->rule != htonl(-1) && sp->anchor == htonl(-1) &&
            (flags & (PFSYNC_SI_IOCTL | PFSYNC_SI_CKSUM)) && ntohl(sp->rule) <
-           pf_main_ruleset.rules.active.rcount)
-               r = pf_main_ruleset.rules.active.ptr_array[ntohl(sp->rule)];
-       else
+           pf_main_ruleset.rules.active.rcount) {
+               TAILQ_FOREACH(r, pf_main_ruleset.rules.active.ptr, entries)
+                       if (ntohl(sp->rule) == n++)
+                               break;
+       } else
                r = &pf_default_rule;
 
        if ((r->max_states && r->states_cur >= r->max_states))
Index: pfvar.h
===================================================================
RCS file: /cvs/src/sys/net/pfvar.h,v
retrieving revision 1.495
diff -u -p -r1.495 pfvar.h
--- pfvar.h     28 Jul 2020 16:47:42 -0000      1.495
+++ pfvar.h     23 Aug 2020 12:09:39 -0000
@@ -924,7 +924,6 @@ struct pf_ruleset {
                struct pf_rulequeue      queues[2];
                struct {
                        struct pf_rulequeue     *ptr;
-                       struct pf_rule          **ptr_array;
                        u_int32_t                rcount;
                        u_int32_t                ticket;
                        int                      open;
Index: pf_ioctl.c
===================================================================
RCS file: /cvs/src/sys/net/pf_ioctl.c,v
retrieving revision 1.354
diff -u -p -r1.354 pf_ioctl.c
--- pf_ioctl.c  21 Jul 2020 14:13:17 -0000      1.354
+++ pf_ioctl.c  23 Aug 2020 13:50:53 -0000
@@ -97,7 +97,7 @@ int                    pf_rollback_rules(u_int32_t, char
 void                    pf_remove_queues(void);
 int                     pf_commit_queues(void);
 void                    pf_free_queues(struct pf_queuehead *);
-int                     pf_setup_pfsync_matching(struct pf_ruleset *);
+void                    pf_calc_chksum(struct pf_ruleset *);
 void                    pf_hash_rule(MD5_CTX *, struct pf_rule *);
 void                    pf_hash_rule_addr(MD5_CTX *, struct pf_rule_addr *);
 int                     pf_commit_rules(u_int32_t, char *);
@@ -337,6 +337,9 @@ pf_purge_rule(struct pf_rule *rule)
        ruleset->rules.active.ticket++;
        pf_calc_skip_steps(ruleset->rules.active.ptr);
        pf_remove_if_empty_ruleset(ruleset);
+
+       if (ruleset == &pf_main_ruleset)
+               pf_calc_chksum(ruleset);
 }
 
 u_int16_t
@@ -805,9 +808,8 @@ int
 pf_commit_rules(u_int32_t ticket, char *anchor)
 {
        struct pf_ruleset       *rs;
-       struct pf_rule          *rule, **old_array;
+       struct pf_rule          *rule;
        struct pf_rulequeue     *old_rules;
-       int                      error;
        u_int32_t                old_rcount;
 
        /* Make sure any expired rules get removed from active rules first. */
@@ -818,23 +820,16 @@ pf_commit_rules(u_int32_t ticket, char *
            ticket != rs->rules.inactive.ticket)
                return (EBUSY);
 
-       /* Calculate checksum for the main ruleset */
-       if (rs == &pf_main_ruleset) {
-               error = pf_setup_pfsync_matching(rs);
-               if (error != 0)
-                       return (error);
-       }
+       if (rs == &pf_main_ruleset)
+               pf_calc_chksum(rs);
 
        /* Swap rules, keep the old. */
        old_rules = rs->rules.active.ptr;
        old_rcount = rs->rules.active.rcount;
-       old_array = rs->rules.active.ptr_array;
 
        rs->rules.active.ptr = rs->rules.inactive.ptr;
-       rs->rules.active.ptr_array = rs->rules.inactive.ptr_array;
        rs->rules.active.rcount = rs->rules.inactive.rcount;
        rs->rules.inactive.ptr = old_rules;
-       rs->rules.inactive.ptr_array = old_array;
        rs->rules.inactive.rcount = old_rcount;
 
        rs->rules.active.ticket = rs->rules.inactive.ticket;
@@ -844,9 +839,6 @@ pf_commit_rules(u_int32_t ticket, char *
        /* Purge the old rule list. */
        while ((rule = TAILQ_FIRST(old_rules)) != NULL)
                pf_rm_rule(old_rules, rule);
-       if (rs->rules.inactive.ptr_array)
-               free(rs->rules.inactive.ptr_array, M_TEMP, 0);
-       rs->rules.inactive.ptr_array = NULL;
        rs->rules.inactive.rcount = 0;
        rs->rules.inactive.open = 0;
        pf_remove_if_empty_ruleset(rs);
@@ -857,35 +849,23 @@ pf_commit_rules(u_int32_t ticket, char *
        return (pf_commit_queues());
 }
 
-int
-pf_setup_pfsync_matching(struct pf_ruleset *rs)
+void
+pf_calc_chksum(struct pf_ruleset *rs)
 {
        MD5_CTX                  ctx;
        struct pf_rule          *rule;
        u_int8_t                 digest[PF_MD5_DIGEST_LENGTH];
 
        MD5Init(&ctx);
-       if (rs->rules.inactive.ptr_array)
-               free(rs->rules.inactive.ptr_array, M_TEMP, 0);
-       rs->rules.inactive.ptr_array = NULL;
 
        if (rs->rules.inactive.rcount) {
-               rs->rules.inactive.ptr_array =
-                   mallocarray(rs->rules.inactive.rcount, sizeof(caddr_t),
-                   M_TEMP, M_NOWAIT);
-
-               if (!rs->rules.inactive.ptr_array)
-                       return (ENOMEM);
-
                TAILQ_FOREACH(rule, rs->rules.inactive.ptr, entries) {
                        pf_hash_rule(&ctx, rule);
-                       (rs->rules.inactive.ptr_array)[rule->nr] = rule;
                }
        }
 
        MD5Final(digest, &ctx);
        memcpy(pf_status.pf_chksum, digest, sizeof(pf_status.pf_chksum));
-       return (0);
 }
 
 int

Reply via email to