Hello group,

I'm hunting some bugs in FreeBSD's pf and I thought to ask here to better undernstad how the global source tracking is supposed to work in the reference implementation of pf. Or maybe it does not work, maybe this ia s bug report.

I have the following ruleset on OpenBSD 7.3. The LB Nodes run a web server serving just their hostname and have a static route towards the LB Pool via the OpenBSD router with pf so that they can be clients too.

table <lb_nodes> {
        10.64.2.31
        10.64.2.32
        10.64.2.33
}

table <lb_pool> {
        10.64.2.30
}

pass in quick on vio0 \
        inet proto tcp to <lb_pool> port 81 \
        rdr-to <lb_nodes> port 80 round-robin sticky-address \
        keep state (source-track global max-src-states 2)

pass in quick on vio0 \
        inet proto tcp to <lb_pool> port 82 \
        rdr-to <lb_nodes> port 80 round-robin sticky-address \
        keep state (source-track global max-src-states 2)

pass in quick on vio0 \
        inet proto tcp to <lb_pool> port 83 \
        rdr-to <lb_nodes> port 80 round-robin sticky-address \
        keep state (source-track global max-src-states 2)

pass out quick on vio0 \
        inet proto tcp to <lb_nodes> port 80 \
        nat-to vio0 \
        keep state


Now let's make some connections from node 31:

[14:03:30] kajetan-test-aw-31 ~/ # curl http://10.64.2.30:81
kajetan-test-aw-31.test.ig.local
[14:04:33] kajetan-test-aw-31 ~/ # curl http://10.64.2.30:81
kajetan-test-aw-31.test.ig.local
[14:04:33] kajetan-test-aw-31 ~/ # curl http://10.64.2.30:81
^C
Timeout. So far so good.

Let's make more connections from the same source to another rule:

[14:04:39] kajetan-test-aw-31 ~/ # curl http://10.64.2.30:82
kajetan-test-aw-31.test.ig.local
[14:04:40] kajetan-test-aw-31 ~/ # curl http://10.64.2.30:82
kajetan-test-aw-31.test.ig.local
[14:04:40] kajetan-test-aw-31 ~/ # curl http://10.64.2.30:82
^C

For each destination port, and thus each rule, 2 connections have passed and the 3rd one resulted in a timeout. But that's not what the manual says. With global tracking "The number of states created by all rules that use this option is limited" according to the pf.conf man page. And yet tracking has been performed per rule:

10.64.2.31 ( states 2, connections 2, rate 0.0/0s )
   age 00:00:05, 20 pkts, 1764 bytes, rule 1
10.64.2.31 rdr-to 10.64.2.31 ( states 2, connections 0, rate 0.0/0s )
   age 00:00:05, 20 pkts, 1764 bytes, rule 1
10.64.2.31 ( states 2, connections 2, rate 0.0/0s )
   age 00:00:07, 20 pkts, 1764 bytes, rule 0
10.64.2.31 rdr-to 10.64.2.31 ( states 2, connections 0, rate 0.0/0s )
   age 00:00:07, 20 pkts, 1764 bytes, rule 0

I've looked around the code and it looks like the type of tracking is ignored. All that is checked is the PFRULE_SRCTRACK, while PFRULE_RULESRCTRACK is not:

pf_test_rule(…) {
…
        if (r->rule_flag & PFRULE_SRCTRACK &&
            pf_insert_src_node(&ctx.sns[PF_SN_NONE], r, PF_SN_NONE,
            pd->af, pd->src, NULL, NULL) != 0) {
                REASON_SET(&ctx.reason, PFRES_SRCLIMIT);
                goto cleanup;
        }

I've checked the commit history and at some point there was a flag "global" in pf_insert_src_node but it seems to have never been fully implemmented and now it's gone. I think that to fix this issue it would be proper to bind the src nodes of type PF_SN_NONE to pf_default_rule if PFRULE_RULESRCTRACK is not given and to the current rule if it is.

Another issue is with this limit:

max-src-nodes number
    Limits the maximum number of source addresses which can
    simultaneously have state table entries.

The description states that the number of source addresses is limited, but it is not - it's the number of src nodes. For a ruleset similar to above but with max-src-nodes:

pass in quick on vio0 \
        inet proto tcp to <lb_pool> port 81 \
        rdr-to <lb_nodes> port 80 round-robin sticky-address \
        keep state (max-src-nodes 2)

every client creates 2 src nodes. One of the RDR type and one of the NONE type. So the limit of clients is limit of nodes * 0.5. What if a rule cointains a mix of rdr-to / nat-to / route-to, each with its own src node? Maybe it would be better to have the limit checks in pf_insert_src_node() and rule->src_nodes counter to apply only to the node of PF_SN_NONE type.

--
| pozdrawiam / greetings | Powered by macOS, Debian and FreeBSD |
|  Kajetan Staszkiewicz  |  www: http://vegeta.tuxpowered.net   |
`------------------------^--------------------------------------'

Reply via email to