Hi, pf.conf:
anchor { pass in on rdomain 102 quick proto tcp to 10.0.0.101 port 8080 \ keep state ( sloppy ) route-to <LB> \ least-states sticky-address } table <LB> { 10.0.0.11@pair102 } this doesn't work. All packets going to 10.0.0.101 are dropped with 'no-route'. The problem doesn't happen if the pass rule is moved to outside of the anchor or uses "round-robin" instead of "least-states". In sys/net/pf_lb.c: 594 if (rpool->addr.type == PF_ADDR_TABLE) { 595 if (pfr_states_increase(rpool->addr.p.tbl, 596 naddr, af) == -1) { 597 if (pf_status.debug >= LOG_DEBUG) { 598 log(LOG_DEBUG,"pf: pf_map_addr: " 599 "selected address "); 600 pf_print_host(naddr, 0, af); 601 addlog(". Failed to increase count!\n"); 602 } 603 return (1); 604 } This chunk is to increase the counter for "least-state". The packets drops here because pfr_states_increase() returns -1. pfr_states_increase() uses pfr_kentry_byaddr(), and pfr_kentry_byaddr() uses pfr_lookup_addr() to lookup a kentry in the table. pfr_lookup_addr() never succeeded for above case, because it doesn't care about using global (root) tables from rules in an anchor. All other functions which lookup a kentry from the table than pfr_lookup_addr() seem to take care about that. I thought that pfr_lookup_addr() is a local function used for ioctl to create tables and manage its members. So the keep it untouched. Instead, the diff replaces the body of pfr_kentry_byaddr() by the logic of pfr_match_addr(). * * * Test 1. prepare network ifconfig pair101 rdomain 101 10.0.0.1/24 ifconfig pair102 rdomain 102 10.0.0.10/24 ifconfig pair102 alias 10.0.0.101/24 ifconfig pair103 rdomain 103 10.0.0.11/24 ifconfig pair104 rdomain 100 patch pair101 up ifconfig pair105 rdomain 100 patch pair102 up ifconfig pair106 rdomain 100 patch pair103 up ifconfig lo103 127.0.0.1/8 ifconfig lo103 alias 10.0.0.101/24 ifconfig bridge100 add pair104 ifconfig bridge100 add pair105 ifconfig bridge100 add pair106 up 2. setup pf.conf anchor { pass in on rdomain 102 quick proto tcp to 10.0.0.101 port 8080 \ keep state ( sloppy ) route-to <LB> \ least-states sticky-address } table <LB> { 10.0.0.11@pair102 } 3. start a daemon on 8080/tcp on #103 doas route -T 103 exec nc -l 8080 4. try to connect to it from #101 doas route -T 101 exec telnet 10.0.0.101 8080 - test OK if the connection is established 5. teardown ifconfig pair106 destroy ifconfig pair105 destroy ifconfig pair104 destroy ifconfig pair103 destroy ifconfig pair102 destroy ifconfig pair101 destroy ifconfig bridge100 destroy * * * ok? Fix pfr_kentry_byaddr() to be used for a rule in an anchor. It couldn't find an entry if its table is attached a table on the root. This fixes the problem "route-to <TABLE> least-states" doesn't work. The problem is found by IIJ. Index: sys/net/pf_table.c =================================================================== RCS file: /cvs/src/sys/net/pf_table.c,v retrieving revision 1.131 diff -u -p -r1.131 pf_table.c --- sys/net/pf_table.c 8 Jul 2019 17:49:57 -0000 1.131 +++ sys/net/pf_table.c 3 Jun 2020 07:21:27 -0000 @@ -2085,11 +2085,28 @@ int pfr_match_addr(struct pfr_ktable *kt, struct pf_addr *a, sa_family_t af) { struct pfr_kentry *ke = NULL; + int match; + + ke = pfr_kentry_byaddr(kt, a, af, 0); + + match = (ke && !(ke->pfrke_flags & PFRKE_FLAG_NOT)); + if (match) + kt->pfrkt_match++; + else + kt->pfrkt_nomatch++; + + return (match); +} + +struct pfr_kentry * +pfr_kentry_byaddr(struct pfr_ktable *kt, struct pf_addr *a, sa_family_t af, + int exact) +{ + struct pfr_kentry *ke = NULL; struct sockaddr_in tmp4; #ifdef INET6 struct sockaddr_in6 tmp6; #endif /* INET6 */ - int match; if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE) && kt->pfrkt_root != NULL) kt = kt->pfrkt_root; @@ -2116,12 +2133,10 @@ pfr_match_addr(struct pfr_ktable *kt, st default: unhandled_af(af); } - match = (ke && !(ke->pfrke_flags & PFRKE_FLAG_NOT)); - if (match) - kt->pfrkt_match++; - else - kt->pfrkt_nomatch++; - return (match); + if (exact && ke && KENTRY_NETWORK(ke)) + ke = NULL; + + return (ke); } void @@ -2497,39 +2512,6 @@ pfr_states_decrease(struct pfr_ktable *k "pfr_states_decrease: states-- when states <= 0"); return ke->pfrke_counters->states; -} - -/* - * Added for load balancing to find a kentry outside of the table. - * We need to create a custom pfr_addr struct. - */ -struct pfr_kentry * -pfr_kentry_byaddr(struct pfr_ktable *kt, struct pf_addr *addr, sa_family_t af, - int exact) -{ - struct pfr_kentry *ke; - struct pfr_addr p; - - bzero(&p, sizeof(p)); - p.pfra_af = af; - switch (af) { - case AF_INET: - p.pfra_net = 32; - p.pfra_ip4addr = addr->v4; - break; -#ifdef INET6 - case AF_INET6: - p.pfra_net = 128; - p.pfra_ip6addr = addr->v6; - break; -#endif /* INET6 */ - default: - unhandled_af(af); - } - - ke = pfr_lookup_addr(kt, &p, exact); - - return ke; } void