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);
}