分析访问谷歌却返回百度的原因

我在某个办公网络中没挂代理的情况下访问 www.google.com,结果会被跳转到百度首页,如下是 curl 结果:

$ curl www.google.com -v
* Rebuilt URL to: www.google.com/
*   Trying 14.215.177.38...
* Connected to www.google.com (14.215.177.38) port 80 (#0)
> GET / HTTP/1.1
> Host: www.google.com
> User-Agent: curl/7.47.0
> Accept: */*
>
< HTTP/1.1 302 Found
< Location: https://www.baidu.com/error.html
< Server: bfe
< Date: Sun, 07 Oct 2018 09:44:17 GMT
< Content-Length: 0
< Content-Type: text/plain; charset=utf-8
<
* Connection #0 to host www.google.com left intact

从 curl 输出可看出:

1、www.google.com 解析出来的 IP 为 14.215.177.38,这不是 Google 的 IP;

2、服务端被 302 跳转到了百度的页面。

所以我怀疑是内部 DNS 服务器的问题,于是本地抓 DNS 的数据包:

tshark -w dns.pcap -f 'tcp port 53 || udp port 53' -i enp0s25

关键包如下:

3	2.462437548	10.8.250.222	10.8.2.1	DNS	74	Standard query 0xc451 A www.google.com
4	2.462449011	10.8.250.222	10.8.2.1	DNS	74	Standard query 0x1e72 AAAA www.google.com
5	2.462589097	10.8.2.1	10.8.250.222	DNS	102	Standard query response 0xc451 A www.google.com A 14.215.177.38 [ETHERNET FRAME CHECK SEQUENCE INCORRECT]
6	2.462619342	10.8.2.1	10.8.250.222	DNS	102	Standard query response 0x1e72 AAAA www.google.com A 14.215.177.38 [ETHERNET FRAME CHECK SEQUENCE INCORRECT]

3号、4号包是我机器发出的 DNS 查询请求;5号、6号包是 DNS 的响应,从5号、6号包的摘要里可以看到有“[ETHERNET FRAME CHECK SEQUENCE INCORRECT]”字样,意味着包的 FCS 校验失败。

标准的 Ethernet II 帧的最后4字节是校验码,一般来说网卡驱动会去校验 FCS,对于校验失败的帧则会被直接丢弃,而不会被传到协议栈中;正常的帧传到协议栈中后,会去掉 FCS 字段的。所以,理论上如果这条帧的 FCS 校验失败,是会被丢弃的,而不该被 Wireshark 抓到。

我选取校验失败的5号包,如下:

Frame 5: 102 bytes on wire (816 bits), 102 bytes captured (816 bits) on interface 0
Ethernet II, Src: Hangzhou_72:41:82 (60:0b:03:72:41:82), Dst: Dell_42:7c:28 (78:45:c4:42:7c:28)
    Destination: Dell_42:7c:28 (78:45:c4:42:7c:28)
    Source: Hangzhou_72:41:82 (60:0b:03:72:41:82)
    Type: IPv4 (0x0800)
    Trailer: 8c709a4284f86b10
    Frame check sequence: 0x18bf0f4a incorrect, should be 0xe10c60dc
        [Expert Info (Error/Checksum): Bad checksum [should be 0xe10c60dc]]
            [Bad checksum [should be 0xe10c60dc]]
            [Severity level: Error]
            [Group: Checksum]
    [FCS Status: Bad]
Internet Protocol Version 4, Src: 10.8.2.1, Dst: 10.8.250.222
User Datagram Protocol, Src Port: 53, Dst Port: 52466
    Source Port: 53
    Destination Port: 52466
    Length: 56
    [Checksum: [missing]]
    [Checksum Status: Not present]
    [Stream index: 1]
Domain Name System (response)
    Transaction ID: 0xc451
    Flags: 0x8180 Standard query response, No error
    Questions: 1
    Answer RRs: 1
    Authority RRs: 0
    Additional RRs: 0
    Queries
    Answers
    [Request In: 3]
    [Time: 0.000151549 seconds]

包的16进制原始数据如下:

0000   78 45 c4 42 7c 28 60 0b 03 72 41 82 08 00 45 00
0010   00 4c 58 26 40 00 7f 11 92 8b 0a 08 02 01 0a 08
0020   fa de 00 35 cc f2 00 38 00 00 c4 51 81 80 00 01
0030   00 01 00 00 00 00 03 77 77 77 06 67 6f 6f 67 6c
0040   65 03 63 6f 6d 00 00 01 00 01 c0 0c 00 01 00 01
0050   00 00 0e 10 00 04 0e d7 b1 26 8c 70 9a 42 84 f8
0060   6b 10 18 bf 0f 4a

仔细看这句提示“Frame check sequence: 0x18bf0f4a incorrect”,意思是 Wireshark 认为 0x18bf0f4a 是 FCS 字段的数据,是错误的 FCS 值;其实对照16进制的内容可以看出这是最后的4个字节。但是按之前说的理论,FCS 字段已在驱动层就被丢弃了,最后的字段当属于传输的数据内容,为何 Wireshark 会把它当作了 FCS 了呢?我们先计算下包应有的大小:

以太网头:14字节
IP 头:20字节
UDP 头 + DNS 包:56字节

14 + 20 + 56 = 90,包的总大小应当是90字节,但根据提示(102 bytes captured),这个帧总共有102字节,比预计的多出了12个字节,由于 Ethernet II 帧的最后4字节是 FCS,所以那多出的12字节中,最后4字节被 Wireshark 误认为是 FCS,这也就是理论上不会捕捉到 FCS 错误的包,却被 Wireshark 捕获到的原因。

其实说白了,帧数据错乱这种现象的根本原因就是 DNS 服务器返回给客户端的数据在途中被某个设备强行篡改了,导致的数据畸形,之前我还误认为是故意在 DNS 服务器上加的 google.com 的解析。