[原创]分析解决lvs fullnat模式下后端服务器获取真实IP地址异常问题

简介: 分析解决lvs fullnat模式下少量的请求记录client IP不是用户真实的IP地址问题.

摘要

分析解决lvs fullnat模式下少量的请求记录client IP不是用户真实的IP地址问题.

原创文章:来自分析lvs fullnat模式下后端服务器获取真实IP地址异常问题

问题背景

lvs fullnat模式下观察后端服务器realserver http/https业务运行系统日志,有时候可以发现有少量的请求记录的client IP不是用户真实的IP地址(存在但出现的概率很小,增加了问题排查的难度),而是属于lvs主机私有的IP地址。关于fullnat的简介可以参考http://www.baokaijian.com/?tag=fullnat, 这里借用一下文中的图片。
1

问题分析

大多数TCP正常建立连接过程
2.png

realserver linux系统属于用户自属管理默认配置MTU=1500、MSS=1460,在TCP建连时可以通过syn+ack报文通告给client,而client端可属于移动端或PC端MTU、MSS的差异性较大,通过syn报文通告给realserver端,从而建立的TCP连接取两者之小值做为链路的MSS。中间的LVS只是做连接与报文转发的负载均衡。

fullnat 模式下获取真实IP地址的原理:
开源的lvs fullnat代码与patch可查阅lvs官方网址http://kb.linuxvirtualserver.org/wiki/IPVS_FULLNAT_and_SYNPROXY

lvs fullnat应用模式通过TCP三次握手的ack,ack+data报文将client端的真实IP地址插入到tcp options中从而带给后端的realserver。而后端realserver toa内核模块中通过hook了tcp_v4_syn_recv_sock()函数,然后调用get_toa_data()从tcp_options中取得客户端的真实IP地址,具体的调用过程下边分析。

非标准的tcp三次握手情况分析

先来看以下几个典型的报文序列是否可以建立三次握手:

  1. 乱序的情况(第三个ack和第四个ack+data报文乱序)
    3.png
  2. 直接使用ack+data报文(或含有push标志)不存在bare ack报文

4.png

3.直接使用fin+ack+data报文( 或含有push标志)

5.png

来点内核TCP代码中深度分析:

看一下建立TCP连接时两次经过的tcp_v4_hnd_req函数流程.

syn报文到来时的处理流程:

tcp_v4_rcv() -->tcp_v4_do_rcv()-->tcp_v4_hnd_req()

查找代表客户端连接的sock结构,如果没有找到则返回代表服务器的sock结构, 然后在tcp_rcv_state_process()函数中调用tcp_v4_conn_request()创建req_sock请求结构并放入半连接队列。

握手阶段收到第三个ack报文或其他携带ACK标志的报文处理,tcp_v4_hnd_req()函数将返回代表连接请求的sock结构.

tcp_v4_rcv() ->tcp_v4_do_rcv()-->tcp_v4_hnd_req()-->tcp_check_req()-->tcp_v4_syn_recv_sock()

tcp_v4_syn_recv_sock()函数中将对报文序列号判断,并完成握手,最后将连接请求从半连接请求移到全连接队列,等待应用层调用accept。

特别注意在tcp_check_req()函数中检查了clinet端到来报文的序列号

