- RFC 1191
- man 7 ip
结论
- ICMP packet too big
对ICMP packet too big的处理,TCP/UDP最终都对对应路由条目进行MTU更新。而TCP的MTU、MSS、窗口大小和最终段大小的关系比较复杂,目前没有看明白。 - Don’t fragment
DF位的默认配置,受系统配置控制,Ubuntu 14.04默认开启MTU发现功能。推断结果:UDP大报文默认未置上,而TCP大报文默认置上。
Linux内核如何处理ICMP Packet too big
ICMP流程
报文类型:1
223 #define ICMP_DEST_UNREACH 3 /* Destination Unreachable */
43 #define ICMP_FRAG_NEEDED 4 /* Fragmentation Needed/DF set */
icmp_rcv(struct sk_buff *skb),根据ICMP类型处理skb:1
icmp_pointers[icmph->type].handler(skb);
icmp_pointers的定义显示,ICMP_DEST_UNRAECH的handler为icmp_unreach,后者获取出ICMP头部的mtu后,投递给icmp_socket_deliver(skb, mtu)。icmp_socket_deliver
最终将skb和mtu值投递给ipprot->err_handler(skb, info);
TCP流程
tcp_v4_err(skb, info),
调用tcp_v4_mtu_reduced(struct sock *sk)
更新路由的MTU,inet_csk_update_pmtu(struct sock *sk, u32 mtu)
找到路由inet_csk_rebuild_route(sk, &inet->cork.fl);
更新路由MTUdst->ops->update_pmtu(dst, sk, NULL, mtu);
tcp_v4_err
,如果MTU变小,且可分片,则会tcp_sync_mss(struct sock *sk, u32 pmtu)。关于TCP的MSS与MTU相关的内容很多:
1 | 1274 /* This function synchronize snd mss to current pmtu/exthdr set. |
处理TCP MSS
未看懂。
UDP流程
__udp4_lib_err(struct sk_buff skb, u32 info, struct udp_table udptable)
ipv4_sk_update_pmtu(struct sk_buff skb, struct sock sk, u32 mtu),也是基于路由更新MTU。
__ip_rt_update_pmtu
ping报文的处理也是类似,见ping_err。
Linux内核对DF的默认处理
经阅读代码,发现控制DF标志的地方主要有两处:
- inet_sock.pmtudisc
- sk_buff.local_df
以关键字pmtudisc和local_df做全局字符串搜索,发现对pmtudisc和local_df有赋值的地方很少。如下各小节分析。
另附:IP MTU DISCOVER相关宏1
2
3
4
590 /* IP_MTU_DISCOVER values */
91 #define IP_PMTUDISC_DONT 0 /* Never send DF frames */
92 #define IP_PMTUDISC_WANT 1 /* Use per route hints */
93 #define IP_PMTUDISC_DO 2 /* Always DF */
94 #define IP_PMTUDISC_PROBE 3 /* Ignore dst pmtu */
IP报文发送出口
源代码:ip_queue_xmit和__ip_make_skb,后者用于UDP。
1 | 382 if (ip_dont_fragment(sk, &rt->dst) && !skb->local_df) |
AF_INET协议族
inet_create,依赖ipv4_config
配置。该配置体现在/proc/sys/net/ipv4/ip_no_pmtu_disc
,目前Ubuntu 14.04上默认ip_no_pmtu_disc=0
,即由每个路由确定。
1 | 374 if (ipv4_config.no_pmtu_disc) |
系统配置
可用sysctl
命令配置,或通过/etc/sysctl.conf
配置文件配置ip_no_pmtu_disc
值,最终体现在/proc/sys/net/ipv4/ip_no_pmtu_disc
。源码链接。
ICMP报文
默认创建ICMP sk时,不发送DF报文。icmp_sk_init(struct net *net)。
PS:ICMP报文不超过576字节的限制在icmp_send接口中有体现。