路径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;
    else
        inet->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);
    else
        iph->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



已标记关键词 清除标记
一、课程简介<br /> <br /> 随着技术的飞速发展,经过多年的数据积累,各互联网公司已保存了海量的原始数据和各种业务数据,所以数据仓库技术是各大公司目前都需要着重发展投入的技术领域。数据仓库是面向分析的集成化数据环境,为企业所有决策制定过程,提供系统数据支持的战略集合。通过对数据仓库中数据的分析,可以帮助企业改进业务流程、控制成本、提高产品质量等。<br /> <br /> 二、课程内容<br /> <br /> 本次精心打造的数仓项目的课程,从项目架构的搭建,到数据采集模块的设计、数仓架构的设计、实战需求实现、即席查询的实现,我们针对国内目前广泛使用的Apache原生框架和CDH版本框架进行了分别介绍,Apache原生框架介绍中涉及到的技术框架包括Flume、Kafka、Sqoop、MySql、HDFS、Hive、Tez、Spark、Presto、Druid等,CDH版本框架讲解包括CM的安装部署、Hadoop、Zookeeper、Hive、Flume、Kafka、Oozie、Impala、HUE、Kudu、Spark的安装配置,透彻了解不同版本框架的区别联系,将大数据全生态系统前沿技术一网打尽。在过程中对大数据生态体系进行了系统的讲解,对实际企业数仓项目中可能涉及到的技术点都进行了深入的讲解和探讨。同时穿插了大量数仓基础理论知识,让你在掌握实战经验的同时能够打下坚实的理论基础。<br /> <br /> <br /> 三、课程目标<br /> <br /> 本课程以国内电商巨头实际业务应用场景为依托,对电商数仓的常见实战指标以及难点实战指标进行了详尽讲解,具体指标包括:每日、周、月活跃设备明细,留存用户比例,沉默用户、回流用户、流失用户统计,最近连续3周活跃用户统计,最近7天内连续3天活跃用户统计,GMV成交总额分析,转化率及漏斗分析,品牌复购率分析、订单表拉链表的设计等,让学生拥有更直观全面的实战经验。通过对本课程的学习,对数仓项目可以建立起清晰明确的概念,系统全面的掌握各项数仓项目技术,轻松应对各种数仓难题。<br /> <br /> 四、课程亮点<br /> 本课程结合国内多家企业实际项目经验,特别加入了项目架构模块,从集群规模的确定到框架版本选型以及服务器选型,手把手教你从零开始搭建大数据集群。并且总结大量项目实战中会遇到的问题,针对各个技术框架,均有调优实战经验,具体包括:常用Linux运维命令、Hadoop集群调优、Flume组件选型及性能优化、Kafka集群规模确认及关键参数调优。通过这部分学习,助学生迅速成长,获取前沿技术经验,从容解决实战问题。<br /> <br /> <br /> <div> <br /> </div>
相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页