[原創(chuàng)]分析解決lvs fullnat模式下后端服務器獲取真實IP地址異常問題

摘要:分析解決lvs fullnat模式下少量的請求記錄client IP不是用戶真實的IP地址問題.


摘要

分析解決lvs fullnat模式下少量的請求記錄client IP不是用戶真實的IP地址問題.

原創(chuàng)文章:來自分析lvs fullnat模式下后端服務器獲取真實IP地址異常問題

問題背景

lvs fullnat模式下觀察后端服務器realserver http/https業(yè)務運行系統(tǒng)日志,有時候可以發(fā)現(xiàn)有少量的請求記錄的client IP不是用戶真實的IP地址(存在但出現(xiàn)的概率很小,增加了問題排查的難度),而是屬于lvs主機私有的IP地址。關于fullnat的簡介可以參考http://www.baokaijian.com/?tag=fullnat,這里借用一下文中的圖片。

問題分析

大多數(shù)TCP正常建立連接過程

realserver linux系統(tǒng)屬于用戶自屬管理默認配置MTU=1500、MSS=1460,在TCP建連時可以通過syn+ack報文通告給client,而client端可屬于移動端或PC端MTU、MSS的差異性較大,通過syn報文通告給realserver端,從而建立的TCP連接取兩者之小值做為鏈路的MSS。中間的LVS只是做連接與報文轉發(fā)的負載均衡。

fullnat 模式下獲取真實IP地址的原理:

開源的lvs fullnat代碼與patch可查閱lvs官方網(wǎng)址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()函數(shù),然后調用get_toa_data()從tcp_options中取得客戶端的真實IP地址,具體的調用過程下邊分析。

非標準的tcp三次握手情況分析

先來看以下幾個典型的報文序列是否可以建立三次握手:

1. 亂序的情況(第三個ack和第四個ack+data報文亂序)

直接使用ack+data報文(或含有push標志)不存在bare ack報文

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

來點內核TCP代碼中深度分析:

看一下建立TCP連接時兩次經過的tcp_v4_hnd_req函數(shù)流程.

syn報文到來時的處理流程:

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

查找代表客戶端連接的sock結構,如果沒有找到則返回代表服務器的sock結構, 然后在tcp_rcv_state_process()函數(shù)中調用tcp_v4_conn_request()創(chuàng)建req_sock請求結構并放入半連接隊列。

握手階段收到第三個ack報文或其他攜帶ACK標志的報文處理,tcp_v4_hnd_req()函數(shù)將返回代表連接請求的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()函數(shù)中將對報文序列號判斷,并完成握手,最后將連接請求從半連接請求移到全連接隊列,等待應用層調用accept。

特別注意在tcp_check_req()函數(shù)中檢查了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 = 初始發(fā)送的seq + 1(syn)

(TCP_SKB_CB(skb)->ack_seq !=? ? //此報序列號不等于發(fā)送初始序列號? ? ? tcp_rsk(req)->snt_isn +1+ tcp_s_data_size(tcp_sk(sk))))returnsk;

結論: 完成握手最后一個clinet端到來報文ack序號必須是client 端tcp連接ISN初始序號+1(沒有檢查seq序列號),所以只要符合此規(guī)則的都可以通過,然后調用調用tcp_v4_syn_recv_sock()從而調用toa模塊取client IP tcp options。再解釋明白點: 非bare ack亂序報文即帶有數(shù)據(jù)的ack報文,也可以通過。注:至于seq序列號有效性以及影響能不能真正完成連接建立,這是在后續(xù)的tcp_ack()中判斷了。

接著tcp_v4_syn_recv_sock()函數(shù)中創(chuàng)建代表與client端連接的sock結構(設置此條TCP連接狀態(tài)是TCP_SYN_RECV),然后在tcp_rcv_state_process()函數(shù)中case TCP_SYN_RECV代碼段中通過tcp_set_state(sk, TCP_ESTABLISHED); 改狀態(tài)為ESTABLISHED連接狀態(tài)。

經過在toa模塊中探點調試發(fā)現(xiàn):

從realserver端看到大量使用FIN+ACK+PUSH的含數(shù)據(jù)報文完成三次握手,這時也不能插入toa options(用戶業(yè)務很多用http/https這種post請求推數(shù)據(jù),realserver收到后會回復response,但若是反向代理的proxy收到后會FIN掉client,同時向后端realserver再發(fā)RST報文)

上述圖中因亂序或直接ack+data報文沒超但達到了1444字節(jié)(假如toa需要16字節(jié)); 使用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()函數(shù)中去掉!tcph->fin的條件判斷,可以解決FIN+PUSH+ACK+數(shù)據(jù)報文中不能攜帶client端ip與端口的缺陷

2、 后端RS建立監(jiān)聽時直接使用 setsockopt, 將MSS改?。ㄈ鐃oa需要16字節(jié)則可減小到1444字節(jié)), 從而client到來的報文可以有插入clientip:port的空間

3、 (第2條的替代方法但會增加lvs處理的復雜性:

TCP握手階段lvs收到后端RS發(fā)送的syn+ack報文時,將其中的MSS改小(如16字節(jié)),傳給client, 從而client到來的報文就有了插入clientip:port的空間。

另一種方法雖然client發(fā)送來的報文已經達到了client與RS協(xié)商的MSS(如1460), 但若lvs與RS之間設置的MTU(如9000)要大于client與LVS之間的值1460, 并且tcp options有足夠的空間可以強制在tcp options中插入clientip:port)

4、以上修改后最后還是不行(可能存在非標準的client tcp處理),建議客戶端程序(特別是手機APP)建立socket時使用 setsockopt 將MSS改小適當?shù)臄?shù)值(發(fā)現(xiàn)存在有部分終端出現(xiàn)通告給其MSS是1444,但仍然發(fā)送1460的報文)

備注:

1. tcp options滿載40字節(jié),如果在握手時用戶有插入自己的數(shù)據(jù)占滿則lvs判斷沒有空間再插入用戶IP了。

發(fā)現(xiàn)手機終端的MSS值多種多樣,這里列里幾個值, 1412 1120 1452 1400 1394 1260 1460

原文鏈接

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 18.1 引言 TCP是一個面向連接的協(xié)議。無論哪一方向另一方發(fā)送數(shù)據(jù)之前,都必須先在雙方之間建立一條連接。本章將...
    張芳濤閱讀 3,528評論 0 13
  • 個人認為,Goodboy1881先生的TCP /IP 協(xié)議詳解學習博客系列博客是一部非常精彩的學習筆記,這雖然只是...
    貳零壹柒_fc10閱讀 5,195評論 0 8
  • 1、TCP狀態(tài)linux查看tcp的狀態(tài)命令:1)、netstat -nat 查看TCP各個狀態(tài)的數(shù)量2)、lso...
    北辰青閱讀 9,718評論 0 11
  • 1 概述 要了解三次握手&四次揮手的過程,就需要對TCP的報頭以及有限狀態(tài)機的概念有所了解,本文將介紹TCP報頭的...
    ghbsunny閱讀 1,214評論 0 3
  • 1.這篇文章不是本人原創(chuàng)的,只是個人為了對這部分知識做一個整理和系統(tǒng)的輸出而編輯成的,在此鄭重地向本文所引用文章的...
    SOMCENT閱讀 13,365評論 6 174

友情鏈接更多精彩內容