summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorAlexandr Nedvedicky <sashan@cvs.openbsd.org>2022-08-03 08:16:05 +0000
committerAlexandr Nedvedicky <sashan@cvs.openbsd.org>2022-08-03 08:16:05 +0000
commit074bb74698406aaeea8d12e803d61d9a6ba48e4b (patch)
tree6d696df7179c1ff53f09dc50fdb0cf1ef60299b8 /sys
parentf11be7da09a9db97130a71a3ccf206d940b07d96 (diff)
Bug was reported by Chriss Cappucio. It has turned out my earlier change
to pf_lb.c was not complete. We must add a test to determine number of addresses defined by pool, so we don't treat pool definition 172.16.0.0/16 as a single IP address in pool. If pool is defined as 172.16.0.0/16, then we don't want to fall back to PF_POOL_NONE. Missing this measure in pf_map_addr() may cause pf_get_sport() to enter infinite loop when source ports translation become depleted for the first address found in pool (like 172.16.0.1), because the bug prevents pf_map_addr() to move to next address in pool (like 172.16.0.2). while investigating issue I've also noticed an oddity for small random pools such as 192.168.1.32/28. One would expect the addresses for nat will be randomly picked from range .32 - .47 in this case. however the random selection yield significantly more (like 20%) addresses ending by .32 In order to fix it we make random pool to use arc4random_uniform(~mask + 1) instead of current arc4random(). feedback by claudio@ tested by hrvoje@
Diffstat (limited to 'sys')
-rw-r--r--sys/net/pf_lb.c45
1 files changed, 33 insertions, 12 deletions
diff --git a/sys/net/pf_lb.c b/sys/net/pf_lb.c
index d106073d372..588115cbff7 100644
--- a/sys/net/pf_lb.c
+++ b/sys/net/pf_lb.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pf_lb.c,v 1.70 2022/02/16 08:46:11 sashan Exp $ */
+/* $OpenBSD: pf_lb.c,v 1.71 2022/08/03 08:16:04 sashan Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -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: