在linux内核中修改TCP MSS值

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的代码:

[cpp]  view plain copy
  1. static inline u32 set_tcp_mss(struct sk_buff *pskb, struct tcphdr  *tcph, u16 mtu)  
  2. {  
  3.     u32 optlen, i;  
  4.     u8  *op;  
  5.     u16 newmss, oldmss;  
  6.     u8  *mss;  
  7.   
  8.     if ( !tcph->syn )  
  9.         return 0;  
  10.   
  11.     // 判断是否为合法tcp选项  
  12.     if (tcph->doff*4 < sizeof(struct tcphdr))  
  13.         return 0;  
  14.   
  15.     optlen = tcph->doff*4 - sizeof(struct tcphdr);  
  16.     if (!optlen)  
  17.         return 0;  
  18.   
  19.     // 扫描是否有MSS选项  
  20.     op = ((u8*)tcph + sizeof(struct tcphdr));  
  21.     for (i = 0; i < optlen; ) {  
  22.         if (op[i] == TCPOPT_MSS  
  23.             && (optlen - i) >= TCPOLEN_MSS  
  24.             && op[i+1] == TCPOLEN_MSS) {  
  25.             u16 mssval;  
  26.               
  27.             //newmss = htons( 1356 );  
  28.             oldmss = (op[i+3] << 8) | op[i+2];  
  29.             mssval = (op[i+2] << 8) | op[i+3];  
  30.               
  31.             // 是否小于MTU-( iphdr + tcphdr )  
  32.             if ( mssval > mtu - 40 ) {  
  33.                 newmss = htons( mtu - 52 );   
  34.             }  
  35.             else {  
  36.                 break;  
  37.             }  
  38.         //   
  39.         mss = &newmss;  
  40.             op[i+2] = newmss & 0xFF;  
  41.             op[i+3] = (newmss & 0xFF00) >> 8;  
  42.             // 计算checksum  
  43.             inet_proto_csum_replace2( &tcph->check, pskb,  
  44.                 oldmss, newmss, 0);  
  45.               
  46.             mssval = (op[i+2] << 8) | op[i+3];  
  47.             dprintf( "Change TCP MSS %d to %d/n", ntohs( oldmss ), mssval );  
  48.             break;  
  49.               
  50.         }  
  51.         if (op[i] < 2)  
  52.             i++;  
  53.         else  
  54.             i += op[i+1] ? : 1;  
  55.     }  
  56.     return 0;  
  57. }  

windows可以通过一个工具来修改  DrTCP  http://www.dslreports.com/drtcp

