[原创]分析解决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
相关实践学习
深入解析Docker容器化技术
Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。Docker是世界领先的软件容器平台。开发人员利用Docker可以消除协作编码时“在我的机器上可正常工作”的问题。运维人员利用Docker可以在隔离容器中并行运行和管理应用,获得更好的计算密度。企业利用Docker可以构建敏捷的软件交付管道,以更快的速度、更高的安全性和可靠的信誉为Linux和Windows Server应用发布新功能。 在本套课程中,我们将全面的讲解Docker技术栈,从环境安装到容器、镜像操作以及生产环境如何部署开发的微服务应用。本课程由黑马程序员提供。     相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
目录
相关文章
|
弹性计算 监控 负载均衡
|
Java 关系型数据库 API
探索后端技术:构建高效、可靠的服务器端应用
在当今数字化时代,后端技术是任何成功应用程序的基石。它涉及服务器、数据库和应用程序之间的交互,处理数据存储、业务逻辑和系统性能等关键任务。本文将深入探讨后端开发的核心概念、常见技术栈及其实际应用,帮助读者更好地理解和掌握构建高效、可靠后端系统的技巧与策略。
|
监控 中间件 Java
后端技术:构建高效、稳定的服务器端应用
【10月更文挑战第5天】后端技术:构建高效、稳定的服务器端应用
424 0
|
4月前
|
监控 Java 应用服务中间件
低并发编程|如何用720个请求让后端服务器瘫痪
本次故障因应用启动时未有效校验核心依赖模块初始化异常,导致后续请求处理中抛出无法捕获的错误,引发线程阻塞,最终耗尽HSF线程池,服务不可用。排查发现类初始化失败且异常未被正确处理,结合线程无限等待问题,确认为依赖初始化异常与流处理中断所致。修复措施包括加强启动校验、捕获Throwable及设置合理超时。总结指出,系统稳定性需从细节入手,防微杜渐,避免连锁故障。
低并发编程|如何用720个请求让后端服务器瘫痪
|
8月前
|
JSON 自然语言处理 前端开发
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
461 72
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
|
监控 关系型数据库 Serverless
探索后端技术:构建高效、可靠的服务器端应用
本文将深入探讨后端开发的核心概念和关键技术,从服务器架构到数据库管理,再到安全防护,为读者提供全面的后端技术指南。无论是初学者还是经验丰富的开发者,都能从中汲取灵感,提升自己的技术水平。
|
负载均衡 监控 应用服务中间件
配置Nginx反向代理时如何指定后端服务器的权重?
配置Nginx反向代理时如何指定后端服务器的权重?
546 61
|
11月前
|
运维 监控 负载均衡
slb后端服务器故障
slb后端服务器故障
221 13
|
JavaScript
使用node.js搭建一个express后端服务器
Express 是 Node.js 的一个库,用于搭建后端服务器。本文将指导你从零开始构建一个简易的 Express 服务器,包括项目初始化、代码编写、服务启动与项目结构优化。通过创建 handler 和 router 文件夹分离路由和处理逻辑,使项目更清晰易维护。最后,通过 Postman 测试确保服务正常运行。
798 1
|
运维 NoSQL Java
后端架构演进:微服务架构的优缺点与实战案例分析
【10月更文挑战第28天】本文探讨了微服务架构与单体架构的优缺点,并通过实战案例分析了微服务架构在实际应用中的表现。微服务架构具有高内聚、低耦合、独立部署等优势,但也面临分布式系统的复杂性和较高的运维成本。通过某电商平台的实际案例,展示了微服务架构在提升系统性能和团队协作效率方面的显著效果,同时也指出了其带来的挑战。
486 4