• [原创]分析解决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

    1. 直接使用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了。

      1. 发现手机终端的MSS值多种多样,这里列里几个值, 1412 1120 1452 1400 1394 1260 1460      

        原文链接
  • 相关阅读:
    IE下全局对象报 脚本错误提示“对象不支持此属性或方法”解决方案
    IE6、7下inline-block不起作用
    IE下图片切换的时候,图片总是切换不成功---根本问题是IE缓存图片
    Fiddler 跟踪 手机页面数据包
    Regular Expression Matching——没理解的动态规划
    常见排序算法分析
    Flyweight模式(亨元模式)
    组合模式(Composite Pattern)
    装饰者模式(不太理解的设计模式)
    适配器模式
  • 原文地址:https://www.cnblogs.com/jzy996492849/p/7205723.html
Copyright © 2020-2023  润新知