路径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

相关文章

python获取指定名字的程序的文件路径的两种方法

《python获取指定名字的程序的文件路径的两种方法》本文主要介绍了python获取指定名字的程序的文件路径的两种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要... 最近在做项目,需要用到给定一个程序名字就可以自动获取到这个程序在Windows系统下的绝对路径,以下

SpringBoot 多环境开发实战(从配置、管理与控制)

《SpringBoot多环境开发实战(从配置、管理与控制)》本文详解SpringBoot多环境配置,涵盖单文件YAML、多文件模式、MavenProfile分组及激活策略,通过优先级控制灵活切换环境... 目录一、多环境开发基础(单文件 YAML 版)(一)配置原理与优势(二)实操示例二、多环境开发多文件版

SpringBoot路径映射配置的实现步骤

《SpringBoot路径映射配置的实现步骤》本文介绍了如何在SpringBoot项目中配置路径映射,使得除static目录外的资源可被访问,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一... 目录SpringBoot路径映射补:springboot 配置虚拟路径映射 @RequestMapp

Spring Boot集成/输出/日志级别控制/持久化开发实践

《SpringBoot集成/输出/日志级别控制/持久化开发实践》SpringBoot默认集成Logback,支持灵活日志级别配置(INFO/DEBUG等),输出包含时间戳、级别、类名等信息,并可通过... 目录一、日志概述1.1、Spring Boot日志简介1.2、日志框架与默认配置1.3、日志的核心作用

python设置环境变量路径实现过程

《python设置环境变量路径实现过程》本文介绍设置Python路径的多种方法:临时设置(Windows用`set`,Linux/macOS用`export`)、永久设置(系统属性或shell配置文件... 目录设置python路径的方法临时设置环境变量(适用于当前会话)永久设置环境变量(Windows系统

Spring Boot中的路径变量示例详解

《SpringBoot中的路径变量示例详解》SpringBoot中PathVariable通过@PathVariable注解实现URL参数与方法参数绑定,支持多参数接收、类型转换、可选参数、默认值及... 目录一. 基本用法与参数映射1.路径定义2.参数绑定&nhttp://www.chinasem.cnbs

浅析Spring如何控制Bean的加载顺序

《浅析Spring如何控制Bean的加载顺序》在大多数情况下,我们不需要手动控制Bean的加载顺序,因为Spring的IoC容器足够智能,但在某些特殊场景下,这种隐式的依赖关系可能不存在,下面我们就来... 目录核心原则:依赖驱动加载手动控制 Bean 加载顺序的方法方法 1:使用@DependsOn(最直

Spring如何使用注解@DependsOn控制Bean加载顺序

《Spring如何使用注解@DependsOn控制Bean加载顺序》:本文主要介绍Spring如何使用注解@DependsOn控制Bean加载顺序,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录1.javascript 前言2. 代码实现总结1. 前言默认情况下,Spring加载Bean的顺

基于Python开发Windows屏幕控制工具

《基于Python开发Windows屏幕控制工具》在数字化办公时代,屏幕管理已成为提升工作效率和保护眼睛健康的重要环节,本文将分享一个基于Python和PySide6开发的Windows屏幕控制工具,... 目录概述功能亮点界面展示实现步骤详解1. 环境准备2. 亮度控制模块3. 息屏功能实现4. 息屏时间

mtu设置多少网速最快? 路由器MTU设置最佳网速的技巧

《mtu设置多少网速最快?路由器MTU设置最佳网速的技巧》mtu设置多少网速最快?想要通过设置路由器mtu获得最佳网速,该怎么设置呢?下面我们就来看看路由器MTU设置最佳网速的技巧... 答:1500 MTU值指的是在网络传输中数据包的最大值,合理的设置MTU 值可以让网络更快!mtu设置可以优化不同的网