Hello,

diff below fixes my a tiny glitch I've introduced with
commit [1] back in May. Fortunately the impact is hardly
noticeable (I think).

The current code reads as follows:
1495 pfr_add_tables(struct pfr_table *tbl, int size, int *nadd, int flags)
1496 {
....
1507         for (i = 0; i < size; i++) {
1508                 YIELD(flags & PFR_FLAG_USERIOCTL);
1509                 if (COPYIN(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t), 
flags))
1510                         senderr(EFAULT);
1511                 if (pfr_validate_table(&key.pfrkt_t, PFR_TFLAG_USRMASK,
1512                     flags & PFR_FLAG_USERIOCTL))
1513                         senderr(EINVAL);
1514                 key.pfrkt_flags |= PFR_TFLAG_ACTIVE;
1515                 p = pfr_create_ktable(&key.pfrkt_t, tzero, 0,
1516                     (flags & PFR_FLAG_USERIOCTL? PR_WAITOK : PR_NOWAIT));
1517                 if (p == NULL)
1518                         senderr(ENOMEM);
1519 
1520                 /*
1521                  * Note: we also pre-allocate a root table here. We keep it
1522                  * at ->pfrkt_root, which we must not forget about.
1523                  */
1524                 key.pfrkt_flags = 0;
....
1551         }
1552 
1553         /*
1554          * auxq contains freshly allocated tables with no dups.
1555          * also note there are no rulesets attached, because
1556          * the attach operation requires PF_LOCK().
1557          */
1558         NET_LOCK();
1559         PF_LOCK();
1560         SLIST_FOREACH_SAFE(n, &auxq, pfrkt_workq, w) {
1561                 p = RB_FIND(pfr_ktablehead, &pfr_ktables, n);
1562                 if (p == NULL) {
1563                         SLIST_REMOVE(&auxq, n, pfr_ktable, pfrkt_workq);
1564                         SLIST_INSERT_HEAD(&addq, n, pfrkt_workq);
1565                         xadd++;
1566                 } else if (!(flags & PFR_FLAG_DUMMY) &&
1567                     !(p->pfrkt_flags & PFR_TFLAG_ACTIVE)) {
1568                         p->pfrkt_nflags = (p->pfrkt_flags &
1569                             ~PFR_TFLAG_USRMASK) | key.pfrkt_flags;
1570                         SLIST_INSERT_HEAD(&changeq, p, pfrkt_workq);
1571                 }
1572         }

at line 1569 local variable 'key.pfrkt_flags' is always 0, because
it's set to 0 at line 1524. Thus the code has no effect. We should
be using 'n->pfrkt_flags' instead at line 1569.

It took me a while to figure out when else branch at line 1568 gets
executed. If I understand code right the branch handles kind of
edge case which deals with situation the table exists already but
transaction which inserts/creates table is not yet committed.
For example consider situation of pfctl which creates a table 'test'
via pfr_ina_define() (on behalf of transaction when it loads pf.conf).
The pfctl process gets terminated for whatever reason before transaction
is committed. In this case we might be left with inactive table hanging
in pf(4) driver. Such table is never reported by 'pfctl -sT' but is
still present in pfr_ktables tree.

There is pf_tbltrans.c test program which emulates a theoretical failure
of pfctl. It creates a table via transaction (like parse.y does) but
it never commits the transaction. This is what happens when we run
commands below on current pf(4):
    netlock# ./pf_tbltrans test
    netlock# pfctl -t test -T add 192.168.1.0/24
    pfctl: Table does not exist
    netlock# pfctl -FT
    0 tables deleted.
    netlock# pfctl -t test -T add 192.168.1.0/24
    pfctl: Table does not exist
There 'pfctl -t test -T add 192.168.1.0/24' fails because it is not able
to add address to inactive (not committed) table 'test'. As soon as
we replay the same scenario on system with diff applied the
story looks as follows:
    netlock# ./pf_tbltrans test                                                 
   
    netlock# pfctl -t test -T add 192.168.1.0/24
    1/1 addresses added.
The reason why it works is that fixed line 1569 kicks in and
active flag gets set which makes table active ('visible') for
all other operations.

OK to commit?

thanks and
regards
sashan

[1] https://marc.info/?l=openbsd-cvs&m=165222430111103&w=2

--------8<---------------8<-----------------8<---------------8<---------
diff --git a/sys/net/pf_table.c b/sys/net/pf_table.c
index f537aac2387..f6d5c355b1f 100644
--- a/sys/net/pf_table.c
+++ b/sys/net/pf_table.c
@@ -1566,7 +1566,7 @@ pfr_add_tables(struct pfr_table *tbl, int size, int 
*nadd, int flags)
                } else if (!(flags & PFR_FLAG_DUMMY) &&
                    !(p->pfrkt_flags & PFR_TFLAG_ACTIVE)) {
                        p->pfrkt_nflags = (p->pfrkt_flags &
-                           ~PFR_TFLAG_USRMASK) | key.pfrkt_flags;
+                           ~PFR_TFLAG_USRMASK) | n->pfrkt_flags;
                        SLIST_INSERT_HEAD(&changeq, p, pfrkt_workq);
                }
        }
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <netinet/in.h>
#include <net/pfvar.h>
#include <sys/ioctl.h>

int
main(int argc, const char *argv[])
{
        int     pf;
        struct pfioc_table      io;
        struct pfioc_trans      pfioc_t;
        struct pfioc_trans_e    pfioc_e;

        if (argc != 2) {
                fprintf(stderr, "run as '%s tblname'\n", argv[0]);
                return (1);
        }

        pf = open("/dev/pf", O_RDWR);
        if (pf == -1)
                err(1, "Ubale to open /dev/pf: %s\n", strerror(errno));

        memset(&pfioc_t, 0, sizeof (pfioc_t));
        memset(&pfioc_e, 0, sizeof (pfioc_e));
        pfioc_e.type = PF_TRANS_TABLE;
        pfioc_t.size = 1;
        pfioc_t.esize = sizeof (struct pfioc_trans_e);
        pfioc_t.array = &pfioc_e;
        if (ioctl(pf, DIOCXBEGIN, &pfioc_t) == -1) {
                err(1, "DIOCXBEGIN: ");
        }
        memset(&io, 0, sizeof (struct pfioc_table));
        strlcpy(io.pfrio_table.pfrt_name, argv[1], PF_TABLE_NAME_SIZE);
        io.pfrio_table.pfrt_flags = PFR_TFLAG_PERSIST|PFR_TFLAG_CONST;
        io.pfrio_ticket = pfioc_e.ticket;
        io.pfrio_esize = sizeof (struct pfr_addr);
        if (ioctl(pf, DIOCRINADEFINE, &io) == -1) {
                err(1, "DIOCRINADEFINE: ");
        }

        close(pf);
        return (0);
}

Reply via email to