路径MTU(PMTU)发现控制与DF位

2023-12-19 10:58
文章标签 路径 发现 控制 mtu df pmtu

本文主要是介绍路径MTU(PMTU)发现控制与DF位,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

路径MTU发现是用来确定到达目的地的路径中最大传输单元(MTU)的大小。通过在IP报头中设置不分片DF(Don't Fragment)标志来探测路径中的MTU值, 如果路径中设备的MTU值小于此报文长度,并且发现DF标志,就会发回一个Internet控制消息协议(ICMP)(类型3、代码4需要分片的消息ICMP_FRAG_NEEDED),消息中包含它可接受的MTU值。


PMTU发现控制模式

#define IP_PMTUDISC_DONT        0   /* Never send DF frames */
#define IP_PMTUDISC_WANT        1   /* Use per route hints     */
#define IP_PMTUDISC_DO             2   /* Always DF                   */
#define IP_PMTUDISC_PROBE       3   /* Ignore dst pmtu         */
#define IP_PMTUDISC_INTERFACE   4   /* 使用出接口的设备MTU值 */
#define IP_PMTUDISC_OMIT            5   /* 忽略DF位 */

IP_PMTUDISC_DONT策略表示从不设置DF位,即不进行PMTU发现(参见函数ip_dont_fragment)。
IP_PMTUDISC_WANT策略根据路由中表项是否锁定了MTU,来决定是否设置DF位,如锁定,不设置DF位。
IP_PMTUDISC_DO策略总是设置DF位,除非内核设置了忽略df(ignore_df),参见以下内容。
IP_PMTUDISC_INTERFACE策略不设置DF位,不发送设置了DF位并且长度超过出接口设备MTU的数据包。
IP_PMTUDISC_OMIT策略与IP_PMTUDISC_INTERFACE策略含义相同,唯一区别在于,即使数据包设置了DF位,内核也会对长度超过出接口设备MTU的数据包进行分片处理并发送。


PMTU默认策略

通过porc文件ip_no_pmtu_disc 设置pmtu的全局默认策略(/proc/sys/net/ipv4/ip_no_pmtu_disc),在sock创建时根据此值初始化pmtudisc变量。内核初始将其设置为0,即系统缺省的pmtu策略为IP_PMTUDISC_WANT,尝试进行pmtu发现。如果置0,不进行pmtu发现(IP_PMTUDISC_DONT),系统不发送IP头带有DF标志的报文。

static int inet_create(struct net *net, struct socket *sock, int protocol, int kern)
{struct inet_sock *inet;//初始化sock中的pmtudisc值。if (net->sysctl_ip_no_pmtu_disc)inet->pmtudisc = IP_PMTUDISC_DONT;elseinet->pmtudisc = IP_PMTUDISC_WANT;
}

使能PMTU路径发现(DF标志置位)


如前所述IP报头的DF标志,用于PMTU发现,由ip_queue_xmit函数可知,在满足以下两个条件之一,
1)pmtudisc等于IP_PMTUDISC_DO;
2)或者pmtudisc等于IP_PMTUDISC_WANT,并且mtu没有在路由表中锁定。

而且内核没有设置忽略DF的条件下,设置IP报头的DF标志位,进行PMTU发现操作。IP_PMTUDISC_DO为使能PMTU发现策略,IP_PMTUDISC_WANT会根据mtu是否锁定进行pmtu发现。

路径发现策略pmtudisc也可设置为IP_PMTUDISC_PROBE,此策略下只有在发送聚合了的分片报文的情况下设置DF位,此发送流程不经过ip_queue_xmit,所以不与前面两个策略冲突。

int ip_dont_fragment(struct sock *sk, struct dst_entry *dst)
{return  inet_sk(sk)->pmtudisc == IP_PMTUDISC_DO ||(inet_sk(sk)->pmtudisc == IP_PMTUDISC_WANT && !(dst_metric_locked(dst, RTAX_MTU)));
}
int ip_queue_xmit(struct sk_buff *skb, struct flowi *fl)
{if (ip_dont_fragment(sk, &rt->dst) && !skb->ignore_df)iph->frag_off = htons(IP_DF);elseiph->frag_off = 0;
}struct sk_buff *__ip_make_skb(...)
{if (inet->pmtudisc == IP_PMTUDISC_DO ||inet->pmtudisc == IP_PMTUDISC_PROBE ||(skb->len <= dst_mtu(&rt->dst) && ip_dont_fragment(sk, &rt->dst)))df = htons(IP_DF);
}

sock结构体中pmtudisc变量可通过setsockopt系统调用进行设置。用户也可使用ip命令对MTU值进行锁定,不允许进行修改,如下命令,锁定lock到网关192.168.1.1的mtu值为1300字节大小:

static int do_ip_setsockopt(...)
{struct inet_sock *inet = inet_sk(sk);case IP_MTU_DISCOVER:if (val < IP_PMTUDISC_DONT || val > IP_PMTUDISC_OMIT)goto e_inval;inet->pmtudisc = val;
}ip route add 0.0.0.0 via 192.168.1.1 mtu lock 1300

关闭路径MTU发现

路径mtu发现策略设置为IP_PMTUDISC_INTERFACE或者IP_PMTUDISC_OMIT的时候,内核都不会保存ICMP消息中发送的新MTU值,ipv4_sk_update_pmtu函数判断之后直接返回。需要注意的是,除去这两种PMTU策略外,其它情况下,内核还是会保存通过ICMP接收到的新MTU值,这样在用户之后修改pmtu策略后能马上生效。另外在策略配置为非IP_PMTUDISC_DONT时,设置sock错误标志。

static inline bool ip_sk_accept_pmtu(const struct sock *sk)
{return inet_sk(sk)->pmtudisc != IP_PMTUDISC_INTERFACE &&inet_sk(sk)->pmtudisc != IP_PMTUDISC_OMIT;
}
void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu)
{if (!ip_sk_accept_pmtu(sk))goto out;
}
void __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable)
{case ICMP_DEST_UNREACH:if (code == ICMP_FRAG_NEEDED) { /* Path MTU discovery */ipv4_sk_update_pmtu(skb, sk, info);if (inet->pmtudisc != IP_PMTUDISC_DONT) {err = EMSGSIZE;harderr = 1;break;}}
}

强制关闭PMTU发现(忽略DF位)


在ip_queue_xmit函数中看到,如果skb->ignore_df为真,就会清除IP报头的DF位,ignore_df变量有函数ip_sk_ignore_df赋值。当pmtudisc策略设置成IP_PMTUDISC_DONT、IP_PMTUDISC_WANT或者IP_PMTUDISC_OMIT的时候,ignore_df变量为真,内核将会在发出的报文中清除DF标志位。

static inline bool ip_sk_ignore_df(const struct sock *sk)
{return inet_sk(sk)->pmtudisc < IP_PMTUDISC_DO ||inet_sk(sk)->pmtudisc == IP_PMTUDISC_OMIT;
}


在分片处理函数中,对于本机产生的报文,在发送时即便设置了不允许分片DF标志位,只要ignore_df为真,强制进行分片。对于转发的报文,如果设置了DF位,同时接收的时候就是一个已经经过分片的报文,内核进行了重组,但是原始报文的最大分片值大于出接口的mtu值,此时不分片,回复ICMP消息ICMP_FRAG_NEEDED,告知对方内核的MTU值。

static int ip_fragment(...)
{if ((iph->frag_off & htons(IP_DF)) == 0)return ip_do_fragment(sk, skb, output);if (unlikely(!skb->ignore_df ||(IPCB(skb)->frag_max_size && IPCB(skb)->frag_max_size > mtu))) {icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));return -EMSGSIZE;}return ip_do_fragment(sk, skb, output);
}


测试PMTU策略

使用ping命令即可测试PMTU策略:

ping 
       -M pmtudisc_opt
           Select Path MTU Discovery strategy.  pmtudisc_option may be
           either do (prohibit fragmentation, even local one), want (do PMTU
           discovery, fragment locally when packet size is large), or dont
           (do not set DF flag).

例如发送长度超过超过MTU值(1500)的数据包,并且设置IP头的DF位,系统提示message too long:

ping -c 3 -s 1473 -M do 192.168.1.133
PING 192.168.1.133 (192.168.1.133) 1473(1501) bytes of data.
ping: local error: Message too long, mtu=1500
ping: local error: Message too long, mtu=1500
ping: local error: Message too long, mtu=1500

--- 192.168.1.133 ping statistics ---

3 packets transmitted, 0 received, +3 errors, 100% packet loss, time 1999ms


内核版本

linux-3.10.0



这篇关于路径MTU(PMTU)发现控制与DF位的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/511911

相关文章

用js控制视频播放进度基本示例代码

《用js控制视频播放进度基本示例代码》写前端的时候,很多的时候是需要支持要网页视频播放的功能,下面这篇文章主要给大家介绍了关于用js控制视频播放进度的相关资料,文中通过代码介绍的非常详细,需要的朋友可... 目录前言html部分:JavaScript部分:注意:总结前言在javascript中控制视频播放

SpringCloud之consul服务注册与发现、配置管理、配置持久化方式

《SpringCloud之consul服务注册与发现、配置管理、配置持久化方式》:本文主要介绍SpringCloud之consul服务注册与发现、配置管理、配置持久化方式,具有很好的参考价值,希望... 目录前言一、consul是什么?二、安装运行consul三、使用1、服务发现2、配置管理四、数据持久化总

Python异步编程中asyncio.gather的并发控制详解

《Python异步编程中asyncio.gather的并发控制详解》在Python异步编程生态中,asyncio.gather是并发任务调度的核心工具,本文将通过实际场景和代码示例,展示如何结合信号量... 目录一、asyncio.gather的原始行为解析二、信号量控制法:给并发装上"节流阀"三、进阶控制

Linux修改pip和conda缓存路径的几种方法

《Linux修改pip和conda缓存路径的几种方法》在Python生态中,pip和conda是两种常见的软件包管理工具,它们在安装、更新和卸载软件包时都会使用缓存来提高效率,适当地修改它们的缓存路径... 目录一、pip 和 conda 的缓存机制1. pip 的缓存机制默认缓存路径2. conda 的缓

使用DrissionPage控制360浏览器的完美解决方案

《使用DrissionPage控制360浏览器的完美解决方案》在网页自动化领域,经常遇到需要保持登录状态、保留Cookie等场景,今天要分享的方案可以完美解决这个问题:使用DrissionPage直接... 目录完整代码引言为什么要使用已有用户数据?核心代码实现1. 导入必要模块2. 关键配置(重点!)3.

SpringSecurity 认证、注销、权限控制功能(注销、记住密码、自定义登入页)

《SpringSecurity认证、注销、权限控制功能(注销、记住密码、自定义登入页)》SpringSecurity是一个强大的Java框架,用于保护应用程序的安全性,它提供了一套全面的安全解决方案... 目录简介认识Spring Security“认证”(Authentication)“授权” (Auth

python之流程控制语句match-case详解

《python之流程控制语句match-case详解》:本文主要介绍python之流程控制语句match-case使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录match-case 语法详解与实战一、基础值匹配(类似 switch-case)二、数据结构解构匹

Windows系统下如何查找JDK的安装路径

《Windows系统下如何查找JDK的安装路径》:本文主要介绍Windows系统下如何查找JDK的安装路径,文中介绍了三种方法,分别是通过命令行检查、使用verbose选项查找jre目录、以及查看... 目录一、确认是否安装了JDK二、查找路径三、另外一种方式如果很久之前安装了JDK,或者在别人的电脑上,想

Python中Windows和macOS文件路径格式不一致的解决方法

《Python中Windows和macOS文件路径格式不一致的解决方法》在Python中,Windows和macOS的文件路径字符串格式不一致主要体现在路径分隔符上,这种差异可能导致跨平台代码在处理文... 目录方法 1:使用 os.path 模块方法 2:使用 pathlib 模块(推荐)方法 3:统一使

Spring Security注解方式权限控制过程

《SpringSecurity注解方式权限控制过程》:本文主要介绍SpringSecurity注解方式权限控制过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、摘要二、实现步骤2.1 在配置类中添加权限注解的支持2.2 创建Controller类2.3 Us