路径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中获取当前文件路径和目录的方法,包括使用__file__关键字、os.path.abspath、os.path.realp... 目录1、获取当前文件路径2、获取当前文件所在目录3、os.path.abspath和os.path.re

Python实现局域网远程控制电脑

《Python实现局域网远程控制电脑》这篇文章主要为大家详细介绍了如何利用Python编写一个工具,可以实现远程控制局域网电脑关机,重启,注销等功能,感兴趣的小伙伴可以参考一下... 目录1.简介2. 运行效果3. 1.0版本相关源码服务端server.py客户端client.py4. 2.0版本相关源码1

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

hdu2544(单源最短路径)

模板题: //题意:求1到n的最短路径,模板题#include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#i

poj 1734 (floyd求最小环并打印路径)

题意: 求图中的一个最小环,并打印路径。 解析: ans 保存最小环长度。 一直wa,最后终于找到原因,inf开太大爆掉了。。。 虽然0x3f3f3f3f用memset好用,但是还是有局限性。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#incl

【408DS算法题】039进阶-判断图中路径是否存在

Index 题目分析实现总结 题目 对于给定的图G,设计函数实现判断G中是否含有从start结点到stop结点的路径。 分析实现 对于图的路径的存在性判断,有两种做法:(本文的实现均基于邻接矩阵存储方式的图) 1.图的BFS BFS的思路相对比较直观——从起始结点出发进行层次遍历,遍历过程中遇到结点i就表示存在路径start->i,故只需判断每个结点i是否就是stop

Android Environment 获取的路径问题

1. 以获取 /System 路径为例 /*** Return root of the "system" partition holding the core Android OS.* Always present and mounted read-only.*/public static @NonNull File getRootDirectory() {return DIR_ANDR

控制反转 的种类

之前对控制反转的定义和解释都不是很清晰。最近翻书发现在《Pro Spring 5》(免费电子版在文章最后)有一段非常不错的解释。记录一下,有道翻译贴出来方便查看。如有请直接跳过中文,看后面的原文。 控制反转的类型 控制反转的类型您可能想知道为什么有两种类型的IoC,以及为什么这些类型被进一步划分为不同的实现。这个问题似乎没有明确的答案;当然,不同的类型提供了一定程度的灵活性,但

图的最短路径算法——《啊哈!算法》

图的实现方式 邻接矩阵法 int[][] map;// 图的邻接矩阵存储法map = new int[5][5];map[0] = new int[] {0, 1, 2, 3, 4};map[1] = new int[] {1, 0, 2, 6, 4};map[2] = new int[] {2, 999, 0, 3, 999};map[3] = new int[] {3, 7

深入解析秒杀业务中的核心问题 —— 从并发控制到事务管理

深入解析秒杀业务中的核心问题 —— 从并发控制到事务管理 秒杀系统是应对高并发、高压力下的典型业务场景,涉及到并发控制、库存管理、事务管理等多个关键技术点。本文将深入剖析秒杀商品业务中常见的几个核心问题,包括 AOP 事务管理、同步锁机制、乐观锁、CAS 操作,以及用户限购策略。通过这些技术的结合,确保秒杀系统在高并发场景下的稳定性和一致性。 1. AOP 代理对象与事务管理 在秒杀商品