/* RFC793 page 36: "If the connection is in any non-synchronized state ...
 * and the incoming segment acknowledges something not yet
 * sent (the segment carries an unacceptable ACK) ...
 * a reset is sent."
 *
 * Invalid ACK: reset will be sent by listening socket
 */
    if ((flg & TCP_FLAG_ACK) &&// 正常情况下 收包的ack_seq = 初始发送的seq + 1(syn)
        (TCP_SKB_CB(skb)->ack_seq !=    //此报序列号不等于发送初始序列号         tcp_rsk(req)->snt_isn + 1 + tcp_s_data_size(tcp_sk(sk))))
        return sk;

结论: 完成握手最后一个clinet端到来报文ack序号必须是client 端tcp连接ISN初始序号+1(没有检查seq序列号),所以只要符合此规则的都可以通过,然后调用调用tcp_v4_syn_recv_sock()从而调用toa模块取client IP tcp options。再解释明白点: 非bare ack乱序报文即带有数据的ack报文,也可以通过。注:至于seq序列号有效性以及影响能不能真正完成连接建立,这是在后续的tcp_ack()中判断了。

接着tcp_v4_syn_recv_sock()函数中创建代表与client端连接的sock结构(设置此条TCP连接状态是TCP_SYN_RECV),然后在tcp_rcv_state_process()函数中case TCP_SYN_RECV代码段中通过tcp_set_state(sk, TCP_ESTABLISHED); 改状态为ESTABLISHED连接状态。

经过在toa模块中探点调试发现:

  1. 从realserver端看到大量使用FIN+ACK+PUSH的含数据报文完成三次握手,这时也不能插入toa options(用户业务很多用http/https这种post请求推数据,realserver收到后会回复response,但若是反向代理的proxy收到后会FIN掉client,同时向后端realserver再发RST报文)
  2. 上述图中因乱序或直接ack+data报文没超但达到了1444字节(假如toa需要16字节); 使用ack+data报文完成了三次握手, 使lvs层不能插入用户的IP:PORT

改进建议

官方lvs fullnat代码:
http://kb.linuxvirtualserver.org/wiki/IPVS_FULLNAT_and_SYNPROXY
Lvs-fullnat-synproxy.tar.gz 包中包含fullnat 和toa的patch.

1、 lvs代码tcp_fnat_in_handler()函数中去掉!tcph->fin的条件判断,可以解决FIN+PUSH+ACK+数据报文中不能携带client端ip与端口的缺陷

6

2、 后端RS建立监听时直接使用 setsockopt, 将MSS改小(如toa需要16字节则可减小到1444字节), 从而client到来的报文可以有插入clientip:port的空间

3、 (第2条的替代方法但会增加lvs处理的复杂性:
TCP握手阶段lvs收到后端RS发送的syn+ack报文时,将其中的MSS改小(如16字节),传给client, 从而client到来的报文就有了插入clientip:port的空间。
另一种方法虽然client发送来的报文已经达到了client与RS协商的MSS(如1460), 但若lvs与RS之间设置的MTU(如9000)要大于client与LVS之间的值1460, 并且tcp options有足够的空间可以强制在tcp options中插入clientip:port)

4、以上修改后最后还是不行(可能存在非标准的client tcp处理),建议客户端程序(特别是手机APP)建立socket时使用 setsockopt 将MSS改小适当的数值(发现存在有部分终端出现通告给其MSS是1444,但仍然发送1460的报文)

备注:

  1. tcp options满载40字节,如果在握手时用户有插入自己的数据占满则lvs判断没有空间再插入用户IP了。
  2. 发现手机终端的MSS值多种多样,这里列里几个值, 1412 1120 1452 1400 1394 1260 1460
相关实践学习
容器服务Serverless版ACK Serverless 快速入门:在线魔方应用部署和监控
通过本实验,您将了解到容器服务Serverless版ACK Serverless 的基本产品能力,即可以实现快速部署一个在线魔方应用,并借助阿里云容器服务成熟的产品生态,实现在线应用的企业级监控,提升应用稳定性。
云原生实践公开课
课程大纲 开篇:如何学习并实践云原生技术 基础篇: 5 步上手 Kubernetes 进阶篇:生产环境下的 K8s 实践 相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
目录
相关文章
|
25天前
|
弹性计算 运维 监控
slb后端服务器组(Backend Server Group)关联
阿里云SLB的后端服务器组提供逻辑分组、精细化路由、端口隔离、权重分配与会话保持等功能,便于资源管理、流量控制和故障隔离。通过监听规则、健康检查、标签和API关联,实现灵活的后端资源配置,适应多变的业务需求,确保服务稳定性和资源利用率。
15 1
|
29天前
|
弹性计算 负载均衡 容灾
slb配置后端服务器组
配置阿里云SLB后端服务器组涉及四个主要步骤:创建服务器组、添加ECS实例、关联监听规则和设定负载均衡策略。这使得流量根据业务需求和服务器特性进行转发,便于应用架构的灵活管理和扩展,支持蓝绿部署、灰度发布,并通过多可用区提升系统可用性和容灾能力。
25 3
|
2月前
|
域名解析 应用服务中间件 Linux
【服务器】使用域名解析服务器的IP地址并配置SSL证书
【服务器】使用域名解析服务器的IP地址并配置SSL证书
406 0
|
2月前
|
安全 Linux 网络安全
【Windows】搭建Emby媒体库服务器,实现无公网IP远程访问
【Windows】搭建Emby媒体库服务器,实现无公网IP远程访问
208 0
|
28天前
|
弹性计算 负载均衡
slb添加后端服务器
阿里云SLB服务用于流量分发,提升系统可用性和响应速度。添加ECS实例到SLB的步骤包括:登录控制台,选择“弹性计算”->“负载均衡”,找到目标SLB实例,进入详情页,点击“添加后端服务器”。挑选ECS实例,可按ID、名称或标签筛选,设置权重(默认100),并确保ECS实例状态正常,与SLB在同一地域,安全组允许SLB连接。添加后可考虑配置健康检查等高级选项。
14 1
|
28天前
|
弹性计算 负载均衡 网络安全
slb创建后端服务器组
在阿里云SLB服务中创建后端服务器组,需登录控制台,选择目标SLB实例,进入监听器设置,添加后端服务器。选择ECS实例或ENI,设定服务器端口和权重,然后保存设置。此操作将ECS实例纳入SLB的负载均衡与健康检查管理。
15 1
|
30天前
|
弹性计算 负载均衡
slb添加后端服务器
阿里云SLB服务实现流量分发,提升业务可用性和响应速度。添加后端服务器步骤包括:登录控制台,选择“弹性计算”->“负载均衡”,点击目标SLB实例进入详情,点击“后端服务器”->“添加ECS实例”。筛选并选择ECS实例,设置权重,确认后点击“确定”。添加后检查健康检查状态,确保ECS实例状态正常,同地域、可用区,并配置好安全组规则。
16 2
|
1月前
|
网络协议 安全 网络架构
无需公网IP联机Minecraft,我的世界服务器本地搭建教程
无需公网IP联机Minecraft,我的世界服务器本地搭建教程
|
1月前
|
监控 JavaScript 安全
监控内网电脑软件设计与实现:基于Node.js的服务器端架构分析
在当今信息技术高度发达的时代,监控内网电脑的需求日益增长。企业需要确保网络安全,个人用户也需要监控家庭网络以保护隐私和安全。本文将介绍一种基于Node.js的服务器端架构,用于设计和实现监控内网电脑软件。
90 0
|
1月前
|
Java Python
如何设置代理ip服务器地址
如何设置代理ip服务器地址
54 0

热门文章

最新文章