TCP/IP - MTU/MSS

本文详细解释了MTU(最大传输单元)的概念及其在网络设备中的应用,包括如何通过Ping命令获取MTU值,以及PPPoE对接入MTU的影响。此外还介绍了MSS(最大段大小)的作用和其与MTU的关系。

MTU


概念

MTU是 Maximum Transmission Unit的缩写,意思是网络上传送的最大数据包,MTU的单位是字节。

大部分网络设备的MTU都是1500。如果路由器的MTU比网关(电信部门)的MTU大,大的数据包就会被拆开来传送,这样会产生很多数据包碎片,增加丢包率,降低网络速度。把路由器的MTU设成比电信服务商设备的MTU小或相同,就可以减少丢包。同样,把计算机的MTU设成比本地路由器的MTU小或相同,也能减少丢包。

设备的MTU调成多少比较好?

以ISP(电信服务商)的MTU为基准,每下级设备的MTU都最好比上级设置的略低为好。

例如在ISP=>猫=>路由器=>电脑的布局中,ISP的MTU是1466,那么下级的猫可以设置成1464,再下级的路由器可以设置成1462,再下级的电脑的MTu可以设置成1460,以此类推。


MTU最大1500的由来

让我们先仔细回忆一下EthernetII帧的结构DMAC+SMAC+Type+Data+CRC由于以太网传输电气方面的限制,每个以太网帧都有最小的大小64bytes最大不能超过1518bytes,对于小于或者大于这个限制的以太网帧我们都可以视之为错误的数据帧,一般的以太网转发设备会丢弃这些数据帧。(注:小于64Bytes的数据帧一般是由于以太网冲突产生的“碎片”或者线路干扰或者坏的以太网接口产生的,对于大于1518Bytes的数据帧我们一般把它叫做Giant帧,这种一般是由于线路干扰或者坏的以太网口产生)

由于以太网EthernetII最大的数据帧是1518Bytes这样,刨去以太网帧的帧头(DMAC目的MAC地址48bit=6Bytes+SMAC源MAC地址48bit=6Bytes+Type域2bytes)14Bytes和帧尾CRC校验部分4Bytes(这个部门有时候大家也把它叫做FCS),那么剩下承载上层协议的地方也就是Data域最大就只能有1500Bytes这个值我们就把它称之为MTU。


ping获得MTU

如 ping -l 1400 -f www.baidu.com,如果显示结果是“Packet needs to be fragmented but DF set.”,说明电脑到www.baidu.com之间的MTU小于1400B;如果显示结果是“Reply from 119.75.218.77: bytes=1400 time=61ms TTL=58”,则说明电脑到www.baidu.com之间的MTU至少是1400B。


PPPoE对MTU的影响

所谓PPPoE(PPP on Ethernet)就是在以太网上面跑PPP协议,有人奇怪了,PPP协议和Ethernet不都是链路层协议吗?怎么一个链路层跑到另外一个链路层上面去了,难道升级成网络层协议了不成。其实这是个误区:就是某层协议只能承载更上一层协议。

为什么会产生这种奇怪的需求呢?这是因为随着宽带接入(这种宽带接入一般为Cable Modem或者xDSL或者以太网的接入)由于以太网缺乏认证计费机制而传统运营商是通过PPP协议来对拨号等接入服务进行认证计费的,所以就出了这么一个怪胎:PPPoE。(有关PPPoE的详细介绍参见V大以及本站其他成员的一些介绍文章,我就不啰里啰唆的了)

PPPoE带来了好处,也带来了一些坏处,比如:二次封装耗费资源,降低了传输效能等等,这些坏处俺也不多说了,最大的坏处就是PPPoE导致MTU变小了以太网的MTU是1500,再减去PPP的包头包尾的开销(8Bytes),就变成1492。如果两台主机之间的某段网络使用了PPPoE那么就会导致某些不能分片的应用无法通讯。这个时候就需要我们调整一下主机的MTU,通过降低主机的MTU,这样我们就能够顺利地进行通讯了。


MSS


概念

MSS就是TCP数据包每次能够传输的最大数据分段。为了达到最佳的传输效能TCP协议在建立连接的时候通常要协商双方的MSS值,这个值TCP协议在实现的时候往往用MTU值代替(需要减去IP数据包包头的大小20Bytes和TCP数据段的包头20Bytes)所以往往MSS为1460。通讯双方会根据双方提供的MSS值得最小值确定为这次连接的最大MSS值。




你遇到的错误信息如下: ``` Linux tun write(): Invalid argument FAIL [gro-mss-option.pkt (ipv4)] Ran 2 tests: 0 passing, 2 failing ``` 这是一个典型的 **packetdrill 测试执行失败**,特别是在使用 `tun` 设备注入数据包时出现了系统调用错误。我们来一步步分析原因、定位问题,并提供解决方案。 --- ## 🔍 错误核心:`Linux tun write(): Invalid argument` 这个错误来自 packetdrill 内部尝试通过 `TUN/TAP` 设备向内核协议栈注入数据包时,调用了 `write()` 到 `/dev/net/tun`,但返回了 `EINVAL`(Invalid argument)。 ### 常见原因 | 原因 | 说明 | |------|------| | 1. **注入的数据包格式不合法** | 如 IP header 长度错误、checksum 错误、TCP offset 越界等 | | 2. **packetdrill 构造的 SKB 不符合 GRO/GSO 要求** | 特别是在测试 `gro-mss-option.pkt` 这类涉及 GRO 分段的场景 | | 3. **内核版本与 packetdrill 不兼容** | 某些字段语义变化(如 `gso_size`, `mss_override`) | | 4. **缺少必要的模块或权限** | 未加载 `tun` 模块、无 CAP_NET_ADMIN 权限 | | 5. **IPv6 映射地址处理异常** | 测试同时运行在 `ipv4` 和 `ipv4-mapped-v6` 模式下 | --- ## 🧪 具体上下文分析 你的命令是: ```bash ./run_all.py -S -v -L -l tcp/gro ``` - `-l tcp/gro` → 只运行 `tcp/gro/` 目录下的测试 - `gro-mss-option.pkt` → 这个脚本很可能在测试: - 使用 GRO 接收一个大包 - 包含 TCP Option 中的 MSS 字段 - 验证内核是否正确解析并设置接收段大小 这类测试对 packet 构造非常敏感。 --- ## 📂 示例:可能出错的 gro-mss-option.pkt 片段 假设内容类似: ```text --mss=1000 `../common/defaults.sh` 0 socket(...) = 3 +0 bind(...) = 0 +0 listen(3, 1) = 0 +0 < S 0:0(0) <mss 1400> +0 > S. 0:0(0) ack 1 <mss 1460> +0 < . 1:1(0) ack 1 // 注入一个 GRO 数据包,共 4000 字节,分 4 段,每段 1000 字节 +0 < . 1:4001(4000) win 300 +0 > . 1:1(0) ack 1 ``` 关键点在于:`< . 1:4001(4000)` 表示一个逻辑上的大数据包,会被 packetdrill 构造成带 `GRO` 属性的 SKB。 --- ## 🛠️ 故障排查步骤 ### ✅ 步骤 1:确认 TUN 设备可用 ```bash ls -l /dev/net/tun # 应该存在且可读写 ``` 如果没有,请创建: ```bash mkdir -p /dev/net mknod /dev/net/tun c 10 200 chmod 666 /dev/net/tun ``` ### ✅ 步骤 2:确保有权限和能力 以 root 运行(你已经是),并检查是否具备: ```bash capsh --print | grep net_admin ``` 应包含 `CAP_NET_ADMIN`。 ### ✅ 步骤 3:加载 tun 模块 ```bash modprobe tun dmesg | tail -5 # 查看是否有错误 ``` ### ✅ 步骤 4:验证 packetdrill 是否能正常工作(最小测试) 写一个最简单的测试 `minimal.pkt`: ```text 0.0 < S 0:0(0) win 65535 <mss 1460> 0.0 > S. 0:0(0) ack 1 <mss 1460> 0.0 < . 1:1(0) ack 1 ``` 运行: ```bash ./packetdrill minimal.pkt ``` 如果也报 `tun write(): Invalid argument`,说明是通用环境问题。 --- ### ✅ 步骤 5:检查 packet 构造合法性 #### ❌ 常见构造错误包括: | 错误 | 后果 | |------|------| | TCP 头部偏移不合理(data_off too small) | `EINVAL` | | 总长度超过 MTU 且未分片 | `EINVAL` | | 校验和为 0 且硬件卸载不支持 | 可能被丢弃 | | GSO size 设置为非整除总长度的值 | 如 4000 字节用 gso_size=900 → 错误 | > 在 `--mss=1000` 下发送 `4001` 字节?最后一段只有 1 字节 → 非法! ✅ 正确做法:数据总量必须是 `gso_size` 的整数倍,或至少最后一段合理。 --- ### ✅ 步骤 6:启用调试输出 重新编译 packetdrill 加上调试日志: ```bash cd tools/testing/selftests/net/packetdrill/ make clean && make V=1 DEBUG=1 ``` 然后运行单个测试: ```bash ./packetdrill --verbose tcp/gro/gro-mss-option.pkt ``` 观察输出中哪一步调用 `write(fd_tun, ..., )` 失败。 --- ### ✅ 步骤 7:查看内核日志 ```bash dmesg | tail -20 ``` 寻找类似: ``` tun: Illegal buffer length gso: invalid size tcp_input: bad segment ``` 这些可以帮助判断是用户态传参错误还是内核拒绝接受。 --- ## 💡 潜在修复方案 ### 方案一:修正数据长度使其可被整除 将原脚本中的: ```text < . 1:4001(4000) ``` 改为: ```text < . 1:4001(4000) # 改成 4 × 1000 字节 ``` 确保 `len % gso_size == 0` 或最后一段 ≥ 最小阈值(如 64 字节) ### 方案二:显式指定 `gso_size` 有些版本需要手动声明: ```text +0 < . 1:4001(4000) gso_size:1000 ``` 而不是依赖 `--mss=1000` ### 方案三:避免 IPv4-mapped IPv6 测试问题 你在两个模式下都失败了: - ipv4 - ipv4-mapped-v6 后者更复杂,因为涉及 `AF_INET6` socket 绑定 IPv4 地址(如 `::ffff:192.168.1.1`) 👉 尝试只运行纯 IPv4 测试: ```bash ./run_all.py -S -v -L -l tcp/gro --ipv4-only ``` 或者修改脚本避免使用 dual-stack sockets。 --- ## ✅ 成功运行的关键条件总结 | 条件 | 必须满足? | 如何验证 | |------|-----------|---------| | `/dev/net/tun` 存在 | ✅ | `ls /dev/net/tun` | | `tun` 模块已加载 | ✅ | `lsmod | grep tun` | | `CAP_NET_ADMIN` 权限 | ✅ | `capsh --print` | | 数据包长度合法 | ✅ | `total_len % gso_size == 0` | | TCP header 完整 | ✅ | data_off ≥ 20 + options | | Checksum 正确 | ✅ | 或设为 0 并允许卸载 | | 内核支持 GRO | ✅ | `ethtool -k <dev> \| grep generic-receive-offload` | --- ## ✅ 推荐调试流程图 ```bash 开始 ↓ 检查 /dev/net/tun 和 tun 模块 ↓ 运行最小 pkt 测试(SYN exchange) ↓ → 若失败 → 检查权限、内核配置、编译选项 ↓ → 若成功 → 单独运行 gro-mss-option.pkt ↓ 查看 dmesg 输出 ↓ 检查 packet 长度、gso_size、校验和 ↓ 修复后重试 ``` --- ## ✅ 总结 你当前的问题: > `Linux tun write(): Invalid argument` > 导致 `gro-mss-option.pkt` 测试失败 根本原因极有可能是: 🔧 **packetdrill 构造了一个非法的 GRO 数据包**(例如长度不能被 `gso_size` 整除、TCP 头错误、checksum 缺失等),导致内核通过 `tun` 设备写入时返回 `EINVAL`。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值