Linux 网络:PMTUD 简介

文章目录

  • 1. 前言
  • 2. Path MTU Discovery(PMTUD) 协议
    • 2.1 PMTUD 发现最小 MTU 的过程
  • 3. Linux 的 PMTUD 简析
    • 3.1 创建 socket 时初始化 PMTUD 模式
    • 3.2 数据发送时 PMTUD 相关处理
      • 3.2.1 源头主机发送过程中 PMTU 处理
      • 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,而中间的路由设备的 MTU576 ,也就是说,数据经过的各种传输媒介,它们各自可能拥有不同的 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(Don't Fragment) 标记,如下图:
在这里插入图片描述
然后数据接收端如果发现接收的 IP 数据的长度超过自己的 MTU ,则回复发送端一个 Type=3,Code=4 的 ICMP 消息,表示 Destination Unreachable Message, fragmentation needed and DF set ,告知发送端数据太长,需要进行分片,同时带上接收端的自己 MTU;发送端接收到 ICMP 消息后,缓存接收端会送的 MTU 值,然后调整数据重新进行发送。更多关于 ICMP(Internet Control Message Protocol) 的细节可参考 RFC792 。
应该了解的是,Path MTU Discovery(PMTUD) 协议只适用于 TCPUDP 协议。

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 源头主机发送过程中 PMTU 处理

// 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(Don't Fragment) 标记,则会送 Type=3,Code=4 的 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;}...

发送端收到 Type=3,Code=4 的 ICMP 消息 后更新缓存 PMTU

// 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 it's* 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 】 && 【 数据源头设定不允许分片(DF=1) 】 * 时, 给数据发送源头回送 ICMP 包 {ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED}* 数据将被丢弃.*/icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));goto drop;}
}

数据发送源收到 Type=3,Code=4 的 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, mtu=1500

我们可以通过 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/#:~:text=RFC%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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/194433.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

配置VNC环境时,出现xauth: file /root/.Xauthority does not exist的解决方案。

问题描述 在配置VNC(Virtual Network Computing)环境的过程时,首先安装了tigervnc-server包。在使用: vncserver命令创建VNC会话号的时候出现了一个报错:xauth: file /root/.Xauthority does not exist 原因分析&…

【Linux进阶之路】动静态库

文章目录 回顾一. 静态库1.代码传递的方式2.简易制作3.原理 二. 动态库1.简易制作2.基本原理 尾序 回顾 前面在gcc与g的使用中,我们简单的介绍了动态库与静态库的各自的优点与区别: 动态链接库,也就是所有的程序公用一份代码,虽然方便省空间&…

SpringBoot 整合 JdbcTemplate

数据持久化有几个常见的方案,有 Spring 自带的 JdbcTemplate 、有 MyBatis,还有 JPA,在这些方案中,最简单的就是 Spring 自带的 JdbcTemplate 了,这个东西虽然没有 MyBatis 那么方便,但是比起最开始的 Jdbc…

Go——一、Go语言安装及介绍

Go 一、Windows下安装Go1、下载Go2、配置环境变量3、下载Jetbrain下的GoLang4、编写hello world5、编译和执行 二、Go语言介绍1、开发文档2、Go语言核心开发团队3、为什么要创建Go4、Go语言发展史5、Go语言特点6、Golang执行过程6.1 执行过程分析6.2 编译是什么 7、开发注意事项…

RobotFramework进阶之自定义的python模块(十四)

引言 RobotFramework自动化框架(以下简称RF)之前文章我们讲了通过import第三方的library(RequestsLibrary等),在实际项目中第三方的包并不能满足我们的需要,此时我们可自己编写python模块(.py文…

算法通关村第十六关白银挑战——滑动窗口高频问题

关注微信公众号:怒码少年。回复关键词【电子书】,领取多本计算机相关电子书 公众号后台开启了咨询业务,欢迎大家向我提问,免费,为爱发电😎 大家好,我是怒码少年小码。 武汉今天真的超级冷&…

pr出现由于找不到msvcp110.dll,无法继续执行代码怎么办?如何修复

为什么我们在打开运行电脑软件会出现msvcr110.dll无法继续执行此代码的问题呢?因为msvcr110.dll是Microsoft Visual C Redistributable Package for Visual Studio 2013的一个动态链接库。它是一个重要的组件,用于帮助游戏和软件运行。如果某个程序是用它…

volatile 无法保证原子性 案例展示

volatile 无法保证原子性 在 Java 中,原子性是指一个操作是不可中断的,要么都执行要么都不执行。 但是 volatile 修饰的变量,只是保证了从主内存加载到工作内存的值是最新的,并不能保证对变量的操作是原子性的 变量的写操作和读…

OS 进程同步

基本概念 定义:把异步环境下的一组并发进程因直接制约而相互发送消息、相互合作、相互等待,使得各进程按一定的速度执行的过程,称为进程同步 协作进程:具有同步关系的一组并发进程 进程同步机制的主要任务:在执行次…

laravel-admin导出excel全部,表中无id列导出失败

laravel-admin导出excel时,导出全部数据,但是表中没有id字段,然后就无法导出excel; 就直接显示 一开始我也很着急,弄了半天还是不行,然后重写还是有问题 最后发现底层代码排序是按照id排序的orderBy(id, a…

SPASS-偏相关分析

基本概念 偏相关分析的任务就是在研究两个变量之间的线性相关关系时控制可能对其产生影响的变量,这种相关系数称为偏相关系数。偏相关系数的数值和简单相关系数的数值常常是不同的,在计算简单相关系数时,所有其他自变量不予考虑。 统计原理 控制一个变量和控制两个变量的偏…

数据结构与算法-图

图 🎈2.图的存储结构📖2.4.2邻接表的存储✅2.4.2.1逆邻接表✅2.4.2.2邻接表存储结构的定义✅2.4.2.3邻接表存储结构的类定义✅2.4.2.4创建n个顶点m条边的无向网✅2.4.2.5创建n个顶点m条边的有向网✅2.4.2.6定位操作-查找定点信息在顶点数组中的下标✅2.4…