TCP maximum segment size 是什麼以及是如何決定的 - Medium

文章推薦指數: 80 %
投票人數:10人

回傳來源用更小的封包大小(送出ICMP “packet too big” 訊息)。

IPv4 有支援1 & 2,但1 會帶給router 太多負擔(memory & CPU),可能會被用作攻擊 ... GetunlimitedaccessOpeninappHomeNotificationsListsStoriesWritePublishedinfcamel的程式開發心得TCPmaximumsegmentsize是什麼以及是如何決定的網路傳輸時需要一些標頭檔決定封包如何傳送,所以封包帶的資料愈大愈划算。

以TCP為例,TCP和IP標頭檔都是20bytes,用40bytes的標頭檔只傳1byte就很不划算。

TCP設定封包所帶的資料上限稱為maximumsegmentsize(MSS),理論上我們希望它愈大愈好。

以下記錄和MSS相關的知識,懶得看的話只要記住一個結論:OS都處理好了,沒事不要手癢亂改MSS的值。

MaximumTransmissionUnit(MTU)在討論MSS之前,先看IPlayer的情況。

router之間傳輸封包大小的上限稱為MTU。

當router發現封包大小超出下一站router的上限時,它有兩個選擇:拆成更小的封包送出。

回傳來源用更小的封包大小(送出ICMP“packettoobig”訊息)。

IPv4有支援1&2,但1會帶給router太多負擔(memory&CPU),可能會被用作攻擊手段,所以很多router不支援1。

IPv6只支援2。

PathMTUDiscovery透過ICMP通知“packettoobig”不斷嘗試找出兩端機器可行的MTU,稱為PathMTUDiscovery(PMTUD)。

但是有些router基於安全理由(#1),會濾掉ICMP封包,所以PMTUD也不可靠。

若用了太大的封包,且中間的router沒有回傳ICMP“packettoobig”,結果是TCPhandshake成功,但後面都收不到也傳不出資料。

最穩的作法是一開始就不要讓封包超出MTU上限。

IPv4規定router至少要能處理576bytes,IPv6是1280bytes。

使用最小值固然能避開最壞情況,但是這樣傳輸效率不好。

幸好如今的Internet幾乎都用Ethernet,Ethernet標準規範至少有1500bytes,所以可以安心地假設MTU=1500。

Linux上可以用ping作PMTUD,下面是計算本機到8.8.8.8(GoogleDNS)的PMTU:$ping-c1-Mdo-s20008.8.8.8PING8.8.8.8(8.8.8.8)2000(2028)bytesofdata.ping:localerror:Messagetoolong,mtu=1500我用adbshell連上Androidphone用一樣的指令,在Wifi和4G得到一樣的結果:#Wifielsa:/$iproute192.168.1.0/24devwlan0protokernelscopelinksrc192.168.1.103elsa:/$ping-c1-Mdo-s20008.8.8.8PING8.8.8.8(8.8.8.8)2000(2028)bytesofdata.ping:localerror:Messagetoolong,mtu=1500#4Gelsa:/$iproute10.47.188.192/26devrmnet_data0protokernelscopelinksrc10.47.188.223elsa:/$ping-c1-Mdo-s20008.8.8.8PING8.8.8.8(8.8.8.8)2000(2028)bytesofdata.ping:localerror:Messagetoolong,mtu=1500不確定行動網路的MTU是否都≥1500。

即使不是,TCP也不會有問題(後述)。

TCPSYN與MSS了解IPlayer的情況後,再來看TCP的情況。

TCP會設IP的Don’tFragmentflag,這樣router覺得封包太大的時候,會回傳ICMP“packettoobig”,避開router不處理IPfragmentation的問題。

如前所述,我們不能完全依賴這個。

TCP在three-wayhandshake的時候還會透過SYN封包帶上MSS,告訴另一邊不要送超出MSS的封包過來。

OS會自動用下一站的MTU—40作為MSS(TCP和IP標頭檔各20bytes),所以無論是直接用Ethernet或行動網路,都不會有問題。

對Ethernet來說,MSS就是1500—40=1460。

MSSclamping某些情況下中間router的MTU會比1500小,常見的情況有:使用PPPoE。

使用VPN(#2)。

IPv6tunnel通過IPv4網路。

以PPPoE為例,會多8~12bytes的標頭檔,所以MSS必須再少8~12bytes。

要求全部client更改OS的MSS設定並不實際,所以現行的作法是PPPoE的server會偷改SYN封包裡的MSS[#3],讓它不要超出上限,這個行為稱為MSSclamping。

也就是說client以為它用MSS=1460,但PPPoEserver在經手的時候改成MSS=1452(假設PPP標頭是8bytes)。

不合規定(MTU≥1500)的router負責作TCPclamping,相當合理。

於是天下太平,不會有人用超出MTU上限,也就不用擔心某些router濾掉ICMP的問題了。

Linux設定MSS的方法可以透過iproute設定不同目標的advmss,這樣MSS不會超過advmss。

程式裡也可透過setsockopt(...,TCP_MAXSEG)設定,但通常沒必要。

我在Ubuntu16.04用tcpdump看SYN的封包,發現預設值已是最佳值了。

用以下的指令觀察SYN帶的MSS:sudotcpdump-s0-p-niINTERFACE'(ipandip[20+13]&tcp-syn!=0)'連本機的MSS=65495(65535—40),已是上限:23:52:45.009601IP192.168.1.109.38728>192.168.1.109.8000:Flags[S],seq2523441450,win43690,options[mss65495,sackOK,TSval27165590ecr0,nop,wscale7],length023:52:45.009629IP192.168.1.109.8000>192.168.1.109.38728:Flags[S.],seq1592261220,ack2523441451,win43690,options[mss65495,sackOK,TSval27165590ecr27165590,nop,wscale7],length0VM連VM的MSS=1460:23:52:00.297788IP192.168.1.110.52397>192.168.1.109.8000:Flags[S],seq1208990698,win29200,options[mss1460,sackOK,TSval4294911018ecr0,nop,wscale7],length023:52:00.297833IP192.168.1.109.8000>192.168.1.110.52397:Flags[S.],seq2916193787,ack1208990699,win28960,options[mss1460,sackOK,TSval27154412ecr4294911018,nop,wscale7],length0Linux計算MSS的方法計算的方式頗複雜的,分成傳送用的MSS和接收用的MSS。

接收用的MSS是估算另一端的傳送用的MSS,會影響delayedACK的發送時機。

關於這點,有閒研究清楚再來寫篇blog。

以下只討論傳送用的MSS。

綜合前述的知識,我們知道使用者設的MSS、advmss、PMTU、另一端傳來SYN帶的MSS都會影響真正使用的MSS。

除此之外,TCP的window大小、變動大小的標頭等也有影響。

MSS的值其實會不斷地改變。

還有很多細節沒搞懂,就加減看吧。

這裡備忘幾個相關函式:tcp_advertise_mss()tcp_current_mss()tcp_sync_mss()《用SystemTap找出TCP如何決定MSS的值》有稍微進一步的說明。

參考資料看了拉里拉雜的資料,這幾篇最為精華:PathMTUdiscoveryinpracticeBrokenpackets:IPfragmentationisflawedMTUDiscoveryandMSSClamping備註#1不懂為啥要擋這個,這篇認為這是腦殘的行為XD。

#2不熟VPN,但應該是layer2or3的VPN,不是layer7的。

#3《修改MSS解決LinuxPPPOENAT後部份網頁無法瀏覽問題》有相關細節,用iptables的--clamp-mss-to-pmtu。

--2Morefromfcamel的程式開發心得Notesaboutsoftwaredevelopment.Readmorefromfcamel的程式開發心得AboutHelpTermsPrivacyGettheMediumappGetstartedfcamel859FollowersFollowMorefromMediumRedouaneOTMANIKerberosHarminderSinghSettingupSPARK3.1.1LivyandMinioobjectstorageSantiagoVelezHolisticBackend/CloudDevGibinFrancisAzureIoTEdgewithproxyandCertificateHelpStatusWritersBlogCareersPrivacyTermsAboutKnowable



請為這篇文章評分?