Linux MTU 协商详解
要理解 Linux 下的 MTU 协商,首先需要明确MTU(Maximum Transmission Unit,最大传输单元) 的核心定义:它是数据链路层帧中 "数据部分" 的最大长度(不包含帧头),决定了单次能传输的最大数据包大小。MTU 协商的本质是让通信双方或网络路径上的设备达成 "一致的最大数据包尺寸",避免因数据包超过设备 / 链路 MTU 导致的分片(IPv4)、丢弃(IPv6)或传输效率下降。
Linux 的 MTU 协商并非单一机制,而是根据网络场景(如以太网、PPP、跨网段通信) 分为多个层级,核心覆盖链路层协商、网络层发现、配置式分配三类实现方式。以下分模块详细解析:
一、核心背景:MTU 协商的必要性
不同链路 / 设备的默认 MTU 存在差异(如以太网默认 1500 字节、PPP 拨号默认 1492 字节、VPN 封装后 MTU 可能降至 1300 字节以下)。若不协商:
-
IPv4 会对超 MTU 数据包分片(增加延迟和丢包风险);
-
IPv6不允许分片,超 MTU 数据包直接丢弃;
-
最终导致通信中断或性能劣化。
二、Linux MTU 协商的核心实现机制
Linux 通过 "链路层协商确定本地链路 MTU""网络层发现确定端到端路径 MTU""配置式分配补充固定场景" 三者结合,实现完整的 MTU 适配。
1. 链路层协商:确定本地直连链路的 MTU
链路层协商仅作用于直连设备(如 Linux 主机与交换机、Linux 主机与 PPP 拨号服务器),通过链路层协议交换 MTU 能力,确定双方支持的最大 MTU。
(1)以太网:基于 Auto-Negotiation(自动协商)
以太网是 Linux 最常见的链路场景,其 MTU 协商依赖IEEE 802.3u 自动协商协议(速率、双工、MTU 三位一体协商):
-
默认行为:以太网标准 MTU 为 1500 字节("标准帧"),现代千兆 / 万兆以太网交换机与 Linux 主机通过 Auto-Negotiation 默认协商为 1500 字节;
-
Jumbo Frame(巨帧) :若需支持大于 1500 字节的 MTU(如 9000 字节,提升大文件传输效率),需手动配置(因 IEEE 未定义巨帧的协商标准),双方需同时开启巨帧并设置相同 MTU(如 Linux 端
ip link set dev eth0 mtu 9000
); -
协商过程 :Linux 网卡通过
ethtool
工具与对端设备交换 "能力广告帧",其中包含 MTU 支持范围,最终取双方交集的最大值(标准场景下为 1500)。
(2)PPP/PPPoE:基于 LCP 协议协商
PPP(点对点协议,如拨号上网、ADSL)通过LCP(Link Control Protocol,链路控制协议) 明确协商 MTU,是 Linux 拨号场景的核心机制:
-
链路建立阶段 :Linux PPP 客户端(如
pppd
工具)向服务器发送Configure-Request
帧,其中携带MTU Option
(字段值为客户端支持的最大 MTU,默认 1492); -
服务器响应 :PPP 服务器若支持该 MTU,回复
Configure-Ack
确认;若不支持(如服务器最大 MTU 为 1400),则回复Configure-Nak
并携带服务器支持的 MTU; -
最终确认 :客户端接收
Configure-Nak
后,用服务器指定的 MTU 重新发送Configure-Request
,双方确认后 MTU 生效; -
Linux 实现 :
pppd
工具默认启用 MTU 协商,可通过配置文件/etc/ppp/options
手动指定 MTU(如mtu 1492
)强制覆盖协商结果。
2. 网络层协商:Path MTU Discovery(PMTUD,路径 MTU 发现)
当 Linux 主机与跨网段的目标通信 时(如访问互联网),本地链路 MTU 可能小于 "端到端路径上最小的 MTU"(即 "路径 MTU")。此时需通过PMTUD动态发现路径 MTU,避免跨网段传输问题。
(1)PMTUD 的核心原理
PMTUD 是端到端的主动发现机制,依赖 ICMP(IPv4)或 ICMPv6(IPv6)消息实现:
-
初始发送:Linux 主机向目标发送 "不分片(Don't Fragment,DF)" 标记的数据包(IPv4 默认设置 DF 位,IPv6 强制不分片),初始 MTU 使用本地链路 MTU(如 1500);
-
路径检测 :若路径上某路由器(如 MTU=1400)收到超过自身 MTU 的 DF 数据包,会丢弃该包,并向 Linux 主机发送ICMP 不可达消息(IPv4:类型 3,代码 4;IPv6:类型 2,代码 0),消息中携带 "该路由器支持的最大 MTU(1400)";
-
MTU 调整:Linux 内核接收 ICMP 消息后,会将 "到该目标的路径 MTU" 更新为路由器指定的值(1400),后续向该目标发送的数据包会使用新 MTU;
-
动态更新:若路径 MTU 后续变化(如链路切换),Linux 会重复上述过程重新发现新的路径 MTU。
(2)Linux 对 PMTUD 的默认配置
Linux 内核默认启用 PMTUD,通过以下内核参数控制:
内核参数 | 作用 | 默认值 | 说明 |
---|---|---|---|
net.ipv4.ip_no_pmtu_disc |
禁用 IPv4 PMTUD | 0(启用) | 设为 1 时,IPv4 数据包不设置 DF 位,允许路由器分片 |
net.ipv6.conf.all.disable_ipv6_pmtu_disc |
禁用 IPv6 PMTUD | 0(启用) | IPv6 强制不分片,禁用 PMTUD 会导致跨网段通信失效 |
可通过sysctl
命令查看 / 修改:
ini
\# 查看IPv4 PMTUD状态
sysctl net.ipv4.ip\_no\_pmtu\_disc
\# 临时禁用IPv4 PMTUD(不推荐)
sysctl -w net.ipv4.ip\_no\_pmtu\_disc=1
3. 配置式 MTU:DHCP 分配与手动配置
当链路层协商或 PMTUD 无法覆盖场景(如 DHCP 管理的局域网、固定 MTU 的专用网络),Linux 通过配置式方式设置 MTU,本质是 "静态分配" 而非 "动态协商",但属于 MTU 生效的核心补充手段。
(1)DHCP Option 26:自动分配 MTU
DHCP 服务器可通过Option 26(MTU Option) 向 Linux 客户端推送 MTU 值,适用于局域网统一 MTU 配置(如 VPN 客户端需固定 MTU=1300):
-
服务器配置 :在 DHCP 服务器(如
dhcpd
)配置文件中添加option interface-mtu 1300;
,指定该网段的 MTU; -
客户端接收 :Linux DHCP 客户端(如
dhclient
)获取 IP 时,会解析 Option 26 并自动设置网卡 MTU; -
验证 :Linux 客户端可通过
cat /var/lib/dhcp/dhclient.leases
查看 DHCP 分配的 MTU。
(2)手动配置 MTU
若需固定 MTU(如巨帧场景、PMTUD 失效时),可通过 Linux 命令或配置文件手动设置,重启后生效需写入配置文件:
- 临时设置(重启网卡后失效):
bash
\# 用ip命令设置eth0的MTU为9000(巨帧)
ip link set dev eth0 mtu 9000
\# 用ifconfig命令(部分系统已弃用)
ifconfig eth0 mtu 9000
-
永久设置:
- Debian/Ubuntu(
/etc/network/interfaces
):
- Debian/Ubuntu(
yaml
auto eth0
iface eth0 inet dhcp
  mtu 9000 # 新增MTU配置
- RHEL/CentOS(
/etc/sysconfig/network-scripts/ifcfg-eth0
):
ini
TYPE=Ethernet
BOOTPROTO=dhcp
NAME=eth0
MTU=9000 # 新增MTU配置
三、特殊场景:MTU 协商的补充方案
在部分网络环境中(如 ICMP 被过滤、VPN 封装),PMTUD 可能失效,需通过额外手段保证 MTU 适配。
1. VPN 场景:MTU 缩减与 MSS Clamping
VPN(如 OpenVPN、IPsec)会对数据包添加封装头(如 IPsec 的 ESP 头约 50 字节),导致 "实际可用 MTU"="物理链路 MTU - 封装头长度"(如 1500-50=1450)。若不调整,数据包会因超 MTU 被丢弃。
Linux 常用TCP MSS Clamping解决(MSS=MTU-IP 头 - TCP 头,限制 TCP 段最大尺寸):
- 通过
iptables
强制设置 TCP MSS 值,避免超 MTU:
css
\# 对VPN接口tun0的出站TCP包,设置MSS=1400(对应MTU=1450)
iptables -A OUTPUT -o tun0 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1400
2. ICMP 过滤导致 PMTUD 失效
部分防火墙会过滤 ICMP 不可达消息(PMTUD 依赖的核心消息),导致 Linux 无法收到 MTU 调整通知,数据包持续超 MTU 被丢弃。此时需:
-
手动降低 MTU(如
ip link set dev eth0 mtu 1400
); -
启用 TCP MSS Clamping(绕过 ICMP 依赖)。
四、Linux MTU 的验证与排查工具
1. 查看当前 MTU
bash
\# 查看所有网卡MTU(推荐)
ip link show
\# 查看指定网卡(如eth0)MTU
ip link show dev eth0
\# 用ifconfig查看(兼容旧系统)
ifconfig eth0
2. 测试路径 MTU(PMTUD 验证)
用ping
命令发送带 DF 位的数据包,通过调整 payload 大小测试路径 MTU(公式:payload size + IP头(20) + ICMP头(8) = MTU
):
bash
\# 测试目标IP 8.8.8.8的路径MTU,初始payload=1472(1472+28=1500)
ping -D -s 1472 8.8.8.8
\# 若提示"无法分片",减小payload(如1462,对应MTU=1490)
ping -D -s 1462 8.8.8.8
\# 直到ping通,此时MTU=payload+28
五、总结:Linux MTU 协商的层级逻辑
Linux 通过 "链路层协商确定本地能力 → 网络层 PMTUD 发现端到端路径 → 配置式补充固定场景" 的三层逻辑,实现 MTU 的动态适配与静态控制:
-
链路层:以太网 Auto-Negotiation(默认 1500)、PPP LCP(拨号场景);
-
网络层:PMTUD(跨网段核心,依赖 ICMP);
-
配置层:DHCP Option 26(自动分配)、手动命令 / 配置文件(固定 MTU)。
理解这一逻辑后,可快速定位 MTU 相关问题(如丢包、延迟),通过调整 PMTUD、MSS Clamping 或手动 MTU 实现最优传输。
参考: