mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-11-14 18:36:34 +00:00
netfilter: nat: limit port clash resolution attempts
In case almost or all available ports are taken, clash resolution can take a very long time, resulting in soft lockup. This can happen when many to-be-natted hosts connect to same destination:port (e.g. a proxy) and all connections pass the same SNAT. Pick a random offset in the acceptable range, then try ever smaller number of adjacent port numbers, until either the limit is reached or a useable port was found. This results in at most 248 attempts (128 + 64 + 32 + 16 + 8, i.e. 4 restarts with new search offset) instead of 64000+, Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
parent
b635cbf68f
commit
a504b703bb
1 changed files with 23 additions and 6 deletions
|
|
@ -40,9 +40,10 @@ void nf_nat_l4proto_unique_tuple(const struct nf_nat_l3proto *l3proto,
|
||||||
enum nf_nat_manip_type maniptype,
|
enum nf_nat_manip_type maniptype,
|
||||||
const struct nf_conn *ct)
|
const struct nf_conn *ct)
|
||||||
{
|
{
|
||||||
unsigned int range_size, min, max, i;
|
unsigned int range_size, min, max, i, attempts;
|
||||||
__be16 *portptr;
|
__be16 *portptr;
|
||||||
u_int16_t off;
|
u16 off;
|
||||||
|
static const unsigned int max_attempts = 128;
|
||||||
|
|
||||||
if (maniptype == NF_NAT_MANIP_SRC)
|
if (maniptype == NF_NAT_MANIP_SRC)
|
||||||
portptr = &tuple->src.u.all;
|
portptr = &tuple->src.u.all;
|
||||||
|
|
@ -86,12 +87,28 @@ void nf_nat_l4proto_unique_tuple(const struct nf_nat_l3proto *l3proto,
|
||||||
off = prandom_u32();
|
off = prandom_u32();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; ; ++off) {
|
attempts = range_size;
|
||||||
|
if (attempts > max_attempts)
|
||||||
|
attempts = max_attempts;
|
||||||
|
|
||||||
|
/* We are in softirq; doing a search of the entire range risks
|
||||||
|
* soft lockup when all tuples are already used.
|
||||||
|
*
|
||||||
|
* If we can't find any free port from first offset, pick a new
|
||||||
|
* one and try again, with ever smaller search window.
|
||||||
|
*/
|
||||||
|
another_round:
|
||||||
|
for (i = 0; i < attempts; i++, off++) {
|
||||||
*portptr = htons(min + off % range_size);
|
*portptr = htons(min + off % range_size);
|
||||||
if (++i != range_size && nf_nat_used_tuple(tuple, ct))
|
if (!nf_nat_used_tuple(tuple, ct))
|
||||||
continue;
|
return;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (attempts >= range_size || attempts < 16)
|
||||||
|
return;
|
||||||
|
attempts /= 2;
|
||||||
|
off = prandom_u32();
|
||||||
|
goto another_round;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(nf_nat_l4proto_unique_tuple);
|
EXPORT_SYMBOL_GPL(nf_nat_l4proto_unique_tuple);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue