Problem:
- 1 client 1 server, connected with non-block tcp socket. Linux 2.6.*+.
- Client 写入大概 3k 数据到 socket。
- Write()正确返回实际写入字节数。
- Server 什么也收不到。
Causes:
- 发送端 MTU稍大于路由器上的MTU设置
- 通知发送端需要拆包的ICMP在某处被杀掉了
- 发送端不停的重发包
设置了DF标志的ip包当遇到路由器的MTU比包小的时候,不会被路由器拆包。而路由器发送icmp消息到发送端,通知它应该拆包。
但icmp消息被防火墙拦截下来。
环境和现象:
这个例子中,MTU在client和server都是1500.
dump出来的包如下:
客户端看到的:
发送了2个包,后1个包成功,第1个过大而不停的被发送:
17:23:06.933574 IP (tos 0×0, ttl 64, id 57558, offset 0, flags [DF], proto: TCP (6), length: 1500) 10.54.40.43.43145 > 10.29.14.74.http: ., cksum 0×5096 (incorrect (-> 0×5c4e), 0:1448(1448) ack 1 win 46
17:23:06.933580 IP (tos 0×0, ttl 64, id 57559, offset 0, flags [DF], proto: TCP (6), length: 730) 10.54.40.43.43145 > 10.29.14.74.http: P, cksum 0×4d94 (incorrect (-> 0×3933), 1448:2126(678) ack 1 win 46
17:23:07.167049 IP (tos 0×0, ttl 64, id 57560, offset 0, flags [DF], proto: TCP (6), length: 1500) 10.54.40.43.43145 > 10.29.14.74.http: ., cksum 0×5096 (incorrect (-> 0×5b5b), 0:1448(1448) ack 1 win 46
17:23:07.634922 IP (tos 0×0, ttl 64, id 57561, offset 0, flags [DF], proto: TCP (6), length: 1500) 10.54.40.43.43145 > 10.29.14.74.http: ., cksum 0×5096 (incorrect (-> 0×5987), 0:1448(1448) ack 1 win 46
接受端看到的:
只有730大小的包接受成功
17:23:08.605622 IP (tos 0×0, ttl 59, id 57559, offset 0, flags [DF], proto: TCP (6), length: 730) 202.108.3.204.43145 > 10.29.14.74.http: P, cksum 0×9d5b (correct), 1448:2126(678) ack 1 win 46
解决方法:
调整发送端机器的配置:(任选1个)
在网络层上:
Decrease mtu on network adapter:
ifconfig eth* mtu 1400
操作系统配置:
Clear the default ‘MTU discovery’ flag with sysctl:
net.ipv4.ip_no_pmtu_disc = 1
或在应用程序里:
Set socket option ‘IP_MTU_DISCOVER’ with setsockopt(2) to clear ‘DF’ flag of IP package.
Reference:
- DF flag of IP package Header
- Internet Control Message Protocol
- IP fragmentation
- MTU or Maximum transmission unit
- IP programming
- Path MTU Discovery
- sysctl
Thanks:
esx kobe steve
来自:http://blog.developers.api.sina.com.cn/?p=672
原文:http://drdr-xp-tech.blogspot.com/2009/04/black-hole-socket-problem.html