[心缘地方]同学录
首页 | 功能说明 | 站长通知 | 最近更新 | 编码查看转换 | 代码下载 | 常见问题及讨论 | 《深入解析ASP核心技术》 | 王小鸭自动发工资条VBA版
登录系统:用户名: 密码: 如果要讨论问题,请先注册。

[备忘]关于MTU,MSS

上一篇:[备忘]java调用socket的write时,发生了什么
下一篇:[备忘]某客户端传文件导致连接中断的分析过程

添加日期:2022/4/18 0:13:21 快速返回   返回列表 阅读4457次
原文:https://sakura-paris.org/Internet%20protocol%20suite
Internet protocol suite
-------------------------
标准 TCP/IP 模型。标准模型采用分层设计,下层的数据包为 Header+ Payload + Footer (Footer 只有部分协议有),其中 Payload 即为上一层的数据包。

|-应用层(Application layer):App Data
|-传输层(Transfer layer):TCP / UDP Packet
|-网络层(Network layer):IP Packet / ARP Packet
|-链路层(Link layer):Ethernet Frame
-------------------------
Structure
Ethernet Frame
即以太网帧。有很多版本标准。目前大部分设备实际使用的是 802.1 "Ethernet II framing" 格式,也是 IP 网络标准以太网帧,其帧格式如下:

MAC Header (14 bytes) + PAYLOAD (46-1500 bytes) + CRC Checksum (4 bytes)

路由器等网络设备支持 802.1q,在 802.1 基础上增加了 VLAN tag 等字段。
无线AP使用完全不同的 802.11 帧格式。共 4 个 Mac 地址字段。(sender, receiver, destination,还有一个地址用于 ad-hoc 模式)
-------------------------
ARP Packet
ARP 是二层协议,用于查询本地 LAN 网络里指定 IP 主机的 MAC 地址。发送任何 IP 包之前,都必须获取对方的 Mac 地址;但如果对方不在本地 LAN 网段,那么获取的是查询路由表得到的出口网关的 Mac 地址。ARP 协议的查询 request 包会广播给本地 LAN 里所有设备。因此对于较大规模的 LAN,存在广播风暴(broadcast storm)问题,解决方法是 VLAN。

发送 IP 包流程:

本机 App 想要发送 IP 包 src IP -> dst IP ->
    dst IP 位于本机所属的 LAN ? -> ARP 查询 dst IP 的 dst MAC -> 发送 Ethernet Frame: "dst Mac, src MAC, src IP, dst IP"
    否则: 查询路由表寻找到 dst IP 的路由 -> 找到匹配的路由项的网关 gw IP -> ARP 查询 gw IP 的 gw MAC -> 发送 Ethernet Frame: "gw Mac, src Mac, src IP, dst IP"
主机设备网卡接收到 Ethernet Frame 后,会检查 Ethernet Frame Header 里的 "destination Mac" 字段,如果与本机 MAC 一致,才会将从帧拆出 IP Packet,交给 TCP / IP 协议栈处理(否则直接忽略)。

而交换机(包括路由器集成的 LAN 交换芯片)接收到 Ethernet Frame 后,则会根据 Header 里的 "destination Mac" 将数据帧转发到对应的网络端口上。
-------------------------
IP Packet
即 IP 包。格式: Header + Payload

其中:

IPV4:Header 为固定的 20 bytes + options (0-40 bytes, 必须是4的倍数)。(现实中,大多数情况下没有使用 options,所以 Header 一般就是 20 bytes)

IPV6:Header 为固定的 40 bytes。
Payload 理论上可以有任意字节,只要整个 IP Packet (Header + Payload) 不超过 65536 字节(因为 IP header 里“IP 数据包长度”字段是 16 bit)即可。但如果 IP 包大小超过 Ethernet Frame 的 Payload 容量限制,那么 IP 包通过以太网传输时必须要分片(Fragmentation),把一个 IP 包分成多个 IP 包传输(分片后的 IP 包称为 "fragmented packet",以下简称 "子 IP 包")。分片有很多潜在的问题:

如果 IP 包的 Header 里设置了 DF (禁止分片)标志位,那么这个 IP 包在通过链路中某个中间节点设备时如果需要分片才能传输,设备会直接丢弃这个 IP 包(并会通过 ICMP 协议给源设备发送一个回应以告知其)。
分片后的所有子 IP 包会在目的地设备 (dst) 的网络层重组还原为 1 个 IP 包 (链路的中间节点设备会直接转发子 IP 包,不会重组),如果其中任意一个子 IP 包在传输中丢失或延迟了,那么(等待超时后)dst 会丢弃所有已收到的 IP 子包。
网络链路的中间节点路由器可能根据IP包的四层信息(如tcp/udp端口)做策略路由,由于分片后的 IP 包只有第一个子包里含有四层信息,路由器可能将这些IP子包通过不同的端口路由发送,造成网络延迟(Round trip time, RTT)不一致。
网络链路的中间节点如果做了NAT,那么还难处理分片的IP数据包。

IPV6 的一些变更:
完全取消了分片。从header 里删除了 "16-bit Identifier", "3-bit Flags + 13-bit Fragmentation offset" 这些主要用于分片处理的字段。
取消了 Optional 可变区域。Header 固定 40 bytes。
用 "Payload size" 字段代替了 "Datagram size"字段。"Payload size" 字段同样是16-bit,但是只包含数据(Data)部分的大小,不包含 Header 固定 40 bytes。
取消了 cheksum 字段。(应用层 TCP / UDP 均有自己的 checksum 字段,功能与 IPV4 的 checksum 重复)。并且由于 IPV4 数据包的 checksum 需要在链路每个节点都重新计算(TTL 发生变化),影响性能。
-------------------------
ICMP Packet
ICMP 是 IP 层附属协议。ICMP Packet 也是一个 IP 包,其格式如下:(以 IPV4 环境下的 ICMPV4 为例)

IP Header (20 bytes)(典型值) + ICMP Header (8 bytes) + Payload
-------------------------
MTU
链路层允许的(无需分片的)最大发送 IP 包大小即为 MTU (Maximum transmission unit, 最大传输单元)。

对于标准的 Ethernet,MTU 即为 1500,这也就是 IP 包通常的最大大小。

但如果使用了封装协议(PPPoE、GRE / ipsec 以及其它任何 IP over X 的 tunnel / VPN),那么协议里封装的 IP 流的 MTU 都会降低,因为任何包装协议都会增加 overhead。

例如,PPPoE 协议(常用于宽带拨号)在实际的 IP 包前增加 8 bytes 的 overhead,所以通过 PPPoE 连接的网络 MTU 只有 1500 - 8 = 1492。

本地回环网络 lo 的 MTU 通常为(IP包)最大值 65536。

常见坑:
Google Cloud 全球服务器网络的 MTU 均为 1460。
https://cloud.google.com/vpn/docs/concepts/mtu-considerations
-------------------------
PMTU
PMTU (Path MTU,链路 MTU)指经过多个节点的一条网络链路的所有节点设备的 MTU 的最小值。PMTU 是一条网络链路上能够传输的最大 Ethernet Payload 大小。

可以使用 ping 来测试 PMTU。设置 ping 参数以指定 ICMP 包的 Payload 大小,并设置禁止 IP 包分片标志位:

Linux: ping -M do 8.8.8.8 -s 1472 (部分busybox等精简版ping不支持 -M 参数)
Windows: ping -f -l 1472 8.8.8.8
上面的示例命令中的 1472 是链路 MTU 为 1500 时的最大能够发送成功的 ICMP 包 Payload 大小(IPV4 header + ICMP Header = 28 bytes; 1500 - 28 = 1472)。如果使用 PPPoE 拨号上网,由于 PPP 协议在链路层增加了 8 bytes 的 overhead,能够发送成功的最大 ICMP Payload 大小会降低为 1464。

