本文轉自http://www.cublog.cn/u3/92327/showart_2255574.html
MTU: Maxitum Transmission Unit 傳輸單元最大值
MSS: Maxitum Segment Size 最大分段大小
MSS最大傳輸大小的縮寫,是TCP協議裡面的一個概念。
MSS就是 TCP資料包每次能夠傳輸的最大資料分段。為了達到最佳的傳輸效能TCP協議在建立串連的時候通常要協商雙方的MSS值,這個值TCP協議在實現的時候往往用MTU值代替(需要減去IP資料包包頭的大小20Bytes和TCP資料區段的包頭20Bytes), 通訊雙方會根據雙方提供的MSS值得最小值確定為這次串連的最大MSS值。
而一般乙太網路MTU都為1500, 所以在乙太網路中, 往往TCP MSS為1460。
協商TCP MSS大小具體過程如下:
TCP client發出SYN報文,其中option選項填充的MSS欄位一般為(MTU-IP頭大小-TCP頭大小),同樣TCP server收到SYN報文後,會發送SYN+ACK報文應答,option選項填充的mss欄位也為(MTU-IP頭大小-TCP頭大小);協商雙方會比較SYN和SYN+ACK報文中MSS欄位大小,選擇較小的MSS作為發送TCP分區的大小。
對於涉及PPPOE+NAT、IPsec、L2TP、GRE等組網,通常由於報文太大需要分區,這樣會降低傳輸速率; 所以選擇一個合適的MSS對傳輸資料來說比較重要. linux中一般可以通過netfilter iptables設定TCP MSS來解決。
iptables -A FORWARD -p tcp- -tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
這條規則的目的就是改變TCP MSS以適應PMTU(Path MTU)
iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN- j TCPMSS --set-mss 128
設定MSS為128
以下是一段核心中修改TCP MSS的代碼:
static inline u32 set_tcp_mss(struct sk_buff *pskb, struct tcphdr *tcph, u16 mtu) { u32 optlen, i; u8 *op; u16 newmss, oldmss; u8 *mss;
if ( !tcph->syn ) return 0;
// 判斷是否為合法tcp選項
if (tcph->doff*4 < sizeof(struct tcphdr)) return 0;
optlen = tcph->doff*4 - sizeof(struct tcphdr); if (!optlen) return 0;
// 掃描是否有MSS選項
op = ((u8*)tcph + sizeof(struct tcphdr)); for (i = 0; i < optlen; ) { if (op[i] == TCPOPT_MSS && (optlen - i) >= TCPOLEN_MSS && op[i+1] == TCPOLEN_MSS) { u16 mssval; //newmss = htons( 1356 );
oldmss = (op[i+3] << 8) | op[i+2]; mssval = (op[i+2] << 8) | op[i+3]; // 是否小於MTU-( iphdr + tcphdr )
if ( mssval > mtu - 40 ) { newmss = htons( mtu - 52 ); } else { break; } //
mss = &newmss; op[i+2] = newmss & 0xFF; op[i+3] = (newmss & 0xFF00) >> 8; // 計算checksum
inet_proto_csum_replace2( &tcph->check, pskb, oldmss, newmss, 0); mssval = (op[i+2] << 8) | op[i+3]; dprintf( "Change TCP MSS %d to %d/n", ntohs( oldmss ), mssval ); break; } if (op[i] < 2) i++; else i += op[i+1] ? : 1; } return 0; }
|
windows可以通過一個工具來修改 DrTCP http://www.dslreports.com/drtcp