解决一个奇怪的网络连接错误
Solve a strange network problem
我的服务器bunny最近出现了一个奇怪的网络连接错误:只有台式机和服务器能连接到bunny,笔记本就连不上。
假设网络IP如下:
A. 服务器bunny:198.215.54.48 10G 网络
B. 服务器: 198.215.54.5 10G 网络
C. 服务器bronco: 129.112.7.169 1G 网络
D. 台式机:129.112.185.246 局域网
E. 笔记本:172.17.157.121 无线网
现在的问题是可以从B, C, D 连接到A,但不能从E连接到A。
解决思路如下:
1. 怀疑E->A的路由有问题
通过traceroute,发现E到A的最后一跳是*,状态是Host unavailable.
但进一步发现E->B,A和B在同一个子网,因此E->A的路由不大可能有问题
2. 检查A的防火墙设置
先关闭所有防火墙,用:
ufw disable
但E仍然不能连A
3. 检查A的路由
> route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 198.215.54.254 0.0.0.0 UG 0 0 0 em1
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 em4
198.215.54.0 0.0.0.0 255.255.255.0 U 0 0 0 em1
> ip route --list
ip rule list
0: from all lookup local
32766: from all lookup main
32767: from all lookup default
仔细检查发现172.17.0.0这个路由有问题:因为E在172.17.0.0网络,从E发出的ICMP包到达A,而从A返回的ICMP包没有通过em1接口而是docker0发出,这样E->A就显示了Host unavailable。
值得注意的是在路由IP包时,它的终点并不是第一个可以匹配的路由,而是所有路由里的第一个匹配最好的路由。
原文:Any entry whose first field matches the destination IP address completely(a host) or partially (a network) would signal the IP address of the next router. (link)
找到原因后,我们可以让docker0的网络IP地址避开172.17.0.0子网,这就可以解决E->A的连接问题。
这个解决方案里的重要命令是 (参考链接)
ip link set dev docker0 down
ip addr del 172.17.42.1/16 dev docker0 # 172.17.42.1是docker0的ip
ip addr add 10.0.0.10/24 dev docker0
ip link set dev docker0 up
ip addr show docker0 #检查docker0的ip
此外要在/etc/default/docker里加上DOCKER_OPTS (–bip参数见Docker文档Customize the docker0 bridge)。这样docker0的IP不会在服务器重启后改变。
DOCKER_OPTS="--bip=10.0.0.10/24"
题外话
1. 为什么docker和无线网都用172.17.0.0子网?
根据RFC 1918 3. Private Address Space, 保留IP除了常见的192.168.0.0外, 还有:
10.0.0.0 - 10.255.255.255 (10/8 prefix) 172.16.0.0 - 172.31.255.255 (172.16/12 prefix) 192.168.0.0 - 192.168.255.255 (192.168/16 prefix)
因为172.17.0.0是保留地址,正巧docker和无线网同时都使用了这个子网,因此造成了我遇到的网络问题。
2. 怎么监测服务器是否接收到IP包?
可以使用iptables
例如参考 http://askubuntu.com/questions/348439/iptables-log-file-and-how-change-it, 记录192.168.11.0/24发来的包:
iptables -A INPUT -s 192.168.11.0/24 -j LOG --log-prefix='[netfilter] '
可以在/var/log/kern.log 找含有“[netfilter]”的日志。
如果想监测ICMP包,可以用:
iptables -A INPUT -p icmp -j LOG --log-prefix='[netfilter] '
其他常用命令(参考这个链接)包括:
sudo iptables -L # 打印iptables内容
sudo iptables -L INPUT -v # 检查INPUT表收到了多少数据
sudo iptables -Z # 清零
sudo iptables -Z INPUT # 清零INPUT表数据清零
sudo iptables -A INPUT -p icmp -j LOG --log-prefix='[netfilter] ' # 记录ICMP包
sudo iptables -D INPUT -p icmp -j LOG --log-prefix='[netfilter] ' # 删除防火墙规则(记录ICMP包)
sudo iptables -F #防火墙规则生效
以下命令重置防火墙
sudo iptables -P INPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -P OUTPUT ACCEPT
sudo iptables -t nat -F
sudo iptables -t mangle -F
sudo iptables -F
sudo iptables -X
iptable是个复杂的防火墙,它使用了table,chain等概念,比如下图:
(来自链接)
更详细的介绍可以参考[2]。
除iptables外,Ubuntu还提供了UFW,这个工具建立在iptables上,提供比iptables更简易的配置。比如:
ufw enable # 启用防火墙
ufw disable # 关闭防火墙
sudo ufw status # 显示防火墙设置
ufw deny 80/tcp # 封锁tcp的80端口
sudo ufw delete deny 80/tcp # 删除上一条(封锁tcp的80端口)
更多的信息可以参考Ubuntu UFW文档 (链接)。
参考
[1]7 Linux Route Command Examples (How to Add Route in Linux)
[2]Linux Firewall Tutorial: IPTables Tables, Chains, Rules Fundamentals