netfilter: nat: avoid long-running port range loop
authorFlorian Westphal <fw@strlen.de>
Tue, 6 Sep 2022 15:20:36 +0000 (17:20 +0200)
committerFlorian Westphal <fw@strlen.de>
Wed, 7 Sep 2022 14:46:04 +0000 (16:46 +0200)
Looping a large port range takes too long. Instead select a random
offset within [ntohs(exp->saved_proto.tcp.port), 65535] and try 128
ports.

This is a rehash of an erlier patch to do the same, but generalized
to handle other helpers as well.

Link: https://patchwork.ozlabs.org/project/netfilter-devel/patch/20210920204439.13179-2-Cole.Dishington@alliedtelesis.co.nz/
Signed-off-by: Florian Westphal <fw@strlen.de>
net/netfilter/nf_nat_helper.c

index 067d6d6f6b7dc2bb6015ee50c6b79b33bd4ba7d1..a95a25196943d07c4a45a6cb127b44b75e08ba65 100644 (file)
@@ -201,8 +201,18 @@ EXPORT_SYMBOL(nf_nat_follow_master);
 
 u16 nf_nat_exp_find_port(struct nf_conntrack_expect *exp, u16 port)
 {
+       static const unsigned int max_attempts = 128;
+       int range, attempts_left;
+       u16 min = port;
+
+       range = USHRT_MAX - port;
+       attempts_left = range;
+
+       if (attempts_left > max_attempts)
+               attempts_left = max_attempts;
+
        /* Try to get same port: if not, try to change it. */
-       for (; port != 0; port++) {
+       for (;;) {
                int res;
 
                exp->tuple.dst.u.tcp.port = htons(port);
@@ -210,8 +220,10 @@ u16 nf_nat_exp_find_port(struct nf_conntrack_expect *exp, u16 port)
                if (res == 0)
                        return port;
 
-               if (res != -EBUSY)
+               if (res != -EBUSY || (--attempts_left < 0))
                        break;
+
+               port = min + prandom_u32_max(range);
        }
 
        return 0;