[原创]分析解决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
相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
目录
相关文章
|
1月前
|
弹性计算 监控 负载均衡
|
2月前
|
监控 中间件 Java
后端技术:构建高效、稳定的服务器端应用
【10月更文挑战第5天】后端技术:构建高效、稳定的服务器端应用
94 0
|
6天前
|
运维 监控 负载均衡
slb后端服务器故障
slb后端服务器故障
28 13
|
19天前
|
负载均衡 监控 应用服务中间件
配置Nginx反向代理时如何指定后端服务器的权重?
配置Nginx反向代理时如何指定后端服务器的权重?
33 4
|
20天前
|
JavaScript
使用node.js搭建一个express后端服务器
Express 是 Node.js 的一个库,用于搭建后端服务器。本文将指导你从零开始构建一个简易的 Express 服务器,包括项目初始化、代码编写、服务启动与项目结构优化。通过创建 handler 和 router 文件夹分离路由和处理逻辑,使项目更清晰易维护。最后,通过 Postman 测试确保服务正常运行。
37 1
|
1月前
|
监控 负载均衡 算法
slb管理后端服务器
【10月更文挑战第18天】
30 5
|
1月前
|
关系型数据库 API 数据库
后端开发的艺术:从零到一构建高效服务器
在数字化时代,后端开发是支撑现代互联网应用的基石。本文旨在探讨后端开发的核心概念、关键技术以及如何构建一个高效的服务器。我们将从基础的编程语言选择开始,逐步深入到数据库设计、API开发和性能优化等关键领域。通过实际案例分析,我们将揭示后端开发的复杂性和挑战性,同时提供实用的解决方案和最佳实践。无论你是初学者还是有经验的开发者,这篇文章都将为你提供宝贵的见解和启发。
|
2月前
|
监控 Serverless API
利用云函数实现后端服务的无服务器化
【10月更文挑战第7天】本文介绍了无服务器架构中的核心组件——云函数,探讨了其概念、优势及应用。云函数使开发者能在无需管理服务器的情况下运行代码,具备自动扩展、成本效益、快速迭代和聚焦业务逻辑等优势。文章还详细说明了实施云函数的步骤,并分享了实战技巧,旨在帮助读者更好地理解和应用这一技术。
|
2月前
|
前端开发 Java Shell
后端项目打包上传服务器部署运行记录
后端项目打包上传服务器部署运行记录
43 0
|
2月前
|
域名解析 弹性计算 安全
无法ping通ECS服务器公网IP的排查方法
无法ping通ECS服务器公网IP的排查方法

热门文章

最新文章