我已经尝试追踪这个问题好几天了。我仍然不知道发生了什么,或者如何正确修复它,但我有一个解决方法。
问题摘要
在我的内部网络的任何机器上观看 Netflix 时,通过充当 nat 路由器和 adsl 的 linux 机器访问互联网,Netflix 都会停顿。在连接到互联网的 linux 机器上直接观看时,Netflix 运行正常。
细节
我的 Linux 服务器机箱“chimp”通过一个网卡 (eth0) 连接到房屋内部网络,并通过另一个网卡 (eth3) 连接到 adsl 调制解调器。它正在运行 pppoe 到 adsl 调制解调器,互联网链接工作正常,下载速度为 15Mb/s,上传速度为 1Mb/s。Chimp 正在运行 debian jessie 8.6.0(内核 3.16.0-4-amd64),尽管在我将其升级为调查此问题的一部分之前,它是 debian wheezy 7.1.0。
家庭网络上的其他计算机通过 chimp 上的 iptables 规则访问互联网,这些规则使用伪装(我也尝试了 snat)从 eth0 转发到 ppp0。
在 chimp 上观看 Netflix 本身没有问题。在网络上的任何计算机上观看 BBC iplayer(我在英国)都没有问题。在 chimp 以外的任何计算机上观看 Netflix 都会卡住,尽管我相信它在 2016 年 11 月初左右可以正常工作。出现问题的计算机包括几台 Mac(Safari)和另一台 Debian Jessie Box(Chrome)。
查看“/sbin/ifconfig ppp0”中的“RX 字节”数字显示它正在下载几秒钟,然后几乎停止了。
我在互联网上发现一些东西,说我的 iptables 规则应该丢弃 conntrack 标记为“无效”的数据包。我添加了丢弃无效数据包的规则,但这并没有改善情况。我也尝试了日志记录,我发现我从 Netflix 收到了一堆这样的数据包,但只有在使用 Chimp 以外的机器观看时才会出现这种情况。
我尝试了“echo 255 >/proc/sys/net/netfilter/nf_conntrack_log_invalid”,发现无效数据包是由于 TCP 校验和错误造成的:
Dec 30 20:16:40 chimp kernel: [185803.594182] nf_ct_tcp: bad TCP checksum IN= OUT= SRC=78.146.119.61 DST=<my_public_ip_address> LEN=1500 TOS=0x00 PREC=0x00 TTL=59 ID=0 PROTO=TCP SPT=443 DPT=56711 SEQ=92431783 ACK=3940029300 WINDOW=2050 RES=0x00 ACK URGP=0 OPT (0101080AC67B959B27E48AC7)
Dec 30 20:16:40 chimp kernel: [185803.594200] input invalid: IN=ppp0 OUT= MAC= SRC=78.146.119.61 DST=<my_public_ip_address> LEN=1500 TOS=0x00 PREC=0x00 TTL=59 ID=0 PROTO=TCP SPT=443 DPT=56711 WINDOW=2050 RES=0x00 ACK URGP=0
(“输入无效”前缀表明它来自我的 iptables 规则,用于在丢弃无效数据包之前在输入链中记录它。)
我尝试使用“sysctl -w net.netfilter.nf_conntrack_checksum=0”禁用 conntrack 中的校验和检查。这让上述日志行停止,但并没有解决问题。
所以我最好的猜测是:
- 我向 Netflix 流发送的上游出现了问题,因此收到 TCP 校验和错误。
- 当在 chimp 上观看 Netflix 时,一个错误的数据包到达 chimp 的 tcp 层,它能够恢复,也许是通过再次请求数据包。
- 在另一台机器上观察时,即使禁用了 tcp 校验和检查,conntrack 也无法将数据包与连接关联起来,因此它永远不会到达客户端机器的 tcp 层。客户端的 tcp 层会以更激烈的方式恢复,可能是超时时间更长或整个连接出错。
最后才是真正的问题
对于比我更了解 tcp、conntrack 和 nat 的人来说,这有意义吗?还是我可能做错了什么?
解决方法
我已经有点精疲力竭了,无法再调查这个问题。但也许这个解决方法对遇到同样问题的人有用。
解决方法是使用在 chimp(连接到互联网的机器)上运行的 http/https 代理设置整个房屋网络。我使用 chimp 上的 wpad.dat 自动配置其他机器,并将其设置为仅代理 *.netflix.com 和 *.nflxvideo.net。
每台客户端机器都需要进行一些配置才能使用代理自动发现,只需在 mac 和 gnome 上的网络设置中选中一个复选框即可(我猜 windows 上也是一样)。
iptables -L -xvn 的输出
Chain INPUT (policy DROP 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 LOG all -- * * 0.0.0.0/0 0.0.0.0/0 state INVALID LOG flags 0 level 4 prefix "input invalid: "
0 0 DROP all -- * * 0.0.0.0/0 0.0.0.0/0 state INVALID
347 19177 ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0
259 33840 ACCEPT all -- eth+ * 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT udp -- * * 0.0.0.0/0 0.0.0.0/0 udp dpts:1024:5999
0 0 ACCEPT udp -- * * 0.0.0.0/0 0.0.0.0/0 udp dpts:6010:65535
0 0 ACCEPT udp -- * * 0.0.0.0/0 0.0.0.0/0 udp dpt:123
0 0 ACCEPT udp -- * * 0.0.0.0/0 0.0.0.0/0 udp dpt:500
0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:25
0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:143
0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:993
0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80
0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22
0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:443
0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:8443
0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp flags:!0x17/0x02
0 0 ACCEPT 47 -- * * 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT esp -- * * 0.0.0.0/0 0.0.0.0/0
0 0 DROP all -- * * 0.0.0.0/0 0.0.0.0/0
Chain FORWARD (policy DROP 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0
0 0 LOG all -- * * 0.0.0.0/0 0.0.0.0/0 state INVALID LOG flags 0 level 4 prefix "forward invalid: "
0 0 DROP all -- * * 0.0.0.0/0 0.0.0.0/0 state INVALID
0 0 ACCEPT tcp -- eth0 ppp+ 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp flags:!0x17/0x02
0 0 ACCEPT udp -- eth0 * 0.0.0.0/0 0.0.0.0/0 udp dpt:53
0 0 ACCEPT udp -- * eth0 0.0.0.0/0 0.0.0.0/0 udp spt:53
0 0 DROP all -- * * 0.0.0.0/0 0.0.0.0/0
Chain OUTPUT (policy ACCEPT 639 packets, 103120 bytes)
pkts bytes target prot opt in out source destination
iptables-save 的输出
# Generated by iptables-save v1.4.21 on Mon Jan 2 13:24:22 2017
*nat
:PREROUTING ACCEPT [115:43215]
:INPUT ACCEPT [43:4146]
:OUTPUT ACCEPT [2:606]
:POSTROUTING ACCEPT [2:606]
-A POSTROUTING -o ppp+ -j MASQUERADE
COMMIT
# Completed on Mon Jan 2 13:24:22 2017
# Generated by iptables-save v1.4.21 on Mon Jan 2 13:24:22 2017
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [692:108312]
-A INPUT -m state --state INVALID -j LOG --log-prefix "input invalid: "
-A INPUT -m state --state INVALID -j DROP
-A INPUT -i lo -j ACCEPT
-A INPUT -i eth+ -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -p udp -m udp --dport 1024:5999 -j ACCEPT
-A INPUT -p udp -m udp --dport 6010:65535 -j ACCEPT
-A INPUT -p udp -m udp --dport 123 -j ACCEPT
-A INPUT -p udp -m udp --dport 500 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 25 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 143 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 993 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 8443 -j ACCEPT
-A INPUT -p tcp -m tcp ! --tcp-flags FIN,SYN,RST,ACK SYN -j ACCEPT
-A INPUT -p gre -j ACCEPT
-A INPUT -p esp -j ACCEPT
-A INPUT -j DROP
-A FORWARD -i lo -j ACCEPT
-A FORWARD -m state --state INVALID -j LOG --log-prefix "forward invalid: "
-A FORWARD -m state --state INVALID -j DROP
-A FORWARD -i eth0 -o ppp+ -p tcp -j ACCEPT
-A FORWARD -p tcp -m tcp ! --tcp-flags FIN,SYN,RST,ACK SYN -j ACCEPT
-A FORWARD -i eth0 -p udp -m udp --dport 53 -j ACCEPT
-A FORWARD -o eth0 -p udp -m udp --sport 53 -j ACCEPT
-A FORWARD -j DROP
COMMIT
# Completed on Mon Jan 2 13:24:22 2017