制作网站软件免费,佛山seo整站优化承接,织梦企业黄页网站源码,百度搜索如何去广告文章目录 1. 前言2. Path MTU Discovery(PMTUD) 协议2.1 PMTUD 发现最小 MTU 的过程 3. Linux 的 PMTUD 简析3.1 创建 socket 时初始化 PMTUD 模式3.2 数据发送时 PMTUD 相关处理3.2.1 源头主机发送过程中 PMTUD 处理3.2.2 转发过程中 PMTUD 处理 4. PMTUD 观察5. 参考链接 1. … 文章目录 1. 前言2. Path MTU Discovery(PMTUD) 协议2.1 PMTUD 发现最小 MTU 的过程 3. Linux 的 PMTUD 简析3.1 创建 socket 时初始化 PMTUD 模式3.2 数据发送时 PMTUD 相关处理3.2.1 源头主机发送过程中 PMTUD 处理3.2.2 转发过程中 PMTUD 处理 4. PMTUD 观察5. 参考链接 1. 前言
限于作者能力水平本文可能存在谬误因此而给读者带来的损失作者不做任何承诺。
2. Path MTU Discovery(PMTUD) 协议
在说明 Path MTU Discovery(PMTUD) 之前先得说说 MTU(Maximum Transmission Unit) 。什么是 MTU(Maximum Transmission Unit) MTU 是网卡的最大传输单元即网卡一次最多传输数据的字节数这是一个网卡硬件的参数。当数据从 IP 层 向下传递到 数据链路层 时如果发现 IP 数据包的长度 大于 网卡的 MTU 就需要将 IP 数据包 进程 分片(在 IP 协议没有设置 DF 标志位时)以适应网卡的 MTU。需要知道的是MTU 限定的是 IP 层 向下传递数据的最大长度这并不包含以太网帧头和帧尾长度在内。 说完了 MTU 接下来说说本篇的主角 Path MTU Discovery(PMTUD) 。数据在传输过程中可能经过多个各种类型的网络数据的传输介质如交换机、路由器等下图给出一个简单的示例 从上图中看到数据的源头和目的设备的 MTU 均为 1500而中间的路由设备的 MTU 为 576 也就是说数据经过的各种传输媒介它们各自可能拥有不同的 MTU 值这就意味着数据帧经过不同 MTU 的设备时要进行分片(从 更大 MTU 设备 到 更小 MTU 的设备)、组包(从更小 MTU 设备 到 更大 MTU 设备)。这样的不停分片、组包需要开辟额外的缓存进行数据排队通常来说对于网络传输效率是不利的(尤其是交换机这类设备)更不要说丢包等情形的处理。为了适应这种不同设备具有 MTU 的情形引入了 Path MTU Discovery(PMTUD) 协议协议 RFC 编号为 RFC1191 该协议用来发现网络数据传输整个路径中的 最小 MTU然后数据传输路径中所有设备使用这个 最小 MTU 来传输数据因此所有的 IP 数据 都可以不用进行分片以期达到更大的传输效率。这个 最小 MTU 有个名目叫做 PMTU(Path MTU) 。
2.1 PMTUD 发现最小 MTU 的过程
上面说了Path MTU Discovery(PMTUD) 用来发现传输路径中的 最小 MTU 那是如何发现的呢过程也不复杂就是在传输 IP 数据 的时候发送端设置 DF(Dont Fragment) 标记如下图 然后如果接收端发现接收的 IP 数据的长度超过自己的 MTU 同时 IP 数据设置了 DF 标记则接收端回复发送端一个 Type3,Code4 的 ICMP 消息表示 Destination Unreachable Message, fragmentation needed and DF set 告知发送端数据太长被丢弃需要进行分片回送的 ICMP 消息也会带上接收端的自己 MTU发送端接收到 ICMP 消息后缓存接收端回送的 MTU 值然后调整数据长度重新进行发送。ICMP 协议数据格式如下 0 1 2 30 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7--------------------------------| Type | Code | Checksum |--------------------------------| unused |--------------------------------| Internet Header 64 bits of Original Data Datagram |--------------------------------
更多关于 ICMP(Internet Control Message Protocol) 的细节可参考 RFC792 。 应该了解的是Path MTU Discovery(PMTUD) 协议只适用于 TCP 和 UDP 协议。另外并非所有设备都支持 PMTUD 协议不支持 PMTUD 协议的设备在收到大于自身 MTU 长度、且设置了 DF 标志的 IP 数据时回送给发送端的 ICMP 消息里不会包含其 MTU 值此时发送端需要采取某种策略来处理这种情形。RFC1191 协议的章节 5 对此给出了一些建议性的方案但这些建议并非 RFC1191 协议的一部分感兴趣的读者可自行阅读RFC1191 协议的章节 5以了解更多的相关细节。
3. Linux 的 PMTUD 简析
首先本文分析以 Linux 4.14 内核代码为背景进行分析。Linux 下默认开启 Path MTU Discovery(PMTUD) 功能。另外可以通过文件节点 /proc/sys/net/ipv4/ip_no_pmtu_disc 来开启或关闭 Path MTU Discovery(PMTUD) 向文件写 0 开启 PMTUD写非零值(1-3)关闭 PMTUD 。 本文只讨论 IPv4 协议栈下 Path MTU Discovery(PMTUD) 开启的情形对其它情形感兴趣的读者可自行阅读源码进行分析。
3.1 创建 socket 时初始化 PMTUD 模式
socket()...inet_create() // net/ipv4/af_inet.c...if (net-ipv4.sysctl_ip_no_pmtu_disc)...else /* 开启 PMTUD 的情形 */inet-pmtudisc IP_PMTUDISC_WANT;...
当然内核也提供了接口修改 socket 的 PMTUD 的配置。如
on IP_PMTUDISC_PROBE;
setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, on, sizeof(on));
3.2 数据发送时 PMTUD 相关处理
要发送的数据当前可能有两种情形
情形1当前正从源头主机往外发送
情形2当前数据正经过某中间设备(譬如路由器)往外转发
下面分别对这两种情形下和 PMTUD 协议相关的处理部分。
3.2.1 源头主机发送过程中 PMTUD 处理
// net/ipv4/ip_output.cip_queue_xmit()...
packet_routed:if (ip_dont_fragment(sk, rt-dst) !skb-ignore_df) /* 不允许对 IP 数据分片 */iph-frag_off htons(IP_DF); /* 标记 DF */else......res ip_local_out(net, sk, skb); /* 将数据包传递给网络设备 */...
接收端设备收到数据后如果发现大于自己的 MTU 且设置了 DF(Dont Fragment) 标记则回送 Type3,Code4 的 ICMP 消息
// net/ipv4/ip_output.cip_finish_output().../* 包长度大于本机 MTU, 进行分片处理 */if (skb-len mtu || (IPCB(skb)-flags IPSKB_FRAG_PMTU))return ip_fragment(net, sk, skb, mtu, ip_finish_output2);struct iphdr *iph ip_hdr(skb);if ((iph-frag_off htons(IP_DF)) 0) /* 允许 IP 数据 分片 */.../* 不允许 IP 数据 分片(设置了 IP_DF 标记) */if (unlikely(!skb-ignore_df ||(IPCB(skb)-frag_max_size IPCB(skb)-frag_max_size mtu)/*IP 分片 的 长度大于 MTU*/)) {IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS);/** IP 分片长度 超过 MTU 禁止分片, * 则给本地 socket 发送 ICMP 的 {ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED} 包,* 告知其包将不被发送 (IP 数据 由本地 socket 往外发送发不出去就回送* 给 socket 回 ICMP 的 {ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED} 包 告知 socket).*/icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));kfree_skb(skb);return -EMSGSIZE;}...
发送端收到 Type3,Code4 的 ICMP 消息 后更新缓存 PMTU
// include/uapi/linux/icmp.hstruct icmphdr { /* ICMP 数据报头部格式: https://datatracker.ietf.org/doc/html/rfc792 */__u8 type;__u8 code;__sum16 checksum;union {struct {__be16 id;__be16 sequence;} echo;__be32 gateway;struct {__be16 __unused;__be16 mtu; // type3, code4 时回送接收端的 MTU} frag;__u8 reserved[4];} un;
};// net/ipv4/icmp.c
static bool icmp_unreach(struct sk_buff *skb)
{const struct iphdr *iph;struct icmphdr *icmph;...icmph icmp_hdr(skb);iph (const struct iphdr *)skb-data;...switch (icmph-type) {case ICMP_DEST_UNREACH:switch (icmph-code 15) {...case ICMP_FRAG_NEEDED:switch (net-ipv4.sysctl_ip_no_pmtu_disc) {...case 0:info ntohs(icmph-un.frag.mtu); /* 解析 接收端回传 的 MTU */}}}...icmp_socket_deliver(skb, info);...ipprot rcu_dereference(inet_protos[protocol]);if (ipprot ipprot-err_handler)ipprot-err_handler(skb, info); /* tcp_v4_err() */tcp_v4_err()...
}// net/ipv4/tcp_ipv4.c
void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
{...const int type icmp_hdr(icmp_skb)-type;const int code icmp_hdr(icmp_skb)-code;...switch (type) {...case ICMP_DEST_UNREACH:...if (code ICMP_FRAG_NEEDED) { /* PMTU discovery (RFC1191) */...tp-mtu_info info;if (!sock_owned_by_user(sk)) {tcp_v4_mtu_reduced(sk);} else {...}goto out;}...}...
out:...
}void tcp_v4_mtu_reduced(struct sock *sk)
{...u32 mtu;...mtu tcp_sk(sk)-mtu_info; /* 接收端 回送 的 MTU */dst inet_csk_update_pmtu(sk, mtu);...mtu dst_mtu(dst);if (inet-pmtudisc ! IP_PMTUDISC_DONT ip_sk_accept_pmtu(sk) inet_csk(sk)-icsk_pmtu_cookie mtu) {tcp_sync_mss(sk, mtu); /* MSS 同步 *//* Resend the TCP packet because its* clear that the old packet has been* dropped. This is the new fast path mtu* discovery.*/tcp_simple_retransmit(sk); /* 数据重传 */}
}
3.2.2 转发过程中 PMTUD 处理
// net/ipv4/ip_forward.cint ip_forward(struct sk_buff *skb)
{...IPCB(skb)-flags | IPSKB_FORWARDED;mtu ip_dst_mtu_maybe_forward(rt-dst, true);if (ip_exceeds_mtu(skb, mtu)) { /* 转发的 skb 的 数据长度 超过 MTU */IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS);/* * 当前 skb 正经过 【交换机】 或 【路由器 上】 进行 转发, 当* 【 skb 的 数据长度 超过 MTU 】 【 数据源头设定不允许分片(DF1) 】 * 时, 给数据发送源头回送 ICMP 包 {ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED}* 数据将被丢弃.*/icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));goto drop;}
}
数据发送源收到 Type3,Code4 的 ICMP 消息 后的处理和 3.2.1 处理一样。
4. PMTUD 观察
ifconfig 等工具可看到网卡配置的 MTU
$ ifconfig ens33
ens33 Link encap:Ethernet HWaddr 00:0c:29:4f:b1:e7 inet addr:192.168.0.9 Bcast:192.168.0.255 Mask:255.255.255.0inet6 addr: fe80::bbc7:b835:be2a:a578/64 Scope:LinkUP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1RX packets:2077 errors:0 dropped:0 overruns:0 frame:0TX packets:775 errors:0 dropped:0 overruns:0 carrier:0collisions:0 txqueuelen:1000 RX bytes:1684142 (1.6 MB) TX bytes:74056 (74.0 KB)
用 ping 发送超过 MTU 的数据包且禁止 IP 分片
$ ping www.baidu.com -s 2000 -M do
PING www.baidu.com (183.2.172.185) 2000(2028) bytes of data.
ping: local error: Message too long, mtu1500
我们可以通过 tracepath 工具来跟踪数据发送超 MTU 时接收设备回送的 ICMP 包
$ tracepath www.baidu.com1?: [LOCALHOST] pmtu 15001: 192.168.0.1 43.888ms 1: 192.168.0.1 2.902ms 2: 192.168.1.1 37.109ms 3: 192.168.1.1 117.816ms pmtu 14923: 100.64.0.1 33.586ms 4: 61.146.242.189 33.665ms 5: 177.107.38.59.broad.fs.gd.dynamic.163data.com.cn 39.025ms 6: 113.96.5.38 54.439ms 7: no reply8: 121.14.67.174 64.413ms 9: 182.61.216.71 39.233ms
用 tcpdump 工具抓回送的 ICMP 包
$ sudo tcpdump icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes
......
16:17:26.350958 IP 192.168.1.1 192.168.0.9: ICMP time exceeded in-transit, length 556
16:17:26.421870 IP 192.168.1.1 192.168.0.9: ICMP 183.2.172.185 unreachable - need to frag (mtu 1492), length 556
再来用 WireShark 的观察一下抓到的数据包
5. 参考链接
[1] https://packetlife.net/blog/2008/aug/18/path-mtu-discovery/#:~:textRFC%201191%20defines%20path%20MTU%20discovery%2C%20a%20simple,of%20the%20ICMP%20Destination%20Unreachable%20message%2C%20Fragmentation%20Needed. [2] https://www.rfc-editor.org/rfc/rfc1191 [3] https://datatracker.ietf.org/doc/html/rfc792