Linux客户端临时端口冲突

问题表现

在使用数据库建立连接时,发现数据库客户端占用的临时端口在不同服务器上会出现大概率与即将使用的预留端口发生碰撞,导致需要预留端口的应用启动时无法绑定其需要端口。

问题解决方案

通过以下命令修改linux配置文件参数

## 修改配置文件
vi /etc/sysctl.conf
## 使配置文件生效
sysctl -p

1.设置ip_local_port_range参数,与预留端口进行分隔

使用net.ipv4.ip_local_port_range参数,规划出一段端口段预留作为服务的端口。

2.设置ip_local_reserved_ports 参数,作为预留端口

将服务监听的端口以逗号分隔全部添加到ip_local_reserved_ports中,TCP/IP协议栈从ip_local_port_range中随机选取源端口时,会排除ip_local_reserved_ports中定义的端口,因此就不会出现端口被占用了服务无法启动。

问题提出

虽然解决上述的解决方案可以解决此问题,但是在此次事情的表现上为,在不同服务器上启动数据库连接池与数据库建立连接时会大概率发生碰撞。如果端口真的是随机分配的话,则与目前表现出的大概率不匹配。

问题分析

数据库客户端与数据库服务建立tcp连接占用的客户端端口会大概率与某预留端口发生冲突,是否与系统分配随机端口的算法有关?需要验证系统获取随机端口的算法。

分配算法资料

发起一个TCP连接,4元组是必须的,即源IP,源端口,目标IP,目标端口。目标IP和端口都是确定的,源IP根据路由选择或者bind也可以确定,基本上最终的源IP都是本机的IP地址,然而通过IP_TRANSPARENT参数可以bind一个不属于本机的IP地址。一切的前提是同时必须保证四元组具有唯一性,在此前提下有以下两种算法来选择源端口

1.bind情形的列维搜索算法

这种情况下4元组中的目标IP和目标端口是不确定的,所以与本次建立tcp连接客户端不匹配。


对于bind的情形,由于缺失信息,需要采用非常严格的方式选择源端口,即要做到:只要有可能四元组冲突,就不能分配。比如已经有一个连接的四元组为:Tuple1(IPsrc,PORTsrc,IPdst,PORTdst),现在为一个新建立的套接字bind一个源端口,其不bind任何确定的IP地址,那么它就不能使用PORTsrc这个端口作为源端口,因为它可能和Tuple1冲突,虽然仅仅是可能而已!在这种情况下基本符合列维模型,即寻找一个基准点,在其基准点附近进行局部搜索,然后搜索无果后,跳跃到下一个更远的基准点,再进行局部搜索。


2.connect情形的精确判定模式

connect的时候,四元组中的三元组已经确定,因此可以精确匹配了,和bind时的端口选择相反,此时只要有一个元组不同即可成功,符合本次的事件情形。


确定性的查找不需要列维搜索,而是大家都可以根据顺序递增加简单冲突判定的方式进行端口选择,最合常理的方式就是,每一个三元组(源IP,目标IP,目标端口)都可以有一个65534个端口可供选择,每次递增即可。但是这样的话需要为每一个端口维护一个计数器,Linux使用了更加巧妙的方法,可以采用为每一个三元组用哈希计算一个确定的基准端口,全局维护一个递增的计数器,根据这个计数器与基准端口之和和端口空间大小做模运算,这样的一个取模操作可以确定一个offset,加上最小端口确定一个候选端口,这样就保证了候选端口和三元组的线性关系,也就是说,每一个三元组独立选择端口。
这么做的好处在于,对每一个三元组而言,都是从基准端口开始顺序分配的,相同三元组的端口都集中在一起,因为我们正是要和相同三元组的那些已经确定的端口来比较,以判断有没有冲突,通过这种方式,将相同三元组的已经分配的端口集中在了一起,省去了维护链表的麻烦,只需要从计算出的候选端口开始线性搜索整个端口空间即可,由于全局计数器是递增的,所以除非使用bind占据了某个端口,一般都会很快找到可用端口号,最多搜索几个就能找到。


实验猜想

根据算法资料分析,本次建立tcp连接符合“connect情形的精确判定模式”,如果真的是以这种算法来进行分配则可以解释在不同服务器上会大概率的发生源端口碰撞,因为在指定随机端口范围不断增加的情况下,都会大概率的与当初预留端口发生碰撞。

实验验证

1.验证步骤

本次验证主要分为两大步骤:
第一步通过socket的方式去让系统分配临时端口,并记录其算法规律,第二步,在本次不同linux内核服务器测试环境上建立数据库连接,与第一步进行佐证。如果两者规律一致,则认为其算法规律符合。

1.1步骤一:通过socket建立连接

验证目的:得到线性建立tcp连接,系统分配端口规律的算法

验证方式:通过线性创建socket连接的方式去获取系统随机分配的端口

验证工具说明:

  • 服务端:一个socket方式的服务端,会对建立的通道保持一定的存活时间。模拟对应数据库服务端
  • 客户端:针对开发的socket客户端,能够在一定时间内平滑的与服务端建立网络连接。模拟数据库客户端。

验证结果获取方式:

通过netstat命令去获取建立为连接通道的被分配的随机端口列表,并以此列表作为分析的数据来源。

验证随机端口前置:
设置随机端口范围:32768-60999

1.1.1验证过程

1.1.1.1环境截图

image

1.1.1.2验证结果

样本验证为保证严谨性,总共进行5次数据取样。结果如下表,为方便查看截图通过

netstat -anp|grep 5201|awk '{print $5}'|grep ":[0-9][0-9][0-9][0-9][0-9]"|awk -F ':' '{print $2}'|sort

做过过滤排序处理。

获取随机端口次数系统分配端口部分截图(从左到右)规律结果
50image靠近随机端口范围设置最低点,并端口大小呈线性平滑增长
100image靠近最近被使用的端口,端口大小呈线性平滑增长
200image靠近最近被使用的端口,端口大小呈线性平滑增长
400image靠近最近被使用的端口,端口大小呈线性平滑增长
800image靠近最近被使用的端口,端口大小呈线性平滑增长

1.1.2验证结果分析

通过对系统随机分配的验证分析,结果为会在指定的范围内靠近最小起始点,不断自增,并非为无规律随机

2.1步骤二:数据库连接池获取连接

验证目的:得到以数据库连接池去建立连接的方式,获取系统分配端口规律的算法

验证方式:配置数据库连接池,并查看连接池建立后,占用的随机端口的规律

验证工具说明:

  • 服务端:oracle数据库
  • 客户端:数据库连接池。

验证结果获取方式:

通过netstat命令去获取建立为连接通道的被分配的随机端口列表,并以此列表作为分析的数据来源。

验证随机端口前置:
设置随机端口范围:1024-65000

2.1.1验证过程

2.1.1.1环境截图

image

2.1.1.2验证结果

image
image

2.1.2验证结果分析

在建立数据库连接池后,其分配的随机端口并非完全随机,而是通过自增的方式去分配。

结论

经过测试验证,本次的大概率碰撞事件确实与“connect情形的精确判定模式”的算法有关,不过通过解决方案的端口分隔完全可以避免碰撞发生。

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值