diff mbox series

[ovs-dev,2/2] conntrack: limit port clash resolution attempts

Message ID 1649648615-59908-2-git-send-email-wenx05124561@163.com
State Superseded
Headers show
Series [ovs-dev,1/2] conntrack: remove the IP iterations in nat_get_unique_l4 | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/intel-ovs-compilation success test: success
ovsrobot/github-robot-_Build_and_Test success github build: passed

Commit Message

wenxu April 11, 2022, 3:43 a.m. UTC
From: wenxu <wenxu@chinatelecom.cn>

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: wenxu <wenxu@chinatelecom.cn>
---
 lib/conntrack.c | 29 +++++++++++++++++++++++++----
 1 file changed, 25 insertions(+), 4 deletions(-)

Comments

Paolo Valerio May 4, 2022, 4:56 p.m. UTC | #1
wenx05124561@163.com writes:

> From: wenxu <wenxu@chinatelecom.cn>
>
> 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: wenxu <wenxu@chinatelecom.cn>
> ---
>  lib/conntrack.c | 29 +++++++++++++++++++++++++----
>  1 file changed, 25 insertions(+), 4 deletions(-)
>
> diff --git a/lib/conntrack.c b/lib/conntrack.c
> index ac95d0f..a776e57 100644
> --- a/lib/conntrack.c
> +++ b/lib/conntrack.c
> @@ -2359,16 +2359,37 @@ nat_get_unique_l4(struct conntrack *ct, struct conn *nat_conn,
>                    ovs_be16 *port, uint16_t curr, uint16_t min,
>                    uint16_t max)
>  {
> +    static const unsigned int max_attempts = 128;
> +    uint16_t range = max - min + 1;
> +    unsigned int attempts;
>      uint16_t orig = curr;
> +    unsigned int i = 0;
>  
> +    attempts = range;
> +    if (attempts > max_attempts) {
> +        attempts = max_attempts;
> +    }
> +
> +another_round:
> +    i = 0;
>      FOR_EACH_PORT_IN_RANGE (curr, min, max) {
> -        *port = htons(curr);
> -        if (!conn_lookup(ct, &nat_conn->rev_key,
> -                         time_msec(), NULL, NULL)) {
> -            return true;

can't we rewrite this as:
if (i++ >= attempts) {
    break;
}
...

I find it a bit more readable in this case.

> +        if (i++ < attempts) {
> +            *port = htons(curr);
> +            if (!conn_lookup(ct, &nat_conn->rev_key,
> +                             time_msec(), NULL, NULL)) {
> +                return true;
> +            }
> +        } else {
> +            break;
>          }
>      }
>  
> +    if (attempts < range && attempts >= 16) {
> +        attempts /= 2;
> +        curr = min + (random_uint32() % range);
> +        goto another_round;
> +    }
> +
>      *port = htons(orig);
>  
>      return false;
> -- 
> 1.8.3.1
diff mbox series

Patch

diff --git a/lib/conntrack.c b/lib/conntrack.c
index ac95d0f..a776e57 100644
--- a/lib/conntrack.c
+++ b/lib/conntrack.c
@@ -2359,16 +2359,37 @@  nat_get_unique_l4(struct conntrack *ct, struct conn *nat_conn,
                   ovs_be16 *port, uint16_t curr, uint16_t min,
                   uint16_t max)
 {
+    static const unsigned int max_attempts = 128;
+    uint16_t range = max - min + 1;
+    unsigned int attempts;
     uint16_t orig = curr;
+    unsigned int i = 0;
 
+    attempts = range;
+    if (attempts > max_attempts) {
+        attempts = max_attempts;
+    }
+
+another_round:
+    i = 0;
     FOR_EACH_PORT_IN_RANGE (curr, min, max) {
-        *port = htons(curr);
-        if (!conn_lookup(ct, &nat_conn->rev_key,
-                         time_msec(), NULL, NULL)) {
-            return true;
+        if (i++ < attempts) {
+            *port = htons(curr);
+            if (!conn_lookup(ct, &nat_conn->rev_key,
+                             time_msec(), NULL, NULL)) {
+                return true;
+            }
+        } else {
+            break;
         }
     }
 
+    if (attempts < range && attempts >= 16) {
+        attempts /= 2;
+        curr = min + (random_uint32() % range);
+        goto another_round;
+    }
+
     *port = htons(orig);
 
     return false;