如果 ping 命令显示 "Frag needed and DF set" 或 "Packet needs to be fragmented but DF set." 之类的错误提示,则说明由于链路 MTU 问题导致了 ICMP 包发送失败。
-------------------------
TCP / UDP Packet
TCP Packet (TCP包) 格式: Header (20-60 bytes,实际一般就是20) + Payload。

UDP Packet UDP包) 格式:Header (8 bytes) + Payload。

理论上 TCP (UDP) 包的 Payload 大小也可以是任意字节,只要整个 TCP / UDP 包大小不超过 65535。但由于实际上 TCP 包必须通过下层的网络层和链路层传输,TCP / UDP 包的 Payload 实际最大大小有限制,对于 TCP 协议,称这个限制为 MSS (Maximum segment size)。

TCP包 header 的可选区域在实际使用中用于放 Tcp Timestamp option 和 SACK。
-------------------------
MSS
MSS 即为 TCP 包 Payload 的最大大小。

MSS = MTU - IP Header Size - TCP Header Size (根据标准,计算 MSS 时,TCP header 按照固定的 20 字节算,不考虑 optional 部分)

对于标准 Ethernet, MTU = 1500, IP Header 和 TCP Header 都是 20 字节,所以 MSS = 1500 - 20 -20 = 1460。

TCP 协议支持通信双方之间的 MSS 协商,取己方 MSS 与对方 MSS 当中的较小值作为己方发送数据包时的 MSS。

通过 PMTUD (Path MTU Discovery),整个通信链路中的任意一个节点都可以改变经过其的 TCP 会话的 MSS。

对于 Linux 而言,一条 iptables 命令就能解决几乎所有 MSS / MTU 相关问题:

iptables -t mangle -A POSTROUTING -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

OpenVPN 可以使用 --mssfix 指令配置自动调节通过 VPN 隧道的 TCP 连接在 MSS 协商时允许的最大值。其值为 OpenVPN 包装后的数据包最大大小,不包含 IPV4/V6 Header 和 TCP/UDP header。默认值 1450。如果 OpenVPN 线路是 IPV6地址并且使用 TCP协议,那么需要修改这个默认值(因为 IPV6 header 40 + TCP header 20 = 60 bytes > 1500 - 1450)。

关于 MTU / MSS 的详细解释,参考这篇文档:Resolve IP Fragmentation, MTU, MSS, and PMTUD Issues with GRE and IPSEC
-------------------------
PORT
TCP / UDP 协议使用一个2字节的无符号整数型 PORT (端口) 字段用于区分设备上运行的不同程序。PORT 范围为 0 - 65535。(其中 "0" 端口特殊,一般不使用)

特权端口(Privileged ports):0-1023。拥有 root 权限的 App 才能监听或绑定本地设备上这些端口。
普通端口:1024-65535。任何 App 都可以监听或绑定(只要端口没有已经被其他 App 占用)。
Ephemeral port
App 作为客户端连接服务器端时,如果没有显式地指定自身绑定的端口号,OS 会自动给其分配一个随机的临时端口,即 Ephemeral port。在很多使用 Berkeley sockets API 的 OS 里,App 显式地绑定(bind) 0 端口会被理解为使用 Ephemeral port,尽管 0 端口本身在 TCP/UDP 协议里是完全合法的端口号。

Ephemeral port 范围:

Linux:32768-61000 (/proc/sys/net/ipv4/ip_local_port_range)
Windows Vista+:49152-65535 (IANA 推荐使用的 Ephemeral port 范围)
Linux
# cat /proc/sys/net/ipv4/ip_local_port_range | hexdump  -C
00000000  33 32 37 36 38 09 36 31  30 30 30 0a              |32768.61000.|
0000000c
0x09 == "\t",0x0a == "\n"
 

评论 COMMENTS
没有评论 No Comments.

添加评论 Add new comment.
昵称 Name:
评论内容 Comment:
验证码(不区分大小写)
Validation Code:
(not case sensitive)
看不清?点这里换一张!(Change it here!)
 
评论由管理员查看后才能显示。the comment will be showed after it is checked by admin.
CopyRight © 心缘地方 2005-2999. All Rights Reserved