解决一个奇怪的网络连接错误

解决一个奇怪的网络连接错误
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等概念,比如下图:

iptables Overview
iptables Overview

(来自链接
更详细的介绍可以参考[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