<think>嗯,用户这次的问题非常聚焦于Linux内核TCP实现的一个具体字段:tp->delivered。从提问的精准度来看,用户应该是有网络协议栈开发或性能调优需求的工程师,至少是熟悉TCP协议基本概念的研究者。用户能直接提到tp结构体成员,说明已经看过部分内核源码。 注意到用户引用了三个站内技术文档片段,其中引用[1]和[2]都涉及TCP发送队列管理机制,尤其引用[2]提到的tcp_fragment函数和TCPWQUEUETOOBIG计数器,与tp->delivered存在潜在关联——因为分片行为会影响实际交付的报文计数。不过用户核心诉求明确指向delivered字段的语义,需要重点解析这个字段的计量逻辑。 tp->delivered字段的官方定义在include/net/tcp.h里其实比较简略,需要结合tcp_input.c和tcp_output.c的上下文理解。它的核心价在于实时追踪原始数据字节的传输进展,注意三个关键点: 1. 计量单位是字节而非报文段,这从tcp_ack()中tp->delivered += acked的累加方式可验证 2. 包含重传数据,因为网络丢包时该计数仍会累积(参考RFC6298的重传计时器机制) 3. 与TCPTSO机制有隐含互动,当GSO分片发生时,实际delivered增量可能大于MSS 用户可能真正关心的是如何利用这个字段做带宽评估。比如BBR算法正是通过delivered/delivered_mstamp计算即时带宽。但需要提醒用户注意delivered_mstamp的更新时机问题——它只在ACK到达时更新,可能导致采样间隙内的带宽估算偏差。 考虑到用户的技术深度,后续问题可以延伸到具体应用场景。比如如何基于/proc/net/tcp获取该(需要修改内核暴露接口),或者解释delivered与RFC4898定义的DeliveredData的区别(后者是TCP选项需要额外计算)。</think>在 Linux 内核TCP 协议栈实现中,`tp->delivered` 是 `struct tcp_sock` 结构体的一个关键成员变量,主要用于**精确追踪实际交付给接收端应用层的数据量**。其核心作用与运作机制如下: --- ### **核心作用** 1. **计量已确认交付的数据量 (字节单位)** `tp->delivered` 记录的是**已被接收端成功接收并确认 (ACKed)** 且**对端应用层已消费**的数据总量(单位:字节)。它反映的是真正“有效交付”的数据,而非单纯发送出去的数据。 2. **支撑拥塞控制算法** 该字段是 BBR (Bottleneck Bandwidth and Round-trip propagation time) 等现代拥塞控制算法的核心输入: - **计算实时带宽**:通过采样周期内 `tp->delivered` 的增长量 Δdelivered 与时间间隔 Δt,计算瞬时带宽:$Bandwidth = \frac{\Delta delivered}{\Delta t}$。 - **判断网络瓶颈**:结合 RTT 测量,识别网络路径的带宽与延迟特性[^1]。 3. **启用 RACK (Recent ACK) 丢包检测** 在 RACK 机制中,`tp->delivered` 用作数据包的“发送序列号”,通过比较不同数据包的 `(tp->delivered, 发送时间戳)` 二元组,推断丢包事件的发生时机[^2]。 --- ### **运作机制详解** 1. **初始化** 连接建立时(如 SYN-ACK 到达),`tp->delivered` 初始化为 0: ```c tp->delivered = 0; // 初始无交付数据 ``` 2. **数据交付确认时的更新** 当收到接收端的 ACK 确认新数据时,内核在 `tcp_ack()` 函数中更新: ```c delivered = tp->delivered; // 记录更新前的 (用于带宽计算) tp->delivered += newly_acked_bytes; // 增加本次确认的字节数 ``` 3. **记录数据包元信息** 数据包发送时(如调用 `tcp_transmit_skb()`),其当时的 `tp->delivered` 会被记录到该包的 TCP 控制块中(`struct tcp_skb_cb->delivered`): ```c tcb->delivered = tp->delivered; // 标记该包发送时的全局交付量 ``` 此代表“该包发送时,连接上已交付的总字节数”,用于后续计算该包本身的交付量。 4. **计算单包交付量** 当某个数据包被确认时,其**实际交付量** = `当前 tp->delivered - 该包记录的 tcb->delivered`。 例如: - 包 A 发送时:`tcb->delivered = 100` - 包 A 被确认时:`tp->delivered = 120` - 则包 A 的交付量 = $120 - 100 = 20$ 字节 --- ### **技术意义** - **精准拥塞控制**:BBR 依赖 `tp->delivered` 计算真实带宽,替代传统基于丢包或延迟的启发式方法,显著提升高带宽、高延迟网络下的性能[^1]。 - **丢包诊断优化**:RACK 通过比较数据包的 `(delivered, 发送时间)` 而非序列号,能更早、更准确地检测乱序和丢包[^2]。 - **性能监控基础**:该字段是计算吞吐量、传输效率的核心指标,可通过 `ss -t -i` 命令查看(输出中的 `delivery_rate` 和 `busy` 相关字段)。 ```bash $ ss -t -i ESTAB 0 0 192.168.1.2:443 192.168.1.3:55678 cubic wscale:7,7 rto:204 rtt:1.234/0.75 mss:1448 delivered:105600 // 连接累计交付量 (字节) delivery_rate:2.5Gbps // 基于delivered计算的瞬时带宽 ``` --- ### **与相关字段的对比** | 字段 | 含义 | 与 `tp->delivered` 的关系 | |------|------|---------------------------| | `tp->bytes_sent` | 累计发送字节数(含重传) | 始终 ≥ `tp->delivered` | | `tp->bytes_acked` | 累计确认字节数(含重传数据的确认) | 可能 > `tp->delivered`(存在乱序确认) | | `tp->snd_una` | 最早未确认序列号 | 用于计算 `newly_acked_bytes` 以更新 `delivered` | ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值