fangpsh's blog

ip_local_port_range 小问题

发现系统打印了一条日志,类似redhat solution:2887631

Jan 23 14:37:22 localhost kernel: ip_local_port_range: prefer different parity for start/end values.

意思是建议将/proc/sys/net/ipv4/ip_local_port_range 的两个数设置一个奇数,一个偶数。

ip_local_port_range - 2 INTEGERS  
        Defines the local port range that is used by TCP and UDP to  
        choose the local port. The first number is the first, the
        second the last local port number.
        If possible, it is better these numbers have different parity.
        (one even and one odd values)
        The default values are 32768 and 60999 respectively.

建议改,那就改一下咯。可是为什么呢?能让系统分配端口的时候更高效?

搜索一下,找到2个相关的Patch:

找下第一个Patch 里面的 __inet_hash_connect 函数的完整内容看下:linux/net/ipv4/inet_hashtables.c

参考:

看完好像有点明白了:

diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c
index 3766bddb3e8a7303123aa7e32507f6f7801c10d5..8c0fc6fbc1afa08baf07ca86e98aa966a3f8e826 100644
--- a/net/ipv4/inet_hashtables.c
+++ b/net/ipv4/inet_hashtables.c
@@ -501,8 +501,14 @@ int __inet_hash_connect(struct inet_timewait_death_row *death_row,
         inet_get_local_port_range(net, &low, &high);
         remaining = (high - low) + 1;

+        /* By starting with offset being an even number,
+         * we tend to leave about 50% of ports for other uses,
+         * like bind(0).
+         */
+        offset &= ~1;  // offset 始终为偶数
+
         local_bh_disable();
-        for (i = 1; i <= remaining; i++) {
+        for (i = 0; i < remaining; i++) {
             port = low + (i + offset) % remaining;  // 每次总是加偶数开始找,即i=0 的时候
             if (inet_is_local_reserved_port(net, port))
                 continue;
@@ -546,7 +552,7 @@ int __inet_hash_connect(struct inet_timewait_death_row *death_row,
         return -EADDRNOTAVAIL;

 ok:
-        hint += i;
+        hint += (i + 2) & ~1;  // 每次调用函数时, offset = hint + port_offset; hint 是全局静态变量,始终为偶数
                                // 这样在同一个目地地址+端口,重复调用,且没其他connect 请求插入的时候,分配的端口号是加2递增

         /* Head lock still held and bh's disabled */
         inet_bind_hash(sk, tb, port);

N 个端口,每次从第偶数个开始找,看下bind(0) 分配端口的相关函数

smallest_rover = rover = prandom_u32() % remaining + low;

后者看意思是完全随机的,所以意思是connect从偶数个开始找,让出一半的空间给bind(0),提高bind(0)的效率。

如果是这样的话,为什么建议ip_local_port_range 可用的端口是偶数,即要有一个奇数、一个偶数呢?offset 每次也都是无论哪种情况是偶数, remaining 为偶数为奇数好像都不影响逻辑,只差一个而已,一定要50%?

愚钝,没想明白。

看了下新的__inet_hash_connect,感觉更合理一些, connect 优先从偶数个开始,如果分配失败,每次找下一个的时候,也是跳到下一个偶数个,即port+=2inet_csk_find_open_port则是反过来,更加高效,大家都优先用一半,不行再找对方的一半。