Hello,

below is a final version of patch for NAT issue discussed at bugs@ [1].
Patch below is updated according to feedback I got from Chris, claudio@
and hrvoje@.

The summary of changes is as follows:

    - prevent infinite loop when packet hits NAT rule as follows:
        pass out on em0 from 172.16.0.0/16 to any nat-to { 49/27 }
        the issue has been introduced by my earlier commit [2]. The earlier
        change makes pf(4) to interpret 49/27 as single IP address (POOL_NONE)
        this is wrong, because pool 49/27 actually contains 32 addresses.

    - while investigating the issue I've realized 'random' pool should
      rather be using arc4_uniform() with upper limit derived from mask.
      also the random number should be turned to netorder.

    - also while I was debugging my change I've noticed we should be using
      pf_poolmask() to obtain address as a combination of pool address
      and result of generator (round-robin all random).

OK to commit?

thanks and
regards
sashan


[1] https://marc.info/?t=165813368200001&r=1&w=2
    https://marc.info/?t=165732546500001&r=1&w=2
    https://marc.info/?l=openbsd-bugs&m=165817500514813&w=2

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

--------8<---------------8<---------------8<------------------8<--------
diff --git a/sys/net/pf_lb.c b/sys/net/pf_lb.c
index d106073d372..dff2349cde7 100644
--- a/sys/net/pf_lb.c
+++ b/sys/net/pf_lb.c
@@ -344,6 +344,17 @@ pf_map_addr_sticky(sa_family_t af, struct pf_rule *r, 
struct pf_addr *saddr,
        return (0);
 }
 
+uint32_t
+pf_rand_addr(uint32_t mask)
+{
+       uint32_t addr;
+
+       mask = ~ntohl(mask);
+       addr = arc4random_uniform(mask + 1);
+
+       return (htonl(addr));
+}
+
 int
 pf_map_addr(sa_family_t af, struct pf_rule *r, struct pf_addr *saddr,
     struct pf_addr *naddr, struct pf_addr *init_addr, struct pf_src_node **sns,
@@ -428,24 +439,29 @@ pf_map_addr(sa_family_t af, struct pf_rule *r, struct 
pf_addr *saddr,
                } else if (init_addr != NULL && PF_AZERO(init_addr, af)) {
                        switch (af) {
                        case AF_INET:
-                               rpool->counter.addr32[0] = arc4random();
+                               rpool->counter.addr32[0] = pf_rand_addr(
+                                   rmask->addr32[0]);
                                break;
 #ifdef INET6
                        case AF_INET6:
                                if (rmask->addr32[3] != 0xffffffff)
-                                       rpool->counter.addr32[3] = arc4random();
+                                       rpool->counter.addr32[3] = pf_rand_addr(
+                                           rmask->addr32[3]);
                                else
                                        break;
                                if (rmask->addr32[2] != 0xffffffff)
-                                       rpool->counter.addr32[2] = arc4random();
+                                       rpool->counter.addr32[2] = pf_rand_addr(
+                                           rmask->addr32[2]);
                                else
                                        break;
                                if (rmask->addr32[1] != 0xffffffff)
-                                       rpool->counter.addr32[1] = arc4random();
+                                       rpool->counter.addr32[1] = pf_rand_addr(
+                                           rmask->addr32[1]);
                                else
                                        break;
                                if (rmask->addr32[0] != 0xffffffff)
-                                       rpool->counter.addr32[0] = arc4random();
+                                       rpool->counter.addr32[0] = pf_rand_addr(
+                                           rmask->addr32[0]);
                                break;
 #endif /* INET6 */
                        default:
@@ -500,11 +516,16 @@ pf_map_addr(sa_family_t af, struct pf_rule *r, struct 
pf_addr *saddr,
                        }
                } else if (PF_AZERO(&rpool->counter, af)) {
                        /*
-                        * fall back to POOL_NONE if there are no addresses in
-                        * pool
+                        * fall back to POOL_NONE if there is a single host
+                        * address in pool.
                         */
-                       pf_addrcpy(naddr, raddr, af);
-                       break;
+                       if ((af == AF_INET &&
+                           rmask->addr32[0] == INADDR_BROADCAST) ||
+                           (af == AF_INET6 &&
+                           IN6_ARE_ADDR_EQUAL(&rmask->v6, &in6mask128))) {
+                               pf_addrcpy(naddr, raddr, af);
+                               break;
+                       }
                } else if (pf_match_addr(0, raddr, rmask, &rpool->counter, af))
                        return (1);
 
@@ -532,9 +553,9 @@ pf_map_addr(sa_family_t af, struct pf_rule *r, struct 
pf_addr *saddr,
                        weight = rpool->weight;
                }
 
-               pf_addrcpy(naddr, &rpool->counter, af);
+               pf_poolmask(naddr, raddr, rmask, &rpool->counter, af);
                if (init_addr != NULL && PF_AZERO(init_addr, af))
-                       pf_addrcpy(init_addr, naddr, af);
+                       pf_addrcpy(init_addr, &rpool->counter, af);
                pf_addr_inc(&rpool->counter, af);
                break;
        case PF_POOL_LEASTSTATES:

Reply via email to